001package net.tnemc.item.bukkit; 002 003/* 004 * The New Item Library Minecraft Server Plugin 005 * 006 * Copyright (C) 2024 Daniel "creatorfromhell" Vidmar 007 * 008 * This program is free software; you can redistribute it and/or 009 * modify it under the terms of the GNU Lesser General Public 010 * License as published by the Free Software Foundation; either 011 * version 3 of the License, or (at your option) any later version. 012 * 013 * This program is distributed in the hope that it will be useful, 014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 016 * Lesser General Public License for more details. 017 * 018 * You should have received a copy of the GNU Lesser General Public License 019 * along with this program; if not, write to the Free Software Foundation, 020 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 021 */ 022 023import net.tnemc.item.InventoryType; 024import net.tnemc.item.bukkit.platform.BukkitItemPlatform; 025import net.tnemc.item.providers.CalculationsProvider; 026import net.tnemc.item.providers.ItemProvider; 027import net.tnemc.item.providers.VersionUtil; 028import org.bukkit.Bukkit; 029import org.bukkit.Material; 030import org.bukkit.OfflinePlayer; 031import org.bukkit.block.Container; 032import org.bukkit.block.ShulkerBox; 033import org.bukkit.entity.Item; 034import org.bukkit.entity.Player; 035import org.bukkit.inventory.Inventory; 036import org.bukkit.inventory.ItemStack; 037import org.bukkit.inventory.meta.BlockStateMeta; 038import org.bukkit.inventory.meta.BundleMeta; 039 040import java.util.ArrayList; 041import java.util.Collection; 042import java.util.Iterator; 043import java.util.List; 044import java.util.Map; 045import java.util.Optional; 046import java.util.UUID; 047 048/** 049 * Represents a Bukkit implementation of the {@link CalculationsProvider}. 050 * 051 * @author creatorfromhell 052 * @since 0.1.5.0 053 */ 054public class BukkitCalculationsProvider implements CalculationsProvider<BukkitItemStack, ItemStack, Inventory> { 055 056 /** 057 * Removes items from a collection based on certain criteria. 058 * 059 * @param left The collection of items from which to remove items. 060 * @param player The UUID of the player associated with the removal operation. 061 * @param setOwner Indicates whether to set the owner of the removed items.(supports spigot/paper 1.16.5+) 062 * 063 * @return True if the removal operation was successful, false otherwise. 064 */ 065 @Override 066 public boolean drop(final Collection<BukkitItemStack> left, final UUID player, final boolean setOwner) { 067 068 final Player playerObj = Bukkit.getPlayer(player); 069 if(playerObj == null) { 070 return false; 071 } 072 073 for(final BukkitItemStack stack : left) { 074 075 if(setOwner && VersionUtil.isOneSixteen(BukkitItemPlatform.instance().version())) { 076 077 final Item it = playerObj.getWorld().dropItemNaturally(playerObj.getLocation(), stack.provider().locale(stack)); 078 it.setOwner(player); 079 continue; 080 } 081 082 playerObj.getWorld().dropItemNaturally(playerObj.getLocation(), stack.provider().locale(stack)); 083 } 084 return false; 085 } 086 087 /** 088 * Removes all items that are equal to the stack from an inventory. 089 * 090 * @param stack The stack to compare to for removal from the inventory. 091 * @param inventory The inventory to remove the items from. 092 */ 093 @Override 094 public int removeAll(final BukkitItemStack stack, final Inventory inventory) { 095 096 final ItemStack compare = stack.provider().locale(stack).clone(); 097 compare.setAmount(1); 098 099 int amount = 0; 100 //final BukkitItemStack comp = new BukkitItemStack().of(compare); 101 final ItemProvider<ItemStack> provider = stack.provider(); 102 103 for(int i = 0; i < inventory.getStorageContents().length; i++) { 104 105 final ItemStack item = inventory.getItem(i); 106 if(item == null) { 107 continue; 108 } 109 110 if(provider.similar(stack, item)) { 111 amount += item.getAmount(); 112 inventory.setItem(i, null); 113 continue; 114 } 115 116 if(item.getItemMeta() instanceof final BlockStateMeta meta && meta.getBlockState() instanceof final ShulkerBox shulker) { 117 118 System.out.println("Entering container"); 119 120 final Inventory shulkerInventory = shulker.getInventory(); 121 for(int shulkerSlot = 0; shulkerSlot < shulkerInventory.getStorageContents().length; shulkerSlot++) { 122 123 System.out.println("Slot: " + shulkerSlot); 124 125 final ItemStack shulkerStack = shulkerInventory.getItem(shulkerSlot); 126 if(shulkerStack == null) { 127 128 System.out.println("Stack is null"); 129 continue; 130 } 131 132 if(!provider.similar(stack, shulkerStack)) { 133 System.out.println("Stacks aren't similar"); 134 continue; 135 } 136 137 amount += item.getAmount(); 138 inventory.setItem(shulkerSlot, null); 139 shulkerInventory.setItem(shulkerSlot, null); 140 } 141 142 System.out.println("Leaving container"); 143 shulker.update(true); 144 meta.setBlockState(shulker); 145 item.setItemMeta(meta); 146 inventory.setItem(i, item); 147 } 148 149 if(item.getItemMeta() instanceof final BundleMeta bundle) { 150 151 final List<ItemStack> items = new ArrayList<>(bundle.getItems()); 152 final Iterator<ItemStack> it = items.iterator(); 153 while(it.hasNext()) { 154 155 final ItemStack bundleStack = it.next(); 156 if(bundleStack == null) { 157 158 continue; 159 } 160 161 if(provider.similar(stack, bundleStack)) { 162 163 amount += item.getAmount(); 164 it.remove(); 165 } 166 } 167 168 bundle.setItems(items); 169 item.setItemMeta(bundle); 170 inventory.setItem(i, item); 171 } 172 } 173 return amount; 174 } 175 176 /** 177 * Returns a count of items equal to the specific stack in an inventory. 178 * 179 * @param stack The stack to get a count of. 180 * @param inventory The inventory to check. 181 * 182 * @return The total count of items in the inventory. 183 */ 184 @Override 185 public int count(final BukkitItemStack stack, final Inventory inventory) { 186 187 final ItemStack compare = stack.provider().locale(stack).clone(); 188 compare.setAmount(1); 189 190 //TODO: make this more efficient 191 final ItemProvider<ItemStack> provider = stack.provider(); 192 int amount = 0; 193 194 for(final ItemStack item : inventory.getStorageContents()) { 195 196 if(item == null) { 197 continue; 198 } 199 200 if(provider.similar(stack, item)) { 201 202 amount += item.getAmount(); 203 } 204 205 if(item.getItemMeta() instanceof final BlockStateMeta meta && meta.getBlockState() instanceof final ShulkerBox shulker) { 206 207 final Inventory shulkerInventory = shulker.getInventory(); 208 if(shulkerInventory.isEmpty()) { 209 210 continue; 211 } 212 213 for(int ci = 0; ci < shulkerInventory.getStorageContents().length; ci++) { 214 215 final ItemStack shulkerStack = shulkerInventory.getItem(ci); 216 if(shulkerStack == null) { 217 218 continue; 219 } 220 221 if(provider.similar(stack, shulkerStack)) { 222 223 amount += shulkerStack.getAmount(); 224 } 225 } 226 } 227 228 if(item.getItemMeta() instanceof final BundleMeta bundle) { 229 230 for(final ItemStack bundleItem : bundle.getItems()) { 231 232 if(bundleItem == null) { 233 234 continue; 235 } 236 237 if(provider.similar(stack, bundleItem)) { 238 239 amount += bundleItem.getAmount(); 240 } 241 } 242 } 243 } 244 return amount; 245 } 246 247 /** 248 * Takes a collection of items from an inventory. 249 * 250 * @param items The collection of items to remove. 251 * @param inventory The inventory to remove the items from. 252 */ 253 @Override 254 public void takeItems(final Collection<BukkitItemStack> items, final Inventory inventory) { 255 256 items.forEach(itemStack->removeItem(itemStack, inventory)); 257 } 258 259 /** 260 * Adds a collection of net.tnemc.item stacks to an inventory, dropping them on the ground if it's 261 * a player inventory and overflow exists. 262 * 263 * @param items The collection of items to add to the inventory. 264 * @param inventory The inventory to add the collection of items to. 265 */ 266 @Override 267 public Collection<BukkitItemStack> giveItems(final Collection<BukkitItemStack> items, final Inventory inventory) { 268 269 final Collection<BukkitItemStack> leftOver = new ArrayList<>(); 270 271 for(final BukkitItemStack item : items) { 272 273 if(item == null) { 274 continue; 275 } 276 277 final Map<Integer, ItemStack> left = inventory.addItem(item.provider().locale(item, item.amount())); 278 if(left.isEmpty()) { 279 280 continue; 281 } 282 283 for(final Map.Entry<Integer, ItemStack> entry : left.entrySet()) { 284 final ItemStack i = entry.getValue(); 285 286 if(i == null || i.getType() == Material.AIR) { 287 continue; 288 } 289 290 leftOver.add(new BukkitItemStack().of(i)); 291 } 292 } 293 return leftOver; 294 } 295 296 /** 297 * Removes an ItemStack with a specific amount from an inventory. 298 * 299 * @param stack The stack, with the correct amount, to remove. 300 * @param inventory The inventory to return the net.tnemc.item stack from. 301 * 302 * @return The remaining amount of items to remove. 303 */ 304 @Override 305 public int removeItem(final BukkitItemStack stack, final Inventory inventory) { 306 307 int left = stack.provider().locale(stack).clone().getAmount(); 308 309 final ItemStack compare = stack.provider().locale(stack).clone(); 310 compare.setAmount(1); 311 312 System.out.println("Calc removeItem: Left: " + left); 313 314 //TODO: improve this 315 316 final ItemProvider<ItemStack> provider = stack.provider(); 317 318 for(int i = 0; i < inventory.getStorageContents().length; i++) { 319 if(left <= 0) break; 320 321 final ItemStack item = inventory.getItem(i); 322 323 if(item == null) continue; 324 325 if(provider.similar(stack, item)) { 326 327 if(item.getAmount() > left) { 328 item.setAmount(item.getAmount() - left); 329 inventory.setItem(i, item); 330 left = 0; 331 break; 332 } 333 334 if(item.getAmount() == left) { 335 inventory.setItem(i, null); 336 left = 0; 337 break; 338 } 339 340 left -= item.getAmount(); 341 inventory.setItem(i, null); 342 } 343 344 if(item.getItemMeta() instanceof final BlockStateMeta meta && meta.getBlockState() instanceof final ShulkerBox shulker) { 345 346 System.out.println("Entering container"); 347 348 final Inventory shulkerInventory = shulker.getInventory(); 349 for(int shulkerSlot = 0; shulkerSlot < shulkerInventory.getStorageContents().length; shulkerSlot++) { 350 351 System.out.println("Slot: " + shulkerSlot); 352 if(left <= 0) break; 353 354 final ItemStack shulkerStack = shulkerInventory.getItem(shulkerSlot); 355 if(shulkerStack == null) { 356 357 System.out.println("Stack is null"); 358 continue; 359 } 360 361 if(!provider.similar(stack, shulkerStack)) { 362 System.out.println("Stacks aren't similar"); 363 continue; 364 } 365 366 if(shulkerStack.getAmount() > left) { 367 368 System.out.println("changing stack size"); 369 370 shulkerStack.setAmount(shulkerStack.getAmount() - left); 371 shulkerInventory.setItem(shulkerSlot, shulkerStack); 372 left = 0; 373 break; 374 } 375 376 if(shulkerStack.getAmount() == left) { 377 System.out.println("Removing stack containerStack.getAmount() == left"); 378 shulkerInventory.setItem(shulkerSlot, null); 379 left = 0; 380 break; 381 } 382 383 System.out.println("Removing stack left -= containerStack.getAmount()"); 384 left -= shulkerStack.getAmount(); 385 shulkerInventory.setItem(shulkerSlot, null); 386 } 387 388 System.out.println("Leaving container"); 389 shulker.update(true); 390 meta.setBlockState(shulker); 391 item.setItemMeta(meta); 392 inventory.setItem(i, item); 393 } 394 395 if(item.getItemMeta() instanceof final BundleMeta bundle) { 396 397 final List<ItemStack> items = new ArrayList<>(bundle.getItems()); 398 final Iterator<ItemStack> it = items.iterator(); 399 while(it.hasNext()) { 400 401 final ItemStack bundleStack = it.next(); 402 if(bundleStack == null) { 403 404 System.out.println("Stack is null"); 405 continue; 406 } 407 408 if(!provider.similar(stack, bundleStack)) { 409 System.out.println("Stacks aren't similar"); 410 continue; 411 } 412 413 if(bundleStack.getAmount() > left) { 414 415 System.out.println("changing stack size"); 416 417 bundleStack.setAmount(bundleStack.getAmount() - left); 418 left = 0; 419 break; 420 } 421 422 if(bundleStack.getAmount() == left) { 423 System.out.println("Removing stack containerStack.getAmount() == left"); 424 it.remove(); 425 left = 0; 426 break; 427 } 428 429 System.out.println("Removing stack left -= containerStack.getAmount()"); 430 left -= bundleStack.getAmount(); 431 it.remove(); 432 } 433 434 bundle.setItems(items); 435 item.setItemMeta(bundle); 436 inventory.setItem(i, item); 437 } 438 } 439 return left; 440 } 441 442 /** 443 * Used to locate an inventory for a UUID identifier. 444 * 445 * @param identifier The identifier to use for the search. 446 * @param type The inventory type to return. 447 * 448 * @return An optional containing the inventory if it works, otherwise false. 449 */ 450 @Override 451 public Optional<Inventory> inventory(final UUID identifier, final InventoryType type) { 452 453 final OfflinePlayer player = Bukkit.getOfflinePlayer(identifier); 454 if(player.isOnline() && player.getPlayer() != null) { 455 456 if(type.equals(InventoryType.ENDER_CHEST)) { 457 return Optional.of(player.getPlayer().getEnderChest()); 458 } else { 459 return Optional.of(player.getPlayer().getInventory()); 460 } 461 } 462 return Optional.empty(); 463 } 464}