001package net.tnemc.item.paper.platform; 002/* 003 * The New Item Library 004 * Copyright (C) 2022 - 2025 Daniel "creatorfromhell" Vidmar 005 * 006 * This program is free software; you can redistribute it and/or 007 * modify it under the terms of the GNU Lesser General Public 008 * License as published by the Free Software Foundation; either 009 * version 3 of the License, or (at your option) any later version. 010 * 011 * This program is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 014 * Lesser General Public License for more details. 015 * 016 * You should have received a copy of the GNU Lesser General Public License 017 * along with this program; if not, write to the Free Software Foundation, 018 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 019 */ 020 021import io.papermc.paper.registry.RegistryAccess; 022import io.papermc.paper.registry.RegistryKey; 023import net.tnemc.item.AbstractItemStack; 024import net.tnemc.item.bukkitbase.platform.providers.ItemAdderProvider; 025import net.tnemc.item.bukkitbase.platform.providers.MMOItemProvider; 026import net.tnemc.item.bukkitbase.platform.providers.NexoProvider; 027import net.tnemc.item.bukkitbase.platform.providers.NovaProvider; 028import net.tnemc.item.bukkitbase.platform.providers.OraxenProvider; 029import net.tnemc.item.bukkitbase.platform.providers.SlimefunProvider; 030import net.tnemc.item.paper.PaperCalculationsProvider; 031import net.tnemc.item.paper.PaperItemStack; 032import net.tnemc.item.paper.VanillaProvider; 033import net.tnemc.item.paper.platform.impl.modern.PaperBundleComponent; 034import net.tnemc.item.paper.platform.impl.modern.PaperContainerComponent; 035import net.tnemc.item.paper.platform.impl.modern.PaperCustomNameComponent; 036import net.tnemc.item.paper.platform.impl.modern.PaperDamageComponent; 037import net.tnemc.item.paper.platform.impl.modern.PaperEnchantmentsComponent; 038import net.tnemc.item.paper.platform.impl.modern.PaperItemModelComponent; 039import net.tnemc.item.paper.platform.impl.modern.PaperItemNameComponent; 040import net.tnemc.item.paper.platform.impl.modern.PaperLoreComponent; 041import net.tnemc.item.paper.platform.impl.modern.PaperModelDataComponent; 042import net.tnemc.item.paper.platform.impl.modern.PaperProfileComponent; 043import net.tnemc.item.paper.platform.impl.modern.PaperShulkerColorComponent; 044import net.tnemc.item.paper.platform.impl.old.PaperOldBundleComponent; 045import net.tnemc.item.paper.platform.impl.old.PaperOldContainerComponent; 046import net.tnemc.item.paper.platform.impl.old.PaperOldCustomNameComponent; 047import net.tnemc.item.paper.platform.impl.old.PaperOldDamageComponent; 048import net.tnemc.item.paper.platform.impl.old.PaperOldEnchantmentsComponent; 049import net.tnemc.item.paper.platform.impl.old.PaperOldItemModelComponent; 050import net.tnemc.item.paper.platform.impl.old.PaperOldItemNameComponent; 051import net.tnemc.item.paper.platform.impl.old.PaperOldLoreComponent; 052import net.tnemc.item.paper.platform.impl.old.PaperOldMaxStackSizeComponent; 053import net.tnemc.item.paper.platform.impl.old.PaperOldModelDataComponent; 054import net.tnemc.item.paper.platform.impl.old.PaperOldModelDataLegacyComponent; 055import net.tnemc.item.paper.platform.impl.old.PaperOldProfileComponent; 056import net.tnemc.item.paper.platform.impl.old.PaperOldShulkerColorComponent; 057import net.tnemc.item.platform.ItemPlatform; 058import net.tnemc.item.providers.CalculationsProvider; 059import net.tnemc.item.providers.ItemProvider; 060import net.tnemc.item.providers.VersionUtil; 061import org.bukkit.Bukkit; 062import org.bukkit.DyeColor; 063import org.bukkit.NamespacedKey; 064import org.bukkit.Registry; 065import org.bukkit.attribute.AttributeModifier; 066import org.bukkit.block.banner.PatternType; 067import org.bukkit.enchantments.Enchantment; 068import org.bukkit.inventory.*; 069import org.bukkit.inventory.meta.trim.TrimMaterial; 070import org.bukkit.inventory.meta.trim.TrimPattern; 071import org.bukkit.potion.PotionEffectType; 072import org.jetbrains.annotations.NotNull; 073import org.json.simple.JSONObject; 074import org.json.simple.parser.ParseException; 075 076import java.util.Locale; 077import java.util.Optional; 078 079/** 080 * PaperItemPlatform 081 * 082 * @author creatorfromhell 083 * @since 0.1.7.7 084 */ 085public class PaperItemPlatform extends ItemPlatform<PaperItemStack, ItemStack, Inventory> { 086 087 private static volatile PaperItemPlatform instance; 088 089 //public static final PaperItemPlatform PLATFORM = new PaperItemPlatform(); 090 091 protected final VanillaProvider defaultProvider = new VanillaProvider(); 092 protected final PaperCalculationsProvider calculationsProvider = new PaperCalculationsProvider(); 093 094 private PaperItemPlatform() { 095 096 super(); 097 } 098 099 @Override 100 public PaperItemStack createStack(final String material) { 101 return new PaperItemStack().of(material, 1); 102 } 103 104 public static PaperItemPlatform instance() { 105 106 final PaperItemPlatform result = instance; 107 if(result != null) { 108 return result; 109 } 110 111 synchronized(PaperItemPlatform.class) { 112 113 if(instance == null) { 114 115 instance = new PaperItemPlatform(); 116 instance.addDefaults(); 117 } 118 return instance; 119 } 120 } 121 122 /** 123 * @return the version that is being used currently 124 * @since 0.2.0.0 125 */ 126 @Override 127 public String version() { 128 return Bukkit.getServer().getBukkitVersion().split("-")[0]; 129 } 130 131 @Override 132 public void addDefaults() { 133 134 registerConversions(); 135 136 //bukkit base implementation. 137 if(VersionUtil.isLessThan(version(), "1.21.4")) { 138 addMulti(new PaperOldBundleComponent()); 139 addMulti(new PaperOldContainerComponent()); 140 addMulti(new PaperOldCustomNameComponent()); 141 addMulti(new PaperOldDamageComponent()); 142 addMulti(new PaperOldEnchantmentsComponent()); 143 addMulti(new PaperOldItemModelComponent()); 144 addMulti(new PaperOldItemNameComponent()); 145 addMulti(new PaperOldLoreComponent()); 146 addMulti(new PaperOldMaxStackSizeComponent()); 147 addMulti(new PaperOldModelDataComponent()); 148 addMulti(new PaperOldModelDataLegacyComponent()); 149 addMulti(new PaperOldProfileComponent()); 150 addMulti(new PaperOldShulkerColorComponent()); 151 } 152 153 //Paper-specific 154 if(VersionUtil.isOneTwentyOneFour(version())) { 155 addMulti(new PaperBundleComponent()); 156 addMulti(new PaperContainerComponent()); 157 addMulti(new PaperCustomNameComponent()); 158 addMulti(new PaperDamageComponent()); 159 addMulti(new PaperEnchantmentsComponent()); 160 addMulti(new PaperItemModelComponent()); 161 addMulti(new PaperItemNameComponent()); 162 addMulti(new PaperLoreComponent()); 163 addMulti(new PaperModelDataComponent()); 164 addMulti(new PaperOldModelDataLegacyComponent()); 165 addMulti(new PaperProfileComponent()); 166 addMulti(new PaperShulkerColorComponent()); 167 } 168 169 170 if(Bukkit.getPluginManager().isPluginEnabled("ItemsAdder")) { 171 addItemProvider(new ItemAdderProvider()); 172 } 173 174 if(Bukkit.getPluginManager().isPluginEnabled("MythicMobs")) { 175 addItemProvider(new MMOItemProvider()); 176 } 177 178 if(Bukkit.getPluginManager().isPluginEnabled("Nexo")) { 179 addItemProvider(new NexoProvider()); 180 } 181 182 if(Bukkit.getPluginManager().isPluginEnabled("Nova")) { 183 addItemProvider(new NovaProvider()); 184 } 185 186 if(Bukkit.getPluginManager().isPluginEnabled("Oraxen")) { 187 addItemProvider(new OraxenProvider()); 188 } 189 190 if(Bukkit.getPluginManager().isPluginEnabled("Slimefun")) { 191 addItemProvider(new SlimefunProvider()); 192 } 193 addItemProvider(defaultProvider); 194 } 195 196 /** 197 * Retrieves the default provider for the item stack comparison. 198 * 199 * @return the default provider for the item stack comparison. 200 * @since 0.2.0.0 201 */ 202 @Override 203 public @NotNull ItemProvider<ItemStack> defaultProvider() { 204 205 return defaultProvider; 206 } 207 208 /** 209 * Retrieves the identifier of the default provider for the item stack comparison. 210 * 211 * @return The identifier of the default provider for the item stack comparison. 212 * @since 0.2.0.0 213 */ 214 @Override 215 public @NotNull String defaultProviderIdentifier() { 216 217 return defaultProvider.identifier(); 218 } 219 220 @Override 221 public PaperCalculationsProvider calculations() { 222 return calculationsProvider; 223 } 224 225 /** 226 * Converts the given locale stack to an instance of {@link AbstractItemStack} 227 * 228 * @param locale the locale to convert 229 * 230 * @return the converted locale of type I 231 * @since 0.2.0.0 232 */ 233 @Override 234 public PaperItemStack locale(final ItemStack locale) { 235 236 return new PaperItemStack().of(locale); 237 } 238 239 private void registerConversions() { 240 241 //RegisterConversion for EquipmentSlot 242 converter.registerConversion(String.class, EquipmentSlot.class, input -> { 243 switch (input.toUpperCase()) { 244 case "HAND": return EquipmentSlot.HAND; 245 case "OFF_HAND": return EquipmentSlot.OFF_HAND; 246 case "FEET": return EquipmentSlot.FEET; 247 case "LEGS": return EquipmentSlot.LEGS; 248 case "CHEST": return EquipmentSlot.CHEST; 249 case "HEAD": return EquipmentSlot.HEAD; 250 case "BODY": return EquipmentSlot.BODY; 251 default: return EquipmentSlot.HAND; 252 } 253 }); 254 255 converter.registerConversion(EquipmentSlot.class, String.class, input->switch(input) { 256 case HAND -> "HAND"; 257 case OFF_HAND -> "OFF_HAND"; 258 case FEET -> "FEET"; 259 case LEGS -> "LEGS"; 260 case CHEST -> "CHEST"; 261 case HEAD -> "HEAD"; 262 case BODY -> "BODY"; 263 }); 264 265 //RegisterConversion for EquipmentSlotGroup 266 converter.registerConversion(String.class, EquipmentSlotGroup.class, input -> { 267 final EquipmentSlotGroup group = EquipmentSlotGroup.getByName(input); 268 if(group == null) { 269 throw new IllegalArgumentException("Unknown input: " + input); 270 } 271 return group; 272 }); 273 274 converter.registerConversion(EquipmentSlotGroup.class, String.class, EquipmentSlotGroup::toString); 275 276 //Register Conversions for AttributeModifier 277 converter.registerConversion(String.class, AttributeModifier.Operation.class, input->switch(input.toLowerCase()) { 278 case "add_value" -> AttributeModifier.Operation.ADD_NUMBER; 279 case "add_multiplied_base" -> AttributeModifier.Operation.ADD_SCALAR; 280 case "add_multiplied_total" -> AttributeModifier.Operation.MULTIPLY_SCALAR_1; 281 default -> throw new IllegalArgumentException("Unknown input: " + input); 282 }); 283 284 converter.registerConversion(AttributeModifier.Operation.class, String.class, input ->switch(input) { 285 case ADD_NUMBER -> "add_value"; 286 case ADD_SCALAR -> "add_multiplied_base"; 287 case MULTIPLY_SCALAR_1 -> "add_multiplied_total"; 288 }); 289 290 //Register conversions for DyeColor 291 converter.registerConversion(String.class, DyeColor.class, input ->switch(input.toLowerCase(Locale.ROOT)) { 292 case "white" -> DyeColor.WHITE; 293 case "orange" -> DyeColor.ORANGE; 294 case "magenta" -> DyeColor.MAGENTA; 295 case "light_blue" -> DyeColor.LIGHT_BLUE; 296 case "yellow" -> DyeColor.YELLOW; 297 case "lime" -> DyeColor.LIME; 298 case "pink" -> DyeColor.PINK; 299 case "gray" -> DyeColor.GRAY; 300 case "light_gray" -> DyeColor.LIGHT_GRAY; 301 case "cyan" -> DyeColor.CYAN; 302 case "purple" -> DyeColor.PURPLE; 303 case "blue" -> DyeColor.BLUE; 304 case "brown" -> DyeColor.BROWN; 305 case "green" -> DyeColor.GREEN; 306 case "red" -> DyeColor.RED; 307 case "black" -> DyeColor.BLACK; 308 default -> throw new IllegalArgumentException("Unknown DyeColor: " + input); 309 }); 310 311 converter.registerConversion(DyeColor.class, String.class, input ->switch(input) { 312 case WHITE -> "white"; 313 case ORANGE -> "orange"; 314 case MAGENTA -> "magenta"; 315 case LIGHT_BLUE -> "light_blue"; 316 case YELLOW -> "yellow"; 317 case LIME -> "lime"; 318 case PINK -> "pink"; 319 case GRAY -> "gray"; 320 case LIGHT_GRAY -> "light_gray"; 321 case CYAN -> "cyan"; 322 case PURPLE -> "purple"; 323 case BLUE -> "blue"; 324 case BROWN -> "brown"; 325 case GREEN -> "green"; 326 case RED -> "red"; 327 case BLACK -> "black"; 328 }); 329 330 //Register Conversions for PatternType, which will be dependent on versions 331 //We'll separate the legacy find methods from the modern ones in order to maintain one component 332 // class for both. 333 if(VersionUtil.isOneTwentyOne(version())) { 334 335 converter.registerConversion(String.class, PatternType.class, input->{ 336 337 final NamespacedKey key = NamespacedKey.fromString(input); 338 if(key != null) { 339 340 final PatternType patternType = Registry.BANNER_PATTERN.get(key); 341 if(patternType != null) { 342 343 return patternType; 344 } 345 } 346 throw new IllegalArgumentException("Unknown PatternType: " + input); 347 }); 348 349 if(VersionUtil.isOneTwentyOneFour(version())) { 350 351 converter.registerConversion(PatternType.class, String.class, input->{ 352 353 final NamespacedKey key = input.getKey(); 354 355 return key.toString(); 356 }); 357 } else { 358 359 converter.registerConversion(PatternType.class, String.class, input->{ 360 361 final NamespacedKey key = input.getKey(); 362 363 return key.toString(); 364 }); 365 } 366 367 } else { 368 369 converter.registerConversion(String.class, PatternType.class, PatternType::valueOf); 370 371 converter.registerConversion(PatternType.class, String.class, PatternType::getIdentifier); 372 } 373 374 //Register Conversions for Enchantment, which will be dependent on versions 375 //We'll separate the legacy find methods from the modern ones in order to maintain one component 376 // class for both. 377 if(VersionUtil.isOneTwentyOneFour(version())) { 378 379 converter.registerConversion(String.class, Enchantment.class, (final String input)->{ 380 381 final NamespacedKey key = NamespacedKey.fromString(input); 382 if(key != null) { 383 384 return Registry.ENCHANTMENT.getOrThrow(key); 385 } 386 throw new IllegalArgumentException("Unknown Enchantment: " + input); 387 }); 388 389 converter.registerConversion(Enchantment.class, String.class, (final Enchantment input)->input.getKey().toString()); 390 391 } else if(VersionUtil.isOneThirteen(version())) { 392 converter.registerConversion(String.class, Enchantment.class, (final String input)->{ 393 394 final Enchantment enchantment = Enchantment.getByKey(NamespacedKey.fromString(input)); 395 if(enchantment == null) { 396 397 throw new IllegalArgumentException("Unknown Enchantment: " + input); 398 } 399 return enchantment; 400 }); 401 402 converter.registerConversion(Enchantment.class, String.class, (final Enchantment input)->input.getKey().getKey()); 403 } else { 404 405 converter.registerConversion(String.class, Enchantment.class, (final String input)->{ 406 407 final Enchantment enchantment = Enchantment.getByName(input); 408 if(enchantment == null) { 409 410 throw new IllegalArgumentException("Unknown Enchantment: " + input); 411 } 412 return enchantment; 413 }); 414 415 converter.registerConversion(Enchantment.class, String.class, Enchantment::getName); 416 } 417 418 //Register Conversions for Trim Material, which will be dependent on versions 419 //We'll separate the legacy find methods from the modern ones in order to maintain one component 420 // class for both. 421 if(VersionUtil.isOneTwentyOneFour(version())) { 422 423 converter.registerConversion(TrimMaterial.class, String.class, input->{ 424 425 final NamespacedKey key = RegistryAccess.registryAccess().getRegistry(RegistryKey.TRIM_MATERIAL).getKey(input); 426 if(key != null) { 427 428 return key.asString(); 429 } 430 431 throw new IllegalArgumentException("Unknown TrimMaterial: " + input); 432 }); 433 434 converter.registerConversion(String.class, TrimMaterial.class, input->{ 435 final NamespacedKey key = NamespacedKey.fromString(input); 436 if(key != null) { 437 438 return RegistryAccess.registryAccess().getRegistry(RegistryKey.TRIM_MATERIAL).getOrThrow(key); 439 } 440 throw new IllegalArgumentException("Unknown TrimMaterial: " + input); 441 }); 442 443 } else if(VersionUtil.isOneTwenty(version())) { 444 445 converter.registerConversion(TrimMaterial.class, String.class, input->input.getKey().toString()); 446 447 converter.registerConversion(String.class, TrimMaterial.class, input->{ 448 final NamespacedKey key = NamespacedKey.fromString(input); 449 if(key != null) { 450 451 return Registry.TRIM_MATERIAL.get(key); 452 } 453 throw new IllegalArgumentException("Unknown TrimMaterial: " + input); 454 }); 455 } 456 457 //Register Conversions for NamespacedKey, which will be dependent on versions 458 //We'll separate the legacy find methods from the modern ones in order to maintain one component 459 // class for both. 460 converter.registerConversion(ItemRarity.class, String.class, input->switch(input) { 461 case EPIC -> "epic"; 462 case RARE -> "rare"; 463 case UNCOMMON -> "uncommon"; 464 default -> "common"; 465 }); 466 467 converter.registerConversion(String.class, ItemRarity.class, input->switch(input.toLowerCase()) { 468 case "epic" -> ItemRarity.EPIC; 469 case "rare" -> ItemRarity.RARE; 470 case "uncommon" -> ItemRarity.UNCOMMON; 471 default -> ItemRarity.COMMON; 472 }); 473 474 //Register Conversions for Trim Pattern, which will be dependent on versions 475 //We'll separate the legacy find methods from the modern ones in order to maintain one component 476 // class for both. 477 if(VersionUtil.isOneTwentyOneFour(version())) { 478 479 converter.registerConversion(TrimPattern.class, String.class, input->{ 480 481 final NamespacedKey key = RegistryAccess.registryAccess().getRegistry(RegistryKey.TRIM_PATTERN).getKey(input); 482 if(key != null) { 483 484 return key.asString(); 485 } 486 487 throw new IllegalArgumentException("Unknown TrimPattern: " + input); 488 }); 489 490 converter.registerConversion(String.class, TrimPattern.class, input->{ 491 final NamespacedKey key = NamespacedKey.fromString(input); 492 if(key != null) { 493 494 return RegistryAccess.registryAccess().getRegistry(RegistryKey.TRIM_PATTERN).getOrThrow(key); 495 } 496 throw new IllegalArgumentException("Unknown TrimPattern: " + input); 497 }); 498 499 } else if(VersionUtil.isOneTwenty(version())) { 500 501 converter.registerConversion(TrimPattern.class, String.class, input->input.getKey().toString()); 502 503 converter.registerConversion(String.class, TrimPattern.class, input->{ 504 final NamespacedKey key = NamespacedKey.fromString(input); 505 if(key != null) { 506 507 return Registry.TRIM_PATTERN.get(key); 508 } 509 throw new IllegalArgumentException("Unknown TrimPattern: " + input); 510 }); 511 } 512 513 if(VersionUtil.isOneTwentyOneFour(version())) { 514 515 converter.registerConversion(PotionEffectType.class, String.class, input->input.getKey().toString()); 516 converter.registerConversion(String.class, PotionEffectType.class, input->{ 517 final NamespacedKey key = NamespacedKey.fromString(input); 518 if(key != null) { 519 520 return Registry.EFFECT.getOrThrow(key); 521 } 522 throw new IllegalArgumentException("Unknown PotionEffectType: " + input); 523 }); 524 } else { 525 526 converter.registerConversion(PotionEffectType.class, String.class, PotionEffectType::getName); 527 converter.registerConversion(String.class, PotionEffectType.class, input->{ 528 529 final PotionEffectType type = PotionEffectType.getByName(input); 530 if(type != null) { 531 532 return type; 533 } 534 throw new IllegalArgumentException("Unknown PotionEffectType: " + input); 535 }); 536 } 537 } 538 539 /** 540 * Initializes and returns an AbstractItemStack object by deserializing the provided JSON object. 541 * 542 * @param object the JSON object to deserialize 543 * 544 * @return an initialized AbstractItemStack object 545 * @since 0.2.0.0 546 */ 547 @Override 548 public Optional<PaperItemStack> initSerialized(final JSONObject object) { 549 550 try { 551 return Optional.ofNullable(new PaperItemStack().of(object)); 552 } catch(final ParseException e) { 553 return Optional.empty(); 554 } 555 } 556}