From bf203bb1f37979e0ed36deb00bf5e6ebaa8d6fe2 Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Sun, 28 Apr 2024 19:46:18 +0100 Subject: [PATCH] Rewrite upgrades to use dynamic registries Ever since 1.17, turtle and pocket upgrades have been loaded from datpacks, rather than being hard-coded in Java. However, upgrades have always been stored in our own registry-like structure, rather than using vanilla's registries. This has become a bit of a problem with the introduction of components. The upgrade components now hold the upgrade object (rather than just its id), which means we need the upgrades to be available much earlier (e.g. when reading recipes). The easiest fix here is to store upgrades in proper registries instead. This means that upgrades can no longer be reloaded (it requires a world restart), but otherwise is much nicer: - UpgradeData now stores a Holder rather than a T. - UpgradeSerialiser has been renamed to UpgradeType. This now just provides a Codec, rather than JSON and network reading/writing functions. - Upgrade classes no longer implement getUpgradeID(), but instead have a getType() function, which returns the associated UpgradeType. - Upgrades are now stored in turtle_upgrade (or pocket_upgrade) rather than turtle_upgrades (or pocket_upgrades). This will break existing datapacks, sorry! --- .reuse/dep5 | 2 +- .../turtle/RegisterTurtleUpgradeModeller.java | 10 +- .../api/pocket/AbstractPocketUpgrade.java | 15 +- .../api/pocket/IPocketUpgrade.java | 28 ++- .../api/pocket/PocketUpgradeDataProvider.java | 8 +- .../api/turtle/AbstractTurtleUpgrade.java | 17 +- .../api/turtle/ITurtleUpgrade.java | 32 ++- .../api/turtle/TurtleUpgradeDataProvider.java | 48 ++-- .../api/upgrades/UpgradeBase.java | 10 +- .../api/upgrades/UpgradeData.java | 43 ++-- .../api/upgrades/UpgradeDataProvider.java | 128 ++++------ .../api/upgrades/UpgradeSerialiser.java | 97 -------- .../api/upgrades/UpgradeType.java | 89 +++++++ .../impl/ComputerCraftAPIService.java | 14 +- .../computercraft/impl/RegistryHelper.java | 6 + .../upgrades/SerialiserWithCraftingItem.java | 49 ---- .../impl/upgrades/SimpleSerialiser.java | 44 ---- .../impl/upgrades/TurtleToolSpec.java | 50 ++++ .../impl/upgrades/UpgradeTypeImpl.java | 20 ++ .../computercraft/client/ClientHooks.java | 4 +- .../computercraft/client/ClientRegistry.java | 10 +- .../client/turtle/TurtleUpgradeModellers.java | 20 +- .../speaker.json | 0 .../wireless_modem_advanced.json | 0 .../wireless_modem_normal.json | 0 .../speaker.json | 0 .../wireless_modem_advanced.json | 0 .../wireless_modem_normal.json | 0 .../crafting_table.json | 0 .../turtle_upgrade/diamond_axe.json | 6 + .../turtle_upgrade/diamond_hoe.json | 6 + .../turtle_upgrade/diamond_pickaxe.json | 5 + .../diamond_shovel.json | 1 + .../diamond_sword.json | 1 + .../turtle_upgrades/diamond_axe.json | 1 - .../turtle_upgrades/diamond_hoe.json | 1 - .../turtle_upgrades/diamond_pickaxe.json | 1 - .../computercraft/data/DataProviders.java | 27 ++- .../computercraft/data/LanguageProvider.java | 4 +- .../data/PocketUpgradeProvider.java | 13 +- .../computercraft/data/RecipeProvider.java | 59 +++-- .../data/TurtleUpgradeProvider.java | 30 +-- .../impl/AbstractComputerCraftAPI.java | 28 ++- .../computercraft/impl/PocketUpgrades.java | 12 +- .../computercraft/impl/TurtleUpgrades.java | 12 +- .../computercraft/impl/UpgradeManager.java | 224 ++++++------------ .../computercraft/shared/CommonHooks.java | 4 - .../computercraft/shared/ModRegistry.java | 89 ++++--- .../shared/integration/RecipeModHelpers.java | 40 +++- .../integration/UpgradeRecipeGenerator.java | 43 ++-- .../integration/jei/JEIComputerCraft.java | 14 +- .../integration/jei/RecipeResolver.java | 3 +- .../shared/network/NetworkMessages.java | 1 - .../network/client/UpgradesLoadedMessage.java | 104 -------- .../shared/pocket/apis/PocketAPI.java | 4 +- .../pocket/core/PocketServerComputer.java | 6 +- .../pocket/items/PocketComputerItem.java | 10 +- .../pocket/peripherals/PocketModem.java | 14 +- .../pocket/peripherals/PocketSpeaker.java | 12 +- .../recipes/PocketComputerUpgradeRecipe.java | 4 +- .../recipe/function/RecipeFunction.java | 4 +- .../turtle/blocks/TurtleBlockEntity.java | 4 +- .../shared/turtle/core/TurtleBrain.java | 20 +- .../turtle/core/TurtleEquipCommand.java | 2 +- .../shared/turtle/core/TurtleToolCommand.java | 2 +- .../shared/turtle/inventory/TurtleMenu.java | 5 +- .../turtle/inventory/UpgradeContainer.java | 2 +- .../shared/turtle/inventory/UpgradeSlot.java | 7 +- .../shared/turtle/items/TurtleItem.java | 12 +- .../turtle/recipes/TurtleUpgradeRecipe.java | 2 +- .../turtle/upgrades/TurtleCraftingTable.java | 12 +- .../shared/turtle/upgrades/TurtleModem.java | 13 +- .../shared/turtle/upgrades/TurtleSpeaker.java | 12 +- .../shared/turtle/upgrades/TurtleTool.java | 32 ++- .../turtle/upgrades/TurtleToolSerialiser.java | 69 ------ .../shared/util/ComponentizationFixers.java | 4 +- .../shared/util/SafeDispatchCodec.java | 118 +++++++++ .../upgrades/TurtleToolSerialiserTest.java | 65 ----- .../test/shared/NetworkSupport.java | 58 ----- .../computercraft/gametest/Turtle_Test.kt | 15 +- .../netherite_pickaxe.json | 1 + .../wooden_pickaxe.json | 1 + .../loot_tables/treasure_disk.json | 4 +- .../client/FabricComputerCraftAPIClient.java | 12 +- .../FabricComputerCraftAPIClientService.java | 4 +- .../integration/rei/REIComputerCraft.java | 21 +- .../rei/UpgradeDisplayGenerator.java | 3 +- .../FabricComputerCraftAPIClientImpl.java | 6 +- .../data/FabricDataGenerators.java | 66 +++++- .../impl/ComputerCraftAPIImpl.java | 2 +- .../computercraft/shared/ComputerCraft.java | 9 +- .../turtle/RegisterTurtleModellersEvent.java | 6 +- .../dan200/computercraft/ComputerCraft.java | 13 +- .../shared/ForgeCommonHooks.java | 17 +- 94 files changed, 1031 insertions(+), 1115 deletions(-) delete mode 100644 projects/common-api/src/main/java/dan200/computercraft/api/upgrades/UpgradeSerialiser.java create mode 100644 projects/common-api/src/main/java/dan200/computercraft/api/upgrades/UpgradeType.java delete mode 100644 projects/common-api/src/main/java/dan200/computercraft/impl/upgrades/SerialiserWithCraftingItem.java delete mode 100644 projects/common-api/src/main/java/dan200/computercraft/impl/upgrades/SimpleSerialiser.java create mode 100644 projects/common-api/src/main/java/dan200/computercraft/impl/upgrades/TurtleToolSpec.java create mode 100644 projects/common-api/src/main/java/dan200/computercraft/impl/upgrades/UpgradeTypeImpl.java rename projects/common/src/generated/resources/data/computercraft/computercraft/{pocket_upgrades => pocket_upgrade}/speaker.json (100%) rename projects/common/src/generated/resources/data/computercraft/computercraft/{pocket_upgrades => pocket_upgrade}/wireless_modem_advanced.json (100%) rename projects/common/src/generated/resources/data/computercraft/computercraft/{pocket_upgrades => pocket_upgrade}/wireless_modem_normal.json (100%) rename projects/common/src/generated/resources/data/computercraft/computercraft/{turtle_upgrades => turtle_upgrade}/speaker.json (100%) rename projects/common/src/generated/resources/data/computercraft/computercraft/{turtle_upgrades => turtle_upgrade}/wireless_modem_advanced.json (100%) rename projects/common/src/generated/resources/data/computercraft/computercraft/{turtle_upgrades => turtle_upgrade}/wireless_modem_normal.json (100%) rename projects/common/src/generated/resources/data/minecraft/computercraft/{turtle_upgrades => turtle_upgrade}/crafting_table.json (100%) create mode 100644 projects/common/src/generated/resources/data/minecraft/computercraft/turtle_upgrade/diamond_axe.json create mode 100644 projects/common/src/generated/resources/data/minecraft/computercraft/turtle_upgrade/diamond_hoe.json create mode 100644 projects/common/src/generated/resources/data/minecraft/computercraft/turtle_upgrade/diamond_pickaxe.json rename projects/common/src/generated/resources/data/minecraft/computercraft/{turtle_upgrades => turtle_upgrade}/diamond_shovel.json (68%) rename projects/common/src/generated/resources/data/minecraft/computercraft/{turtle_upgrades => turtle_upgrade}/diamond_sword.json (72%) delete mode 100644 projects/common/src/generated/resources/data/minecraft/computercraft/turtle_upgrades/diamond_axe.json delete mode 100644 projects/common/src/generated/resources/data/minecraft/computercraft/turtle_upgrades/diamond_hoe.json delete mode 100644 projects/common/src/generated/resources/data/minecraft/computercraft/turtle_upgrades/diamond_pickaxe.json delete mode 100644 projects/common/src/main/java/dan200/computercraft/shared/network/client/UpgradesLoadedMessage.java delete mode 100644 projects/common/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleToolSerialiser.java create mode 100644 projects/common/src/main/java/dan200/computercraft/shared/util/SafeDispatchCodec.java delete mode 100644 projects/common/src/test/java/dan200/computercraft/shared/turtle/upgrades/TurtleToolSerialiserTest.java delete mode 100644 projects/common/src/testFixtures/java/dan200/computercraft/test/shared/NetworkSupport.java rename projects/common/src/testMod/resources/data/cctest/computercraft/{turtle_upgrades => turtle_upgrade}/netherite_pickaxe.json (70%) rename projects/common/src/testMod/resources/data/cctest/computercraft/{turtle_upgrades => turtle_upgrade}/wooden_pickaxe.json (63%) diff --git a/.reuse/dep5 b/.reuse/dep5 index 451e288c7..640c0ff75 100644 --- a/.reuse/dep5 +++ b/.reuse/dep5 @@ -6,7 +6,7 @@ Upstream-Contact: Jonathan Coates Files: projects/common/src/main/resources/assets/computercraft/sounds.json projects/common/src/main/resources/assets/computercraft/sounds/empty.ogg - projects/common/src/testMod/resources/data/cctest/computercraft/turtle_upgrades/* + projects/common/src/testMod/resources/data/cctest/computercraft/turtle_upgrade/* projects/common/src/testMod/resources/data/cctest/structures/* projects/*/src/generated/* projects/web/src/htmlTransform/export/index.json diff --git a/projects/common-api/src/client/java/dan200/computercraft/api/client/turtle/RegisterTurtleUpgradeModeller.java b/projects/common-api/src/client/java/dan200/computercraft/api/client/turtle/RegisterTurtleUpgradeModeller.java index 6a170d1d6..ede3e7675 100644 --- a/projects/common-api/src/client/java/dan200/computercraft/api/client/turtle/RegisterTurtleUpgradeModeller.java +++ b/projects/common-api/src/client/java/dan200/computercraft/api/client/turtle/RegisterTurtleUpgradeModeller.java @@ -5,7 +5,7 @@ package dan200.computercraft.api.client.turtle; import dan200.computercraft.api.turtle.ITurtleUpgrade; -import dan200.computercraft.api.upgrades.UpgradeSerialiser; +import dan200.computercraft.api.upgrades.UpgradeType; /** * A functional interface to register a {@link TurtleUpgradeModeller} for a class of turtle upgrades. @@ -18,9 +18,9 @@ public interface RegisterTurtleUpgradeModeller { /** * Register a {@link TurtleUpgradeModeller}. * - * @param serialiser The turtle upgrade serialiser. - * @param modeller The upgrade modeller. - * @param The type of the turtle upgrade. + * @param type The turtle upgrade type. + * @param modeller The upgrade modeller. + * @param The type of the turtle upgrade. */ - void register(UpgradeSerialiser serialiser, TurtleUpgradeModeller modeller); + void register(UpgradeType type, TurtleUpgradeModeller modeller); } diff --git a/projects/common-api/src/main/java/dan200/computercraft/api/pocket/AbstractPocketUpgrade.java b/projects/common-api/src/main/java/dan200/computercraft/api/pocket/AbstractPocketUpgrade.java index c168bd946..d18891dbe 100644 --- a/projects/common-api/src/main/java/dan200/computercraft/api/pocket/AbstractPocketUpgrade.java +++ b/projects/common-api/src/main/java/dan200/computercraft/api/pocket/AbstractPocketUpgrade.java @@ -4,8 +4,6 @@ package dan200.computercraft.api.pocket; -import dan200.computercraft.api.upgrades.UpgradeBase; -import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.ItemStack; @@ -15,25 +13,14 @@ import net.minecraft.world.item.ItemStack; * One does not have to use this, but it does provide a convenient template. */ public abstract class AbstractPocketUpgrade implements IPocketUpgrade { - private final ResourceLocation id; private final String adjective; private final ItemStack stack; - protected AbstractPocketUpgrade(ResourceLocation id, String adjective, ItemStack stack) { - this.id = id; + protected AbstractPocketUpgrade(String adjective, ItemStack stack) { this.adjective = adjective; this.stack = stack; } - protected AbstractPocketUpgrade(ResourceLocation id, ItemStack stack) { - this(id, UpgradeBase.getDefaultAdjective(id), stack); - } - - @Override - public final ResourceLocation getUpgradeID() { - return id; - } - @Override public final String getUnlocalisedAdjective() { return adjective; diff --git a/projects/common-api/src/main/java/dan200/computercraft/api/pocket/IPocketUpgrade.java b/projects/common-api/src/main/java/dan200/computercraft/api/pocket/IPocketUpgrade.java index 0f4d5be86..fe26f320d 100644 --- a/projects/common-api/src/main/java/dan200/computercraft/api/pocket/IPocketUpgrade.java +++ b/projects/common-api/src/main/java/dan200/computercraft/api/pocket/IPocketUpgrade.java @@ -6,7 +6,7 @@ package dan200.computercraft.api.pocket; import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.upgrades.UpgradeBase; -import dan200.computercraft.api.upgrades.UpgradeSerialiser; +import dan200.computercraft.api.upgrades.UpgradeType; import dan200.computercraft.impl.ComputerCraftAPIService; import net.minecraft.core.Registry; import net.minecraft.resources.ResourceKey; @@ -18,7 +18,7 @@ import javax.annotation.Nullable; * A peripheral which can be equipped to the back side of a pocket computer. *

* Pocket upgrades are defined in two stages. First, one creates a {@link IPocketUpgrade} subclass and corresponding - * {@link UpgradeSerialiser} instance, which are then registered in a registry. + * {@link UpgradeType} instance, which are then registered in a registry. *

* You then write a JSON file in your mod's {@literal data/} folder. This is then parsed when the world is loaded, and * the upgrade automatically registered. It is recommended this is done via {@linkplain PocketUpgradeDataProvider data @@ -26,15 +26,15 @@ import javax.annotation.Nullable; * *

Example

*
{@code
- * // We use Forge's DeferredRegister to register our serialiser. Fabric mods may register their serialiser directly.
- * static final DeferredRegister> SERIALISERS = DeferredRegister.create(IPocketUpgrade.serialiserRegistryKey(), "my_mod");
+ * // We use Forge's DeferredRegister to register our upgrade type. Fabric mods may register their type directly.
+ * static final DeferredRegister> POCKET_UPGRADES = DeferredRegister.create(IPocketUpgrade.typeRegistry(), "my_mod");
  *
- * // Register a new upgrade serialiser called "my_upgrade".
- * public static final RegistryObject> MY_UPGRADE =
- *     SERIALISERS.register("my_upgrade", () -> UpgradeSerialiser.simple(MyUpgrade::new));
+ * // Register a new upgrade upgrade type called "my_upgrade".
+ * public static final RegistryObject> MY_UPGRADE =
+ *     POCKET_UPGRADES.register("my_upgrade", () -> UpgradeType.simple(new MyUpgrade()));
  *
  * // Then in your constructor
- * SERIALISERS.register(bus);
+ * POCKET_UPGRADES.register(bus);
  * }
*

