001package net.tnemc.item.paper; 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.paper.platform.PaperItemPlatform; 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.entity.Item; 033import org.bukkit.entity.Player; 034import org.bukkit.inventory.Inventory; 035import org.bukkit.inventory.ItemStack; 036import org.bukkit.inventory.meta.BlockStateMeta; 037 038import java.util.ArrayList; 039import java.util.Collection; 040import java.util.Map; 041import java.util.Optional; 042import java.util.UUID; 043 044/** 045 * Represents a Bukkit implementation of the {@link CalculationsProvider}. 046 * 047 * @author creatorfromhell 048 * @since 0.1.5.0 049 */ 050public class PaperCalculationsProvider implements CalculationsProvider<PaperItemStack, ItemStack, Inventory> { 051 052 /** 053 * Removes items from a collection based on certain criteria. 054 * 055 * @param left The collection of items from which to remove items. 056 * @param player The UUID of the player associated with the removal operation. 057 * @param setOwner Indicates whether to set the owner of the removed items.(supports spigot/paper 1.16.5+) 058 * 059 * @return True if the removal operation was successful, false otherwise. 060 */ 061 @Override 062 public boolean drop(final Collection<PaperItemStack> left, final UUID player, final boolean setOwner) { 063 064 final Player playerObj = Bukkit.getPlayer(player); 065 if(playerObj == null) { 066 return false; 067 } 068 069 for(final PaperItemStack stack : left) { 070 071 if(setOwner && VersionUtil.isOneSixteen(PaperItemPlatform.instance().version())) { 072 073 final Item it = playerObj.getWorld().dropItemNaturally(playerObj.getLocation(), stack.provider().locale(stack)); 074 it.setOwner(player); 075 continue; 076 } 077 078 playerObj.getWorld().dropItemNaturally(playerObj.getLocation(), stack.provider().locale(stack)); 079 } 080 return false; 081 } 082 083 /** 084 * Removes all items that are equal to the stack from an inventory. 085 * 086 * @param stack The stack to compare to for removal from the inventory. 087 * @param inventory The inventory to remove the items from. 088 */ 089 @Override 090 public int removeAll(final PaperItemStack stack, final Inventory inventory) { 091 092 final ItemStack compare = stack.provider().locale(stack).clone(); 093 compare.setAmount(1); 094 095 int amount = 0; 096 final PaperItemStack comp = new PaperItemStack().of(compare); 097 098 for(int i = 0; i < inventory.getStorageContents().length; i++) { 099 100 final ItemStack item = inventory.getItem(i); 101 if(item == null) { 102 continue; 103 } 104 105 if(itemsEqual(comp, item)) { 106 amount += item.getAmount(); 107 inventory.setItem(i, null); 108 continue; 109 } 110 111 if(item.getItemMeta() instanceof final BlockStateMeta meta && meta.getBlockState() instanceof final Container container) { 112 113 final Inventory containerInventory = container.getInventory(); 114 for(int ci = 0; ci < containerInventory.getSize(); ci++) { 115 116 final ItemStack containerStack = inventory.getItem(ci); 117 if(containerStack == null) { 118 119 continue; 120 } 121 122 if(itemsEqual(comp, containerStack)) { 123 124 amount += item.getAmount(); 125 inventory.setItem(ci, null); 126 } 127 } 128 container.update(true); 129 meta.setBlockState(container); 130 item.setItemMeta(meta); 131 inventory.setItem(i, item); 132 } 133 } 134 return amount; 135 } 136 137 /** 138 * Returns a count of items equal to the specific stack in an inventory. 139 * 140 * @param stack The stack to get a count of. 141 * @param inventory The inventory to check. 142 * 143 * @return The total count of items in the inventory. 144 */ 145 @Override 146 public int count(final PaperItemStack stack, final Inventory inventory) { 147 148 final ItemStack compare = stack.provider().locale(stack).clone(); 149 compare.setAmount(1); 150 151 //TODO: make this more efficient 152 final PaperItemStack comp = new PaperItemStack().of(compare); 153 int amount = 0; 154 155 for(final ItemStack item : inventory.getStorageContents()) { 156 157 if(item == null) { 158 continue; 159 } 160 161 if(itemsEqual(comp, item)) { 162 163 amount += item.getAmount(); 164 } 165 166 if(item.getItemMeta() instanceof final BlockStateMeta meta && meta.getBlockState() instanceof final Container container) { 167 168 final Inventory containerInventory = container.getInventory(); 169 if(containerInventory.isEmpty()) { 170 171 continue; 172 } 173 174 for(int ci = 0; ci < containerInventory.getSize(); ci++) { 175 176 final ItemStack containerStack = containerInventory.getItem(ci); 177 if(containerStack == null) { 178 179 continue; 180 } 181 182 if(itemsEqual(comp, containerStack)) { 183 184 amount += containerStack.getAmount(); 185 } 186 } 187 } 188 } 189 return amount; 190 } 191 192 /** 193 * Takes a collection of items from an inventory. 194 * 195 * @param items The collection of items to remove. 196 * @param inventory The inventory to remove the items from. 197 */ 198 @Override 199 public void takeItems(final Collection<PaperItemStack> items, final Inventory inventory) { 200 201 items.forEach(itemStack->removeItem(itemStack, inventory)); 202 } 203 204 /** 205 * Adds a collection of net.tnemc.item stacks to an inventory, dropping them on the ground if it's 206 * a player inventory and overflow exists. 207 * 208 * @param items The collection of items to add to the inventory. 209 * @param inventory The inventory to add the collection of items to. 210 */ 211 @Override 212 public Collection<PaperItemStack> giveItems(final Collection<PaperItemStack> items, final Inventory inventory) { 213 214 final Collection<PaperItemStack> leftOver = new ArrayList<>(); 215 216 for(final PaperItemStack item : items) { 217 218 if(item == null) { 219 continue; 220 } 221 222 final Map<Integer, ItemStack> left = inventory.addItem(item.provider().locale(item)); 223 if(left.isEmpty()) { 224 continue; 225 } 226 227 for(final Map.Entry<Integer, ItemStack> entry : left.entrySet()) { 228 final ItemStack i = entry.getValue(); 229 230 if(i == null || i.getType() == Material.AIR) { 231 continue; 232 } 233 234 leftOver.add(new PaperItemStack().of(i)); 235 } 236 } 237 return leftOver; 238 } 239 240 /** 241 * Removes an ItemStack with a specific amount from an inventory. 242 * 243 * @param stack The stack, with the correct amount, to remove. 244 * @param inventory The inventory to return the net.tnemc.item stack from. 245 * 246 * @return The remaining amount of items to remove. 247 */ 248 @Override 249 public int removeItem(final PaperItemStack stack, final Inventory inventory) { 250 251 int left = stack.provider().locale(stack).clone().getAmount(); 252 253 final ItemStack compare = stack.provider().locale(stack).clone(); 254 compare.setAmount(1); 255 256 //TODO: improve this 257 258 final PaperItemStack comp = new PaperItemStack().of(compare); 259 final ItemProvider<ItemStack> provider = stack.provider(); 260 261 for(int i = 0; i < inventory.getStorageContents().length; i++) { 262 if(left <= 0) break; 263 264 final ItemStack item = inventory.getItem(i); 265 266 if(item == null) continue; 267 268 if(provider.similar(stack, item)) { 269 270 if(item.getAmount() > left) { 271 item.setAmount(item.getAmount() - left); 272 inventory.setItem(i, item); 273 left = 0; 274 break; 275 } 276 277 if(item.getAmount() == left) { 278 inventory.setItem(i, null); 279 left = 0; 280 break; 281 } 282 283 left -= item.getAmount(); 284 inventory.setItem(i, null); 285 } 286 287 if(item.getItemMeta() instanceof final BlockStateMeta meta && meta.getBlockState() instanceof final Container container) { 288 289 final Inventory containerInventory = container.getInventory(); 290 for(int ci = 0; ci < containerInventory.getSize(); ci++) { 291 292 if(left <= 0) break; 293 294 final ItemStack containerStack = inventory.getItem(ci); 295 if(containerStack == null) { 296 297 continue; 298 } 299 300 if(!itemsEqual(comp, containerStack)) { 301 continue; 302 } 303 304 if(item.getAmount() > left) { 305 306 item.setAmount(item.getAmount() - left); 307 containerInventory.setItem(ci, item); 308 left = 0; 309 break; 310 } 311 312 if(item.getAmount() == left) { 313 containerInventory.setItem(ci, null); 314 left = 0; 315 break; 316 } 317 318 left -= item.getAmount(); 319 containerInventory.setItem(ci, null); 320 } 321 container.update(true); 322 meta.setBlockState(container); 323 item.setItemMeta(meta); 324 inventory.setItem(i, item); 325 } 326 } 327 return left; 328 } 329 330 /** 331 * Used to locate an inventory for a UUID identifier. 332 * 333 * @param identifier The identifier to use for the search. 334 * @param type The inventory type to return. 335 * 336 * @return An optional containing the inventory if it works, otherwise false. 337 */ 338 @Override 339 public Optional<Inventory> inventory(final UUID identifier, final InventoryType type) { 340 341 final OfflinePlayer player = Bukkit.getOfflinePlayer(identifier); 342 if(player.isOnline() && player.getPlayer() != null) { 343 344 if(type.equals(InventoryType.ENDER_CHEST)) { 345 return Optional.of(player.getPlayer().getEnderChest()); 346 } else { 347 return Optional.of(player.getPlayer().getInventory()); 348 } 349 } 350 return Optional.empty(); 351 } 352}