* We can then define a new upgrade using JSON by placing the following in @@ -49,14 +49,22 @@ import javax.annotation.Nullable; */ public interface IPocketUpgrade extends UpgradeBase { /** - * The registry key for upgrade serialisers. + * The registry key for pocket upgrade types. * * @return The registry key. */ - static ResourceKey>> serialiserRegistryKey() { + static ResourceKey>> typeRegistry() { return ComputerCraftAPIService.get().pocketUpgradeRegistryId(); } + /** + * Get the type of this upgrade. + * + * @return The type of this upgrade. + */ + @Override + UpgradeType getType(); + /** * Creates a peripheral for the pocket computer. *

diff --git a/projects/common-api/src/main/java/dan200/computercraft/api/pocket/PocketUpgradeDataProvider.java b/projects/common-api/src/main/java/dan200/computercraft/api/pocket/PocketUpgradeDataProvider.java index 80a2ebc21..e9685fb8e 100644 --- a/projects/common-api/src/main/java/dan200/computercraft/api/pocket/PocketUpgradeDataProvider.java +++ b/projects/common-api/src/main/java/dan200/computercraft/api/pocket/PocketUpgradeDataProvider.java @@ -5,7 +5,9 @@ package dan200.computercraft.api.pocket; import dan200.computercraft.api.upgrades.UpgradeDataProvider; -import dan200.computercraft.api.upgrades.UpgradeSerialiser; +import dan200.computercraft.api.upgrades.UpgradeType; +import dan200.computercraft.impl.ComputerCraftAPIService; +import dan200.computercraft.impl.RegistryHelper; import net.minecraft.data.DataGenerator; import net.minecraft.data.PackOutput; @@ -19,10 +21,10 @@ import java.util.function.Consumer; * generate them. * * @see IPocketUpgrade - * @see UpgradeSerialiser + * @see UpgradeType */ public abstract class PocketUpgradeDataProvider extends UpgradeDataProvider { public PocketUpgradeDataProvider(PackOutput output) { - super(output, "Pocket Computer Upgrades", "computercraft/pocket_upgrades", IPocketUpgrade.serialiserRegistryKey()); + super(output, "Pocket Computer Upgrades", RegistryHelper.POCKET_UPGRADE, ComputerCraftAPIService.get().pocketUpgradeCodec()); } } diff --git a/projects/common-api/src/main/java/dan200/computercraft/api/turtle/AbstractTurtleUpgrade.java b/projects/common-api/src/main/java/dan200/computercraft/api/turtle/AbstractTurtleUpgrade.java index bede70fa3..8ee5a65dd 100644 --- a/projects/common-api/src/main/java/dan200/computercraft/api/turtle/AbstractTurtleUpgrade.java +++ b/projects/common-api/src/main/java/dan200/computercraft/api/turtle/AbstractTurtleUpgrade.java @@ -4,8 +4,6 @@ package dan200.computercraft.api.turtle; -import dan200.computercraft.api.upgrades.UpgradeBase; -import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.ItemStack; @@ -15,34 +13,23 @@ import net.minecraft.world.item.ItemStack; * One does not have to use this, but it does provide a convenient template. */ public abstract class AbstractTurtleUpgrade implements ITurtleUpgrade { - private final ResourceLocation id; private final TurtleUpgradeType type; private final String adjective; private final ItemStack stack; - protected AbstractTurtleUpgrade(ResourceLocation id, TurtleUpgradeType type, String adjective, ItemStack stack) { - this.id = id; + protected AbstractTurtleUpgrade(TurtleUpgradeType type, String adjective, ItemStack stack) { this.type = type; this.adjective = adjective; this.stack = stack; } - protected AbstractTurtleUpgrade(ResourceLocation id, TurtleUpgradeType type, ItemStack stack) { - this(id, type, UpgradeBase.getDefaultAdjective(id), stack); - } - - @Override - public final ResourceLocation getUpgradeID() { - return id; - } - @Override public final String getUnlocalisedAdjective() { return adjective; } @Override - public final TurtleUpgradeType getType() { + public final TurtleUpgradeType getUpgradeType() { return type; } diff --git a/projects/common-api/src/main/java/dan200/computercraft/api/turtle/ITurtleUpgrade.java b/projects/common-api/src/main/java/dan200/computercraft/api/turtle/ITurtleUpgrade.java index 9dbf8ab0c..08c54ca29 100644 --- a/projects/common-api/src/main/java/dan200/computercraft/api/turtle/ITurtleUpgrade.java +++ b/projects/common-api/src/main/java/dan200/computercraft/api/turtle/ITurtleUpgrade.java @@ -6,7 +6,7 @@ package dan200.computercraft.api.turtle; import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.upgrades.UpgradeBase; -import dan200.computercraft.api.upgrades.UpgradeSerialiser; +import dan200.computercraft.api.upgrades.UpgradeType; import dan200.computercraft.impl.ComputerCraftAPIService; import net.minecraft.core.Direction; import net.minecraft.core.Registry; @@ -20,7 +20,7 @@ import javax.annotation.Nullable; * peripheral. *

* Turtle upgrades are defined in two stages. First, one creates a {@link ITurtleUpgrade} subclass and corresponding - * {@link UpgradeSerialiser} instance, which are then registered in a registry. + * {@link UpgradeType} instance, which are then registered in a registry. *

* You then write a JSON file in your mod's {@literal data/} folder. This is then parsed when the world is loaded, and * the upgrade automatically registered. It is recommended this is done via {@linkplain TurtleUpgradeDataProvider data @@ -28,15 +28,15 @@ import javax.annotation.Nullable; * *

Example

*
{@code
- * // We use Forge's DeferredRegister to register our serialiser. Fabric mods may register their serialiser directly.
- * static final DeferredRegister> SERIALISERS = DeferredRegister.create(ITurtleUpgrade.serialiserRegistryKey(), "my_mod");
+ * // We use Forge's DeferredRegister to register our upgrade type. Fabric mods may register their type directly.
+ * static final DeferredRegister> TURTLE_UPGRADES = DeferredRegister.create(ITurtleUpgrade.typeRegistry(), "my_mod");
  *
- * // Register a new upgrade serialiser called "my_upgrade".
- * public static final RegistryObject> MY_UPGRADE =
- *     SERIALISERS.register( "my_upgrade", () -> TurtleUpgradeSerialiser.simple( MyUpgrade::new ) );
+ * // Register a new upgrade type called "my_upgrade".
+ * public static final RegistryObject> MY_UPGRADE =
+ *     TURTLE_UPGRADES.register("my_upgrade", () -> UpgradeType.simple(MyUpgrade::new));
  *
  * // Then in your constructor
- * SERIALISERS.register( bus );
+ * TURTLE_UPGRADES.register(bus);
  * }
*

* We can then define a new upgrade using JSON by placing the following in @@ -44,7 +44,7 @@ import javax.annotation.Nullable; * *

{@code
  * {
- *     "type": my_mod:my_upgrade",
+ *     "type": "my_mod:my_upgrade"
  * }
  * }
*

@@ -60,21 +60,29 @@ import javax.annotation.Nullable; */ public interface ITurtleUpgrade extends UpgradeBase { /** - * The registry key for upgrade serialisers. + * The registry key for turtle upgrade types. * * @return The registry key. */ - static ResourceKey>> serialiserRegistryKey() { + static ResourceKey>> typeRegistry() { return ComputerCraftAPIService.get().turtleUpgradeRegistryId(); } + /** + * Get the type of this upgrade. + * + * @return The type of this upgrade. + */ + @Override + UpgradeType getType(); + /** * Return whether this turtle adds a tool or a peripheral to the turtle. * * @return The type of upgrade this is. * @see TurtleUpgradeType for the differences between them. */ - TurtleUpgradeType getType(); + TurtleUpgradeType getUpgradeType(); /** * Will only be called for peripheral upgrades. Creates a peripheral for a turtle being placed using this upgrade. diff --git a/projects/common-api/src/main/java/dan200/computercraft/api/turtle/TurtleUpgradeDataProvider.java b/projects/common-api/src/main/java/dan200/computercraft/api/turtle/TurtleUpgradeDataProvider.java index 2b11a1fde..e227a3fef 100644 --- a/projects/common-api/src/main/java/dan200/computercraft/api/turtle/TurtleUpgradeDataProvider.java +++ b/projects/common-api/src/main/java/dan200/computercraft/api/turtle/TurtleUpgradeDataProvider.java @@ -4,13 +4,13 @@ package dan200.computercraft.api.turtle; -import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.api.ComputerCraftTags; +import dan200.computercraft.api.upgrades.UpgradeBase; import dan200.computercraft.api.upgrades.UpgradeDataProvider; -import dan200.computercraft.api.upgrades.UpgradeSerialiser; +import dan200.computercraft.impl.ComputerCraftAPIService; import dan200.computercraft.impl.RegistryHelper; +import dan200.computercraft.impl.upgrades.TurtleToolSpec; import net.minecraft.core.component.DataComponents; -import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.data.DataGenerator; import net.minecraft.data.PackOutput; import net.minecraft.resources.ResourceLocation; @@ -21,6 +21,7 @@ import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.Block; import javax.annotation.Nullable; +import java.util.Optional; import java.util.function.Consumer; /** @@ -33,10 +34,8 @@ import java.util.function.Consumer; * @see ITurtleUpgrade */ public abstract class TurtleUpgradeDataProvider extends UpgradeDataProvider { - private static final ResourceLocation TOOL_ID = new ResourceLocation(ComputerCraftAPI.MOD_ID, "tool"); - public TurtleUpgradeDataProvider(PackOutput output) { - super(output, "Turtle Upgrades", "computercraft/turtle_upgrades", ITurtleUpgrade.serialiserRegistryKey()); + super(output, "Turtle Upgrades", RegistryHelper.TURTLE_UPGRADE, ComputerCraftAPIService.get().turtleUpgradeCodec()); } /** @@ -48,7 +47,7 @@ public abstract class TurtleUpgradeDataProvider extends UpgradeDataProvider serialiser; private final Item toolItem; - private @Nullable String adjective; + private String adjective; private @Nullable Item craftingItem; - private @Nullable Float damageMultiplier = null; + private float damageMultiplier = TurtleToolSpec.DEFAULT_DAMAGE_MULTIPLIER; private @Nullable TagKey breakable; private boolean allowEnchantments = false; private TurtleToolDurability consumeDurability = TurtleToolDurability.NEVER; - ToolBuilder(ResourceLocation id, UpgradeSerialiser serialiser, Item toolItem) { + ToolBuilder(ResourceLocation id, Item toolItem) { this.id = id; - this.serialiser = serialiser; + adjective = UpgradeBase.getDefaultAdjective(id); this.toolItem = toolItem; craftingItem = null; } @@ -150,20 +148,16 @@ public abstract class TurtleUpgradeDataProvider extends UpgradeDataProvider>> add) { - add.accept(new Upgrade<>(id, serialiser, s -> { - s.addProperty("item", RegistryHelper.getKeyOrThrow(BuiltInRegistries.ITEM, toolItem).toString()); - if (adjective != null) s.addProperty("adjective", adjective); - if (craftingItem != null) { - s.addProperty("craftItem", RegistryHelper.getKeyOrThrow(BuiltInRegistries.ITEM, craftingItem).toString()); - } - if (damageMultiplier != null) s.addProperty("damageMultiplier", damageMultiplier); - if (breakable != null) s.addProperty("breakable", breakable.location().toString()); - if (allowEnchantments) s.addProperty("allowEnchantments", true); - if (consumeDurability != TurtleToolDurability.NEVER) { - s.addProperty("consumeDurability", consumeDurability.getSerializedName()); - } - })); + public void add(Consumer> add) { + upgrade(id, ComputerCraftAPIService.get().createTurtleTool(new TurtleToolSpec( + adjective, + Optional.ofNullable(craftingItem), + toolItem, + damageMultiplier, + allowEnchantments, + consumeDurability, + Optional.ofNullable(breakable) + ))).add(add); } } } diff --git a/projects/common-api/src/main/java/dan200/computercraft/api/upgrades/UpgradeBase.java b/projects/common-api/src/main/java/dan200/computercraft/api/upgrades/UpgradeBase.java index 2850fea15..654303d19 100644 --- a/projects/common-api/src/main/java/dan200/computercraft/api/upgrades/UpgradeBase.java +++ b/projects/common-api/src/main/java/dan200/computercraft/api/upgrades/UpgradeBase.java @@ -19,15 +19,11 @@ import net.minecraft.world.item.ItemStack; */ public interface UpgradeBase { /** - * Gets a unique identifier representing this type of turtle upgrade. eg: "computercraft:wireless_modem" - * or "my_mod:my_upgrade". - *

- * You should use a unique resource domain to ensure this upgrade is uniquely identified. - * The upgrade will fail registration if an already used ID is specified. + * Get the type of this upgrade. * - * @return The unique ID for this upgrade. + * @return The type of this upgrade. */ - ResourceLocation getUpgradeID(); + UpgradeType getType(); /** * Return an unlocalised string to describe this type of computer in item names. diff --git a/projects/common-api/src/main/java/dan200/computercraft/api/upgrades/UpgradeData.java b/projects/common-api/src/main/java/dan200/computercraft/api/upgrades/UpgradeData.java index e82c4643d..a2f622df4 100644 --- a/projects/common-api/src/main/java/dan200/computercraft/api/upgrades/UpgradeData.java +++ b/projects/common-api/src/main/java/dan200/computercraft/api/upgrades/UpgradeData.java @@ -6,38 +6,53 @@ package dan200.computercraft.api.upgrades; import dan200.computercraft.api.pocket.IPocketUpgrade; import dan200.computercraft.api.turtle.ITurtleUpgrade; +import net.minecraft.core.Holder; import net.minecraft.core.component.DataComponentPatch; import net.minecraft.world.item.ItemStack; /** * An upgrade (i.e. a {@link ITurtleUpgrade}) and its current upgrade data. * - * @param upgrade The current upgrade. - * @param data The upgrade's data. - * @param The type of upgrade, either {@link ITurtleUpgrade} or {@link IPocketUpgrade}. + * @param holder The current upgrade holder. + * @param data The upgrade's data. + * @param The type of upgrade, either {@link ITurtleUpgrade} or {@link IPocketUpgrade}. */ -public record UpgradeData(T upgrade, DataComponentPatch data) { +public record UpgradeData(Holder.Reference holder, DataComponentPatch data) { /** * A utility method to construct a new {@link UpgradeData} instance. * - * @param upgrade An upgrade. - * @param data The upgrade's data. - * @param The type of upgrade. + * @param holder An upgrade. + * @param data The upgrade's data. + * @param The type of upgrade. * @return The new {@link UpgradeData} instance. */ - public static UpgradeData of(T upgrade, DataComponentPatch data) { - return new UpgradeData<>(upgrade, data); + public static UpgradeData of(Holder.Reference holder, DataComponentPatch data) { + return new UpgradeData<>(holder, data); } /** * Create an {@link UpgradeData} containing the default {@linkplain #data() data} for an upgrade. * - * @param upgrade The upgrade instance. - * @param The type of upgrade. + * @param holder The upgrade instance. + * @param The type of upgrade. * @return The default upgrade data. */ - public static UpgradeData ofDefault(T upgrade) { - return of(upgrade, upgrade.getUpgradeData(upgrade.getCraftingItem())); + public static UpgradeData ofDefault(Holder.Reference holder) { + var upgrade = holder.value(); + return of(holder, upgrade.getUpgradeData(upgrade.getCraftingItem())); + } + + public UpgradeData { + if (!holder.isBound()) throw new IllegalArgumentException("Holder is not bound"); + } + + /** + * Get the current upgrade. + * + * @return The current upgrade. + */ + public T upgrade() { + return holder().value(); } /** @@ -49,6 +64,6 @@ public record UpgradeData(T upgrade, DataComponentPatch d * @return This upgrade's item. */ public ItemStack getUpgradeItem() { - return upgrade.getUpgradeItem(data).copy(); + return upgrade().getUpgradeItem(data).copy(); } } diff --git a/projects/common-api/src/main/java/dan200/computercraft/api/upgrades/UpgradeDataProvider.java b/projects/common-api/src/main/java/dan200/computercraft/api/upgrades/UpgradeDataProvider.java index 0d8804837..685d5e0e8 100644 --- a/projects/common-api/src/main/java/dan200/computercraft/api/upgrades/UpgradeDataProvider.java +++ b/projects/common-api/src/main/java/dan200/computercraft/api/upgrades/UpgradeDataProvider.java @@ -5,27 +5,22 @@ package dan200.computercraft.api.upgrades; import com.google.gson.JsonObject; -import com.google.gson.JsonParseException; +import com.mojang.serialization.Codec; +import com.mojang.serialization.JsonOps; import dan200.computercraft.impl.PlatformHelper; -import dan200.computercraft.impl.RegistryHelper; -import dan200.computercraft.impl.upgrades.SerialiserWithCraftingItem; -import dan200.computercraft.impl.upgrades.SimpleSerialiser; import net.minecraft.Util; import net.minecraft.core.Registry; -import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.data.CachedOutput; import net.minecraft.data.DataProvider; import net.minecraft.data.PackOutput; import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.item.Item; import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.Nullable; -import javax.annotation.Nullable; import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; -import java.util.function.Function; /** * A data generator/provider for turtle and pocket computer upgrades. This should not be extended directly, instead see @@ -36,92 +31,66 @@ import java.util.function.Function; public abstract class UpgradeDataProvider implements DataProvider { private final PackOutput output; private final String name; - private final String folder; - private final Registry> registry; + private final ResourceKey> registryName; + private final Codec codec; - private @Nullable List upgrades; + private @Nullable Map, T> upgrades; @ApiStatus.Internal - protected UpgradeDataProvider(PackOutput output, String name, String folder, ResourceKey>> registry) { + protected UpgradeDataProvider(PackOutput output, String name, ResourceKey> registryName, Codec codec) { this.output = output; this.name = name; - this.folder = folder; - this.registry = RegistryHelper.getRegistry(registry); + this.registryName = registryName; + this.codec = codec; } /** - * Register an upgrade using a {@linkplain UpgradeSerialiser#simple(Function) "simple" serialiser}. + * Add a new upgrade. * - * @param id The ID of the upgrade to create. - * @param serialiser The simple serialiser. + * @param id The ID of the upgrade to create. + * @param upgrade The upgrade to add. * @return The constructed upgrade, ready to be passed off to {@link #addUpgrades(Consumer)}'s consumer. */ - public final Upgrade> simple(ResourceLocation id, UpgradeSerialiser serialiser) { - if (!(serialiser instanceof SimpleSerialiser)) { - throw new IllegalStateException(serialiser + " must be a simple() seriaiser."); - } - - return new Upgrade<>(id, serialiser, s -> { + protected final Upgrade upgrade(ResourceLocation id, T upgrade) { + return new Upgrade<>(id, upgrade, j -> { }); } - /** - * Register an upgrade using a {@linkplain UpgradeSerialiser#simple(Function) simple serialiser}. - * - * @param id The ID of the upgrade to create. - * @param serialiser The simple serialiser. - * @param item The crafting upgrade for this item. - * @return The constructed upgrade, ready to be passed off to {@link #addUpgrades(Consumer)}'s consumer. - */ - public final Upgrade> simpleWithCustomItem(ResourceLocation id, UpgradeSerialiser serialiser, Item item) { - if (!(serialiser instanceof SerialiserWithCraftingItem)) { - throw new IllegalStateException(serialiser + " must be a simpleWithCustomItem() serialiser."); - } - - return new Upgrade<>(id, serialiser, s -> - s.addProperty("item", RegistryHelper.getKeyOrThrow(BuiltInRegistries.ITEM, item).toString()) - ); - } - /** * Add all turtle or pocket computer upgrades. *

* Example usage: *

{@code
-     * protected void addUpgrades(Consumer>> addUpgrade) {
-     *     simple(new ResourceLocation("mymod", "speaker"), SPEAKER_SERIALISER.get()).add(addUpgrade);
+     * protected void addUpgrades(Consumer> addUpgrade) {
+     *     upgrade(new ResourceLocation("mymod", "speaker"), new TurtleSpeaker(new ItemStack(Items.NOTE_BLOCK))).add(addUpgrade);
      * }
      * }
* * @param addUpgrade A callback used to register an upgrade. */ - protected abstract void addUpgrades(Consumer>> addUpgrade); + protected abstract void addUpgrades(Consumer> addUpgrade); @Override public CompletableFuture run(CachedOutput cache) { - var base = output.getOutputFolder().resolve("data"); + var base = output.createPathProvider(PackOutput.Target.DATA_PACK, registryName.location().getNamespace() + "/" + registryName.location().getPath()); + + Map, T> upgrades = new LinkedHashMap<>(); - Set seen = new HashSet<>(); - List upgrades = new ArrayList<>(); List> futures = new ArrayList<>(); addUpgrades(upgrade -> { - if (!seen.add(upgrade.id())) throw new IllegalStateException("Duplicate upgrade " + upgrade.id()); + var id = ResourceKey.create(registryName, upgrade.id); + if (upgrades.containsKey(id)) throw new IllegalStateException("Duplicate upgrade " + upgrade.id); - var json = new JsonObject(); - json.addProperty("type", RegistryHelper.getKeyOrThrow(registry, upgrade.serialiser()).toString()); - upgrade.serialise().accept(json); + var json = (JsonObject) codec.encodeStart(JsonOps.INSTANCE, upgrade.upgrade).getOrThrow(); + upgrade.serialise.accept(json); - futures.add(DataProvider.saveStable(cache, json, base.resolve(upgrade.id().getNamespace() + "/" + folder + "/" + upgrade.id().getPath() + ".json"))); + futures.add(DataProvider.saveStable(cache, json, base.json(upgrade.id))); - try { - var result = upgrade.serialiser().fromJson(upgrade.id(), json); - upgrades.add(result); - } catch (IllegalArgumentException | JsonParseException e) { - LOGGER.error("Failed to parse {} {}", name, upgrade.id(), e); - } + upgrades.put(id, upgrade.upgrade); }); - this.upgrades = Collections.unmodifiableList(upgrades); + this.upgrades = Collections.unmodifiableMap(upgrades); + return Util.sequenceFailFast(futures); } @@ -130,34 +99,39 @@ public abstract class UpgradeDataProvider implements Data return name; } - public final UpgradeSerialiser existingSerialiser(ResourceLocation id) { - var result = registry.get(id); - if (result == null) throw new IllegalArgumentException("No such serialiser " + id); - return result; - } - - public List getGeneratedUpgrades() { - if (upgrades == null) throw new IllegalStateException("Upgrades have not been generated yet"); + /** + * Get all registered upgrades. + * + * @return The map of registered upgrades. + */ + public Map, T> getGeneratedUpgrades() { + var upgrades = this.upgrades; + if (upgrades == null) throw new IllegalStateException("Upgrades are not available yet"); return upgrades; } /** * A constructed upgrade instance, produced {@link #addUpgrades(Consumer)}. * - * @param id The ID for this upgrade. - * @param serialiser The serialiser which reads and writes this upgrade. - * @param serialise Augment the generated JSON with additional fields. - * @param The type of upgrade serialiser. + * @param The type of upgrade. */ - public record Upgrade>( - ResourceLocation id, R serialiser, Consumer serialise - ) { + public static final class Upgrade { + private final ResourceLocation id; + private final T upgrade; + private final Consumer serialise; + + private Upgrade(ResourceLocation id, T upgrade, Consumer serialise) { + this.id = id; + this.upgrade = upgrade; + this.serialise = serialise; + } + /** * Convenience method for registering an upgrade. * * @param add The callback given to {@link #addUpgrades(Consumer)} */ - public void add(Consumer> add) { + public void add(Consumer> add) { add.accept(this); } @@ -170,8 +144,8 @@ public abstract class UpgradeDataProvider implements Data * @param modId The id of the mod. * @return A new upgrade instance. */ - public Upgrade requireMod(String modId) { - return new Upgrade<>(id, serialiser, json -> { + public Upgrade requireMod(String modId) { + return new Upgrade<>(id, upgrade, json -> { PlatformHelper.get().addRequiredModCondition(json, modId); serialise.accept(json); }); diff --git a/projects/common-api/src/main/java/dan200/computercraft/api/upgrades/UpgradeSerialiser.java b/projects/common-api/src/main/java/dan200/computercraft/api/upgrades/UpgradeSerialiser.java deleted file mode 100644 index 0b5aef824..000000000 --- a/projects/common-api/src/main/java/dan200/computercraft/api/upgrades/UpgradeSerialiser.java +++ /dev/null @@ -1,97 +0,0 @@ -// SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers -// -// SPDX-License-Identifier: MPL-2.0 - -package dan200.computercraft.api.upgrades; - -import com.google.gson.JsonObject; -import dan200.computercraft.api.pocket.IPocketUpgrade; -import dan200.computercraft.api.pocket.PocketUpgradeDataProvider; -import dan200.computercraft.api.turtle.ITurtleUpgrade; -import dan200.computercraft.api.turtle.TurtleUpgradeDataProvider; -import dan200.computercraft.impl.upgrades.SerialiserWithCraftingItem; -import dan200.computercraft.impl.upgrades.SimpleSerialiser; -import net.minecraft.core.Registry; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.item.crafting.RecipeSerializer; -import net.minecraft.world.item.crafting.SimpleCraftingRecipeSerializer; - -import java.util.function.BiFunction; -import java.util.function.Function; - -/** - * A serialiser for {@link ITurtleUpgrade} or {@link IPocketUpgrade}s. - *

- * These should be registered in a {@link Registry} while the game is loading, much like {@link RecipeSerializer}s. - *

- * This interface is very similar to {@link RecipeSerializer}; each serialiser should correspond to a specific upgrade - * class. Upgrades are then read from JSON files in datapacks, allowing multiple instances of the upgrade to be - * registered. - *

- * If your upgrade doesn't have any associated configurable parameters (like most upgrades), you can use - * {@link #simple(Function)} or {@link #simpleWithCustomItem(BiFunction)} to create a basic upgrade serialiser. - *

- * Upgrades may be data generated via a {@link UpgradeDataProvider} (see {@link TurtleUpgradeDataProvider} and - * {@link PocketUpgradeDataProvider}). - * - * @param The upgrade that this class can serialise and deserialise. - * @see ITurtleUpgrade - * @see IPocketUpgrade - */ -public interface UpgradeSerialiser { - /** - * Read this upgrade from a JSON file in a datapack. - * - * @param id The ID of this upgrade. - * @param object The JSON object to load this upgrade from. - * @return The constructed upgrade, with a {@link UpgradeBase#getUpgradeID()} equal to {@code id}. - * @see net.minecraft.util.GsonHelper For additional JSON helper methods. - */ - T fromJson(ResourceLocation id, JsonObject object); - - /** - * Read this upgrade from a network packet, sent from the server. - * - * @param id The ID of this upgrade. - * @param buffer The buffer object to read this upgrade from. - * @return The constructed upgrade, with a {@link UpgradeBase#getUpgradeID()} equal to {@code id}. - */ - T fromNetwork(ResourceLocation id, RegistryFriendlyByteBuf buffer); - - /** - * Write this upgrade to a network packet, to be sent to the client. - * - * @param buffer The buffer object to write this upgrade to - * @param upgrade The upgrade to write. - */ - void toNetwork(RegistryFriendlyByteBuf buffer, T upgrade); - - /** - * Create an upgrade serialiser for a simple upgrade. This is similar to a {@link SimpleCraftingRecipeSerializer}, - * but for upgrades. - *

- * If you might want to vary the item, it's suggested you use {@link #simpleWithCustomItem(BiFunction)} instead. - * - * @param factory Generate a new upgrade with a specific ID. - * @param The type of the generated upgrade. - * @return The serialiser for this upgrade - */ - static UpgradeSerialiser simple(Function factory) { - return new SimpleSerialiser<>(factory); - } - - /** - * Create an upgrade serialiser for a simple upgrade whose crafting item can be specified. - * - * @param factory Generate a new upgrade with a specific ID and crafting item. The returned upgrade's - * {@link UpgradeBase#getCraftingItem()} MUST equal the provided item. - * @param The type of the generated upgrade. - * @return The serialiser for this upgrade. - * @see #simple(Function) For upgrades whose crafting stack should not vary. - */ - static UpgradeSerialiser simpleWithCustomItem(BiFunction factory) { - return new SerialiserWithCraftingItem<>(factory); - } -} diff --git a/projects/common-api/src/main/java/dan200/computercraft/api/upgrades/UpgradeType.java b/projects/common-api/src/main/java/dan200/computercraft/api/upgrades/UpgradeType.java new file mode 100644 index 000000000..561255690 --- /dev/null +++ b/projects/common-api/src/main/java/dan200/computercraft/api/upgrades/UpgradeType.java @@ -0,0 +1,89 @@ +// SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers +// +// SPDX-License-Identifier: MPL-2.0 + +package dan200.computercraft.api.upgrades; + +import com.mojang.serialization.MapCodec; +import dan200.computercraft.api.pocket.IPocketUpgrade; +import dan200.computercraft.api.pocket.PocketUpgradeDataProvider; +import dan200.computercraft.api.turtle.ITurtleUpgrade; +import dan200.computercraft.api.turtle.TurtleUpgradeDataProvider; +import dan200.computercraft.impl.upgrades.UpgradeTypeImpl; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.Recipe; +import net.minecraft.world.level.storage.loot.functions.LootItemFunction; + +import java.util.function.Function; + +/** + * The type of a {@linkplain ITurtleUpgrade turtle} or {@linkplain IPocketUpgrade pocket} upgrade. + *

+ * Turtle and pocket computer upgrades are registered using Minecraft's dynamic registry system. As a result, they + * follow a similar design to other dynamic content, such as {@linkplain Recipe recipes} or {@link LootItemFunction + * loot functions}. + *

+ * First, one adds a new class implementing {@link ITurtleUpgrade} or {@link IPocketUpgrade}). This is responsible for + * handling all the logic of your upgrade. + *

+ * However, the upgrades are not registered directly. Instead, each upgrade class should have a corresponding + * {@link UpgradeType}, which is responsible for loading the upgrade from a datapack. The upgrade type should then be + * registered in its appropriate registry ({@link ITurtleUpgrade#typeRegistry()}, + * {@link IPocketUpgrade#typeRegistry()}). + *

+ * In order to register the actual upgrade, a JSON file referencing your upgrade type should be added to a datapack. It + * is recommended to do this via the data generators (see {@link TurtleUpgradeDataProvider} and + * {@link PocketUpgradeDataProvider}). + * + * @param The upgrade subclass that this upgrade type represents. + * @see ITurtleUpgrade + * @see IPocketUpgrade + */ +public interface UpgradeType { + /** + * The codec to read and write this upgrade from a datapack. + * + * @return The codec for this upgrade. + */ + MapCodec codec(); + + /** + * Create a new upgrade type. + * + * @param codec The codec + * @param The type of the generated upgrade. + * @return The newly created upgrade type. + */ + static UpgradeType create(MapCodec codec) { + return new UpgradeTypeImpl<>(codec); + } + + /** + * Create an upgrade type for an upgrade that takes no arguments. + *

+ * If you might want to vary the item, it's suggested you use {@link #simpleWithCustomItem(Function)} instead. + * + * @param instance Generate a new upgrade with a specific ID. + * @param The type of the generated upgrade. + * @return A new upgrade type. + */ + static UpgradeType simple(T instance) { + return create(MapCodec.unit(instance)); + } + + /** + * Create an upgrade type for a simple upgrade whose crafting item can be specified. + * + * @param factory Generate a new upgrade with a specific ID and crafting item. The returned upgrade's + * {@link UpgradeBase#getCraftingItem()} MUST equal the provided item. + * @param The type of the generated upgrade. + * @return A new upgrade type. + * @see #simple(UpgradeBase) For upgrades whose crafting stack should not vary. + */ + static UpgradeType simpleWithCustomItem(Function factory) { + return create(BuiltInRegistries.ITEM.byNameCodec() + .xmap(x -> factory.apply(new ItemStack(x)), x -> x.getCraftingItem().getItem()) + .fieldOf("item")); + } +} diff --git a/projects/common-api/src/main/java/dan200/computercraft/impl/ComputerCraftAPIService.java b/projects/common-api/src/main/java/dan200/computercraft/impl/ComputerCraftAPIService.java index 597bb51c7..a945108a6 100644 --- a/projects/common-api/src/main/java/dan200/computercraft/impl/ComputerCraftAPIService.java +++ b/projects/common-api/src/main/java/dan200/computercraft/impl/ComputerCraftAPIService.java @@ -4,6 +4,7 @@ package dan200.computercraft.impl; +import com.mojang.serialization.Codec; import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.api.detail.BlockReference; import dan200.computercraft.api.detail.DetailRegistry; @@ -19,7 +20,8 @@ import dan200.computercraft.api.pocket.IPocketUpgrade; import dan200.computercraft.api.redstone.BundledRedstoneProvider; import dan200.computercraft.api.turtle.ITurtleUpgrade; import dan200.computercraft.api.turtle.TurtleRefuelHandler; -import dan200.computercraft.api.upgrades.UpgradeSerialiser; +import dan200.computercraft.api.upgrades.UpgradeType; +import dan200.computercraft.impl.upgrades.TurtleToolSpec; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.Registry; @@ -68,9 +70,15 @@ public interface ComputerCraftAPIService { void registerRefuelHandler(TurtleRefuelHandler handler); - ResourceKey>> turtleUpgradeRegistryId(); + ResourceKey>> turtleUpgradeRegistryId(); - ResourceKey>> pocketUpgradeRegistryId(); + Codec turtleUpgradeCodec(); + + ResourceKey>> pocketUpgradeRegistryId(); + + ITurtleUpgrade createTurtleTool(TurtleToolSpec spec); + + Codec pocketUpgradeCodec(); DetailRegistry getItemStackDetailRegistry(); diff --git a/projects/common-api/src/main/java/dan200/computercraft/impl/RegistryHelper.java b/projects/common-api/src/main/java/dan200/computercraft/impl/RegistryHelper.java index 0c1c02ae7..8d64b1ca7 100644 --- a/projects/common-api/src/main/java/dan200/computercraft/impl/RegistryHelper.java +++ b/projects/common-api/src/main/java/dan200/computercraft/impl/RegistryHelper.java @@ -4,6 +4,9 @@ package dan200.computercraft.impl; +import dan200.computercraft.api.ComputerCraftAPI; +import dan200.computercraft.api.pocket.IPocketUpgrade; +import dan200.computercraft.api.turtle.ITurtleUpgrade; import net.minecraft.core.Registry; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.resources.ResourceKey; @@ -15,6 +18,9 @@ import org.jetbrains.annotations.ApiStatus; */ @ApiStatus.Internal public final class RegistryHelper { + public static final ResourceKey> TURTLE_UPGRADE = ResourceKey.createRegistryKey(new ResourceLocation(ComputerCraftAPI.MOD_ID, "turtle_upgrade")); + public static final ResourceKey> POCKET_UPGRADE = ResourceKey.createRegistryKey(new ResourceLocation(ComputerCraftAPI.MOD_ID, "pocket_upgrade")); + private RegistryHelper() { } diff --git a/projects/common-api/src/main/java/dan200/computercraft/impl/upgrades/SerialiserWithCraftingItem.java b/projects/common-api/src/main/java/dan200/computercraft/impl/upgrades/SerialiserWithCraftingItem.java deleted file mode 100644 index a78008936..000000000 --- a/projects/common-api/src/main/java/dan200/computercraft/impl/upgrades/SerialiserWithCraftingItem.java +++ /dev/null @@ -1,49 +0,0 @@ -// SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers -// -// SPDX-License-Identifier: MPL-2.0 - -package dan200.computercraft.impl.upgrades; - -import com.google.gson.JsonObject; -import dan200.computercraft.api.upgrades.UpgradeBase; -import dan200.computercraft.api.upgrades.UpgradeSerialiser; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.util.GsonHelper; -import net.minecraft.world.item.ItemStack; -import org.jetbrains.annotations.ApiStatus; - -import java.util.function.BiFunction; - -/** - * Simple serialiser which returns a constant upgrade with a custom crafting item. - *

- * Do NOT directly reference this class. It exists for internal use by the API. - * - * @param The upgrade that this class can serialise and deserialise. - */ -@ApiStatus.Internal -public final class SerialiserWithCraftingItem implements UpgradeSerialiser { - private final BiFunction factory; - - public SerialiserWithCraftingItem(BiFunction factory) { - this.factory = factory; - } - - @Override - public T fromJson(ResourceLocation id, JsonObject object) { - var item = GsonHelper.getAsItem(object, "item"); - return factory.apply(id, new ItemStack(item)); - } - - @Override - public T fromNetwork(ResourceLocation id, RegistryFriendlyByteBuf buffer) { - var item = ItemStack.STREAM_CODEC.decode(buffer); - return factory.apply(id, item); - } - - @Override - public void toNetwork(RegistryFriendlyByteBuf buffer, T upgrade) { - ItemStack.STREAM_CODEC.encode(buffer, upgrade.getCraftingItem()); - } -} diff --git a/projects/common-api/src/main/java/dan200/computercraft/impl/upgrades/SimpleSerialiser.java b/projects/common-api/src/main/java/dan200/computercraft/impl/upgrades/SimpleSerialiser.java deleted file mode 100644 index fb01bc435..000000000 --- a/projects/common-api/src/main/java/dan200/computercraft/impl/upgrades/SimpleSerialiser.java +++ /dev/null @@ -1,44 +0,0 @@ -// SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers -// -// SPDX-License-Identifier: MPL-2.0 - -package dan200.computercraft.impl.upgrades; - -import com.google.gson.JsonObject; -import dan200.computercraft.api.upgrades.UpgradeBase; -import dan200.computercraft.api.upgrades.UpgradeSerialiser; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.resources.ResourceLocation; -import org.jetbrains.annotations.ApiStatus; - -import java.util.function.Function; - -/** - * Simple serialiser which returns a constant upgrade. - *

- * Do NOT directly reference this class. It exists for internal use by the API. - * - * @param The upgrade that this class can serialise and deserialise. - */ -@ApiStatus.Internal -public final class SimpleSerialiser implements UpgradeSerialiser { - private final Function constructor; - - public SimpleSerialiser(Function constructor) { - this.constructor = constructor; - } - - @Override - public T fromJson(ResourceLocation id, JsonObject object) { - return constructor.apply(id); - } - - @Override - public T fromNetwork(ResourceLocation id, RegistryFriendlyByteBuf buffer) { - return constructor.apply(id); - } - - @Override - public void toNetwork(RegistryFriendlyByteBuf buffer, T upgrade) { - } -} diff --git a/projects/common-api/src/main/java/dan200/computercraft/impl/upgrades/TurtleToolSpec.java b/projects/common-api/src/main/java/dan200/computercraft/impl/upgrades/TurtleToolSpec.java new file mode 100644 index 000000000..53532a629 --- /dev/null +++ b/projects/common-api/src/main/java/dan200/computercraft/impl/upgrades/TurtleToolSpec.java @@ -0,0 +1,50 @@ +// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers +// +// SPDX-License-Identifier: MPL-2.0 + +package dan200.computercraft.impl.upgrades; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import dan200.computercraft.api.turtle.TurtleToolDurability; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.core.registries.Registries; +import net.minecraft.tags.TagKey; +import net.minecraft.world.item.Item; +import net.minecraft.world.level.block.Block; + +import java.util.Optional; + +/** + * The template for a turtle tool. + * + * @param adjective The adjective for this tool. + * @param craftItem The item used to craft this tool. + * @param toolItem The actual tool used. + * @param damageMultiplier The damage multiplier for this tool. + * @param allowEnchantments Whether to allow enchantments. + * @param consumeDurability When to consume durability. + * @param breakable The items breakable by this tool. + */ +public record TurtleToolSpec( + String adjective, + Optional craftItem, + Item toolItem, + float damageMultiplier, + boolean allowEnchantments, + TurtleToolDurability consumeDurability, + Optional> breakable +) { + public static final float DEFAULT_DAMAGE_MULTIPLIER = 3.0f; + + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group( + Codec.STRING.fieldOf("adjective").forGetter(TurtleToolSpec::adjective), + BuiltInRegistries.ITEM.byNameCodec().optionalFieldOf("craftingItem").forGetter(TurtleToolSpec::craftItem), + BuiltInRegistries.ITEM.byNameCodec().fieldOf("item").forGetter(TurtleToolSpec::toolItem), + Codec.FLOAT.optionalFieldOf("damageMultiplier", DEFAULT_DAMAGE_MULTIPLIER).forGetter(TurtleToolSpec::damageMultiplier), + Codec.BOOL.optionalFieldOf("allowEnchantments", false).forGetter(TurtleToolSpec::allowEnchantments), + TurtleToolDurability.CODEC.optionalFieldOf("consumeDurability", TurtleToolDurability.NEVER).forGetter(TurtleToolSpec::consumeDurability), + TagKey.codec(Registries.BLOCK).optionalFieldOf("breakable").forGetter(TurtleToolSpec::breakable) + ).apply(instance, TurtleToolSpec::new)); +} diff --git a/projects/common-api/src/main/java/dan200/computercraft/impl/upgrades/UpgradeTypeImpl.java b/projects/common-api/src/main/java/dan200/computercraft/impl/upgrades/UpgradeTypeImpl.java new file mode 100644 index 000000000..4c3dcd91a --- /dev/null +++ b/projects/common-api/src/main/java/dan200/computercraft/impl/upgrades/UpgradeTypeImpl.java @@ -0,0 +1,20 @@ +// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers +// +// SPDX-License-Identifier: MPL-2.0 + +package dan200.computercraft.impl.upgrades; + +import com.mojang.serialization.MapCodec; +import dan200.computercraft.api.upgrades.UpgradeBase; +import dan200.computercraft.api.upgrades.UpgradeType; +import org.jetbrains.annotations.ApiStatus; + +/** + * Simple implementation of {@link UpgradeType}. + * + * @param codec The codec to read/write upgrades with. + * @param The upgrade subclass that this upgrade type represents. + */ +@ApiStatus.Internal +public record UpgradeTypeImpl(MapCodec codec) implements UpgradeType { +} diff --git a/projects/common/src/client/java/dan200/computercraft/client/ClientHooks.java b/projects/common/src/client/java/dan200/computercraft/client/ClientHooks.java index ea1575ab1..bd45f9310 100644 --- a/projects/common/src/client/java/dan200/computercraft/client/ClientHooks.java +++ b/projects/common/src/client/java/dan200/computercraft/client/ClientHooks.java @@ -128,8 +128,8 @@ public final class ClientHooks { } private static void addTurtleUpgrade(Consumer out, TurtleBlockEntity turtle, TurtleSide side) { - var upgrade = turtle.getUpgrade(side); - if (upgrade != null) out.accept(String.format("Upgrade[%s]: %s", side, upgrade.getUpgradeID())); + var upgrade = turtle.getAccess().getUpgradeWithData(side); + if (upgrade != null) out.accept(String.format("Upgrade[%s]: %s", side, upgrade.holder().key().location())); } /** diff --git a/projects/common/src/client/java/dan200/computercraft/client/ClientRegistry.java b/projects/common/src/client/java/dan200/computercraft/client/ClientRegistry.java index 09ba31986..d75ae2986 100644 --- a/projects/common/src/client/java/dan200/computercraft/client/ClientRegistry.java +++ b/projects/common/src/client/java/dan200/computercraft/client/ClientRegistry.java @@ -117,17 +117,17 @@ public final class ClientRegistry { } public static void registerTurtleModellers(RegisterTurtleUpgradeModeller register) { - register.register(ModRegistry.TurtleSerialisers.SPEAKER.get(), TurtleUpgradeModeller.sided( + register.register(ModRegistry.TurtleUpgradeTypes.SPEAKER.get(), TurtleUpgradeModeller.sided( new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_speaker_left"), new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_speaker_right") )); - register.register(ModRegistry.TurtleSerialisers.WORKBENCH.get(), TurtleUpgradeModeller.sided( + register.register(ModRegistry.TurtleUpgradeTypes.WORKBENCH.get(), TurtleUpgradeModeller.sided( new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_crafting_table_left"), new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_crafting_table_right") )); - register.register(ModRegistry.TurtleSerialisers.WIRELESS_MODEM_NORMAL.get(), new TurtleModemModeller(false)); - register.register(ModRegistry.TurtleSerialisers.WIRELESS_MODEM_ADVANCED.get(), new TurtleModemModeller(true)); - register.register(ModRegistry.TurtleSerialisers.TOOL.get(), TurtleUpgradeModeller.flatItem()); + register.register(ModRegistry.TurtleUpgradeTypes.WIRELESS_MODEM_NORMAL.get(), new TurtleModemModeller(false)); + register.register(ModRegistry.TurtleUpgradeTypes.WIRELESS_MODEM_ADVANCED.get(), new TurtleModemModeller(true)); + register.register(ModRegistry.TurtleUpgradeTypes.TOOL.get(), TurtleUpgradeModeller.flatItem()); } @SafeVarargs diff --git a/projects/common/src/client/java/dan200/computercraft/client/turtle/TurtleUpgradeModellers.java b/projects/common/src/client/java/dan200/computercraft/client/turtle/TurtleUpgradeModellers.java index 988d5134d..7d62afc67 100644 --- a/projects/common/src/client/java/dan200/computercraft/client/turtle/TurtleUpgradeModellers.java +++ b/projects/common/src/client/java/dan200/computercraft/client/turtle/TurtleUpgradeModellers.java @@ -10,9 +10,8 @@ import dan200.computercraft.api.client.turtle.TurtleUpgradeModeller; import dan200.computercraft.api.turtle.ITurtleAccess; import dan200.computercraft.api.turtle.ITurtleUpgrade; import dan200.computercraft.api.turtle.TurtleSide; -import dan200.computercraft.api.upgrades.UpgradeSerialiser; +import dan200.computercraft.api.upgrades.UpgradeType; import dan200.computercraft.impl.RegistryHelper; -import dan200.computercraft.impl.TurtleUpgrades; import dan200.computercraft.impl.UpgradeManager; import net.minecraft.client.Minecraft; import net.minecraft.core.component.DataComponentPatch; @@ -30,7 +29,7 @@ public final class TurtleUpgradeModellers { private static final TurtleUpgradeModeller NULL_TURTLE_MODELLER = (upgrade, turtle, side, data) -> new TransformedModel(Minecraft.getInstance().getModelManager().getMissingModel(), Transformation.identity()); - private static final Map, TurtleUpgradeModeller> turtleModels = new ConcurrentHashMap<>(); + private static final Map, TurtleUpgradeModeller> turtleModels = new ConcurrentHashMap<>(); private static volatile boolean fetchedModels; /** @@ -44,15 +43,15 @@ public final class TurtleUpgradeModellers { private TurtleUpgradeModellers() { } - public static void register(UpgradeSerialiser serialiser, TurtleUpgradeModeller modeller) { + public static void register(UpgradeType type, TurtleUpgradeModeller modeller) { if (fetchedModels) { throw new IllegalStateException(String.format( - "Turtle upgrade serialiser %s must be registered before models are baked.", - RegistryHelper.getKeyOrThrow(RegistryHelper.getRegistry(ITurtleUpgrade.serialiserRegistryKey()), serialiser) + "Turtle upgrade type %s must be registered before models are baked.", + RegistryHelper.getKeyOrThrow(RegistryHelper.getRegistry(ITurtleUpgrade.typeRegistry()), type) )); } - if (turtleModels.putIfAbsent(serialiser, modeller) != null) { + if (turtleModels.putIfAbsent(type, modeller) != null) { throw new IllegalStateException("Modeller already registered for serialiser"); } } @@ -69,11 +68,8 @@ public final class TurtleUpgradeModellers { return modeller.getModel(upgrade, null, side, data); } - private static TurtleUpgradeModeller getModeller(ITurtleUpgrade upgradeA) { - var wrapper = TurtleUpgrades.instance().getWrapper(upgradeA); - if (wrapper == null) return NULL_TURTLE_MODELLER; - - var modeller = turtleModels.get(wrapper.serialiser()); + private static TurtleUpgradeModeller getModeller(ITurtleUpgrade upgrade) { + var modeller = turtleModels.get(upgrade.getType()); return modeller == null ? NULL_TURTLE_MODELLER : modeller; } diff --git a/projects/common/src/generated/resources/data/computercraft/computercraft/pocket_upgrades/speaker.json b/projects/common/src/generated/resources/data/computercraft/computercraft/pocket_upgrade/speaker.json similarity index 100% rename from projects/common/src/generated/resources/data/computercraft/computercraft/pocket_upgrades/speaker.json rename to projects/common/src/generated/resources/data/computercraft/computercraft/pocket_upgrade/speaker.json diff --git a/projects/common/src/generated/resources/data/computercraft/computercraft/pocket_upgrades/wireless_modem_advanced.json b/projects/common/src/generated/resources/data/computercraft/computercraft/pocket_upgrade/wireless_modem_advanced.json similarity index 100% rename from projects/common/src/generated/resources/data/computercraft/computercraft/pocket_upgrades/wireless_modem_advanced.json rename to projects/common/src/generated/resources/data/computercraft/computercraft/pocket_upgrade/wireless_modem_advanced.json diff --git a/projects/common/src/generated/resources/data/computercraft/computercraft/pocket_upgrades/wireless_modem_normal.json b/projects/common/src/generated/resources/data/computercraft/computercraft/pocket_upgrade/wireless_modem_normal.json similarity index 100% rename from projects/common/src/generated/resources/data/computercraft/computercraft/pocket_upgrades/wireless_modem_normal.json rename to projects/common/src/generated/resources/data/computercraft/computercraft/pocket_upgrade/wireless_modem_normal.json diff --git a/projects/common/src/generated/resources/data/computercraft/computercraft/turtle_upgrades/speaker.json b/projects/common/src/generated/resources/data/computercraft/computercraft/turtle_upgrade/speaker.json similarity index 100% rename from projects/common/src/generated/resources/data/computercraft/computercraft/turtle_upgrades/speaker.json rename to projects/common/src/generated/resources/data/computercraft/computercraft/turtle_upgrade/speaker.json diff --git a/projects/common/src/generated/resources/data/computercraft/computercraft/turtle_upgrades/wireless_modem_advanced.json b/projects/common/src/generated/resources/data/computercraft/computercraft/turtle_upgrade/wireless_modem_advanced.json similarity index 100% rename from projects/common/src/generated/resources/data/computercraft/computercraft/turtle_upgrades/wireless_modem_advanced.json rename to projects/common/src/generated/resources/data/computercraft/computercraft/turtle_upgrade/wireless_modem_advanced.json diff --git a/projects/common/src/generated/resources/data/computercraft/computercraft/turtle_upgrades/wireless_modem_normal.json b/projects/common/src/generated/resources/data/computercraft/computercraft/turtle_upgrade/wireless_modem_normal.json similarity index 100% rename from projects/common/src/generated/resources/data/computercraft/computercraft/turtle_upgrades/wireless_modem_normal.json rename to projects/common/src/generated/resources/data/computercraft/computercraft/turtle_upgrade/wireless_modem_normal.json diff --git a/projects/common/src/generated/resources/data/minecraft/computercraft/turtle_upgrades/crafting_table.json b/projects/common/src/generated/resources/data/minecraft/computercraft/turtle_upgrade/crafting_table.json similarity index 100% rename from projects/common/src/generated/resources/data/minecraft/computercraft/turtle_upgrades/crafting_table.json rename to projects/common/src/generated/resources/data/minecraft/computercraft/turtle_upgrade/crafting_table.json diff --git a/projects/common/src/generated/resources/data/minecraft/computercraft/turtle_upgrade/diamond_axe.json b/projects/common/src/generated/resources/data/minecraft/computercraft/turtle_upgrade/diamond_axe.json new file mode 100644 index 000000000..c94316b76 --- /dev/null +++ b/projects/common/src/generated/resources/data/minecraft/computercraft/turtle_upgrade/diamond_axe.json @@ -0,0 +1,6 @@ +{ + "type": "computercraft:tool", + "adjective": "upgrade.minecraft.diamond_axe.adjective", + "damageMultiplier": 6.0, + "item": "minecraft:diamond_axe" +} diff --git a/projects/common/src/generated/resources/data/minecraft/computercraft/turtle_upgrade/diamond_hoe.json b/projects/common/src/generated/resources/data/minecraft/computercraft/turtle_upgrade/diamond_hoe.json new file mode 100644 index 000000000..66e945689 --- /dev/null +++ b/projects/common/src/generated/resources/data/minecraft/computercraft/turtle_upgrade/diamond_hoe.json @@ -0,0 +1,6 @@ +{ + "type": "computercraft:tool", + "adjective": "upgrade.minecraft.diamond_hoe.adjective", + "breakable": "computercraft:turtle_hoe_harvestable", + "item": "minecraft:diamond_hoe" +} diff --git a/projects/common/src/generated/resources/data/minecraft/computercraft/turtle_upgrade/diamond_pickaxe.json b/projects/common/src/generated/resources/data/minecraft/computercraft/turtle_upgrade/diamond_pickaxe.json new file mode 100644 index 000000000..6d5793315 --- /dev/null +++ b/projects/common/src/generated/resources/data/minecraft/computercraft/turtle_upgrade/diamond_pickaxe.json @@ -0,0 +1,5 @@ +{ + "type": "computercraft:tool", + "adjective": "upgrade.minecraft.diamond_pickaxe.adjective", + "item": "minecraft:diamond_pickaxe" +} diff --git a/projects/common/src/generated/resources/data/minecraft/computercraft/turtle_upgrades/diamond_shovel.json b/projects/common/src/generated/resources/data/minecraft/computercraft/turtle_upgrade/diamond_shovel.json similarity index 68% rename from projects/common/src/generated/resources/data/minecraft/computercraft/turtle_upgrades/diamond_shovel.json rename to projects/common/src/generated/resources/data/minecraft/computercraft/turtle_upgrade/diamond_shovel.json index c3d57f601..0584f4e97 100644 --- a/projects/common/src/generated/resources/data/minecraft/computercraft/turtle_upgrades/diamond_shovel.json +++ b/projects/common/src/generated/resources/data/minecraft/computercraft/turtle_upgrade/diamond_shovel.json @@ -1,5 +1,6 @@ { "type": "computercraft:tool", + "adjective": "upgrade.minecraft.diamond_shovel.adjective", "breakable": "computercraft:turtle_shovel_harvestable", "item": "minecraft:diamond_shovel" } diff --git a/projects/common/src/generated/resources/data/minecraft/computercraft/turtle_upgrades/diamond_sword.json b/projects/common/src/generated/resources/data/minecraft/computercraft/turtle_upgrade/diamond_sword.json similarity index 72% rename from projects/common/src/generated/resources/data/minecraft/computercraft/turtle_upgrades/diamond_sword.json rename to projects/common/src/generated/resources/data/minecraft/computercraft/turtle_upgrade/diamond_sword.json index 2899cfd19..5cdae267d 100644 --- a/projects/common/src/generated/resources/data/minecraft/computercraft/turtle_upgrades/diamond_sword.json +++ b/projects/common/src/generated/resources/data/minecraft/computercraft/turtle_upgrade/diamond_sword.json @@ -1,5 +1,6 @@ { "type": "computercraft:tool", + "adjective": "upgrade.minecraft.diamond_sword.adjective", "breakable": "computercraft:turtle_sword_harvestable", "damageMultiplier": 9.0, "item": "minecraft:diamond_sword" diff --git a/projects/common/src/generated/resources/data/minecraft/computercraft/turtle_upgrades/diamond_axe.json b/projects/common/src/generated/resources/data/minecraft/computercraft/turtle_upgrades/diamond_axe.json deleted file mode 100644 index 5ecefb5e6..000000000 --- a/projects/common/src/generated/resources/data/minecraft/computercraft/turtle_upgrades/diamond_axe.json +++ /dev/null @@ -1 +0,0 @@ -{"type": "computercraft:tool", "damageMultiplier": 6.0, "item": "minecraft:diamond_axe"} diff --git a/projects/common/src/generated/resources/data/minecraft/computercraft/turtle_upgrades/diamond_hoe.json b/projects/common/src/generated/resources/data/minecraft/computercraft/turtle_upgrades/diamond_hoe.json deleted file mode 100644 index 66a5cf9fa..000000000 --- a/projects/common/src/generated/resources/data/minecraft/computercraft/turtle_upgrades/diamond_hoe.json +++ /dev/null @@ -1 +0,0 @@ -{"type": "computercraft:tool", "breakable": "computercraft:turtle_hoe_harvestable", "item": "minecraft:diamond_hoe"} diff --git a/projects/common/src/generated/resources/data/minecraft/computercraft/turtle_upgrades/diamond_pickaxe.json b/projects/common/src/generated/resources/data/minecraft/computercraft/turtle_upgrades/diamond_pickaxe.json deleted file mode 100644 index 1bced4e59..000000000 --- a/projects/common/src/generated/resources/data/minecraft/computercraft/turtle_upgrades/diamond_pickaxe.json +++ /dev/null @@ -1 +0,0 @@ -{"type": "computercraft:tool", "item": "minecraft:diamond_pickaxe"} diff --git a/projects/common/src/main/java/dan200/computercraft/data/DataProviders.java b/projects/common/src/main/java/dan200/computercraft/data/DataProviders.java index 54f773df8..3c7008209 100644 --- a/projects/common/src/main/java/dan200/computercraft/data/DataProviders.java +++ b/projects/common/src/main/java/dan200/computercraft/data/DataProviders.java @@ -5,17 +5,20 @@ package dan200.computercraft.data; import com.mojang.serialization.Codec; +import dan200.computercraft.shared.ModRegistry; import net.minecraft.core.HolderLookup; +import net.minecraft.core.RegistrySetBuilder; import net.minecraft.data.DataProvider; import net.minecraft.data.PackOutput; +import net.minecraft.data.registries.RegistryPatchGenerator; import net.minecraft.data.tags.TagsProvider; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.packs.PackType; import net.minecraft.world.item.Item; import net.minecraft.world.level.block.Block; -import java.util.concurrent.CompletableFuture; import java.util.Set; +import java.util.concurrent.CompletableFuture; import java.util.function.BiConsumer; import java.util.function.BiFunction; import java.util.function.Consumer; @@ -31,7 +34,14 @@ public final class DataProviders { public static void add(GeneratorSink generator) { var turtleUpgrades = generator.add(TurtleUpgradeProvider::new); var pocketUpgrades = generator.add(PocketUpgradeProvider::new); - generator.add((out, registries) -> new RecipeProvider(out, registries, turtleUpgrades, pocketUpgrades)); + + generator.add((out, registries) -> { + var builder = new RegistrySetBuilder(); + builder.add(ModRegistry.TURTLE_UPGRADE, bs -> turtleUpgrades.getGeneratedUpgrades().forEach(bs::register)); + builder.add(ModRegistry.POCKET_UPGRADE, bs -> pocketUpgrades.getGeneratedUpgrades().forEach(bs::register)); + + return new RecipeProvider(out, generator.createPatchedRegistries(registries, builder).thenApply(RegistrySetBuilder.PatchedRegistries::full)); + }); var blockTags = generator.blockTags(TagProvider::blockTags); generator.itemTags(TagProvider::itemTags, blockTags); @@ -62,5 +72,18 @@ public final class DataProviders { TagsProvider blockTags(Consumer> tags); TagsProvider itemTags(Consumer tags, TagsProvider blocks); + + /** + * Extend our registries with additional entries. + * + * @param registries The existing registries. + * @param patch The new registries to apply. + * @return The built registries. + */ + default CompletableFuture createPatchedRegistries( + CompletableFuture registries, RegistrySetBuilder patch + ) { + return RegistryPatchGenerator.createLookup(registries, patch); + } } } diff --git a/projects/common/src/main/java/dan200/computercraft/data/LanguageProvider.java b/projects/common/src/main/java/dan200/computercraft/data/LanguageProvider.java index 497198fe4..12780478d 100644 --- a/projects/common/src/main/java/dan200/computercraft/data/LanguageProvider.java +++ b/projects/common/src/main/java/dan200/computercraft/data/LanguageProvider.java @@ -287,8 +287,8 @@ public final class LanguageProvider implements DataProvider { BuiltInRegistries.ITEM.holders() .filter(x -> x.key().location().getNamespace().equals(ComputerCraftAPI.MOD_ID)) .map(x -> x.value().getDescriptionId()), - turtleUpgrades.getGeneratedUpgrades().stream().map(UpgradeBase::getUnlocalisedAdjective), - pocketUpgrades.getGeneratedUpgrades().stream().map(UpgradeBase::getUnlocalisedAdjective), + turtleUpgrades.getGeneratedUpgrades().values().stream().map(UpgradeBase::getUnlocalisedAdjective), + pocketUpgrades.getGeneratedUpgrades().values().stream().map(UpgradeBase::getUnlocalisedAdjective), Metric.metrics().values().stream().map(x -> AggregatedMetric.TRANSLATION_PREFIX + x.name() + ".name"), ConfigSpec.serverSpec.entries().map(ConfigFile.Entry::translationKey), ConfigSpec.clientSpec.entries().map(ConfigFile.Entry::translationKey), diff --git a/projects/common/src/main/java/dan200/computercraft/data/PocketUpgradeProvider.java b/projects/common/src/main/java/dan200/computercraft/data/PocketUpgradeProvider.java index 4c0f61cd0..74c7a080c 100644 --- a/projects/common/src/main/java/dan200/computercraft/data/PocketUpgradeProvider.java +++ b/projects/common/src/main/java/dan200/computercraft/data/PocketUpgradeProvider.java @@ -7,14 +7,15 @@ package dan200.computercraft.data; import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.api.pocket.IPocketUpgrade; import dan200.computercraft.api.pocket.PocketUpgradeDataProvider; -import dan200.computercraft.api.upgrades.UpgradeSerialiser; +import dan200.computercraft.shared.pocket.peripherals.PocketModem; +import dan200.computercraft.shared.pocket.peripherals.PocketSpeaker; import net.minecraft.data.PackOutput; import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; import java.util.function.Consumer; import static dan200.computercraft.shared.ModRegistry.Items; -import static dan200.computercraft.shared.ModRegistry.PocketUpgradeSerialisers; class PocketUpgradeProvider extends PocketUpgradeDataProvider { PocketUpgradeProvider(PackOutput output) { @@ -22,10 +23,10 @@ class PocketUpgradeProvider extends PocketUpgradeDataProvider { } @Override - protected void addUpgrades(Consumer>> addUpgrade) { - addUpgrade.accept(simpleWithCustomItem(id("speaker"), PocketUpgradeSerialisers.SPEAKER.get(), Items.SPEAKER.get())); - simpleWithCustomItem(id("wireless_modem_normal"), PocketUpgradeSerialisers.WIRELESS_MODEM_NORMAL.get(), Items.WIRELESS_MODEM_NORMAL.get()).add(addUpgrade); - simpleWithCustomItem(id("wireless_modem_advanced"), PocketUpgradeSerialisers.WIRELESS_MODEM_ADVANCED.get(), Items.WIRELESS_MODEM_ADVANCED.get()).add(addUpgrade); + protected void addUpgrades(Consumer> addUpgrade) { + upgrade(id("speaker"), new PocketSpeaker(new ItemStack(Items.SPEAKER.get()))).add(addUpgrade); + upgrade(id("wireless_modem_normal"), new PocketModem(new ItemStack(Items.WIRELESS_MODEM_NORMAL.get()), false)).add(addUpgrade); + upgrade(id("wireless_modem_advanced"), new PocketModem(new ItemStack(Items.WIRELESS_MODEM_ADVANCED.get()), true)).add(addUpgrade); } private static ResourceLocation id(String id) { diff --git a/projects/common/src/main/java/dan200/computercraft/data/RecipeProvider.java b/projects/common/src/main/java/dan200/computercraft/data/RecipeProvider.java index 152cdd97b..bc2f6f139 100644 --- a/projects/common/src/main/java/dan200/computercraft/data/RecipeProvider.java +++ b/projects/common/src/main/java/dan200/computercraft/data/RecipeProvider.java @@ -8,8 +8,6 @@ import com.google.gson.JsonObject; import com.mojang.authlib.GameProfile; import com.mojang.serialization.JsonOps; import dan200.computercraft.api.ComputerCraftAPI; -import dan200.computercraft.api.pocket.PocketUpgradeDataProvider; -import dan200.computercraft.api.turtle.TurtleUpgradeDataProvider; import dan200.computercraft.api.upgrades.UpgradeData; import dan200.computercraft.core.util.Colour; import dan200.computercraft.data.recipe.ShapedSpecBuilder; @@ -61,28 +59,43 @@ import net.minecraft.world.level.block.Blocks; import java.util.List; import java.util.UUID; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutionException; import java.util.function.Consumer; +import java.util.stream.Stream; import static dan200.computercraft.api.ComputerCraftTags.Items.COMPUTER; import static dan200.computercraft.api.ComputerCraftTags.Items.WIRED_MODEM; final class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider { private final RecipeIngredients ingredients = PlatformHelper.get().getRecipeIngredients(); - private final TurtleUpgradeDataProvider turtleUpgrades; - private final PocketUpgradeDataProvider pocketUpgrades; - RecipeProvider(PackOutput output, CompletableFuture registries, TurtleUpgradeDataProvider turtleUpgrades, PocketUpgradeDataProvider pocketUpgrades) { + private final CompletableFuture registries; + + RecipeProvider(PackOutput output, CompletableFuture registries) { super(output, registries); - this.turtleUpgrades = turtleUpgrades; - this.pocketUpgrades = pocketUpgrades; + this.registries = registries; + } + + private HolderLookup.Provider registries() { + try { + return registries.get(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + throw new RuntimeException("Interrupted"); + } catch (ExecutionException e) { + var cause = e.getCause(); + throw cause instanceof RuntimeException rt ? rt : new RuntimeException("Unexpected error", cause); + } } @Override public void buildRecipes(RecipeOutput add) { + var registries = registries(); + basicRecipes(add); diskColours(add); - pocketUpgrades(add); - turtleUpgrades(add); + pocketUpgrades(add, registries); + turtleUpgrades(add, registries); turtleOverlays(add); addSpecial(add, new PrintoutRecipe(CraftingBookCategory.MISC)); @@ -119,15 +132,17 @@ final class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider { /** * Register a crafting recipe for each turtle upgrade. * - * @param add The callback to add recipes. + * @param add The callback to add recipes. + * @param registries The currently available registries. */ - private void turtleUpgrades(RecipeOutput add) { + private void turtleUpgrades(RecipeOutput add, HolderLookup.Provider registries) { for (var turtleItem : turtleItems()) { var name = RegistryHelper.getKeyOrThrow(BuiltInRegistries.ITEM, turtleItem); - for (var upgrade : turtleUpgrades.getGeneratedUpgrades()) { + registries.lookup(ModRegistry.TURTLE_UPGRADE).map(HolderLookup::listElements).orElse(Stream.empty()).forEach(upgradeHolder -> { + var upgrade = upgradeHolder.value(); ShapedSpecBuilder - .shaped(RecipeCategory.REDSTONE, DataComponentUtil.createStack(turtleItem, ModRegistry.DataComponents.RIGHT_TURTLE_UPGRADE.get(), UpgradeData.ofDefault(upgrade))) + .shaped(RecipeCategory.REDSTONE, DataComponentUtil.createStack(turtleItem, ModRegistry.DataComponents.RIGHT_TURTLE_UPGRADE.get(), UpgradeData.ofDefault(upgradeHolder))) .group(name.toString()) .pattern("#T") .define('T', turtleItem) @@ -136,9 +151,9 @@ final class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider { .build(ImpostorShapedRecipe::new) .save( add, - name.withSuffix(String.format("/%s/%s", upgrade.getUpgradeID().getNamespace(), upgrade.getUpgradeID().getPath())) + name.withSuffix(String.format("/%s/%s", upgradeHolder.key().location().getNamespace(), upgradeHolder.key().location().getPath())) ); - } + }); } } @@ -149,15 +164,17 @@ final class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider { /** * Register a crafting recipe for each pocket upgrade. * - * @param add The callback to add recipes. + * @param add The callback to add recipes. + * @param registries The currently available registries. */ - private void pocketUpgrades(RecipeOutput add) { + private void pocketUpgrades(RecipeOutput add, HolderLookup.Provider registries) { for (var pocket : pocketComputerItems()) { var name = RegistryHelper.getKeyOrThrow(BuiltInRegistries.ITEM, pocket).withPath(x -> x.replace("pocket_computer_", "pocket_")); - for (var upgrade : pocketUpgrades.getGeneratedUpgrades()) { + registries.lookup(ModRegistry.POCKET_UPGRADE).map(HolderLookup::listElements).orElse(Stream.empty()).forEach(upgradeHolder -> { + var upgrade = upgradeHolder.value(); ShapedSpecBuilder - .shaped(RecipeCategory.REDSTONE, DataComponentUtil.createStack(pocket, ModRegistry.DataComponents.POCKET_UPGRADE.get(), UpgradeData.ofDefault(upgrade))) + .shaped(RecipeCategory.REDSTONE, DataComponentUtil.createStack(pocket, ModRegistry.DataComponents.POCKET_UPGRADE.get(), UpgradeData.ofDefault(upgradeHolder))) .group(name.toString()) .pattern("#") .pattern("P") @@ -167,9 +184,9 @@ final class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider { .build(ImpostorShapedRecipe::new) .save( add, - name.withSuffix(String.format("/%s/%s", upgrade.getUpgradeID().getNamespace(), upgrade.getUpgradeID().getPath())) + name.withSuffix(String.format("/%s/%s", upgradeHolder.key().location().getNamespace(), upgradeHolder.key().location().getPath())) ); - } + }); } } diff --git a/projects/common/src/main/java/dan200/computercraft/data/TurtleUpgradeProvider.java b/projects/common/src/main/java/dan200/computercraft/data/TurtleUpgradeProvider.java index fac1cb72f..b740b85f4 100644 --- a/projects/common/src/main/java/dan200/computercraft/data/TurtleUpgradeProvider.java +++ b/projects/common/src/main/java/dan200/computercraft/data/TurtleUpgradeProvider.java @@ -8,32 +8,34 @@ import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.api.ComputerCraftTags.Blocks; import dan200.computercraft.api.turtle.ITurtleUpgrade; import dan200.computercraft.api.turtle.TurtleUpgradeDataProvider; -import dan200.computercraft.api.upgrades.UpgradeSerialiser; +import dan200.computercraft.shared.ModRegistry; +import dan200.computercraft.shared.turtle.upgrades.TurtleCraftingTable; +import dan200.computercraft.shared.turtle.upgrades.TurtleModem; +import dan200.computercraft.shared.turtle.upgrades.TurtleSpeaker; import net.minecraft.data.PackOutput; import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; import java.util.function.Consumer; -import static dan200.computercraft.shared.ModRegistry.Items; -import static dan200.computercraft.shared.ModRegistry.TurtleSerialisers; - class TurtleUpgradeProvider extends TurtleUpgradeDataProvider { TurtleUpgradeProvider(PackOutput output) { super(output); } @Override - protected void addUpgrades(Consumer>> addUpgrade) { - simpleWithCustomItem(id("speaker"), TurtleSerialisers.SPEAKER.get(), Items.SPEAKER.get()).add(addUpgrade); - simpleWithCustomItem(vanilla("crafting_table"), TurtleSerialisers.WORKBENCH.get(), net.minecraft.world.item.Items.CRAFTING_TABLE).add(addUpgrade); - simpleWithCustomItem(id("wireless_modem_normal"), TurtleSerialisers.WIRELESS_MODEM_NORMAL.get(), Items.WIRELESS_MODEM_NORMAL.get()).add(addUpgrade); - simpleWithCustomItem(id("wireless_modem_advanced"), TurtleSerialisers.WIRELESS_MODEM_ADVANCED.get(), Items.WIRELESS_MODEM_ADVANCED.get()).add(addUpgrade); + protected void addUpgrades(Consumer> addUpgrade) { + upgrade(id("speaker"), new TurtleSpeaker(new ItemStack(ModRegistry.Items.SPEAKER.get()))).add(addUpgrade); + upgrade(vanilla("crafting_table"), new TurtleCraftingTable(new ItemStack(Items.CRAFTING_TABLE))).add(addUpgrade); + upgrade(id("wireless_modem_normal"), new TurtleModem(new ItemStack(ModRegistry.Items.WIRELESS_MODEM_NORMAL.get()), false)).add(addUpgrade); + upgrade(id("wireless_modem_advanced"), new TurtleModem(new ItemStack(ModRegistry.Items.WIRELESS_MODEM_ADVANCED.get()), true)).add(addUpgrade); - tool(vanilla("diamond_axe"), net.minecraft.world.item.Items.DIAMOND_AXE).damageMultiplier(6.0f).add(addUpgrade); - tool(vanilla("diamond_pickaxe"), net.minecraft.world.item.Items.DIAMOND_PICKAXE).add(addUpgrade); - tool(vanilla("diamond_hoe"), net.minecraft.world.item.Items.DIAMOND_HOE).breakable(Blocks.TURTLE_HOE_BREAKABLE).add(addUpgrade); - tool(vanilla("diamond_shovel"), net.minecraft.world.item.Items.DIAMOND_SHOVEL).breakable(Blocks.TURTLE_SHOVEL_BREAKABLE).add(addUpgrade); - tool(vanilla("diamond_sword"), net.minecraft.world.item.Items.DIAMOND_SWORD).breakable(Blocks.TURTLE_SWORD_BREAKABLE).damageMultiplier(9.0f).add(addUpgrade); + tool(vanilla("diamond_axe"), Items.DIAMOND_AXE).damageMultiplier(6.0f).add(addUpgrade); + tool(vanilla("diamond_pickaxe"), Items.DIAMOND_PICKAXE).add(addUpgrade); + tool(vanilla("diamond_hoe"), Items.DIAMOND_HOE).breakable(Blocks.TURTLE_HOE_BREAKABLE).add(addUpgrade); + tool(vanilla("diamond_shovel"), Items.DIAMOND_SHOVEL).breakable(Blocks.TURTLE_SHOVEL_BREAKABLE).add(addUpgrade); + tool(vanilla("diamond_sword"), Items.DIAMOND_SWORD).breakable(Blocks.TURTLE_SWORD_BREAKABLE).damageMultiplier(9.0f).add(addUpgrade); } private static ResourceLocation id(String id) { diff --git a/projects/common/src/main/java/dan200/computercraft/impl/AbstractComputerCraftAPI.java b/projects/common/src/main/java/dan200/computercraft/impl/AbstractComputerCraftAPI.java index 42e8a0f7b..4652c35e0 100644 --- a/projects/common/src/main/java/dan200/computercraft/impl/AbstractComputerCraftAPI.java +++ b/projects/common/src/main/java/dan200/computercraft/impl/AbstractComputerCraftAPI.java @@ -4,6 +4,7 @@ package dan200.computercraft.impl; +import com.mojang.serialization.Codec; import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.api.detail.BlockReference; import dan200.computercraft.api.detail.DetailRegistry; @@ -19,14 +20,16 @@ import dan200.computercraft.api.pocket.IPocketUpgrade; import dan200.computercraft.api.redstone.BundledRedstoneProvider; import dan200.computercraft.api.turtle.ITurtleUpgrade; import dan200.computercraft.api.turtle.TurtleRefuelHandler; -import dan200.computercraft.api.upgrades.UpgradeSerialiser; +import dan200.computercraft.api.upgrades.UpgradeType; import dan200.computercraft.core.filesystem.WritableFileMount; import dan200.computercraft.impl.detail.DetailRegistryImpl; import dan200.computercraft.impl.network.wired.WiredNodeImpl; +import dan200.computercraft.impl.upgrades.TurtleToolSpec; import dan200.computercraft.shared.computer.core.ResourceMount; import dan200.computercraft.shared.computer.core.ServerContext; import dan200.computercraft.shared.details.BlockDetails; import dan200.computercraft.shared.details.ItemDetails; +import dan200.computercraft.shared.turtle.upgrades.TurtleTool; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.Registry; @@ -45,8 +48,8 @@ public abstract class AbstractComputerCraftAPI implements ComputerCraftAPIServic private final DetailRegistry itemStackDetails = new DetailRegistryImpl<>(ItemDetails::fillBasic); private final DetailRegistry blockDetails = new DetailRegistryImpl<>(BlockDetails::fillBasic); - protected static final ResourceKey>> turtleUpgradeRegistryId = ResourceKey.createRegistryKey(new ResourceLocation(ComputerCraftAPI.MOD_ID, "turtle_upgrade_serialiser")); - protected static final ResourceKey>> pocketUpgradeRegistryId = ResourceKey.createRegistryKey(new ResourceLocation(ComputerCraftAPI.MOD_ID, "pocket_upgrade_serialiser")); + protected static final ResourceKey>> turtleUpgradeRegistryId = ResourceKey.createRegistryKey(new ResourceLocation(ComputerCraftAPI.MOD_ID, "turtle_upgrade_type")); + protected static final ResourceKey>> pocketUpgradeRegistryId = ResourceKey.createRegistryKey(new ResourceLocation(ComputerCraftAPI.MOD_ID, "pocket_upgrade_type")); public static @Nullable InputStream getResourceFile(MinecraftServer server, String domain, String subPath) { var manager = server.getResourceManager(); @@ -117,15 +120,30 @@ public abstract class AbstractComputerCraftAPI implements ComputerCraftAPIServic } @Override - public final ResourceKey>> turtleUpgradeRegistryId() { + public final ResourceKey>> turtleUpgradeRegistryId() { return turtleUpgradeRegistryId; } @Override - public final ResourceKey>> pocketUpgradeRegistryId() { + public Codec turtleUpgradeCodec() { + return TurtleUpgrades.instance().upgradeCodec(); + } + + @Override + public ITurtleUpgrade createTurtleTool(TurtleToolSpec spec) { + return new TurtleTool(spec); + } + + @Override + public final ResourceKey>> pocketUpgradeRegistryId() { return pocketUpgradeRegistryId; } + @Override + public Codec pocketUpgradeCodec() { + return PocketUpgrades.instance().upgradeCodec(); + } + @Override public final DetailRegistry getItemStackDetailRegistry() { return itemStackDetails; diff --git a/projects/common/src/main/java/dan200/computercraft/impl/PocketUpgrades.java b/projects/common/src/main/java/dan200/computercraft/impl/PocketUpgrades.java index 93030adc9..e77856a92 100644 --- a/projects/common/src/main/java/dan200/computercraft/impl/PocketUpgrades.java +++ b/projects/common/src/main/java/dan200/computercraft/impl/PocketUpgrades.java @@ -4,14 +4,12 @@ package dan200.computercraft.impl; -import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.api.pocket.IPocketUpgrade; - -import java.util.stream.Stream; +import dan200.computercraft.shared.ModRegistry; public final class PocketUpgrades { private static final UpgradeManager registry = new UpgradeManager<>( - "pocket computer upgrade", "computercraft/pocket_upgrades", IPocketUpgrade.serialiserRegistryKey() + IPocketUpgrade.typeRegistry(), ModRegistry.POCKET_UPGRADE, IPocketUpgrade::getType ); private PocketUpgrades() { @@ -20,10 +18,4 @@ public final class PocketUpgrades { public static UpgradeManager instance() { return registry; } - - public static Stream getVanillaUpgrades() { - return instance().getUpgradeWrappers().values().stream() - .filter(x -> x.modId().equals(ComputerCraftAPI.MOD_ID)) - .map(UpgradeManager.UpgradeWrapper::upgrade); - } } diff --git a/projects/common/src/main/java/dan200/computercraft/impl/TurtleUpgrades.java b/projects/common/src/main/java/dan200/computercraft/impl/TurtleUpgrades.java index dafdc2089..523d9a602 100644 --- a/projects/common/src/main/java/dan200/computercraft/impl/TurtleUpgrades.java +++ b/projects/common/src/main/java/dan200/computercraft/impl/TurtleUpgrades.java @@ -4,14 +4,12 @@ package dan200.computercraft.impl; -import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.api.turtle.ITurtleUpgrade; - -import java.util.stream.Stream; +import dan200.computercraft.shared.ModRegistry; public final class TurtleUpgrades { private static final UpgradeManager registry = new UpgradeManager<>( - "turtle upgrade", "computercraft/turtle_upgrades", ITurtleUpgrade.serialiserRegistryKey() + ITurtleUpgrade.typeRegistry(), ModRegistry.TURTLE_UPGRADE, ITurtleUpgrade::getType ); private TurtleUpgrades() { @@ -20,10 +18,4 @@ public final class TurtleUpgrades { public static UpgradeManager instance() { return registry; } - - public static Stream getVanillaUpgrades() { - return instance().getUpgradeWrappers().values().stream() - .filter(x -> x.modId().equals(ComputerCraftAPI.MOD_ID)) - .map(UpgradeManager.UpgradeWrapper::upgrade); - } } diff --git a/projects/common/src/main/java/dan200/computercraft/impl/UpgradeManager.java b/projects/common/src/main/java/dan200/computercraft/impl/UpgradeManager.java index d60b44ce3..d11daab61 100644 --- a/projects/common/src/main/java/dan200/computercraft/impl/UpgradeManager.java +++ b/projects/common/src/main/java/dan200/computercraft/impl/UpgradeManager.java @@ -4,36 +4,26 @@ package dan200.computercraft.impl; -import com.google.gson.*; import com.mojang.serialization.Codec; -import com.mojang.serialization.DataResult; import com.mojang.serialization.codecs.RecordCodecBuilder; import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.api.upgrades.UpgradeBase; import dan200.computercraft.api.upgrades.UpgradeData; -import dan200.computercraft.api.upgrades.UpgradeSerialiser; -import dan200.computercraft.shared.platform.PlatformHelper; -import io.netty.buffer.ByteBuf; +import dan200.computercraft.api.upgrades.UpgradeType; +import dan200.computercraft.shared.util.SafeDispatchCodec; +import net.minecraft.core.Holder; +import net.minecraft.core.HolderLookup; import net.minecraft.core.Registry; import net.minecraft.core.component.DataComponentPatch; import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.ByteBufCodecs; import net.minecraft.network.codec.StreamCodec; +import net.minecraft.resources.RegistryFixedCodec; import net.minecraft.resources.ResourceKey; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.packs.resources.ResourceManager; -import net.minecraft.server.packs.resources.SimpleJsonResourceReloadListener; -import net.minecraft.util.GsonHelper; -import net.minecraft.util.profiling.ProfilerFiller; import net.minecraft.world.item.ItemStack; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import javax.annotation.Nullable; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.stream.Collectors; +import java.util.function.Function; /** * Manages turtle and pocket computer upgrades. @@ -42,145 +32,81 @@ import java.util.stream.Collectors; * @see TurtleUpgrades * @see PocketUpgrades */ -public class UpgradeManager extends SimpleJsonResourceReloadListener { - private static final Logger LOG = LoggerFactory.getLogger(UpgradeManager.class); - private static final Gson GSON = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create(); +public final class UpgradeManager { + private final ResourceKey> registry; + private final Codec upgradeCodec; + private final Codec> dataCodec; + private final StreamCodec> dataStreamCodec; - public record UpgradeWrapper( - ResourceLocation id, T upgrade, UpgradeSerialiser serialiser, String modId + UpgradeManager( + ResourceKey>> typeRegistry, + ResourceKey> registry, + Function> getType ) { - } - - private final String kind; - private final ResourceKey>> registry; - - private Map> current = Map.of(); - private Map> currentWrappers = Map.of(); - - private final Codec upgradeCodec = ResourceLocation.CODEC.flatXmap( - x -> { - var upgrade = get(x); - return upgrade == null ? DataResult.error(() -> "Unknown upgrade " + x) : DataResult.success(upgrade); - }, - x -> DataResult.success(x.getUpgradeID()) - ); - - private final Codec> fullCodec = RecordCodecBuilder.create(i -> i.group( - upgradeCodec.fieldOf("id").forGetter(UpgradeData::upgrade), - DataComponentPatch.CODEC.optionalFieldOf("components", DataComponentPatch.EMPTY).forGetter(UpgradeData::data) - ).apply(i, UpgradeData::new)); - - private final Codec> codec = Codec.withAlternative(fullCodec, upgradeCodec, UpgradeData::ofDefault); - - private final StreamCodec upgradeStreamCodec = ResourceLocation.STREAM_CODEC.map( - x -> { - var upgrade = get(x); - if (upgrade == null) throw new IllegalStateException("Unknown upgrade " + x); - return upgrade; - }, - UpgradeBase::getUpgradeID - ); - - private final StreamCodec> streamCodec = StreamCodec.composite( - upgradeStreamCodec, UpgradeData::upgrade, - DataComponentPatch.STREAM_CODEC, UpgradeData::data, - UpgradeData::new - ); - - public UpgradeManager(String kind, String path, ResourceKey>> registry) { - super(GSON, path); - this.kind = kind; this.registry = registry; + + upgradeCodec = SafeDispatchCodec.ofRegistry(typeRegistry, getType, UpgradeType::codec); + + var holderCodec = RegistryFixedCodec.create(registry).xmap(x -> (Holder.Reference) x, x -> x); + Codec> fullCodec = RecordCodecBuilder.create(i -> i.group( + holderCodec.fieldOf("id").forGetter(UpgradeData::holder), + DataComponentPatch.CODEC.optionalFieldOf("components", DataComponentPatch.EMPTY).forGetter(UpgradeData::data) + ).apply(i, UpgradeData::new)); + dataCodec = Codec.withAlternative(fullCodec, holderCodec, UpgradeData::ofDefault); + + dataStreamCodec = StreamCodec.composite( + ByteBufCodecs.holderRegistry(registry).map(x -> (Holder.Reference) x, x -> x), UpgradeData::holder, + DataComponentPatch.STREAM_CODEC, UpgradeData::data, + UpgradeData::new + ); + } + + /** + * The codec for an upgrade instance. + * + * @return The instance codec. + */ + public Codec upgradeCodec() { + return upgradeCodec; + } + + /** + * The codec for an upgrade and its associated data. + * + * @return The upgrade data codec. + */ + public Codec> upgradeDataCodec() { + return dataCodec; + } + + /** + * The stream codec for an upgrade and its associated data. + * + * @return The upgrade data codec. + */ + public StreamCodec> upgradeDataStreamCodec() { + return dataStreamCodec; + } + + public String getOwner(Holder.Reference upgrade) { + var ns = upgrade.key().location().getNamespace(); + return ns.equals("minecraft") ? ComputerCraftAPI.MOD_ID : ns; + + // TODO: Would be nice if we could use the registration info here. } @Nullable - public T get(ResourceLocation id) { - var wrapper = current.get(id); - return wrapper == null ? null : wrapper.upgrade(); - } - - @Nullable - public UpgradeWrapper getWrapper(T upgrade) { - return currentWrappers.get(upgrade); - } - - @Nullable - public String getOwner(T upgrade) { - var wrapper = currentWrappers.get(upgrade); - return wrapper != null ? wrapper.modId() : null; - } - - @Nullable - public UpgradeData get(ItemStack stack) { + public UpgradeData get(HolderLookup.Provider registries, ItemStack stack) { if (stack.isEmpty()) return null; - for (var wrapper : current.values()) { - var craftingStack = wrapper.upgrade().getCraftingItem(); - if (!craftingStack.isEmpty() && craftingStack.getItem() == stack.getItem() && wrapper.upgrade().isItemSuitable(stack)) { - return UpgradeData.of(wrapper.upgrade, wrapper.upgrade.getUpgradeData(stack)); - } - } - - return null; - } - - public Collection getUpgrades() { - return currentWrappers.keySet(); - } - - public Map> getUpgradeWrappers() { - return current; - } - - public Codec> codec() { - return codec; - } - - public StreamCodec> streamCodec() { - return streamCodec; - } - - @Override - protected void apply(Map upgrades, ResourceManager manager, ProfilerFiller profiler) { - var registry = RegistryHelper.getRegistry(this.registry); - Map> newUpgrades = new HashMap<>(); - for (var element : upgrades.entrySet()) { - try { - loadUpgrade(registry, newUpgrades, element.getKey(), element.getValue()); - } catch (IllegalArgumentException | JsonParseException e) { - LOG.error("Error loading {} {} from JSON file", kind, element.getKey(), e); - } - } - - current = Collections.unmodifiableMap(newUpgrades); - currentWrappers = newUpgrades.values().stream().collect(Collectors.toUnmodifiableMap(UpgradeWrapper::upgrade, x -> x)); - LOG.info("Loaded {} {}s", current.size(), kind); - } - - private void loadUpgrade(Registry> registry, Map> current, ResourceLocation id, JsonElement json) { - var root = GsonHelper.convertToJsonObject(json, "top element"); - if (!PlatformHelper.get().shouldLoadResource(root)) return; - - var serialiserId = new ResourceLocation(GsonHelper.getAsString(root, "type")); - var serialiser = registry.get(serialiserId); - if (serialiser == null) throw new JsonSyntaxException("Unknown upgrade type '" + serialiserId + "'"); - - // TODO: Can we track which mod this resource came from and use that instead? It's theoretically possible, - // but maybe not ideal for datapacks. - var modId = id.getNamespace(); - if (modId.equals("minecraft") || modId.isEmpty()) modId = ComputerCraftAPI.MOD_ID; - - var upgrade = serialiser.fromJson(id, root); - if (!upgrade.getUpgradeID().equals(id)) { - throw new IllegalArgumentException("Upgrade " + id + " from " + serialiser + " was incorrectly given id " + upgrade.getUpgradeID()); - } - - var result = new UpgradeWrapper(id, upgrade, serialiser, modId); - current.put(result.id(), result); - } - - public void loadFromNetwork(Map> newUpgrades) { - current = Collections.unmodifiableMap(newUpgrades); - currentWrappers = newUpgrades.values().stream().collect(Collectors.toUnmodifiableMap(UpgradeWrapper::upgrade, x -> x)); + return registries.lookupOrThrow(registry).listElements() + .filter(holder -> { + var upgrade = holder.value(); + var craftingStack = upgrade.getCraftingItem(); + return !craftingStack.isEmpty() && craftingStack.getItem() == stack.getItem() && upgrade.isItemSuitable(stack); + }) + .findAny() + .map(x -> UpgradeData.of(x, x.value().getUpgradeData(stack))) + .orElse(null); } } diff --git a/projects/common/src/main/java/dan200/computercraft/shared/CommonHooks.java b/projects/common/src/main/java/dan200/computercraft/shared/CommonHooks.java index 0a7d8b708..3b077f3b2 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/CommonHooks.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/CommonHooks.java @@ -6,8 +6,6 @@ package dan200.computercraft.shared; import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.core.apis.http.NetworkUtils; -import dan200.computercraft.impl.PocketUpgrades; -import dan200.computercraft.impl.TurtleUpgrades; import dan200.computercraft.shared.computer.core.ResourceMount; import dan200.computercraft.shared.computer.core.ServerContext; import dan200.computercraft.shared.computer.metrics.ComputerMBean; @@ -121,8 +119,6 @@ public final class CommonHooks { public static void onDatapackReload(BiConsumer addReload) { addReload.accept("mounts", ResourceMount.RELOAD_LISTENER); - addReload.accept("turtle_upgrades", TurtleUpgrades.instance()); - addReload.accept("pocket_upgrades", PocketUpgrades.instance()); } public static boolean onEntitySpawn(Entity entity) { diff --git a/projects/common/src/main/java/dan200/computercraft/shared/ModRegistry.java b/projects/common/src/main/java/dan200/computercraft/shared/ModRegistry.java index 538382a60..181891678 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/ModRegistry.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/ModRegistry.java @@ -13,11 +13,12 @@ import dan200.computercraft.api.detail.VanillaDetailRegistries; import dan200.computercraft.api.media.IMedia; import dan200.computercraft.api.pocket.IPocketUpgrade; import dan200.computercraft.api.turtle.ITurtleUpgrade; +import dan200.computercraft.api.upgrades.UpgradeBase; import dan200.computercraft.api.upgrades.UpgradeData; -import dan200.computercraft.api.upgrades.UpgradeSerialiser; +import dan200.computercraft.api.upgrades.UpgradeType; import dan200.computercraft.core.util.Colour; import dan200.computercraft.impl.PocketUpgrades; -import dan200.computercraft.impl.TurtleUpgrades; +import dan200.computercraft.impl.RegistryHelper; import dan200.computercraft.shared.command.UserLevel; import dan200.computercraft.shared.command.arguments.ComputerArgumentType; import dan200.computercraft.shared.command.arguments.RepeatArgumentType; @@ -79,12 +80,18 @@ import dan200.computercraft.shared.turtle.blocks.TurtleBlockEntity; import dan200.computercraft.shared.turtle.inventory.TurtleMenu; import dan200.computercraft.shared.turtle.items.TurtleItem; import dan200.computercraft.shared.turtle.recipes.TurtleUpgradeRecipe; -import dan200.computercraft.shared.turtle.upgrades.*; +import dan200.computercraft.shared.turtle.upgrades.TurtleCraftingTable; +import dan200.computercraft.shared.turtle.upgrades.TurtleModem; +import dan200.computercraft.shared.turtle.upgrades.TurtleSpeaker; +import dan200.computercraft.shared.turtle.upgrades.TurtleTool; import dan200.computercraft.shared.util.DataComponentUtil; import dan200.computercraft.shared.util.NonNegativeId; import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.synchronization.ArgumentTypeInfo; import net.minecraft.commands.synchronization.SingletonArgumentInfo; +import net.minecraft.core.Holder; +import net.minecraft.core.HolderLookup; +import net.minecraft.core.Registry; import net.minecraft.core.cauldron.CauldronInteraction; import net.minecraft.core.component.DataComponentType; import net.minecraft.core.registries.Registries; @@ -92,6 +99,7 @@ import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.network.chat.Component; import net.minecraft.network.codec.ByteBufCodecs; import net.minecraft.network.codec.StreamCodec; +import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.flag.FeatureFlags; import net.minecraft.world.inventory.MenuType; @@ -122,6 +130,9 @@ public final class ModRegistry { private ModRegistry() { } + public static final ResourceKey> TURTLE_UPGRADE = RegistryHelper.TURTLE_UPGRADE; + public static final ResourceKey> POCKET_UPGRADE = RegistryHelper.POCKET_UPGRADE; + public static final class Blocks { static final RegistrationHelper REGISTRY = PlatformHelper.get().createRegistrationHelper(Registries.BLOCK); @@ -289,7 +300,7 @@ public final class ModRegistry { * @see TurtleItem */ public static final RegistryEntry>> LEFT_TURTLE_UPGRADE = register("left_turtle_upgrade", b -> b - .persistent(TurtleUpgrades.instance().codec()).networkSynchronized(TurtleUpgrades.instance().streamCodec()) + .persistent(dan200.computercraft.impl.TurtleUpgrades.instance().upgradeDataCodec()).networkSynchronized(dan200.computercraft.impl.TurtleUpgrades.instance().upgradeDataStreamCodec()) ); /** @@ -298,7 +309,7 @@ public final class ModRegistry { * @see TurtleItem */ public static final RegistryEntry>> RIGHT_TURTLE_UPGRADE = register("right_turtle_upgrade", b -> b - .persistent(TurtleUpgrades.instance().codec()).networkSynchronized(TurtleUpgrades.instance().streamCodec()) + .persistent(dan200.computercraft.impl.TurtleUpgrades.instance().upgradeDataCodec()).networkSynchronized(dan200.computercraft.impl.TurtleUpgrades.instance().upgradeDataStreamCodec()) ); /** @@ -321,7 +332,7 @@ public final class ModRegistry { * @see PocketComputerItem */ public static final RegistryEntry>> POCKET_UPGRADE = register("pocket_upgrade", b -> b - .persistent(PocketUpgrades.instance().codec()).networkSynchronized(PocketUpgrades.instance().streamCodec()) + .persistent(PocketUpgrades.instance().upgradeDataCodec()).networkSynchronized(PocketUpgrades.instance().upgradeDataStreamCodec()) ); /** @@ -374,30 +385,30 @@ public final class ModRegistry { ); } - public static class TurtleSerialisers { - static final RegistrationHelper> REGISTRY = PlatformHelper.get().createRegistrationHelper(ITurtleUpgrade.serialiserRegistryKey()); + public static class TurtleUpgradeTypes { + static final RegistrationHelper> REGISTRY = PlatformHelper.get().createRegistrationHelper(ITurtleUpgrade.typeRegistry()); - public static final RegistryEntry> SPEAKER = - REGISTRY.register("speaker", () -> UpgradeSerialiser.simpleWithCustomItem(TurtleSpeaker::new)); - public static final RegistryEntry> WORKBENCH = - REGISTRY.register("workbench", () -> UpgradeSerialiser.simpleWithCustomItem(TurtleCraftingTable::new)); - public static final RegistryEntry> WIRELESS_MODEM_NORMAL = - REGISTRY.register("wireless_modem_normal", () -> UpgradeSerialiser.simpleWithCustomItem((id, item) -> new TurtleModem(id, item, false))); - public static final RegistryEntry> WIRELESS_MODEM_ADVANCED = - REGISTRY.register("wireless_modem_advanced", () -> UpgradeSerialiser.simpleWithCustomItem((id, item) -> new TurtleModem(id, item, true))); + public static final RegistryEntry> SPEAKER = + REGISTRY.register("speaker", () -> UpgradeType.simpleWithCustomItem(TurtleSpeaker::new)); + public static final RegistryEntry> WORKBENCH = + REGISTRY.register("workbench", () -> UpgradeType.simpleWithCustomItem(TurtleCraftingTable::new)); + public static final RegistryEntry> WIRELESS_MODEM_NORMAL = + REGISTRY.register("wireless_modem_normal", () -> UpgradeType.simpleWithCustomItem(item -> new TurtleModem(item, false))); + public static final RegistryEntry> WIRELESS_MODEM_ADVANCED = + REGISTRY.register("wireless_modem_advanced", () -> UpgradeType.simpleWithCustomItem(item -> new TurtleModem(item, true))); - public static final RegistryEntry> TOOL = REGISTRY.register("tool", () -> TurtleToolSerialiser.INSTANCE); + public static final RegistryEntry> TOOL = REGISTRY.register("tool", () -> UpgradeType.create(TurtleTool.CODEC)); } - public static class PocketUpgradeSerialisers { - static final RegistrationHelper> REGISTRY = PlatformHelper.get().createRegistrationHelper(IPocketUpgrade.serialiserRegistryKey()); + public static class PocketUpgradeTypes { + static final RegistrationHelper> REGISTRY = PlatformHelper.get().createRegistrationHelper(IPocketUpgrade.typeRegistry()); - public static final RegistryEntry> SPEAKER = - REGISTRY.register("speaker", () -> UpgradeSerialiser.simpleWithCustomItem(PocketSpeaker::new)); - public static final RegistryEntry> WIRELESS_MODEM_NORMAL = - REGISTRY.register("wireless_modem_normal", () -> UpgradeSerialiser.simpleWithCustomItem((id, item) -> new PocketModem(id, item, false))); - public static final RegistryEntry> WIRELESS_MODEM_ADVANCED = - REGISTRY.register("wireless_modem_advanced", () -> UpgradeSerialiser.simpleWithCustomItem((id, item) -> new PocketModem(id, item, true))); + public static final RegistryEntry> SPEAKER = + REGISTRY.register("speaker", () -> UpgradeType.simpleWithCustomItem(PocketSpeaker::new)); + public static final RegistryEntry> WIRELESS_MODEM_NORMAL = + REGISTRY.register("wireless_modem_normal", () -> UpgradeType.simpleWithCustomItem(item -> new PocketModem(item, false))); + public static final RegistryEntry> WIRELESS_MODEM_ADVANCED = + REGISTRY.register("wireless_modem_advanced", () -> UpgradeType.simpleWithCustomItem(item -> new PocketModem(item, true))); } public static class Menus { @@ -525,10 +536,10 @@ public final class ModRegistry { out.accept(new ItemStack(Items.COMPUTER_NORMAL.get())); out.accept(new ItemStack(Items.COMPUTER_ADVANCED.get())); if (context.hasPermissions()) out.accept(new ItemStack(Items.COMPUTER_COMMAND.get())); - addTurtle(out, Items.TURTLE_NORMAL.get()); - addTurtle(out, Items.TURTLE_ADVANCED.get()); - addPocket(out, Items.POCKET_COMPUTER_NORMAL.get()); - addPocket(out, Items.POCKET_COMPUTER_ADVANCED.get()); + addTurtle(out, Items.TURTLE_NORMAL.get(), context.holders()); + addTurtle(out, Items.TURTLE_ADVANCED.get(), context.holders()); + addPocket(out, Items.POCKET_COMPUTER_NORMAL.get(), context.holders()); + addPocket(out, Items.POCKET_COMPUTER_ADVANCED.get(), context.holders()); out.accept(Items.WIRELESS_MODEM_NORMAL.get()); out.accept(Items.WIRELESS_MODEM_ADVANCED.get()); @@ -562,8 +573,8 @@ public final class ModRegistry { BlockEntities.REGISTRY.register(); Items.REGISTRY.register(); DataComponents.REGISTRY.register(); - TurtleSerialisers.REGISTRY.register(); - PocketUpgradeSerialisers.REGISTRY.register(); + TurtleUpgradeTypes.REGISTRY.register(); + PocketUpgradeTypes.REGISTRY.register(); Menus.REGISTRY.register(); ArgumentTypes.REGISTRY.register(); LootItemConditionTypes.REGISTRY.register(); @@ -594,15 +605,23 @@ public final class ModRegistry { CauldronInteraction.WATER.map().put(Items.TURTLE_ADVANCED.get(), TurtleItem.CAULDRON_INTERACTION); } - private static void addTurtle(CreativeModeTab.Output out, TurtleItem turtle) { + private static void addTurtle(CreativeModeTab.Output out, TurtleItem turtle, HolderLookup.Provider registries) { out.accept(new ItemStack(turtle)); - TurtleUpgrades.getVanillaUpgrades() + registries.lookupOrThrow(TURTLE_UPGRADE).listElements() + .filter(ModRegistry::isOurUpgrade) .map(x -> DataComponentUtil.createStack(turtle, DataComponents.RIGHT_TURTLE_UPGRADE.get(), UpgradeData.ofDefault(x))) .forEach(out::accept); } - private static void addPocket(CreativeModeTab.Output out, PocketComputerItem pocket) { + private static void addPocket(CreativeModeTab.Output out, PocketComputerItem pocket, HolderLookup.Provider registries) { out.accept(new ItemStack(pocket)); - PocketUpgrades.getVanillaUpgrades().map(x -> DataComponentUtil.createStack(pocket, DataComponents.POCKET_UPGRADE.get(), UpgradeData.ofDefault(x))).forEach(out::accept); + registries.lookupOrThrow(POCKET_UPGRADE).listElements() + .filter(ModRegistry::isOurUpgrade) + .map(x -> DataComponentUtil.createStack(pocket, DataComponents.POCKET_UPGRADE.get(), UpgradeData.ofDefault(x))).forEach(out::accept); + } + + private static boolean isOurUpgrade(Holder.Reference upgrade) { + var namespace = upgrade.key().location().getNamespace(); + return namespace.equals("minecraft") || namespace.equals(ComputerCraftAPI.MOD_ID); } } diff --git a/projects/common/src/main/java/dan200/computercraft/shared/integration/RecipeModHelpers.java b/projects/common/src/main/java/dan200/computercraft/shared/integration/RecipeModHelpers.java index ae13e6436..869b2036a 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/integration/RecipeModHelpers.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/integration/RecipeModHelpers.java @@ -6,18 +6,23 @@ package dan200.computercraft.shared.integration; import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.api.upgrades.UpgradeData; -import dan200.computercraft.impl.PocketUpgrades; -import dan200.computercraft.impl.TurtleUpgrades; import dan200.computercraft.shared.ModRegistry; import dan200.computercraft.shared.pocket.items.PocketComputerItem; import dan200.computercraft.shared.turtle.items.TurtleItem; import dan200.computercraft.shared.util.DataComponentUtil; +import net.minecraft.core.Holder; +import net.minecraft.core.HolderLookup; +import net.minecraft.core.Registry; +import net.minecraft.core.RegistryAccess; +import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.ItemStack; import java.util.ArrayList; import java.util.List; +import java.util.function.Consumer; import java.util.function.Supplier; +import java.util.stream.Stream; /** * Utilities for recipe mod plugins (such as JEI). @@ -48,24 +53,41 @@ public final class RecipeModHelpers { * Get additional ComputerCraft-related items which may not be visible in a creative tab. This includes upgraded * turtle and pocket computers for each upgrade. * + * @param registries The currently available registries. * @return The additional stacks to show. */ - public static List getExtraStacks() { + public static List getExtraStacks(HolderLookup.Provider registries) { List upgradeItems = new ArrayList<>(); for (var turtleSupplier : TURTLES) { var turtle = turtleSupplier.get(); - for (var upgrade : TurtleUpgrades.instance().getUpgrades()) { - upgradeItems.add(DataComponentUtil.createStack(turtle, ModRegistry.DataComponents.RIGHT_TURTLE_UPGRADE.get(), UpgradeData.ofDefault(upgrade))); - } + forEachRegistry(registries, ModRegistry.TURTLE_UPGRADE, upgrade -> + upgradeItems.add(DataComponentUtil.createStack(turtle, ModRegistry.DataComponents.RIGHT_TURTLE_UPGRADE.get(), UpgradeData.ofDefault(upgrade))) + ); } for (var pocketSupplier : POCKET_COMPUTERS) { var pocket = pocketSupplier.get(); - for (var upgrade : PocketUpgrades.instance().getUpgrades()) { - upgradeItems.add(DataComponentUtil.createStack(pocket, ModRegistry.DataComponents.POCKET_UPGRADE.get(), UpgradeData.ofDefault(upgrade))); - } + forEachRegistry(registries, ModRegistry.POCKET_UPGRADE, upgrade -> + upgradeItems.add(DataComponentUtil.createStack(pocket, ModRegistry.DataComponents.POCKET_UPGRADE.get(), UpgradeData.ofDefault(upgrade))) + ); } return upgradeItems; } + + /** + * A temporary function to denote places where we need a {@link HolderLookup.Provider} within our recipe mods, but + * don't have access to one. + * + * @return The empty recipe mod access. + * @deprecated We should get the registry access from a more sensible place. + */ + @Deprecated + public static HolderLookup.Provider getEmptyRegistryAccess() { + return RegistryAccess.EMPTY; + } + + static void forEachRegistry(HolderLookup.Provider registries, ResourceKey> registry, Consumer> consumer) { + registries.lookup(registry).map(HolderLookup::listElements).orElse(Stream.empty()).forEach(consumer); + } } diff --git a/projects/common/src/main/java/dan200/computercraft/shared/integration/UpgradeRecipeGenerator.java b/projects/common/src/main/java/dan200/computercraft/shared/integration/UpgradeRecipeGenerator.java index e4904516c..6995716df 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/integration/UpgradeRecipeGenerator.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/integration/UpgradeRecipeGenerator.java @@ -9,12 +9,12 @@ import dan200.computercraft.api.turtle.ITurtleUpgrade; import dan200.computercraft.api.turtle.TurtleSide; import dan200.computercraft.api.upgrades.UpgradeBase; import dan200.computercraft.api.upgrades.UpgradeData; -import dan200.computercraft.impl.PocketUpgrades; -import dan200.computercraft.impl.TurtleUpgrades; import dan200.computercraft.shared.ModRegistry; import dan200.computercraft.shared.pocket.items.PocketComputerItem; import dan200.computercraft.shared.turtle.items.TurtleItem; import dan200.computercraft.shared.util.DataComponentUtil; +import net.minecraft.core.Holder; +import net.minecraft.core.HolderLookup; import net.minecraft.core.NonNullList; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; @@ -27,8 +27,7 @@ import javax.annotation.Nullable; import java.util.*; import java.util.function.Function; -import static dan200.computercraft.shared.integration.RecipeModHelpers.POCKET_COMPUTERS; -import static dan200.computercraft.shared.integration.RecipeModHelpers.TURTLES; +import static dan200.computercraft.shared.integration.RecipeModHelpers.*; /** * Provides dynamic recipe and usage information for upgraded turtle and pocket computers. This is intended to be @@ -39,14 +38,16 @@ import static dan200.computercraft.shared.integration.RecipeModHelpers.TURTLES; */ public class UpgradeRecipeGenerator { private final Function wrap; + private final HolderLookup.Provider registries; private final Map> upgradeItemLookup = new HashMap<>(); private final List pocketUpgrades = new ArrayList<>(); private final List turtleUpgrades = new ArrayList<>(); private boolean initialised = false; - public UpgradeRecipeGenerator(Function wrap) { + public UpgradeRecipeGenerator(Function wrap, HolderLookup.Provider registries) { this.wrap = wrap; + this.registries = registries; } /** @@ -56,23 +57,25 @@ public class UpgradeRecipeGenerator { if (initialised) return; initialised = true; - for (var upgrade : TurtleUpgrades.instance().getUpgrades()) { + forEachRegistry(registries, ModRegistry.TURTLE_UPGRADE, holder -> { + var upgrade = holder.value(); var stack = upgrade.getCraftingItem(); if (stack.isEmpty()) return; - var info = new UpgradeInfo(stack, upgrade); + var info = new UpgradeInfo(stack, upgrade, holder, null); upgradeItemLookup.computeIfAbsent(stack.getItem(), k -> new ArrayList<>(1)).add(info); turtleUpgrades.add(info); - } + }); - for (var upgrade : PocketUpgrades.instance().getUpgrades()) { + forEachRegistry(registries, ModRegistry.POCKET_UPGRADE, holder -> { + var upgrade = holder.value(); var stack = upgrade.getCraftingItem(); if (stack.isEmpty()) return; - var info = new UpgradeInfo(stack, upgrade); + var info = new UpgradeInfo(stack, upgrade, null, holder); upgradeItemLookup.computeIfAbsent(stack.getItem(), k -> new ArrayList<>(1)).add(info); pocketUpgrades.add(info); - } + }); } /** @@ -248,23 +251,17 @@ public class UpgradeRecipeGenerator { private class UpgradeInfo { final ItemStack stack; final Ingredient ingredient; - final @Nullable ITurtleUpgrade turtle; - final @Nullable IPocketUpgrade pocket; + final @Nullable Holder.Reference turtle; + final @Nullable Holder.Reference pocket; final UpgradeBase upgrade; private @Nullable ArrayList recipes; - UpgradeInfo(ItemStack stack, ITurtleUpgrade turtle) { + UpgradeInfo(ItemStack stack, UpgradeBase upgrade, @Nullable Holder.Reference turtle, @Nullable Holder.Reference pocket) { this.stack = stack; ingredient = Ingredient.of(stack); - upgrade = this.turtle = turtle; - pocket = null; - } - - UpgradeInfo(ItemStack stack, IPocketUpgrade pocket) { - this.stack = stack; - ingredient = Ingredient.of(stack); - turtle = null; - upgrade = this.pocket = pocket; + this.turtle = turtle; + this.pocket = pocket; + this.upgrade = upgrade; } List getRecipes() { diff --git a/projects/common/src/main/java/dan200/computercraft/shared/integration/jei/JEIComputerCraft.java b/projects/common/src/main/java/dan200/computercraft/shared/integration/jei/JEIComputerCraft.java index 825e80bd9..730778631 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/integration/jei/JEIComputerCraft.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/integration/jei/JEIComputerCraft.java @@ -52,7 +52,7 @@ public class JEIComputerCraft implements IModPlugin { var registry = runtime.getRecipeManager(); // Register all turtles/pocket computers (not just vanilla upgrades) as upgrades on JEI. - var upgradeItems = RecipeModHelpers.getExtraStacks(); + var upgradeItems = RecipeModHelpers.getExtraStacks(RecipeModHelpers.getEmptyRegistryAccess()); if (!upgradeItems.isEmpty()) { runtime.getIngredientManager().addIngredientsAtRuntime(VanillaTypes.ITEM_STACK, upgradeItems); } @@ -73,11 +73,11 @@ public class JEIComputerCraft implements IModPlugin { var name = new StringBuilder("turtle:"); // Add left and right upgrades to the identifier - var left = TurtleItem.getUpgrade(stack, TurtleSide.LEFT); - var right = TurtleItem.getUpgrade(stack, TurtleSide.RIGHT); - if (left != null) name.append(left.getUpgradeID()); + var left = TurtleItem.getUpgradeWithData(stack, TurtleSide.LEFT); + var right = TurtleItem.getUpgradeWithData(stack, TurtleSide.RIGHT); + if (left != null) name.append(left.holder().key().location()); if (left != null && right != null) name.append('|'); - if (right != null) name.append(right.getUpgradeID()); + if (right != null) name.append(right.holder().key().location()); return name.toString(); }; @@ -89,8 +89,8 @@ public class JEIComputerCraft implements IModPlugin { var name = new StringBuilder("pocket:"); // Add the upgrade to the identifier - var upgrade = PocketComputerItem.getUpgrade(stack); - if (upgrade != null) name.append(upgrade.getUpgradeID()); + var upgrade = PocketComputerItem.getUpgradeWithData(stack); + if (upgrade != null) name.append(upgrade.holder().key().location()); return name.toString(); }; diff --git a/projects/common/src/main/java/dan200/computercraft/shared/integration/jei/RecipeResolver.java b/projects/common/src/main/java/dan200/computercraft/shared/integration/jei/RecipeResolver.java index d07c57266..f7addc311 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/integration/jei/RecipeResolver.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/integration/jei/RecipeResolver.java @@ -5,6 +5,7 @@ package dan200.computercraft.shared.integration.jei; import dan200.computercraft.api.ComputerCraftAPI; +import dan200.computercraft.shared.integration.RecipeModHelpers; import dan200.computercraft.shared.integration.UpgradeRecipeGenerator; import dan200.computercraft.shared.pocket.items.PocketComputerItem; import dan200.computercraft.shared.turtle.items.TurtleItem; @@ -22,7 +23,7 @@ import java.util.List; class RecipeResolver implements IRecipeManagerPlugin { private static final ResourceLocation RECIPE_ID = new ResourceLocation(ComputerCraftAPI.MOD_ID, "upgrade"); - private final UpgradeRecipeGenerator> resolver = new UpgradeRecipeGenerator<>(x -> new RecipeHolder<>(RECIPE_ID, x)); + private final UpgradeRecipeGenerator> resolver = new UpgradeRecipeGenerator<>(x -> new RecipeHolder<>(RECIPE_ID, x), RecipeModHelpers.getEmptyRegistryAccess()); @Override public List> getRecipeTypes(IFocus focus) { diff --git a/projects/common/src/main/java/dan200/computercraft/shared/network/NetworkMessages.java b/projects/common/src/main/java/dan200/computercraft/shared/network/NetworkMessages.java index 04d6b0cd8..216a39ca4 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/network/NetworkMessages.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/network/NetworkMessages.java @@ -42,7 +42,6 @@ public final class NetworkMessages { public static final CustomPacketPayload.Type SPEAKER_PLAY = registerClientbound("speaker_play", SpeakerPlayClientMessage.STREAM_CODEC); public static final CustomPacketPayload.Type SPEAKER_STOP = registerClientbound("speaker_stop", SpeakerStopClientMessage.STREAM_CODEC); public static final CustomPacketPayload.Type UPLOAD_RESULT = registerClientbound("upload_result", UploadResultMessage.STREAM_CODEC); - public static final CustomPacketPayload.Type UPGRADES_LOADED = registerClientbound("upgrades_loaded", UpgradesLoadedMessage.STREAM_CODEC); private NetworkMessages() { } diff --git a/projects/common/src/main/java/dan200/computercraft/shared/network/client/UpgradesLoadedMessage.java b/projects/common/src/main/java/dan200/computercraft/shared/network/client/UpgradesLoadedMessage.java deleted file mode 100644 index b836f6549..000000000 --- a/projects/common/src/main/java/dan200/computercraft/shared/network/client/UpgradesLoadedMessage.java +++ /dev/null @@ -1,104 +0,0 @@ -// SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers -// -// SPDX-License-Identifier: MPL-2.0 - -package dan200.computercraft.shared.network.client; - -import dan200.computercraft.api.pocket.IPocketUpgrade; -import dan200.computercraft.api.turtle.ITurtleUpgrade; -import dan200.computercraft.api.upgrades.UpgradeBase; -import dan200.computercraft.api.upgrades.UpgradeSerialiser; -import dan200.computercraft.impl.PocketUpgrades; -import dan200.computercraft.impl.RegistryHelper; -import dan200.computercraft.impl.TurtleUpgrades; -import dan200.computercraft.impl.UpgradeManager; -import dan200.computercraft.shared.network.NetworkMessage; -import dan200.computercraft.shared.network.NetworkMessages; -import net.minecraft.core.Registry; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.network.codec.StreamCodec; -import net.minecraft.network.protocol.common.custom.CustomPacketPayload; -import net.minecraft.resources.ResourceKey; -import net.minecraft.resources.ResourceLocation; - -import java.util.HashMap; -import java.util.Map; - -/** - * Syncs turtle and pocket upgrades to the client. - */ -public final class UpgradesLoadedMessage implements NetworkMessage { - public static final StreamCodec STREAM_CODEC = StreamCodec.ofMember(UpgradesLoadedMessage::write, UpgradesLoadedMessage::new); - - private final Map> turtleUpgrades; - private final Map> pocketUpgrades; - - public UpgradesLoadedMessage() { - turtleUpgrades = TurtleUpgrades.instance().getUpgradeWrappers(); - pocketUpgrades = PocketUpgrades.instance().getUpgradeWrappers(); - } - - private UpgradesLoadedMessage(RegistryFriendlyByteBuf buf) { - turtleUpgrades = fromBytes(buf, ITurtleUpgrade.serialiserRegistryKey()); - pocketUpgrades = fromBytes(buf, IPocketUpgrade.serialiserRegistryKey()); - } - - private Map> fromBytes( - RegistryFriendlyByteBuf buf, ResourceKey>> registryKey - ) { - var registry = RegistryHelper.getRegistry(registryKey); - - var size = buf.readVarInt(); - Map> upgrades = new HashMap<>(size); - for (var i = 0; i < size; i++) { - var id = buf.readResourceLocation(); - - var serialiserId = buf.readResourceLocation(); - var serialiser = registry.get(serialiserId); - if (serialiser == null) throw new IllegalStateException("Unknown serialiser " + serialiserId); - - var upgrade = serialiser.fromNetwork(id, buf); - var modId = buf.readUtf(); - - upgrades.put(id, new UpgradeManager.UpgradeWrapper(id, upgrade, serialiser, modId)); - } - - return upgrades; - } - - private void write(RegistryFriendlyByteBuf buf) { - toBytes(buf, ITurtleUpgrade.serialiserRegistryKey(), turtleUpgrades); - toBytes(buf, IPocketUpgrade.serialiserRegistryKey(), pocketUpgrades); - } - - private void toBytes( - RegistryFriendlyByteBuf buf, ResourceKey>> registryKey, Map> upgrades - ) { - var registry = RegistryHelper.getRegistry(registryKey); - - buf.writeVarInt(upgrades.size()); - for (var entry : upgrades.entrySet()) { - buf.writeResourceLocation(entry.getKey()); - - var serialiser = entry.getValue().serialiser(); - @SuppressWarnings("unchecked") - var unwrappedSerialiser = (UpgradeSerialiser) serialiser; - - buf.writeResourceLocation(RegistryHelper.getKeyOrThrow(registry, serialiser)); - unwrappedSerialiser.toNetwork(buf, entry.getValue().upgrade()); - - buf.writeUtf(entry.getValue().modId()); - } - } - - @Override - public void handle(ClientNetworkContext context) { - TurtleUpgrades.instance().loadFromNetwork(turtleUpgrades); - PocketUpgrades.instance().loadFromNetwork(pocketUpgrades); - } - - @Override - public CustomPacketPayload.Type type() { - return NetworkMessages.UPGRADES_LOADED; - } -} diff --git a/projects/common/src/main/java/dan200/computercraft/shared/pocket/apis/PocketAPI.java b/projects/common/src/main/java/dan200/computercraft/shared/pocket/apis/PocketAPI.java index a0749450d..81c3d1f7e 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/pocket/apis/PocketAPI.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/pocket/apis/PocketAPI.java @@ -107,11 +107,11 @@ public class PocketAPI implements ILuaAPI { } } - private static @Nullable UpgradeData findUpgrade(NonNullList inv, int start, @Nullable UpgradeData previous) { + private @Nullable UpgradeData findUpgrade(NonNullList inv, int start, @Nullable UpgradeData previous) { for (var i = 0; i < inv.size(); i++) { var invStack = inv.get((i + start) % inv.size()); if (!invStack.isEmpty()) { - var newUpgrade = PocketUpgrades.instance().get(invStack); + var newUpgrade = PocketUpgrades.instance().get(computer.getLevel().registryAccess(), invStack); if (newUpgrade != null && !Objects.equals(newUpgrade, previous)) { // Consume an item from this stack and exit the loop diff --git a/projects/common/src/main/java/dan200/computercraft/shared/pocket/core/PocketServerComputer.java b/projects/common/src/main/java/dan200/computercraft/shared/pocket/core/PocketServerComputer.java index d9ad11cae..e6bf06627 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/pocket/core/PocketServerComputer.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/pocket/core/PocketServerComputer.java @@ -100,10 +100,10 @@ public class PocketServerComputer extends ServerComputer implements IPocketAcces @Override public void setUpgradeData(DataComponentPatch data) { - var upgrade = PocketComputerItem.getUpgrade(stack); + var upgrade = PocketComputerItem.getUpgradeWithData(stack); if (upgrade == null) return; - PocketComputerItem.setUpgrade(stack, new UpgradeData<>(upgrade, data)); + PocketComputerItem.setUpgrade(stack, new UpgradeData<>(upgrade.holder(), data)); setItemChanged(); } @@ -118,7 +118,7 @@ public class PocketServerComputer extends ServerComputer implements IPocketAcces } public @Nullable UpgradeData getUpgrade() { - return upgrade == null ? null : UpgradeData.of(upgrade, getUpgradeData()); + return PocketComputerItem.getUpgradeWithData(stack); } /** diff --git a/projects/common/src/main/java/dan200/computercraft/shared/pocket/items/PocketComputerItem.java b/projects/common/src/main/java/dan200/computercraft/shared/pocket/items/PocketComputerItem.java index 9ecb666d8..9512ae4d9 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/pocket/items/PocketComputerItem.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/pocket/items/PocketComputerItem.java @@ -150,15 +150,9 @@ public class PocketComputerItem extends Item implements IMedia { @Nullable @ForgeOverride public String getCreatorModId(ItemStack stack) { - var upgrade = getUpgrade(stack); - if (upgrade != null) { - // If we're a non-vanilla, non-CC upgrade then return whichever mod this upgrade - // belongs to. - var mod = PocketUpgrades.instance().getOwner(upgrade); - if (mod != null && !mod.equals(ComputerCraftAPI.MOD_ID)) return mod; - } + var upgrade = getUpgradeWithData(stack); + return upgrade != null ? PocketUpgrades.instance().getOwner(upgrade.holder()) : ComputerCraftAPI.MOD_ID; - return ComputerCraftAPI.MOD_ID; } public PocketServerComputer createServerComputer(ServerLevel level, Entity entity, @Nullable Container inventory, ItemStack stack) { diff --git a/projects/common/src/main/java/dan200/computercraft/shared/pocket/peripherals/PocketModem.java b/projects/common/src/main/java/dan200/computercraft/shared/pocket/peripherals/PocketModem.java index 273c5e8a4..b23faa456 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/pocket/peripherals/PocketModem.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/pocket/peripherals/PocketModem.java @@ -7,8 +7,9 @@ package dan200.computercraft.shared.pocket.peripherals; import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.pocket.AbstractPocketUpgrade; import dan200.computercraft.api.pocket.IPocketAccess; +import dan200.computercraft.api.upgrades.UpgradeType; +import dan200.computercraft.shared.ModRegistry; import dan200.computercraft.shared.peripheral.modem.wireless.WirelessModemPeripheral; -import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.ItemStack; import javax.annotation.Nullable; @@ -16,8 +17,8 @@ import javax.annotation.Nullable; public class PocketModem extends AbstractPocketUpgrade { private final boolean advanced; - public PocketModem(ResourceLocation id, ItemStack stack, boolean advanced) { - super(id, advanced ? WirelessModemPeripheral.ADVANCED_ADJECTIVE : WirelessModemPeripheral.NORMAL_ADJECTIVE, stack); + public PocketModem(ItemStack stack, boolean advanced) { + super(advanced ? WirelessModemPeripheral.ADVANCED_ADJECTIVE : WirelessModemPeripheral.NORMAL_ADJECTIVE, stack); this.advanced = advanced; } @@ -36,4 +37,11 @@ public class PocketModem extends AbstractPocketUpgrade { var state = modem.getModemState(); if (state.pollChanged()) access.setLight(state.isOpen() ? 0xBA0000 : -1); } + + @Override + public UpgradeType getType() { + return advanced + ? ModRegistry.PocketUpgradeTypes.WIRELESS_MODEM_ADVANCED.get() + : ModRegistry.PocketUpgradeTypes.WIRELESS_MODEM_NORMAL.get(); + } } diff --git a/projects/common/src/main/java/dan200/computercraft/shared/pocket/peripherals/PocketSpeaker.java b/projects/common/src/main/java/dan200/computercraft/shared/pocket/peripherals/PocketSpeaker.java index c9508cbc7..74824fc25 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/pocket/peripherals/PocketSpeaker.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/pocket/peripherals/PocketSpeaker.java @@ -7,15 +7,16 @@ package dan200.computercraft.shared.pocket.peripherals; import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.pocket.AbstractPocketUpgrade; import dan200.computercraft.api.pocket.IPocketAccess; +import dan200.computercraft.api.upgrades.UpgradeType; +import dan200.computercraft.shared.ModRegistry; import dan200.computercraft.shared.peripheral.speaker.UpgradeSpeakerPeripheral; -import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.ItemStack; import javax.annotation.Nullable; public class PocketSpeaker extends AbstractPocketUpgrade { - public PocketSpeaker(ResourceLocation id, ItemStack item) { - super(id, UpgradeSpeakerPeripheral.ADJECTIVE, item); + public PocketSpeaker(ItemStack item) { + super(UpgradeSpeakerPeripheral.ADJECTIVE, item); } @Nullable @@ -29,4 +30,9 @@ public class PocketSpeaker extends AbstractPocketUpgrade { if (!(peripheral instanceof PocketSpeakerPeripheral)) return; ((PocketSpeakerPeripheral) peripheral).update(); } + + @Override + public UpgradeType getType() { + return ModRegistry.PocketUpgradeTypes.SPEAKER.get(); + } } diff --git a/projects/common/src/main/java/dan200/computercraft/shared/pocket/recipes/PocketComputerUpgradeRecipe.java b/projects/common/src/main/java/dan200/computercraft/shared/pocket/recipes/PocketComputerUpgradeRecipe.java index 5fa758269..30ff8cc05 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/pocket/recipes/PocketComputerUpgradeRecipe.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/pocket/recipes/PocketComputerUpgradeRecipe.java @@ -59,7 +59,7 @@ public final class PocketComputerUpgradeRecipe extends CustomRecipe { if (computer.isEmpty()) return ItemStack.EMPTY; var itemComputer = (PocketComputerItem) computer.getItem(); - if (PocketComputerItem.getUpgrade(computer) != null) return ItemStack.EMPTY; + if (PocketComputerItem.getUpgradeWithData(computer) != null) return ItemStack.EMPTY; // Check for upgrades around the item UpgradeData upgrade = null; @@ -69,7 +69,7 @@ public final class PocketComputerUpgradeRecipe extends CustomRecipe { if (x == computerX && y == computerY) continue; if (x == computerX && y == computerY - 1) { - upgrade = PocketUpgrades.instance().get(item); + upgrade = PocketUpgrades.instance().get(registryAccess, item); if (upgrade == null) return ItemStack.EMPTY; } else if (!item.isEmpty()) { return ItemStack.EMPTY; diff --git a/projects/common/src/main/java/dan200/computercraft/shared/recipe/function/RecipeFunction.java b/projects/common/src/main/java/dan200/computercraft/shared/recipe/function/RecipeFunction.java index 003c3b255..08f603733 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/recipe/function/RecipeFunction.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/recipe/function/RecipeFunction.java @@ -7,9 +7,9 @@ package dan200.computercraft.shared.recipe.function; import com.mojang.serialization.Codec; import com.mojang.serialization.MapCodec; import dan200.computercraft.api.ComputerCraftAPI; -import dan200.computercraft.impl.RegistryHelper; import dan200.computercraft.shared.recipe.TransformShapedRecipe; import dan200.computercraft.shared.recipe.TransformShapelessRecipe; +import dan200.computercraft.shared.util.SafeDispatchCodec; import net.minecraft.core.Registry; import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.network.codec.ByteBufCodecs; @@ -44,7 +44,7 @@ public interface RecipeFunction { /** * The codec to read and write {@link RecipeFunction}s with. */ - Codec CODEC = Codec.lazyInitialized(() -> RegistryHelper.getRegistry(REGISTRY).byNameCodec().dispatch(RecipeFunction::getType, Type::codec)); + Codec CODEC = SafeDispatchCodec.ofRegistry(REGISTRY, RecipeFunction::getType, Type::codec); /** * A codec for a list of functions. diff --git a/projects/common/src/main/java/dan200/computercraft/shared/turtle/blocks/TurtleBlockEntity.java b/projects/common/src/main/java/dan200/computercraft/shared/turtle/blocks/TurtleBlockEntity.java index 1c6d0b28f..142bdd4d2 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/turtle/blocks/TurtleBlockEntity.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/turtle/blocks/TurtleBlockEntity.java @@ -180,7 +180,7 @@ public class TurtleBlockEntity extends AbstractComputerBlockEntity implements Ba } private static @Nullable UpgradeData withPersistedData(@Nullable UpgradeData upgrade) { - return upgrade == null ? null : UpgradeData.of(upgrade.upgrade(), upgrade.upgrade().getPersistedData(upgrade.data())); + return upgrade == null ? null : UpgradeData.of(upgrade.holder(), upgrade.upgrade().getPersistedData(upgrade.data())); } @Override @@ -313,7 +313,7 @@ public class TurtleBlockEntity extends AbstractComputerBlockEntity implements Ba default: return false; } - return upgrade != null && upgrade.getType().isPeripheral(); + return upgrade != null && upgrade.getUpgradeType().isPeripheral(); } public void transferStateFrom(TurtleBlockEntity copy) { diff --git a/projects/common/src/main/java/dan200/computercraft/shared/turtle/core/TurtleBrain.java b/projects/common/src/main/java/dan200/computercraft/shared/turtle/core/TurtleBrain.java index 652583f47..c4799d3fa 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/turtle/core/TurtleBrain.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/turtle/core/TurtleBrain.java @@ -26,6 +26,7 @@ import dan200.computercraft.shared.util.Holiday; import dan200.computercraft.shared.util.NBTUtil; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; +import net.minecraft.core.Holder; import net.minecraft.core.HolderLookup; import net.minecraft.core.component.DataComponentPatch; import net.minecraft.core.particles.ParticleTypes; @@ -119,7 +120,7 @@ public class TurtleBrain implements TurtleAccessInternal { // Advance upgrades for (var side : TurtleSide.values()) { var upgrade = upgrades[side.ordinal()].upgrade; - if (upgrade != null) upgrade.update(this, side); + if (upgrade != null) upgrade.value().update(this, side); } } @@ -136,8 +137,8 @@ public class TurtleBrain implements TurtleAccessInternal { overlay = nbt.contains(NBT_OVERLAY) ? new ResourceLocation(nbt.getString(NBT_OVERLAY)) : null; // Read upgrades - setUpgradeDirect(TurtleSide.LEFT, NBTUtil.decodeFrom(TurtleUpgrades.instance().codec(), registries, nbt, NBT_LEFT_UPGRADE)); - setUpgradeDirect(TurtleSide.RIGHT, NBTUtil.decodeFrom(TurtleUpgrades.instance().codec(), registries, nbt, NBT_RIGHT_UPGRADE)); + setUpgradeDirect(TurtleSide.LEFT, NBTUtil.decodeFrom(TurtleUpgrades.instance().upgradeDataCodec(), registries, nbt, NBT_LEFT_UPGRADE)); + setUpgradeDirect(TurtleSide.RIGHT, NBTUtil.decodeFrom(TurtleUpgrades.instance().upgradeDataCodec(), registries, nbt, NBT_RIGHT_UPGRADE)); } private void writeCommon(CompoundTag nbt, HolderLookup.Provider registries) { @@ -146,8 +147,8 @@ public class TurtleBrain implements TurtleAccessInternal { if (overlay != null) nbt.putString(NBT_OVERLAY, overlay.toString()); // Write upgrades - NBTUtil.encodeTo(TurtleUpgrades.instance().codec(), registries, nbt, NBT_LEFT_UPGRADE, getUpgradeWithData(TurtleSide.LEFT)); - NBTUtil.encodeTo(TurtleUpgrades.instance().codec(), registries, nbt, NBT_RIGHT_UPGRADE, getUpgradeWithData(TurtleSide.RIGHT)); + NBTUtil.encodeTo(TurtleUpgrades.instance().upgradeDataCodec(), registries, nbt, NBT_LEFT_UPGRADE, getUpgradeWithData(TurtleSide.LEFT)); + NBTUtil.encodeTo(TurtleUpgrades.instance().upgradeDataCodec(), registries, nbt, NBT_RIGHT_UPGRADE, getUpgradeWithData(TurtleSide.RIGHT)); } public void readFromNBT(CompoundTag nbt, HolderLookup.Provider registries) { @@ -459,7 +460,8 @@ public class TurtleBrain implements TurtleAccessInternal { @Override public @Nullable ITurtleUpgrade getUpgrade(TurtleSide side) { - return upgrades[side.ordinal()].upgrade; + var upgrade = upgrades[side.ordinal()].upgrade; + return upgrade == null ? null : upgrade.value(); } @Override @@ -561,7 +563,7 @@ public class TurtleBrain implements TurtleAccessInternal { for (var side : TurtleSide.values()) { var upgrade = getUpgrade(side); IPeripheral peripheral = null; - if (upgrade != null && upgrade.getType().isPeripheral()) { + if (upgrade != null && upgrade.getUpgradeType().isPeripheral()) { peripheral = upgrade.createPeripheral(this, side); } @@ -747,7 +749,7 @@ public class TurtleBrain implements TurtleAccessInternal { } private static final class UpgradeInstance { - private @Nullable ITurtleUpgrade upgrade; + private @Nullable Holder.Reference upgrade; private DataComponentPatch data = DataComponentPatch.EMPTY; private @Nullable IPeripheral peripheral; @@ -759,7 +761,7 @@ public class TurtleBrain implements TurtleAccessInternal { data = DataComponentPatch.EMPTY; cachedUpgradeData = null; } else { - this.upgrade = upgrade.upgrade(); + this.upgrade = upgrade.holder(); this.data = upgrade.data(); this.cachedUpgradeData = upgrade; } diff --git a/projects/common/src/main/java/dan200/computercraft/shared/turtle/core/TurtleEquipCommand.java b/projects/common/src/main/java/dan200/computercraft/shared/turtle/core/TurtleEquipCommand.java index b4b7e910d..fdfce6a6a 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/turtle/core/TurtleEquipCommand.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/turtle/core/TurtleEquipCommand.java @@ -25,7 +25,7 @@ public class TurtleEquipCommand implements TurtleCommand { UpgradeData newUpgrade; var selectedStack = turtle.getInventory().getItem(turtle.getSelectedSlot()); if (!selectedStack.isEmpty()) { - newUpgrade = TurtleUpgrades.instance().get(selectedStack); + newUpgrade = TurtleUpgrades.instance().get(turtle.getLevel().registryAccess(), selectedStack); if (newUpgrade == null) return TurtleCommandResult.failure("Not a valid upgrade"); } else { newUpgrade = null; diff --git a/projects/common/src/main/java/dan200/computercraft/shared/turtle/core/TurtleToolCommand.java b/projects/common/src/main/java/dan200/computercraft/shared/turtle/core/TurtleToolCommand.java index 5bc19294d..5889056f2 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/turtle/core/TurtleToolCommand.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/turtle/core/TurtleToolCommand.java @@ -27,7 +27,7 @@ public class TurtleToolCommand implements TurtleCommand { if (this.side != null && this.side != side) continue; var upgrade = turtle.getUpgrade(side); - if (upgrade == null || !upgrade.getType().isTool()) continue; + if (upgrade == null || !upgrade.getUpgradeType().isTool()) continue; var result = upgrade.useTool(turtle, side, verb, direction.toWorldDir(turtle)); if (result.isSuccess()) { diff --git a/projects/common/src/main/java/dan200/computercraft/shared/turtle/inventory/TurtleMenu.java b/projects/common/src/main/java/dan200/computercraft/shared/turtle/inventory/TurtleMenu.java index 61d2e67d8..d4709295b 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/turtle/inventory/TurtleMenu.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/turtle/inventory/TurtleMenu.java @@ -62,8 +62,9 @@ public final class TurtleMenu extends AbstractComputerMenu { } // Turtle upgrades - addSlot(new UpgradeSlot(turtleUpgrades, TurtleSide.LEFT, 0, UPGRADE_START_X, PLAYER_START_Y + 1)); - addSlot(new UpgradeSlot(turtleUpgrades, TurtleSide.RIGHT, 1, UPGRADE_START_X, PLAYER_START_Y + 1 + 18)); + var registries = playerInventory.player.level().registryAccess(); + addSlot(new UpgradeSlot(turtleUpgrades, registries, TurtleSide.LEFT, 0, UPGRADE_START_X, PLAYER_START_Y + 1)); + addSlot(new UpgradeSlot(turtleUpgrades, registries, TurtleSide.RIGHT, 1, UPGRADE_START_X, PLAYER_START_Y + 1 + 18)); } public static TurtleMenu ofBrain(int id, Inventory player, TurtleBrain turtle) { diff --git a/projects/common/src/main/java/dan200/computercraft/shared/turtle/inventory/UpgradeContainer.java b/projects/common/src/main/java/dan200/computercraft/shared/turtle/inventory/UpgradeContainer.java index 6f98972c9..a8dc08997 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/turtle/inventory/UpgradeContainer.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/turtle/inventory/UpgradeContainer.java @@ -66,7 +66,7 @@ class UpgradeContainer implements Container { @Override public void setItem(int slot, ItemStack itemStack) { - var upgrade = TurtleUpgrades.instance().get(itemStack); + var upgrade = TurtleUpgrades.instance().get(turtle.getLevel().registryAccess(), itemStack); turtle.setUpgrade(getSide(slot), upgrade); setUpgradeStack(slot, upgrade); } diff --git a/projects/common/src/main/java/dan200/computercraft/shared/turtle/inventory/UpgradeSlot.java b/projects/common/src/main/java/dan200/computercraft/shared/turtle/inventory/UpgradeSlot.java index 7947785e1..fb9e42810 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/turtle/inventory/UpgradeSlot.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/turtle/inventory/UpgradeSlot.java @@ -8,6 +8,7 @@ import com.mojang.datafixers.util.Pair; import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.api.turtle.TurtleSide; import dan200.computercraft.impl.TurtleUpgrades; +import net.minecraft.core.HolderLookup; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.Container; import net.minecraft.world.inventory.InventoryMenu; @@ -25,16 +26,18 @@ public class UpgradeSlot extends Slot { public static final ResourceLocation LEFT_UPGRADE = new ResourceLocation(ComputerCraftAPI.MOD_ID, "gui/turtle_upgrade_left"); public static final ResourceLocation RIGHT_UPGRADE = new ResourceLocation(ComputerCraftAPI.MOD_ID, "gui/turtle_upgrade_right"); + private final HolderLookup.Provider registries; private final TurtleSide side; - public UpgradeSlot(Container container, TurtleSide side, int slot, int xPos, int yPos) { + public UpgradeSlot(Container container, HolderLookup.Provider registries, TurtleSide side, int slot, int xPos, int yPos) { super(container, slot, xPos, yPos); + this.registries = registries; this.side = side; } @Override public boolean mayPlace(ItemStack stack) { - return TurtleUpgrades.instance().get(stack) != null; + return TurtleUpgrades.instance().get(registries, stack) != null; } @Override diff --git a/projects/common/src/main/java/dan200/computercraft/shared/turtle/items/TurtleItem.java b/projects/common/src/main/java/dan200/computercraft/shared/turtle/items/TurtleItem.java index f3c8cd925..7e24218c7 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/turtle/items/TurtleItem.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/turtle/items/TurtleItem.java @@ -57,16 +57,16 @@ public class TurtleItem extends AbstractComputerItem { // Determine our "creator mod" from the upgrades. We attempt to find the first non-vanilla/non-CC // upgrade (starting from the left). - var left = getUpgrade(stack, TurtleSide.LEFT); + var left = getUpgradeWithData(stack, TurtleSide.LEFT); if (left != null) { - var mod = TurtleUpgrades.instance().getOwner(left); - if (mod != null && !mod.equals(ComputerCraftAPI.MOD_ID)) return mod; + var mod = TurtleUpgrades.instance().getOwner(left.holder()); + if (!mod.equals(ComputerCraftAPI.MOD_ID)) return mod; } - var right = getUpgrade(stack, TurtleSide.RIGHT); + var right = getUpgradeWithData(stack, TurtleSide.RIGHT); if (right != null) { - var mod = TurtleUpgrades.instance().getOwner(right); - if (mod != null && !mod.equals(ComputerCraftAPI.MOD_ID)) return mod; + var mod = TurtleUpgrades.instance().getOwner(right.holder()); + if (!mod.equals(ComputerCraftAPI.MOD_ID)) return mod; } return ComputerCraftAPI.MOD_ID; diff --git a/projects/common/src/main/java/dan200/computercraft/shared/turtle/recipes/TurtleUpgradeRecipe.java b/projects/common/src/main/java/dan200/computercraft/shared/turtle/recipes/TurtleUpgradeRecipe.java index ab703cd80..61095eb5e 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/turtle/recipes/TurtleUpgradeRecipe.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/turtle/recipes/TurtleUpgradeRecipe.java @@ -113,7 +113,7 @@ public final class TurtleUpgradeRecipe extends CustomRecipe { var items = new ItemStack[]{ rightItem, leftItem }; for (var i = 0; i < 2; i++) { if (!items[i].isEmpty()) { - var itemUpgrade = TurtleUpgrades.instance().get(items[i]); + var itemUpgrade = TurtleUpgrades.instance().get(registryAccess, items[i]); if (itemUpgrade == null || upgrades[i] != null) return ItemStack.EMPTY; upgrades[i] = itemUpgrade; } diff --git a/projects/common/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleCraftingTable.java b/projects/common/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleCraftingTable.java index 83ce27f32..7060853d7 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleCraftingTable.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleCraftingTable.java @@ -9,17 +9,23 @@ import dan200.computercraft.api.turtle.AbstractTurtleUpgrade; import dan200.computercraft.api.turtle.ITurtleAccess; import dan200.computercraft.api.turtle.TurtleSide; import dan200.computercraft.api.turtle.TurtleUpgradeType; -import net.minecraft.resources.ResourceLocation; +import dan200.computercraft.api.upgrades.UpgradeType; +import dan200.computercraft.shared.ModRegistry; import net.minecraft.world.item.ItemStack; public class TurtleCraftingTable extends AbstractTurtleUpgrade { - public TurtleCraftingTable(ResourceLocation id, ItemStack stack) { - super(id, TurtleUpgradeType.PERIPHERAL, "upgrade.minecraft.crafting_table.adjective", stack); + public TurtleCraftingTable(ItemStack stack) { + super(TurtleUpgradeType.PERIPHERAL, "upgrade.minecraft.crafting_table.adjective", stack); } @Override public IPeripheral createPeripheral(ITurtleAccess turtle, TurtleSide side) { return new CraftingTablePeripheral(turtle); } + + @Override + public UpgradeType getType() { + return ModRegistry.TurtleUpgradeTypes.WORKBENCH.get(); + } } diff --git a/projects/common/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleModem.java b/projects/common/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleModem.java index eb00a11ba..547118406 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleModem.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleModem.java @@ -6,12 +6,12 @@ package dan200.computercraft.shared.turtle.upgrades; import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.turtle.*; +import dan200.computercraft.api.upgrades.UpgradeType; import dan200.computercraft.shared.ModRegistry; import dan200.computercraft.shared.peripheral.modem.ModemState; import dan200.computercraft.shared.peripheral.modem.wireless.WirelessModemPeripheral; import net.minecraft.core.Direction; import net.minecraft.core.component.DataComponentPatch; -import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; import net.minecraft.world.phys.Vec3; @@ -50,8 +50,8 @@ public class TurtleModem extends AbstractTurtleUpgrade { private final boolean advanced; - public TurtleModem(ResourceLocation id, ItemStack stack, boolean advanced) { - super(id, TurtleUpgradeType.PERIPHERAL, advanced ? WirelessModemPeripheral.ADVANCED_ADJECTIVE : WirelessModemPeripheral.NORMAL_ADJECTIVE, stack); + public TurtleModem(ItemStack stack, boolean advanced) { + super(TurtleUpgradeType.PERIPHERAL, advanced ? WirelessModemPeripheral.ADVANCED_ADJECTIVE : WirelessModemPeripheral.NORMAL_ADJECTIVE, stack); this.advanced = advanced; } @@ -83,4 +83,11 @@ public class TurtleModem extends AbstractTurtleUpgrade { public DataComponentPatch getPersistedData(DataComponentPatch upgradeData) { return DataComponentPatch.EMPTY; } + + @Override + public UpgradeType getType() { + return advanced + ? ModRegistry.TurtleUpgradeTypes.WIRELESS_MODEM_ADVANCED.get() + : ModRegistry.TurtleUpgradeTypes.WIRELESS_MODEM_NORMAL.get(); + } } diff --git a/projects/common/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleSpeaker.java b/projects/common/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleSpeaker.java index a0766bd85..8c08e8763 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleSpeaker.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleSpeaker.java @@ -9,9 +9,10 @@ import dan200.computercraft.api.turtle.AbstractTurtleUpgrade; import dan200.computercraft.api.turtle.ITurtleAccess; import dan200.computercraft.api.turtle.TurtleSide; import dan200.computercraft.api.turtle.TurtleUpgradeType; +import dan200.computercraft.api.upgrades.UpgradeType; +import dan200.computercraft.shared.ModRegistry; import dan200.computercraft.shared.peripheral.speaker.SpeakerPosition; import dan200.computercraft.shared.peripheral.speaker.UpgradeSpeakerPeripheral; -import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.ItemStack; import net.minecraft.world.phys.Vec3; @@ -36,8 +37,8 @@ public class TurtleSpeaker extends AbstractTurtleUpgrade { } } - public TurtleSpeaker(ResourceLocation id, ItemStack item) { - super(id, TurtleUpgradeType.PERIPHERAL, UpgradeSpeakerPeripheral.ADJECTIVE, item); + public TurtleSpeaker(ItemStack item) { + super(TurtleUpgradeType.PERIPHERAL, UpgradeSpeakerPeripheral.ADJECTIVE, item); } @Override @@ -50,4 +51,9 @@ public class TurtleSpeaker extends AbstractTurtleUpgrade { var peripheral = turtle.getPeripheral(turtleSide); if (peripheral instanceof Peripheral speaker) speaker.update(); } + + @Override + public UpgradeType getType() { + return ModRegistry.TurtleUpgradeTypes.SPEAKER.get(); + } } diff --git a/projects/common/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleTool.java b/projects/common/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleTool.java index 42034b06d..6e49b5292 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleTool.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleTool.java @@ -4,8 +4,12 @@ package dan200.computercraft.shared.turtle.upgrades; +import com.mojang.serialization.MapCodec; import dan200.computercraft.api.ComputerCraftTags; import dan200.computercraft.api.turtle.*; +import dan200.computercraft.api.upgrades.UpgradeType; +import dan200.computercraft.impl.upgrades.TurtleToolSpec; +import dan200.computercraft.shared.ModRegistry; import dan200.computercraft.shared.platform.PlatformHelper; import dan200.computercraft.shared.turtle.TurtleUtil; import dan200.computercraft.shared.turtle.core.TurtlePlaceCommand; @@ -16,7 +20,6 @@ import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.component.DataComponentPatch; import net.minecraft.core.component.DataComponents; -import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.tags.TagKey; @@ -27,7 +30,6 @@ import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.ai.attributes.Attributes; import net.minecraft.world.entity.decoration.ArmorStand; import net.minecraft.world.entity.player.Player; -import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.enchantment.EnchantmentHelper; import net.minecraft.world.level.BlockGetter; @@ -41,25 +43,26 @@ import javax.annotation.Nullable; import java.util.function.Function; public class TurtleTool extends AbstractTurtleUpgrade { + public static final MapCodec CODEC = TurtleToolSpec.CODEC.xmap(TurtleTool::new, x -> x.spec); + private static final TurtleCommandResult UNBREAKABLE = TurtleCommandResult.failure("Cannot break unbreakable block"); private static final TurtleCommandResult INEFFECTIVE = TurtleCommandResult.failure("Cannot break block with this tool"); + final TurtleToolSpec spec; final ItemStack item; final float damageMulitiplier; final boolean allowEnchantments; final TurtleToolDurability consumeDurability; final @Nullable TagKey breakable; - public TurtleTool( - ResourceLocation id, String adjective, Item craftItem, ItemStack toolItem, float damageMulitiplier, - boolean allowEnchantments, TurtleToolDurability consumeDurability, @Nullable TagKey breakable - ) { - super(id, TurtleUpgradeType.TOOL, adjective, new ItemStack(craftItem)); - item = toolItem; - this.damageMulitiplier = damageMulitiplier; - this.allowEnchantments = allowEnchantments; - this.consumeDurability = consumeDurability; - this.breakable = breakable; + public TurtleTool(TurtleToolSpec spec) { + super(TurtleUpgradeType.TOOL, spec.adjective(), new ItemStack(spec.craftItem().orElse(spec.toolItem()))); + this.spec = spec; + item = new ItemStack(spec.toolItem()); + this.damageMulitiplier = spec.damageMultiplier(); + this.allowEnchantments = spec.allowEnchantments(); + this.consumeDurability = spec.consumeDurability(); + this.breakable = spec.breakable().orElse(null); } @Override @@ -328,4 +331,9 @@ public class TurtleTool extends AbstractTurtleUpgrade { // Allow breaking any "instabreak" block. || state.getDestroySpeed(reader, pos) == 0; } + + @Override + public UpgradeType getType() { + return ModRegistry.TurtleUpgradeTypes.TOOL.get(); + } } diff --git a/projects/common/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleToolSerialiser.java b/projects/common/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleToolSerialiser.java deleted file mode 100644 index e8e965d07..000000000 --- a/projects/common/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleToolSerialiser.java +++ /dev/null @@ -1,69 +0,0 @@ -// SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers -// -// SPDX-License-Identifier: MPL-2.0 - -package dan200.computercraft.shared.turtle.upgrades; - -import com.google.gson.JsonObject; -import dan200.computercraft.api.turtle.TurtleToolDurability; -import dan200.computercraft.api.upgrades.UpgradeBase; -import dan200.computercraft.api.upgrades.UpgradeSerialiser; -import net.minecraft.core.registries.Registries; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.network.codec.ByteBufCodecs; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.tags.TagKey; -import net.minecraft.util.GsonHelper; -import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.block.Block; - -public final class TurtleToolSerialiser implements UpgradeSerialiser { - public static final TurtleToolSerialiser INSTANCE = new TurtleToolSerialiser(); - - private TurtleToolSerialiser() { - } - - @Override - public TurtleTool fromJson(ResourceLocation id, JsonObject object) { - var adjective = GsonHelper.getAsString(object, "adjective", UpgradeBase.getDefaultAdjective(id)); - var toolItem = GsonHelper.getAsItem(object, "item"); - var craftingItem = GsonHelper.getAsItem(object, "craftingItem", toolItem).value(); - var damageMultiplier = GsonHelper.getAsFloat(object, "damageMultiplier", 3.0f); - var allowEnchantments = GsonHelper.getAsBoolean(object, "allowEnchantments", false); - var consumeDurability = TurtleToolDurability.CODEC.byName(GsonHelper.getAsString(object, "consumeDurability", null), TurtleToolDurability.NEVER); - - TagKey breakable = null; - if (object.has("breakable")) { - var tag = new ResourceLocation(GsonHelper.getAsString(object, "breakable")); - breakable = TagKey.create(Registries.BLOCK, tag); - } - - return new TurtleTool(id, adjective, craftingItem, new ItemStack(toolItem), damageMultiplier, allowEnchantments, consumeDurability, breakable); - } - - @Override - public TurtleTool fromNetwork(ResourceLocation id, RegistryFriendlyByteBuf buffer) { - var adjective = buffer.readUtf(); - var craftingItem = ByteBufCodecs.registry(Registries.ITEM).decode(buffer); - var toolItem = ItemStack.STREAM_CODEC.decode(buffer); - // damageMultiplier and breakable aren't used by the client, but we need to construct the upgrade exactly - // as otherwise syncing on an SP world will overwrite the (shared) upgrade registry with an invalid upgrade! - var damageMultiplier = buffer.readFloat(); - var allowsEnchantments = buffer.readBoolean(); - var consumesDurability = buffer.readEnum(TurtleToolDurability.class); - - var breakable = buffer.readNullable(b -> TagKey.create(Registries.BLOCK, b.readResourceLocation())); - return new TurtleTool(id, adjective, craftingItem, toolItem, damageMultiplier, allowsEnchantments, consumesDurability, breakable); - } - - @Override - public void toNetwork(RegistryFriendlyByteBuf buffer, TurtleTool upgrade) { - buffer.writeUtf(upgrade.getUnlocalisedAdjective()); - ByteBufCodecs.registry(Registries.ITEM).encode(buffer, upgrade.getCraftingItem().getItem()); - ItemStack.STREAM_CODEC.encode(buffer, upgrade.item); - buffer.writeFloat(upgrade.damageMulitiplier); - buffer.writeBoolean(upgrade.allowEnchantments); - buffer.writeEnum(upgrade.consumeDurability); - buffer.writeNullable(upgrade.breakable, (b, x) -> b.writeResourceLocation(x.location())); - } -} diff --git a/projects/common/src/main/java/dan200/computercraft/shared/util/ComponentizationFixers.java b/projects/common/src/main/java/dan200/computercraft/shared/util/ComponentizationFixers.java index 3f673c041..cc49eb75d 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/util/ComponentizationFixers.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/util/ComponentizationFixers.java @@ -187,10 +187,10 @@ public class ComponentizationFixers { /** * Add our custom data components to the datafixer system. * - * @param type The existing component type definition. + * @param type The existing component type definition. * @param schema The current schema. * @return The new component type definition. - * @see UpgradeManager#codec() + * @see UpgradeManager#upgradeDataCodec() * @see ModRegistry.DataComponents#POCKET_UPGRADE * @see ModRegistry.DataComponents#LEFT_TURTLE_UPGRADE * @see ModRegistry.DataComponents#RIGHT_TURTLE_UPGRADE diff --git a/projects/common/src/main/java/dan200/computercraft/shared/util/SafeDispatchCodec.java b/projects/common/src/main/java/dan200/computercraft/shared/util/SafeDispatchCodec.java new file mode 100644 index 000000000..929474655 --- /dev/null +++ b/projects/common/src/main/java/dan200/computercraft/shared/util/SafeDispatchCodec.java @@ -0,0 +1,118 @@ +// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers +// +// SPDX-License-Identifier: MPL-2.0 + +package dan200.computercraft.shared.util; + +import com.mojang.serialization.*; +import com.mojang.serialization.codecs.KeyDispatchCodec; +import dan200.computercraft.impl.RegistryHelper; +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceKey; + +import java.util.function.Function; +import java.util.stream.Stream; + +/** + * An implementation of {@link Codec#dispatch(Function, Function)}/{@link KeyDispatchCodec} that checks that the + * result of {@code instance.getType()} matches the supplied type. + * + * @param The type of keys. + * @param The type of values. + */ +public final class SafeDispatchCodec extends MapCodec { + private static final String TYPE_KEY = "type"; + private static final String COMPRESSED_VALUE_KEY = "value"; + + private final Codec typeCodec; + private final Function type; + private final Function> instanceCodec; + + private SafeDispatchCodec(Codec typeCodec, Function type, Function> instanceCodec) { + this.typeCodec = typeCodec; + this.type = type; + this.instanceCodec = instanceCodec; + } + + /** + * Create a new safe dispatch codec. + *

+ * This decodes the {@code "type"} field of a map (using {@code typeCodec}), and then uses {@code instanceCodec} to + * find the codec that should be used for that type. + * + * @param typeCodec The codec to decode the type. + * @param type The function to get the type of an instance. + * @param instanceCodec The codec to decode an instance. + * @param The type of keys. + * @param The type of values. + * @return The dispatch codec. + */ + public static Codec codec(Codec typeCodec, Function type, Function> instanceCodec) { + return new SafeDispatchCodec<>(typeCodec, type, instanceCodec).codec(); + } + + /** + * Create a new safe dispatch codec. + *

+ * This decodes the {@code "type"} field of a map (using {@code typeCodec}), and then uses {@code instanceCodec} to + * find the codec that should be used for that type. + * + * @param typeRegistry The built-in registry of types. + * @param type The function to get the type of an instance. + * @param instanceCodec The codec to decode an instance. + * @param The type of keys. + * @param The type of values. + * @return The dispatch codec. + */ + public static Codec ofRegistry(ResourceKey> typeRegistry, Function type, Function> instanceCodec) { + return codec(Codec.lazyInitialized(() -> RegistryHelper.getRegistry(typeRegistry).byNameCodec()), type, instanceCodec); + } + + @Override + public DataResult decode(final DynamicOps ops, final MapLike input) { + var elementName = input.get(TYPE_KEY); + if (elementName == null) { + return DataResult.error(() -> "Input does not contain a key [" + TYPE_KEY + "]: " + input); + } + + return typeCodec.decode(ops, elementName).flatMap(typeResult -> { + var type = typeResult.getFirst(); + var decoder = instanceCodec.apply(type); + + DataResult result; + if (ops.compressMaps()) { + var value = input.get(ops.createString(COMPRESSED_VALUE_KEY)); + if (value == null) return DataResult.error(() -> "Input does not have a \"value\" entry: " + input); + result = decoder.decoder().parse(ops, value); + } else { + result = decoder.decode(ops, input); + } + + return result.flatMap(x -> { + var actualType = this.type.apply(x); + if (actualType != type) { + return DataResult.error(() -> x + " has the incorrect type. Expected " + type + ", but was " + actualType + "."); + } + return DataResult.success(x); + }); + }); + } + + @Override + public RecordBuilder encode(final V input, final DynamicOps ops, final RecordBuilder builder) { + @SuppressWarnings("unchecked") var encoder = (MapCodec) instanceCodec.apply(type.apply(input)); + + if (ops.compressMaps()) { + return builder + .add(TYPE_KEY, typeCodec.encodeStart(ops, type.apply(input))) + .add(COMPRESSED_VALUE_KEY, encoder.encoder().encodeStart(ops, input)); + } + + return encoder.encode(input, ops, builder).add(TYPE_KEY, typeCodec.encodeStart(ops, type.apply(input))); + } + + @Override + public Stream keys(final DynamicOps ops) { + return Stream.of(TYPE_KEY, COMPRESSED_VALUE_KEY).map(ops::createString); + } +} diff --git a/projects/common/src/test/java/dan200/computercraft/shared/turtle/upgrades/TurtleToolSerialiserTest.java b/projects/common/src/test/java/dan200/computercraft/shared/turtle/upgrades/TurtleToolSerialiserTest.java deleted file mode 100644 index 43d8bb1f4..000000000 --- a/projects/common/src/test/java/dan200/computercraft/shared/turtle/upgrades/TurtleToolSerialiserTest.java +++ /dev/null @@ -1,65 +0,0 @@ -// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers -// -// SPDX-License-Identifier: MPL-2.0 - -package dan200.computercraft.shared.turtle.upgrades; - -import dan200.computercraft.api.turtle.ITurtleUpgrade; -import dan200.computercraft.api.turtle.TurtleToolDurability; -import dan200.computercraft.test.core.StructuralEquality; -import dan200.computercraft.test.shared.MinecraftArbitraries; -import dan200.computercraft.test.shared.MinecraftEqualities; -import dan200.computercraft.test.shared.NetworkSupport; -import dan200.computercraft.test.shared.WithMinecraft; -import net.jqwik.api.*; -import net.minecraft.core.registries.Registries; - -import static org.hamcrest.MatcherAssert.assertThat; - -@WithMinecraft -class TurtleToolSerialiserTest { - static { - WithMinecraft.Setup.bootstrap(); // @Property doesn't run test lifecycle methods. - } - - /** - * Sends turtle tools on a roundtrip, ensuring that their contents are reassembled on the other end. - * - * @param tool The message to send. - */ - @Property - public void testRoundTrip(@ForAll("tool") TurtleTool tool) { - var converted = NetworkSupport.roundTripSerialiser( - tool.getUpgradeID(), tool, TurtleToolSerialiser.INSTANCE::toNetwork, TurtleToolSerialiser.INSTANCE::fromNetwork - ); - - if (!equality.equals(tool, converted)) { - System.out.println("Break"); - } - assertThat("Messages are equal", converted, equality.asMatcher(TurtleTool.class, tool)); - } - - @Provide - Arbitrary tool() { - return Combinators.combine( - MinecraftArbitraries.resourceLocation(), - Arbitraries.strings().ofMaxLength(100), - MinecraftArbitraries.item(), - MinecraftArbitraries.nonEmptyItemStack(), - Arbitraries.floats(), - Arbitraries.of(true, false), - Arbitraries.of(TurtleToolDurability.values()), - MinecraftArbitraries.tagKey(Registries.BLOCK) - ).as(TurtleTool::new); - } - - private static final StructuralEquality equality = StructuralEquality.all( - StructuralEquality.at("id", ITurtleUpgrade::getUpgradeID), - StructuralEquality.at("craftingItem", ITurtleUpgrade::getCraftingItem, MinecraftEqualities.itemStack), - StructuralEquality.at("tool", x -> x.item, MinecraftEqualities.itemStack), - StructuralEquality.at("damageMulitiplier", x -> x.damageMulitiplier), - StructuralEquality.at("allowEnchantments", x -> x.allowEnchantments), - StructuralEquality.at("consumeDurability", x -> x.consumeDurability), - StructuralEquality.at("breakable", x -> x.breakable) - ); -} diff --git a/projects/common/src/testFixtures/java/dan200/computercraft/test/shared/NetworkSupport.java b/projects/common/src/testFixtures/java/dan200/computercraft/test/shared/NetworkSupport.java deleted file mode 100644 index d187879d0..000000000 --- a/projects/common/src/testFixtures/java/dan200/computercraft/test/shared/NetworkSupport.java +++ /dev/null @@ -1,58 +0,0 @@ -// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers -// -// SPDX-License-Identifier: MPL-2.0 - -package dan200.computercraft.test.shared; - -import io.netty.buffer.Unpooled; -import net.minecraft.core.RegistryAccess; -import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.network.RegistryFriendlyByteBuf; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.item.crafting.RecipeSerializer; - -import java.util.function.BiConsumer; -import java.util.function.BiFunction; -import java.util.function.Function; - -import static org.junit.jupiter.api.Assertions.assertEquals; - -/** - * Support methods for working with Minecraft's networking code. - */ -public final class NetworkSupport { - private NetworkSupport() { - } - - /** - * Attempt to serialise and then deserialise a value. - * - * @param value The value to serialise. - * @param write Serialise this value to a buffer. - * @param read Deserialise this value from a buffer. - * @param The type of the value to round trip. - * @return The converted value, for checking equivalency. - */ - public static T roundTrip(T value, BiConsumer write, Function read) { - var buffer = new RegistryFriendlyByteBuf(Unpooled.directBuffer(), RegistryAccess.fromRegistryOfRegistries(BuiltInRegistries.REGISTRY)); - write.accept(value, buffer); - - var converted = read.apply(buffer); - assertEquals(buffer.readableBytes(), 0, "Whole packet was read"); - return converted; - } - - /** - * Attempt to serialise and then deserialise a value from a {@link RecipeSerializer}-like interface. - * - * @param id The id of this value. - * @param value The value to serialise. - * @param write Serialise this value to a buffer. - * @param read Deserialise this value from a buffer. - * @param The type of the value to round trip. - * @return The converted value, for checking equivalency. - */ - public static T roundTripSerialiser(ResourceLocation id, T value, BiConsumer write, BiFunction read) { - return roundTrip(value, (x, b) -> write.accept(b, x), b -> read.apply(id, b)); - } -} diff --git a/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/Turtle_Test.kt b/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/Turtle_Test.kt index becb5fc8c..df558ff7b 100644 --- a/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/Turtle_Test.kt +++ b/projects/common/src/testMod/kotlin/dan200/computercraft/gametest/Turtle_Test.kt @@ -13,7 +13,6 @@ import dan200.computercraft.api.upgrades.UpgradeData import dan200.computercraft.core.apis.PeripheralAPI import dan200.computercraft.gametest.api.* import dan200.computercraft.gametest.core.TestHooks -import dan200.computercraft.impl.TurtleUpgrades import dan200.computercraft.mixin.gametest.GameTestHelperAccessor import dan200.computercraft.mixin.gametest.GameTestInfoAccessor import dan200.computercraft.shared.ModRegistry @@ -234,7 +233,7 @@ class Turtle_Test { val turtle = helper.getBlockEntity(BlockPos(2, 2, 2), ModRegistry.BlockEntities.TURTLE_NORMAL.get()).access val upgrade = turtle.getUpgrade(TurtleSide.LEFT) assertEquals( - TurtleUpgrades.instance() + helper.level.registryAccess().registryOrThrow(ModRegistry.TURTLE_UPGRADE) .get(ResourceLocation("cctest", "wooden_pickaxe")), upgrade, "Upgrade is a wooden pickaxe", @@ -262,7 +261,10 @@ class Turtle_Test { helper.assertUpgradeItem( ItemStack(Items.WOODEN_PICKAXE), - UpgradeData.ofDefault(TurtleUpgrades.instance().get(ResourceLocation("cctest", "wooden_pickaxe"))), + UpgradeData.ofDefault( + helper.level.registryAccess().registryOrThrow(ModRegistry.TURTLE_UPGRADE) + .getHolder(ResourceLocation("cctest", "wooden_pickaxe")).orElseThrow(), + ), ) } } @@ -281,7 +283,8 @@ class Turtle_Test { val turtle = helper.getBlockEntity(BlockPos(2, 2, 2), ModRegistry.BlockEntities.TURTLE_NORMAL.get()).access val upgrade = turtle.getUpgrade(TurtleSide.LEFT) assertEquals( - TurtleUpgrades.instance().get(ResourceLocation("cctest", "netherite_pickaxe")), + helper.level.registryAccess().registryOrThrow(ModRegistry.TURTLE_UPGRADE) + .get(ResourceLocation("cctest", "netherite_pickaxe")), upgrade, "Upgrade is a netherite pickaxe", ) @@ -299,8 +302,8 @@ class Turtle_Test { fail("Invalid upgrade item\n Expected => ${expected.componentsPatch}\n Actual => ${upgrade.upgradeItem.componentsPatch}") } - if (!ItemStack.matches(ItemStack(expected.item), upgrade.upgrade.craftingItem)) { - fail("Original upgrade item has changed (is now ${upgrade.upgrade.craftingItem})") + if (!ItemStack.matches(ItemStack(expected.item), upgrade.upgrade().craftingItem)) { + fail("Original upgrade item has changed (is now ${upgrade.upgrade().craftingItem})") } } diff --git a/projects/common/src/testMod/resources/data/cctest/computercraft/turtle_upgrades/netherite_pickaxe.json b/projects/common/src/testMod/resources/data/cctest/computercraft/turtle_upgrade/netherite_pickaxe.json similarity index 70% rename from projects/common/src/testMod/resources/data/cctest/computercraft/turtle_upgrades/netherite_pickaxe.json rename to projects/common/src/testMod/resources/data/cctest/computercraft/turtle_upgrade/netherite_pickaxe.json index 42aa7fa85..c385abddd 100644 --- a/projects/common/src/testMod/resources/data/cctest/computercraft/turtle_upgrades/netherite_pickaxe.json +++ b/projects/common/src/testMod/resources/data/cctest/computercraft/turtle_upgrade/netherite_pickaxe.json @@ -1,5 +1,6 @@ { "type": "computercraft:tool", + "adjective": "upgrade.minecraft.diamond_pickaxe.adjective", "item": "minecraft:netherite_pickaxe", "allowEnchantments": true, "consumeDurability": "when_enchanted" diff --git a/projects/common/src/testMod/resources/data/cctest/computercraft/turtle_upgrades/wooden_pickaxe.json b/projects/common/src/testMod/resources/data/cctest/computercraft/turtle_upgrade/wooden_pickaxe.json similarity index 63% rename from projects/common/src/testMod/resources/data/cctest/computercraft/turtle_upgrades/wooden_pickaxe.json rename to projects/common/src/testMod/resources/data/cctest/computercraft/turtle_upgrade/wooden_pickaxe.json index 0516dd1bb..5da827ea0 100644 --- a/projects/common/src/testMod/resources/data/cctest/computercraft/turtle_upgrades/wooden_pickaxe.json +++ b/projects/common/src/testMod/resources/data/cctest/computercraft/turtle_upgrade/wooden_pickaxe.json @@ -1,5 +1,6 @@ { "type": "computercraft:tool", + "adjective": "upgrade.minecraft.diamond_pickaxe.adjective", "item": "minecraft:wooden_pickaxe", "consumeDurability": "always" } diff --git a/projects/common/src/testMod/resources/data/computercraft/loot_tables/treasure_disk.json b/projects/common/src/testMod/resources/data/computercraft/loot_tables/treasure_disk.json index 0e238db06..9abac80c4 100644 --- a/projects/common/src/testMod/resources/data/computercraft/loot_tables/treasure_disk.json +++ b/projects/common/src/testMod/resources/data/computercraft/loot_tables/treasure_disk.json @@ -9,8 +9,8 @@ "name": "computercraft:treasure_disk", "functions": [ { - "function": "minecraft:set_nbt", - "tag": "{\"Title\": \"Demo disk\", \"SubPath\": \"demo\", \"Colour\": 15905331}" + "function": "minecraft:set_components", + "components": {"computercraft:treasure_disk": {"name": "Demo disk","title": "demo"}} } ] } diff --git a/projects/fabric-api/src/client/java/dan200/computercraft/api/client/FabricComputerCraftAPIClient.java b/projects/fabric-api/src/client/java/dan200/computercraft/api/client/FabricComputerCraftAPIClient.java index b38b532f1..acf9f8037 100644 --- a/projects/fabric-api/src/client/java/dan200/computercraft/api/client/FabricComputerCraftAPIClient.java +++ b/projects/fabric-api/src/client/java/dan200/computercraft/api/client/FabricComputerCraftAPIClient.java @@ -6,7 +6,7 @@ package dan200.computercraft.api.client; import dan200.computercraft.api.client.turtle.TurtleUpgradeModeller; import dan200.computercraft.api.turtle.ITurtleUpgrade; -import dan200.computercraft.api.upgrades.UpgradeSerialiser; +import dan200.computercraft.api.upgrades.UpgradeType; import dan200.computercraft.impl.client.FabricComputerCraftAPIClientService; /** @@ -27,12 +27,12 @@ public final class FabricComputerCraftAPIClient { * This method may be used as a {@link dan200.computercraft.api.client.turtle.RegisterTurtleUpgradeModeller}, for * convenient use in multi-loader code. * - * @param serialiser The turtle upgrade serialiser. - * @param modeller The upgrade modeller. - * @param The type of the turtle upgrade. + * @param type The turtle upgrade type. + * @param modeller The upgrade modeller. + * @param The type of the turtle upgrade. */ - public static void registerTurtleUpgradeModeller(UpgradeSerialiser serialiser, TurtleUpgradeModeller modeller) { - getInstance().registerTurtleUpgradeModeller(serialiser, modeller); + public static void registerTurtleUpgradeModeller(UpgradeType type, TurtleUpgradeModeller modeller) { + getInstance().registerTurtleUpgradeModeller(type, modeller); } private static FabricComputerCraftAPIClientService getInstance() { diff --git a/projects/fabric-api/src/client/java/dan200/computercraft/impl/client/FabricComputerCraftAPIClientService.java b/projects/fabric-api/src/client/java/dan200/computercraft/impl/client/FabricComputerCraftAPIClientService.java index 280c30762..94ae828cc 100644 --- a/projects/fabric-api/src/client/java/dan200/computercraft/impl/client/FabricComputerCraftAPIClientService.java +++ b/projects/fabric-api/src/client/java/dan200/computercraft/impl/client/FabricComputerCraftAPIClientService.java @@ -6,7 +6,7 @@ package dan200.computercraft.impl.client; import dan200.computercraft.api.client.turtle.TurtleUpgradeModeller; import dan200.computercraft.api.turtle.ITurtleUpgrade; -import dan200.computercraft.api.upgrades.UpgradeSerialiser; +import dan200.computercraft.api.upgrades.UpgradeType; import dan200.computercraft.impl.Services; import org.jetbrains.annotations.ApiStatus; @@ -24,7 +24,7 @@ public interface FabricComputerCraftAPIClientService { return instance == null ? Services.raise(FabricComputerCraftAPIClientService.class, Instance.ERROR) : instance; } - void registerTurtleUpgradeModeller(UpgradeSerialiser serialiser, TurtleUpgradeModeller modeller); + void registerTurtleUpgradeModeller(UpgradeType type, TurtleUpgradeModeller modeller); final class Instance { static final @Nullable FabricComputerCraftAPIClientService INSTANCE; diff --git a/projects/fabric/src/client/java/dan200/computercraft/client/integration/rei/REIComputerCraft.java b/projects/fabric/src/client/java/dan200/computercraft/client/integration/rei/REIComputerCraft.java index fb8b22a9a..7a4be36eb 100644 --- a/projects/fabric/src/client/java/dan200/computercraft/client/integration/rei/REIComputerCraft.java +++ b/projects/fabric/src/client/java/dan200/computercraft/client/integration/rei/REIComputerCraft.java @@ -12,7 +12,9 @@ import dan200.computercraft.shared.turtle.items.TurtleItem; import dev.architectury.event.EventResult; import me.shedaniel.rei.api.client.plugins.REIClientPlugin; import me.shedaniel.rei.api.client.registry.display.DisplayRegistry; +import me.shedaniel.rei.api.client.registry.entry.EntryRegistry; import me.shedaniel.rei.api.common.entry.comparison.ItemComparatorRegistry; +import me.shedaniel.rei.api.common.util.EntryStacks; import me.shedaniel.rei.plugin.common.BuiltinPlugin; /** @@ -26,20 +28,27 @@ public class REIComputerCraft implements REIClientPlugin { registry.register((context, stack) -> { long hash = 1; - var left = TurtleItem.getUpgrade(stack, TurtleSide.LEFT); - var right = TurtleItem.getUpgrade(stack, TurtleSide.RIGHT); - if (left != null) hash = hash * 31 + left.getUpgradeID().hashCode(); - if (right != null) hash = hash * 31 + right.getUpgradeID().hashCode(); + var left = TurtleItem.getUpgradeWithData(stack, TurtleSide.LEFT); + var right = TurtleItem.getUpgradeWithData(stack, TurtleSide.RIGHT); + if (left != null) hash = hash * 31 + left.holder().key().location().hashCode(); + if (right != null) hash = hash * 31 + right.holder().key().location().hashCode(); return hash; }, ModRegistry.Items.TURTLE_NORMAL.get(), ModRegistry.Items.TURTLE_ADVANCED.get()); registry.register((context, stack) -> { - var upgrade = PocketComputerItem.getUpgrade(stack); - return upgrade == null ? 1 : upgrade.getUpgradeID().hashCode(); + var upgrade = PocketComputerItem.getUpgradeWithData(stack); + return upgrade == null ? 1 : upgrade.holder().key().location().hashCode(); }, ModRegistry.Items.POCKET_COMPUTER_NORMAL.get(), ModRegistry.Items.POCKET_COMPUTER_ADVANCED.get()); } + @Override + public void registerEntries(EntryRegistry registry) { + for (var stack : RecipeModHelpers.getExtraStacks(RecipeModHelpers.getEmptyRegistryAccess())) { + registry.addEntry(EntryStacks.of(stack)); + } + } + @Override public void registerDisplays(DisplayRegistry registry) { registry.registerDisplayGenerator(BuiltinPlugin.CRAFTING, new UpgradeDisplayGenerator()); diff --git a/projects/fabric/src/client/java/dan200/computercraft/client/integration/rei/UpgradeDisplayGenerator.java b/projects/fabric/src/client/java/dan200/computercraft/client/integration/rei/UpgradeDisplayGenerator.java index 5727e1c19..ebfdb2224 100644 --- a/projects/fabric/src/client/java/dan200/computercraft/client/integration/rei/UpgradeDisplayGenerator.java +++ b/projects/fabric/src/client/java/dan200/computercraft/client/integration/rei/UpgradeDisplayGenerator.java @@ -4,6 +4,7 @@ package dan200.computercraft.client.integration.rei; +import dan200.computercraft.shared.integration.RecipeModHelpers; import dan200.computercraft.shared.integration.UpgradeRecipeGenerator; import me.shedaniel.rei.api.client.registry.display.DynamicDisplayGenerator; import me.shedaniel.rei.api.common.display.basic.BasicDisplay; @@ -23,7 +24,7 @@ import java.util.Optional; * Provides custom recipe and usage hints for pocket/turtle upgrades. */ class UpgradeDisplayGenerator implements DynamicDisplayGenerator> { - private final UpgradeRecipeGenerator> resolver = new UpgradeRecipeGenerator<>(GeneratedShapedDisplay::new); + private final UpgradeRecipeGenerator> resolver = new UpgradeRecipeGenerator<>(GeneratedShapedDisplay::new, RecipeModHelpers.getEmptyRegistryAccess()); @Override public Optional>> getRecipeFor(EntryStack entry) { diff --git a/projects/fabric/src/client/java/dan200/computercraft/impl/client/FabricComputerCraftAPIClientImpl.java b/projects/fabric/src/client/java/dan200/computercraft/impl/client/FabricComputerCraftAPIClientImpl.java index 83202e7f7..500b88772 100644 --- a/projects/fabric/src/client/java/dan200/computercraft/impl/client/FabricComputerCraftAPIClientImpl.java +++ b/projects/fabric/src/client/java/dan200/computercraft/impl/client/FabricComputerCraftAPIClientImpl.java @@ -7,13 +7,13 @@ package dan200.computercraft.impl.client; import com.google.auto.service.AutoService; import dan200.computercraft.api.client.turtle.TurtleUpgradeModeller; import dan200.computercraft.api.turtle.ITurtleUpgrade; -import dan200.computercraft.api.upgrades.UpgradeSerialiser; +import dan200.computercraft.api.upgrades.UpgradeType; import dan200.computercraft.client.turtle.TurtleUpgradeModellers; @AutoService(FabricComputerCraftAPIClientService.class) public final class FabricComputerCraftAPIClientImpl implements FabricComputerCraftAPIClientService { @Override - public void registerTurtleUpgradeModeller(UpgradeSerialiser serialiser, TurtleUpgradeModeller modeller) { - TurtleUpgradeModellers.register(serialiser, modeller); + public void registerTurtleUpgradeModeller(UpgradeType type, TurtleUpgradeModeller modeller) { + TurtleUpgradeModellers.register(type, modeller); } } diff --git a/projects/fabric/src/main/java/dan200/computercraft/data/FabricDataGenerators.java b/projects/fabric/src/main/java/dan200/computercraft/data/FabricDataGenerators.java index ba0229964..6748fe23e 100644 --- a/projects/fabric/src/main/java/dan200/computercraft/data/FabricDataGenerators.java +++ b/projects/fabric/src/main/java/dan200/computercraft/data/FabricDataGenerators.java @@ -5,26 +5,32 @@ package dan200.computercraft.data; import com.mojang.serialization.Codec; +import com.mojang.serialization.Lifecycle; import net.fabricmc.fabric.api.datagen.v1.DataGeneratorEntrypoint; import net.fabricmc.fabric.api.datagen.v1.FabricDataGenerator; import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput; import net.fabricmc.fabric.api.datagen.v1.provider.FabricCodecDataProvider; import net.fabricmc.fabric.api.datagen.v1.provider.FabricTagProvider; -import net.minecraft.core.HolderLookup; +import net.fabricmc.fabric.api.event.registry.DynamicRegistries; +import net.minecraft.core.*; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.data.DataProvider; import net.minecraft.data.PackOutput; import net.minecraft.data.tags.TagsProvider; +import net.minecraft.resources.RegistryDataLoader; +import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.packs.PackType; import net.minecraft.tags.TagKey; import net.minecraft.world.item.Item; import net.minecraft.world.level.block.Block; +import java.util.Optional; import java.util.concurrent.CompletableFuture; import java.util.function.BiConsumer; import java.util.function.BiFunction; import java.util.function.Consumer; +import java.util.stream.Stream; public class FabricDataGenerators implements DataGeneratorEntrypoint { @Override @@ -103,5 +109,63 @@ public class FabricDataGenerators implements DataGeneratorEntrypoint { } }); } + + @Override + public CompletableFuture createPatchedRegistries(CompletableFuture registries, RegistrySetBuilder patch) { + return registries.thenApply(oldRegistries -> { + var factory = new Cloner.Factory(); + DynamicRegistries.getDynamicRegistries().forEach(registryData -> registryData.runWithArguments(factory::addCodec)); + return patch.buildPatch(RegistryAccess.fromRegistryOfRegistries(BuiltInRegistries.REGISTRY), new DynamicRegistryLookup(oldRegistries), factory); + }); + } + + /** + * A {@link HolderLookup.Provider} implementation that adds any Fabric dynamic registry, if missing. + * + * @param parent The parent registry. + */ + private record DynamicRegistryLookup(HolderLookup.Provider parent) implements HolderLookup.Provider { + @Override + public Stream>> listRegistries() { + return Stream.concat( + parent.listRegistries(), + DynamicRegistries.getDynamicRegistries().stream().map(RegistryDataLoader.RegistryData::key) + ).distinct(); + } + + @Override + public Optional> lookup(ResourceKey> registryKey) { + return parent.lookup(registryKey).or(() -> Optional.of(new EmptyRegistry<>(registryKey))); + } + } + + private record EmptyRegistry( + ResourceKey> key + ) implements HolderLookup.RegistryLookup { + @Override + public Lifecycle registryLifecycle() { + return Lifecycle.stable(); + } + + @Override + public Stream> listElements() { + return Stream.empty(); + } + + @Override + public Stream> listTags() { + return Stream.empty(); + } + + @Override + public Optional> get(ResourceKey resourceKey) { + return Optional.empty(); + } + + @Override + public Optional> get(TagKey tagKey) { + return Optional.empty(); + } + } } } diff --git a/projects/fabric/src/main/java/dan200/computercraft/impl/ComputerCraftAPIImpl.java b/projects/fabric/src/main/java/dan200/computercraft/impl/ComputerCraftAPIImpl.java index 14073805b..07f0b872a 100644 --- a/projects/fabric/src/main/java/dan200/computercraft/impl/ComputerCraftAPIImpl.java +++ b/projects/fabric/src/main/java/dan200/computercraft/impl/ComputerCraftAPIImpl.java @@ -22,7 +22,7 @@ public final class ComputerCraftAPIImpl extends AbstractComputerCraftAPI impleme static { // This We create the registries here (rather than in the mod initialiser) to guarantee that they're available - // when people come to register upgrade serialisers. + // when people come to register upgrade types. // This is a little nasty (side effects in static constructors and all that!), but seems to be the easiest way. FabricRegistryBuilder.createSimple(turtleUpgradeRegistryId).buildAndRegister(); FabricRegistryBuilder.createSimple(pocketUpgradeRegistryId).buildAndRegister(); diff --git a/projects/fabric/src/main/java/dan200/computercraft/shared/ComputerCraft.java b/projects/fabric/src/main/java/dan200/computercraft/shared/ComputerCraft.java index 526d68bac..425349db2 100644 --- a/projects/fabric/src/main/java/dan200/computercraft/shared/ComputerCraft.java +++ b/projects/fabric/src/main/java/dan200/computercraft/shared/ComputerCraft.java @@ -9,13 +9,13 @@ import dan200.computercraft.api.detail.FabricDetailRegistries; import dan200.computercraft.api.network.wired.WiredElementLookup; import dan200.computercraft.api.peripheral.PeripheralLookup; import dan200.computercraft.impl.Peripherals; +import dan200.computercraft.impl.PocketUpgrades; +import dan200.computercraft.impl.TurtleUpgrades; import dan200.computercraft.shared.command.CommandComputerCraft; import dan200.computercraft.shared.config.Config; import dan200.computercraft.shared.config.ConfigSpec; import dan200.computercraft.shared.details.FluidDetails; import dan200.computercraft.shared.network.NetworkMessages; -import dan200.computercraft.shared.network.client.UpgradesLoadedMessage; -import dan200.computercraft.shared.network.server.ServerNetworking; import dan200.computercraft.shared.peripheral.commandblock.CommandBlockPeripheral; import dan200.computercraft.shared.peripheral.generic.methods.InventoryMethods; import dan200.computercraft.shared.peripheral.modem.wired.CableBlockEntity; @@ -29,6 +29,7 @@ import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents; import net.fabricmc.fabric.api.event.player.PlayerBlockBreakEvents; import net.fabricmc.fabric.api.event.player.UseBlockCallback; +import net.fabricmc.fabric.api.event.registry.DynamicRegistries; import net.fabricmc.fabric.api.event.registry.FabricRegistryBuilder; import net.fabricmc.fabric.api.event.registry.RegistryAttribute; import net.fabricmc.fabric.api.itemgroup.v1.ItemGroupEvents; @@ -65,6 +66,9 @@ public class ComputerCraft { FabricRegistryBuilder.createSimple(RecipeFunction.REGISTRY).attribute(RegistryAttribute.SYNCED).buildAndRegister(); + DynamicRegistries.registerSynced(ModRegistry.TURTLE_UPGRADE, TurtleUpgrades.instance().upgradeCodec()); + DynamicRegistries.registerSynced(ModRegistry.POCKET_UPGRADE, PocketUpgrades.instance().upgradeCodec()); + ModRegistry.register(); ModRegistry.registerMainThread(); @@ -106,7 +110,6 @@ public class ComputerCraft { ((FabricConfigFile) ConfigSpec.serverSpec).unload(); }); ServerLifecycleEvents.SERVER_STARTED.register(CommonHooks::onServerStarted); - ServerLifecycleEvents.SYNC_DATA_PACK_CONTENTS.register((player, joined) -> ServerNetworking.sendToPlayer(new UpgradesLoadedMessage(), player)); ServerTickEvents.START_SERVER_TICK.register(CommonHooks::onServerTickStart); ServerTickEvents.START_SERVER_TICK.register(s -> CommonHooks.onServerTickEnd()); diff --git a/projects/forge-api/src/client/java/dan200/computercraft/api/client/turtle/RegisterTurtleModellersEvent.java b/projects/forge-api/src/client/java/dan200/computercraft/api/client/turtle/RegisterTurtleModellersEvent.java index 553a4f577..48ea95534 100644 --- a/projects/forge-api/src/client/java/dan200/computercraft/api/client/turtle/RegisterTurtleModellersEvent.java +++ b/projects/forge-api/src/client/java/dan200/computercraft/api/client/turtle/RegisterTurtleModellersEvent.java @@ -6,7 +6,7 @@ package dan200.computercraft.api.client.turtle; import dan200.computercraft.api.turtle.ITurtleUpgrade; import dan200.computercraft.api.turtle.TurtleUpgradeType; -import dan200.computercraft.api.upgrades.UpgradeSerialiser; +import dan200.computercraft.api.upgrades.UpgradeType; import net.neoforged.bus.api.Event; import net.neoforged.fml.event.IModBusEvent; import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent; @@ -33,7 +33,7 @@ public class RegisterTurtleModellersEvent extends Event implements IModBusEvent, * {@inheritDoc} */ @Override - public void register(UpgradeSerialiser serialiser, TurtleUpgradeModeller modeller) { - dispatch.register(serialiser, modeller); + public void register(UpgradeType type, TurtleUpgradeModeller modeller) { + dispatch.register(type, modeller); } } diff --git a/projects/forge/src/main/java/dan200/computercraft/ComputerCraft.java b/projects/forge/src/main/java/dan200/computercraft/ComputerCraft.java index f2006cc75..1d2de4570 100644 --- a/projects/forge/src/main/java/dan200/computercraft/ComputerCraft.java +++ b/projects/forge/src/main/java/dan200/computercraft/ComputerCraft.java @@ -12,7 +12,9 @@ import dan200.computercraft.api.network.wired.WiredElementCapability; import dan200.computercraft.api.peripheral.PeripheralCapability; import dan200.computercraft.api.pocket.IPocketUpgrade; import dan200.computercraft.api.turtle.ITurtleUpgrade; +import dan200.computercraft.impl.PocketUpgrades; import dan200.computercraft.impl.Services; +import dan200.computercraft.impl.TurtleUpgrades; import dan200.computercraft.shared.CommonHooks; import dan200.computercraft.shared.ModRegistry; import dan200.computercraft.shared.config.Config; @@ -48,6 +50,7 @@ import net.neoforged.neoforge.capabilities.RegisterCapabilitiesEvent; import net.neoforged.neoforge.event.BuildCreativeModeTabContentsEvent; import net.neoforged.neoforge.network.event.RegisterPayloadHandlersEvent; import net.neoforged.neoforge.network.registration.PayloadRegistrar; +import net.neoforged.neoforge.registries.DataPackRegistryEvent; import net.neoforged.neoforge.registries.NewRegistryEvent; import net.neoforged.neoforge.registries.RegistryBuilder; @@ -80,11 +83,17 @@ public final class ComputerCraft { @SubscribeEvent public static void registerRegistries(NewRegistryEvent event) { - event.create(new RegistryBuilder<>(ITurtleUpgrade.serialiserRegistryKey())); - event.create(new RegistryBuilder<>(IPocketUpgrade.serialiserRegistryKey())); + event.create(new RegistryBuilder<>(ITurtleUpgrade.typeRegistry())); + event.create(new RegistryBuilder<>(IPocketUpgrade.typeRegistry())); event.create(new RegistryBuilder<>(RecipeFunction.REGISTRY).sync(true)); } + @SubscribeEvent + public static void registerDynamicRegistries(DataPackRegistryEvent.NewRegistry event) { + event.dataPackRegistry(ModRegistry.TURTLE_UPGRADE, TurtleUpgrades.instance().upgradeCodec(), TurtleUpgrades.instance().upgradeCodec()); + event.dataPackRegistry(ModRegistry.POCKET_UPGRADE, PocketUpgrades.instance().upgradeCodec(), PocketUpgrades.instance().upgradeCodec()); + } + @SubscribeEvent public static void init(FMLCommonSetupEvent event) { event.enqueueWork(ModRegistry::registerMainThread); diff --git a/projects/forge/src/main/java/dan200/computercraft/shared/ForgeCommonHooks.java b/projects/forge/src/main/java/dan200/computercraft/shared/ForgeCommonHooks.java index 7aea705a8..f659695dd 100644 --- a/projects/forge/src/main/java/dan200/computercraft/shared/ForgeCommonHooks.java +++ b/projects/forge/src/main/java/dan200/computercraft/shared/ForgeCommonHooks.java @@ -6,8 +6,6 @@ package dan200.computercraft.shared; import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.shared.command.CommandComputerCraft; -import dan200.computercraft.shared.network.client.UpgradesLoadedMessage; -import dan200.computercraft.shared.network.server.ServerNetworking; import net.minecraft.core.registries.Registries; import net.minecraft.resources.ResourceKey; import net.minecraft.server.level.ServerLevel; @@ -15,7 +13,10 @@ import net.minecraft.world.level.chunk.LevelChunk; import net.neoforged.bus.api.EventPriority; import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.fml.common.EventBusSubscriber; -import net.neoforged.neoforge.event.*; +import net.neoforged.neoforge.event.AddReloadListenerEvent; +import net.neoforged.neoforge.event.LootTableLoadEvent; +import net.neoforged.neoforge.event.RegisterCommandsEvent; +import net.neoforged.neoforge.event.TickEvent; import net.neoforged.neoforge.event.entity.EntityJoinLevelEvent; import net.neoforged.neoforge.event.entity.living.LivingDropsEvent; import net.neoforged.neoforge.event.level.ChunkEvent; @@ -80,16 +81,6 @@ public class ForgeCommonHooks { CommonHooks.onDatapackReload((id, listener) -> event.addListener(listener)); } - @SubscribeEvent - public static void onDatapackSync(OnDatapackSyncEvent event) { - var packet = new UpgradesLoadedMessage(); - if (event.getPlayer() == null) { - ServerNetworking.sendToAllPlayers(packet, event.getPlayerList().getServer()); - } else { - ServerNetworking.sendToPlayer(packet, event.getPlayer()); - } - } - @SubscribeEvent public static void lootLoad(LootTableLoadEvent event) { var pool = CommonHooks.getExtraLootPool(ResourceKey.create(Registries.LOOT_TABLE, event.getName()));