CC-Tweaked/projects/common/src/main/java/dan200/computercraft/data/RecipeProvider.java

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

514 lines
23 KiB
Java
Raw Normal View History

// SPDX-FileCopyrightText: 2020 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.data;
import com.google.gson.JsonObject;
import com.mojang.authlib.GameProfile;
2024-01-31 20:55:14 +00:00
import com.mojang.serialization.JsonOps;
import dan200.computercraft.api.ComputerCraftAPI;
Rewrite turtle upgrade registration to be more data driven (#967) The feature nobody asked for, but we're getting anyway. Old way to register a turtle/pocket computer upgrade: ComputerCraftAPI.registerTurtleUpgrade(new MyUpgrade(new ResourceLocation("my_mod", "my_upgrade"))); New way to register a turtle/pocket computer upgrade: First, define a serialiser for your turtle upgrade type: static final DeferredRegister<TurtleUpgradeSerialiser<?>> SERIALISERS = DeferredRegister.create( TurtleUpgradeSerialiser.TYPE, "my_mod" ); public static final RegistryObject<TurtleUpgradeSerialiser<MyUpgrade>> MY_UPGRADE = SERIALISERS.register( "my_upgrade", () -> TurtleUpgradeSerialiser.simple( MyUpgrade::new ) ); SERIALISERS.register(bus); // Call in your mod constructor. Now either create a JSON string or use a data generator to register your upgrades: class TurtleDataGenerator extends TurtleUpgradeDataProvider { @Override protected void addUpgrades( @Nonnull Consumer<Upgrade<TurtleUpgradeSerialiser<?>>> addUpgrade ) simple(new ResourceLocation("my_mod", my_upgrade"), MY_UPGRADE.get()).add(addUpgrade); } } See much better! In all seriousness, this does offer some benefits, namely that it's now possible to overwrite or create upgrades via datapacks. Actual changes: - Remove ComputerCraftAPI.register{Turtle,Pocket}Upgrade functions. - Instead add {Turtle,Pocket}UpgradeSerialiser classes, which are used to load upgrades from JSON files in datapacks, and then read/write them to network packets (much like recipe serialisers). - The upgrade registries now subscribe to datapack reload events. They find all JSON files in the data/$mod_id/computercraft/{turtle,pocket}_upgrades directories, parse them, and then register them as upgrades. Once datapacks have fully reloaded, these upgrades are then sent over the network to the client. - Add data generators for turtle and pocket computer upgrades, to make the creation of JSON files a bit easier. - Port all of CC:T's upgrades over to use the new system.
2021-11-26 23:36:02 +00:00
import dan200.computercraft.api.pocket.PocketUpgradeDataProvider;
import dan200.computercraft.api.turtle.TurtleUpgradeDataProvider;
import dan200.computercraft.api.upgrades.UpgradeData;
import dan200.computercraft.core.util.Colour;
2024-01-31 20:55:14 +00:00
import dan200.computercraft.data.recipe.ShapedSpecBuilder;
import dan200.computercraft.data.recipe.ShapelessSpecBuilder;
import dan200.computercraft.impl.RegistryHelper;
import dan200.computercraft.shared.ModRegistry;
2024-01-31 20:55:14 +00:00
import dan200.computercraft.shared.common.ClearColourRecipe;
import dan200.computercraft.shared.common.ColourableRecipe;
import dan200.computercraft.shared.computer.recipe.ComputerConvertRecipe;
2024-01-31 20:55:14 +00:00
import dan200.computercraft.shared.media.recipes.DiskRecipe;
import dan200.computercraft.shared.media.recipes.PrintoutRecipe;
2022-11-09 10:59:05 +00:00
import dan200.computercraft.shared.platform.PlatformHelper;
import dan200.computercraft.shared.platform.RecipeIngredients;
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
2024-01-31 20:55:14 +00:00
import dan200.computercraft.shared.pocket.recipes.PocketComputerUpgradeRecipe;
import dan200.computercraft.shared.recipe.ImpostorShapedRecipe;
import dan200.computercraft.shared.recipe.ImpostorShapelessRecipe;
import dan200.computercraft.shared.turtle.items.TurtleItem;
2024-01-31 20:55:14 +00:00
import dan200.computercraft.shared.turtle.recipes.TurtleOverlayRecipe;
import dan200.computercraft.shared.turtle.recipes.TurtleUpgradeRecipe;
import dan200.computercraft.shared.util.ColourUtils;
import dan200.computercraft.shared.util.DataComponentUtil;
2024-01-31 20:55:14 +00:00
import net.minecraft.advancements.Criterion;
import net.minecraft.advancements.critereon.InventoryChangeTrigger;
import net.minecraft.advancements.critereon.ItemPredicate;
import net.minecraft.core.HolderLookup;
import net.minecraft.core.component.DataComponents;
2024-01-31 20:55:14 +00:00
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.core.registries.Registries;
import net.minecraft.data.PackOutput;
2024-01-31 20:55:14 +00:00
import net.minecraft.data.recipes.RecipeCategory;
import net.minecraft.data.recipes.RecipeOutput;
import net.minecraft.data.recipes.ShapedRecipeBuilder;
import net.minecraft.data.recipes.ShapelessRecipeBuilder;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.ItemTags;
import net.minecraft.tags.TagKey;
2022-11-09 10:59:05 +00:00
import net.minecraft.util.GsonHelper;
import net.minecraft.world.item.*;
import net.minecraft.world.item.component.DyedItemColor;
import net.minecraft.world.item.component.ResolvableProfile;
2024-01-31 20:55:14 +00:00
import net.minecraft.world.item.crafting.CraftingBookCategory;
2022-11-09 10:59:05 +00:00
import net.minecraft.world.item.crafting.Ingredient;
2024-01-31 20:55:14 +00:00
import net.minecraft.world.item.crafting.Recipe;
import net.minecraft.world.level.ItemLike;
2022-11-09 10:59:05 +00:00
import net.minecraft.world.level.block.Blocks;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
Rewrite turtle upgrade registration to be more data driven (#967) The feature nobody asked for, but we're getting anyway. Old way to register a turtle/pocket computer upgrade: ComputerCraftAPI.registerTurtleUpgrade(new MyUpgrade(new ResourceLocation("my_mod", "my_upgrade"))); New way to register a turtle/pocket computer upgrade: First, define a serialiser for your turtle upgrade type: static final DeferredRegister<TurtleUpgradeSerialiser<?>> SERIALISERS = DeferredRegister.create( TurtleUpgradeSerialiser.TYPE, "my_mod" ); public static final RegistryObject<TurtleUpgradeSerialiser<MyUpgrade>> MY_UPGRADE = SERIALISERS.register( "my_upgrade", () -> TurtleUpgradeSerialiser.simple( MyUpgrade::new ) ); SERIALISERS.register(bus); // Call in your mod constructor. Now either create a JSON string or use a data generator to register your upgrades: class TurtleDataGenerator extends TurtleUpgradeDataProvider { @Override protected void addUpgrades( @Nonnull Consumer<Upgrade<TurtleUpgradeSerialiser<?>>> addUpgrade ) simple(new ResourceLocation("my_mod", my_upgrade"), MY_UPGRADE.get()).add(addUpgrade); } } See much better! In all seriousness, this does offer some benefits, namely that it's now possible to overwrite or create upgrades via datapacks. Actual changes: - Remove ComputerCraftAPI.register{Turtle,Pocket}Upgrade functions. - Instead add {Turtle,Pocket}UpgradeSerialiser classes, which are used to load upgrades from JSON files in datapacks, and then read/write them to network packets (much like recipe serialisers). - The upgrade registries now subscribe to datapack reload events. They find all JSON files in the data/$mod_id/computercraft/{turtle,pocket}_upgrades directories, parse them, and then register them as upgrades. Once datapacks have fully reloaded, these upgrades are then sent over the network to the client. - Add data generators for turtle and pocket computer upgrades, to make the creation of JSON files a bit easier. - Port all of CC:T's upgrades over to use the new system.
2021-11-26 23:36:02 +00:00
import static dan200.computercraft.api.ComputerCraftTags.Items.COMPUTER;
import static dan200.computercraft.api.ComputerCraftTags.Items.WIRED_MODEM;
2024-01-31 20:55:14 +00:00
final class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
2022-11-09 10:59:05 +00:00
private final RecipeIngredients ingredients = PlatformHelper.get().getRecipeIngredients();
Rewrite turtle upgrade registration to be more data driven (#967) The feature nobody asked for, but we're getting anyway. Old way to register a turtle/pocket computer upgrade: ComputerCraftAPI.registerTurtleUpgrade(new MyUpgrade(new ResourceLocation("my_mod", "my_upgrade"))); New way to register a turtle/pocket computer upgrade: First, define a serialiser for your turtle upgrade type: static final DeferredRegister<TurtleUpgradeSerialiser<?>> SERIALISERS = DeferredRegister.create( TurtleUpgradeSerialiser.TYPE, "my_mod" ); public static final RegistryObject<TurtleUpgradeSerialiser<MyUpgrade>> MY_UPGRADE = SERIALISERS.register( "my_upgrade", () -> TurtleUpgradeSerialiser.simple( MyUpgrade::new ) ); SERIALISERS.register(bus); // Call in your mod constructor. Now either create a JSON string or use a data generator to register your upgrades: class TurtleDataGenerator extends TurtleUpgradeDataProvider { @Override protected void addUpgrades( @Nonnull Consumer<Upgrade<TurtleUpgradeSerialiser<?>>> addUpgrade ) simple(new ResourceLocation("my_mod", my_upgrade"), MY_UPGRADE.get()).add(addUpgrade); } } See much better! In all seriousness, this does offer some benefits, namely that it's now possible to overwrite or create upgrades via datapacks. Actual changes: - Remove ComputerCraftAPI.register{Turtle,Pocket}Upgrade functions. - Instead add {Turtle,Pocket}UpgradeSerialiser classes, which are used to load upgrades from JSON files in datapacks, and then read/write them to network packets (much like recipe serialisers). - The upgrade registries now subscribe to datapack reload events. They find all JSON files in the data/$mod_id/computercraft/{turtle,pocket}_upgrades directories, parse them, and then register them as upgrades. Once datapacks have fully reloaded, these upgrades are then sent over the network to the client. - Add data generators for turtle and pocket computer upgrades, to make the creation of JSON files a bit easier. - Port all of CC:T's upgrades over to use the new system.
2021-11-26 23:36:02 +00:00
private final TurtleUpgradeDataProvider turtleUpgrades;
private final PocketUpgradeDataProvider pocketUpgrades;
RecipeProvider(PackOutput output, CompletableFuture<HolderLookup.Provider> registries, TurtleUpgradeDataProvider turtleUpgrades, PocketUpgradeDataProvider pocketUpgrades) {
super(output, registries);
Rewrite turtle upgrade registration to be more data driven (#967) The feature nobody asked for, but we're getting anyway. Old way to register a turtle/pocket computer upgrade: ComputerCraftAPI.registerTurtleUpgrade(new MyUpgrade(new ResourceLocation("my_mod", "my_upgrade"))); New way to register a turtle/pocket computer upgrade: First, define a serialiser for your turtle upgrade type: static final DeferredRegister<TurtleUpgradeSerialiser<?>> SERIALISERS = DeferredRegister.create( TurtleUpgradeSerialiser.TYPE, "my_mod" ); public static final RegistryObject<TurtleUpgradeSerialiser<MyUpgrade>> MY_UPGRADE = SERIALISERS.register( "my_upgrade", () -> TurtleUpgradeSerialiser.simple( MyUpgrade::new ) ); SERIALISERS.register(bus); // Call in your mod constructor. Now either create a JSON string or use a data generator to register your upgrades: class TurtleDataGenerator extends TurtleUpgradeDataProvider { @Override protected void addUpgrades( @Nonnull Consumer<Upgrade<TurtleUpgradeSerialiser<?>>> addUpgrade ) simple(new ResourceLocation("my_mod", my_upgrade"), MY_UPGRADE.get()).add(addUpgrade); } } See much better! In all seriousness, this does offer some benefits, namely that it's now possible to overwrite or create upgrades via datapacks. Actual changes: - Remove ComputerCraftAPI.register{Turtle,Pocket}Upgrade functions. - Instead add {Turtle,Pocket}UpgradeSerialiser classes, which are used to load upgrades from JSON files in datapacks, and then read/write them to network packets (much like recipe serialisers). - The upgrade registries now subscribe to datapack reload events. They find all JSON files in the data/$mod_id/computercraft/{turtle,pocket}_upgrades directories, parse them, and then register them as upgrades. Once datapacks have fully reloaded, these upgrades are then sent over the network to the client. - Add data generators for turtle and pocket computer upgrades, to make the creation of JSON files a bit easier. - Port all of CC:T's upgrades over to use the new system.
2021-11-26 23:36:02 +00:00
this.turtleUpgrades = turtleUpgrades;
this.pocketUpgrades = pocketUpgrades;
}
@Override
2024-01-31 20:55:14 +00:00
public void buildRecipes(RecipeOutput add) {
basicRecipes(add);
diskColours(add);
pocketUpgrades(add);
turtleUpgrades(add);
turtleOverlays(add);
2024-01-31 20:55:14 +00:00
addSpecial(add, new PrintoutRecipe(CraftingBookCategory.MISC));
addSpecial(add, new DiskRecipe(CraftingBookCategory.MISC));
addSpecial(add, new ColourableRecipe(CraftingBookCategory.MISC));
addSpecial(add, new ClearColourRecipe(CraftingBookCategory.MISC));
addSpecial(add, new TurtleUpgradeRecipe(CraftingBookCategory.MISC));
addSpecial(add, new PocketComputerUpgradeRecipe(CraftingBookCategory.MISC));
}
/**
* Register a crafting recipe for a disk of every dye colour.
*
2024-01-31 20:55:14 +00:00
* @param output The callback to add recipes.
*/
2024-01-31 20:55:14 +00:00
private void diskColours(RecipeOutput output) {
for (var colour : Colour.VALUES) {
2024-01-31 20:55:14 +00:00
ShapelessSpecBuilder
.shapeless(RecipeCategory.REDSTONE, DataComponentUtil.createStack(ModRegistry.Items.DISK.get(), DataComponents.DYED_COLOR, new DyedItemColor(colour.getHex(), false)))
2022-11-09 10:59:05 +00:00
.requires(ingredients.redstone())
.requires(Items.PAPER)
.requires(DyeItem.byColor(ofColour(colour)))
.group("computercraft:disk")
.unlockedBy("has_drive", inventoryChange(ModRegistry.Blocks.DISK_DRIVE.get()))
2024-01-31 20:55:14 +00:00
.build(ImpostorShapelessRecipe::new)
.save(output, new ResourceLocation(ComputerCraftAPI.MOD_ID, "disk_" + (colour.ordinal() + 1)));
}
}
private static List<TurtleItem> turtleItems() {
return List.of(ModRegistry.Items.TURTLE_NORMAL.get(), ModRegistry.Items.TURTLE_ADVANCED.get());
}
/**
* Register a crafting recipe for each turtle upgrade.
*
* @param add The callback to add recipes.
*/
2024-01-31 20:55:14 +00:00
private void turtleUpgrades(RecipeOutput add) {
for (var turtleItem : turtleItems()) {
2024-01-31 20:55:14 +00:00
var name = RegistryHelper.getKeyOrThrow(BuiltInRegistries.ITEM, turtleItem);
Rewrite turtle upgrade registration to be more data driven (#967) The feature nobody asked for, but we're getting anyway. Old way to register a turtle/pocket computer upgrade: ComputerCraftAPI.registerTurtleUpgrade(new MyUpgrade(new ResourceLocation("my_mod", "my_upgrade"))); New way to register a turtle/pocket computer upgrade: First, define a serialiser for your turtle upgrade type: static final DeferredRegister<TurtleUpgradeSerialiser<?>> SERIALISERS = DeferredRegister.create( TurtleUpgradeSerialiser.TYPE, "my_mod" ); public static final RegistryObject<TurtleUpgradeSerialiser<MyUpgrade>> MY_UPGRADE = SERIALISERS.register( "my_upgrade", () -> TurtleUpgradeSerialiser.simple( MyUpgrade::new ) ); SERIALISERS.register(bus); // Call in your mod constructor. Now either create a JSON string or use a data generator to register your upgrades: class TurtleDataGenerator extends TurtleUpgradeDataProvider { @Override protected void addUpgrades( @Nonnull Consumer<Upgrade<TurtleUpgradeSerialiser<?>>> addUpgrade ) simple(new ResourceLocation("my_mod", my_upgrade"), MY_UPGRADE.get()).add(addUpgrade); } } See much better! In all seriousness, this does offer some benefits, namely that it's now possible to overwrite or create upgrades via datapacks. Actual changes: - Remove ComputerCraftAPI.register{Turtle,Pocket}Upgrade functions. - Instead add {Turtle,Pocket}UpgradeSerialiser classes, which are used to load upgrades from JSON files in datapacks, and then read/write them to network packets (much like recipe serialisers). - The upgrade registries now subscribe to datapack reload events. They find all JSON files in the data/$mod_id/computercraft/{turtle,pocket}_upgrades directories, parse them, and then register them as upgrades. Once datapacks have fully reloaded, these upgrades are then sent over the network to the client. - Add data generators for turtle and pocket computer upgrades, to make the creation of JSON files a bit easier. - Port all of CC:T's upgrades over to use the new system.
2021-11-26 23:36:02 +00:00
for (var upgrade : turtleUpgrades.getGeneratedUpgrades()) {
2024-01-31 20:55:14 +00:00
ShapedSpecBuilder
.shaped(RecipeCategory.REDSTONE, DataComponentUtil.createStack(turtleItem, ModRegistry.DataComponents.RIGHT_TURTLE_UPGRADE.get(), UpgradeData.ofDefault(upgrade)))
.group(name.toString())
.pattern("#T")
.define('T', turtleItem)
.define('#', upgrade.getCraftingItem().getItem())
.unlockedBy("has_items", inventoryChange(turtleItem, upgrade.getCraftingItem().getItem()))
2024-01-31 20:55:14 +00:00
.build(ImpostorShapedRecipe::new)
.save(
2024-01-31 20:55:14 +00:00
add,
name.withSuffix(String.format("/%s/%s", upgrade.getUpgradeID().getNamespace(), upgrade.getUpgradeID().getPath()))
);
Rewrite turtle upgrade registration to be more data driven (#967) The feature nobody asked for, but we're getting anyway. Old way to register a turtle/pocket computer upgrade: ComputerCraftAPI.registerTurtleUpgrade(new MyUpgrade(new ResourceLocation("my_mod", "my_upgrade"))); New way to register a turtle/pocket computer upgrade: First, define a serialiser for your turtle upgrade type: static final DeferredRegister<TurtleUpgradeSerialiser<?>> SERIALISERS = DeferredRegister.create( TurtleUpgradeSerialiser.TYPE, "my_mod" ); public static final RegistryObject<TurtleUpgradeSerialiser<MyUpgrade>> MY_UPGRADE = SERIALISERS.register( "my_upgrade", () -> TurtleUpgradeSerialiser.simple( MyUpgrade::new ) ); SERIALISERS.register(bus); // Call in your mod constructor. Now either create a JSON string or use a data generator to register your upgrades: class TurtleDataGenerator extends TurtleUpgradeDataProvider { @Override protected void addUpgrades( @Nonnull Consumer<Upgrade<TurtleUpgradeSerialiser<?>>> addUpgrade ) simple(new ResourceLocation("my_mod", my_upgrade"), MY_UPGRADE.get()).add(addUpgrade); } } See much better! In all seriousness, this does offer some benefits, namely that it's now possible to overwrite or create upgrades via datapacks. Actual changes: - Remove ComputerCraftAPI.register{Turtle,Pocket}Upgrade functions. - Instead add {Turtle,Pocket}UpgradeSerialiser classes, which are used to load upgrades from JSON files in datapacks, and then read/write them to network packets (much like recipe serialisers). - The upgrade registries now subscribe to datapack reload events. They find all JSON files in the data/$mod_id/computercraft/{turtle,pocket}_upgrades directories, parse them, and then register them as upgrades. Once datapacks have fully reloaded, these upgrades are then sent over the network to the client. - Add data generators for turtle and pocket computer upgrades, to make the creation of JSON files a bit easier. - Port all of CC:T's upgrades over to use the new system.
2021-11-26 23:36:02 +00:00
}
}
}
private static List<PocketComputerItem> pocketComputerItems() {
return List.of(ModRegistry.Items.POCKET_COMPUTER_NORMAL.get(), ModRegistry.Items.POCKET_COMPUTER_ADVANCED.get());
}
/**
* Register a crafting recipe for each pocket upgrade.
*
* @param add The callback to add recipes.
*/
2024-01-31 20:55:14 +00:00
private void pocketUpgrades(RecipeOutput add) {
for (var pocket : pocketComputerItems()) {
2024-01-31 20:55:14 +00:00
var name = RegistryHelper.getKeyOrThrow(BuiltInRegistries.ITEM, pocket).withPath(x -> x.replace("pocket_computer_", "pocket_"));
Rewrite turtle upgrade registration to be more data driven (#967) The feature nobody asked for, but we're getting anyway. Old way to register a turtle/pocket computer upgrade: ComputerCraftAPI.registerTurtleUpgrade(new MyUpgrade(new ResourceLocation("my_mod", "my_upgrade"))); New way to register a turtle/pocket computer upgrade: First, define a serialiser for your turtle upgrade type: static final DeferredRegister<TurtleUpgradeSerialiser<?>> SERIALISERS = DeferredRegister.create( TurtleUpgradeSerialiser.TYPE, "my_mod" ); public static final RegistryObject<TurtleUpgradeSerialiser<MyUpgrade>> MY_UPGRADE = SERIALISERS.register( "my_upgrade", () -> TurtleUpgradeSerialiser.simple( MyUpgrade::new ) ); SERIALISERS.register(bus); // Call in your mod constructor. Now either create a JSON string or use a data generator to register your upgrades: class TurtleDataGenerator extends TurtleUpgradeDataProvider { @Override protected void addUpgrades( @Nonnull Consumer<Upgrade<TurtleUpgradeSerialiser<?>>> addUpgrade ) simple(new ResourceLocation("my_mod", my_upgrade"), MY_UPGRADE.get()).add(addUpgrade); } } See much better! In all seriousness, this does offer some benefits, namely that it's now possible to overwrite or create upgrades via datapacks. Actual changes: - Remove ComputerCraftAPI.register{Turtle,Pocket}Upgrade functions. - Instead add {Turtle,Pocket}UpgradeSerialiser classes, which are used to load upgrades from JSON files in datapacks, and then read/write them to network packets (much like recipe serialisers). - The upgrade registries now subscribe to datapack reload events. They find all JSON files in the data/$mod_id/computercraft/{turtle,pocket}_upgrades directories, parse them, and then register them as upgrades. Once datapacks have fully reloaded, these upgrades are then sent over the network to the client. - Add data generators for turtle and pocket computer upgrades, to make the creation of JSON files a bit easier. - Port all of CC:T's upgrades over to use the new system.
2021-11-26 23:36:02 +00:00
for (var upgrade : pocketUpgrades.getGeneratedUpgrades()) {
2024-01-31 20:55:14 +00:00
ShapedSpecBuilder
.shaped(RecipeCategory.REDSTONE, DataComponentUtil.createStack(pocket, ModRegistry.DataComponents.POCKET_UPGRADE.get(), UpgradeData.ofDefault(upgrade)))
.group(name.toString())
.pattern("#")
.pattern("P")
.define('P', pocket)
.define('#', upgrade.getCraftingItem().getItem())
.unlockedBy("has_items", inventoryChange(pocket, upgrade.getCraftingItem().getItem()))
2024-01-31 20:55:14 +00:00
.build(ImpostorShapedRecipe::new)
.save(
2024-01-31 20:55:14 +00:00
add,
name.withSuffix(String.format("/%s/%s", upgrade.getUpgradeID().getNamespace(), upgrade.getUpgradeID().getPath()))
);
Rewrite turtle upgrade registration to be more data driven (#967) The feature nobody asked for, but we're getting anyway. Old way to register a turtle/pocket computer upgrade: ComputerCraftAPI.registerTurtleUpgrade(new MyUpgrade(new ResourceLocation("my_mod", "my_upgrade"))); New way to register a turtle/pocket computer upgrade: First, define a serialiser for your turtle upgrade type: static final DeferredRegister<TurtleUpgradeSerialiser<?>> SERIALISERS = DeferredRegister.create( TurtleUpgradeSerialiser.TYPE, "my_mod" ); public static final RegistryObject<TurtleUpgradeSerialiser<MyUpgrade>> MY_UPGRADE = SERIALISERS.register( "my_upgrade", () -> TurtleUpgradeSerialiser.simple( MyUpgrade::new ) ); SERIALISERS.register(bus); // Call in your mod constructor. Now either create a JSON string or use a data generator to register your upgrades: class TurtleDataGenerator extends TurtleUpgradeDataProvider { @Override protected void addUpgrades( @Nonnull Consumer<Upgrade<TurtleUpgradeSerialiser<?>>> addUpgrade ) simple(new ResourceLocation("my_mod", my_upgrade"), MY_UPGRADE.get()).add(addUpgrade); } } See much better! In all seriousness, this does offer some benefits, namely that it's now possible to overwrite or create upgrades via datapacks. Actual changes: - Remove ComputerCraftAPI.register{Turtle,Pocket}Upgrade functions. - Instead add {Turtle,Pocket}UpgradeSerialiser classes, which are used to load upgrades from JSON files in datapacks, and then read/write them to network packets (much like recipe serialisers). - The upgrade registries now subscribe to datapack reload events. They find all JSON files in the data/$mod_id/computercraft/{turtle,pocket}_upgrades directories, parse them, and then register them as upgrades. Once datapacks have fully reloaded, these upgrades are then sent over the network to the client. - Add data generators for turtle and pocket computer upgrades, to make the creation of JSON files a bit easier. - Port all of CC:T's upgrades over to use the new system.
2021-11-26 23:36:02 +00:00
}
}
}
2024-01-31 20:55:14 +00:00
private void turtleOverlays(RecipeOutput add) {
turtleOverlay(add, "turtle_trans_overlay", x -> x
.unlockedBy("has_dye", inventoryChange(itemPredicate(ingredients.dye())))
.requires(ColourUtils.getDyeTag(DyeColor.LIGHT_BLUE))
.requires(ColourUtils.getDyeTag(DyeColor.PINK))
.requires(ColourUtils.getDyeTag(DyeColor.WHITE))
.requires(Items.STICK)
);
turtleOverlay(add, "turtle_rainbow_overlay", x -> x
.unlockedBy("has_dye", inventoryChange(itemPredicate(ingredients.dye())))
.requires(ColourUtils.getDyeTag(DyeColor.RED))
.requires(ColourUtils.getDyeTag(DyeColor.ORANGE))
.requires(ColourUtils.getDyeTag(DyeColor.YELLOW))
.requires(ColourUtils.getDyeTag(DyeColor.GREEN))
.requires(ColourUtils.getDyeTag(DyeColor.BLUE))
.requires(ColourUtils.getDyeTag(DyeColor.PURPLE))
.requires(Items.STICK)
);
}
2024-01-31 20:55:14 +00:00
private void turtleOverlay(RecipeOutput add, String overlay, Consumer<ShapelessSpecBuilder> build) {
for (var turtleItem : turtleItems()) {
2024-01-31 20:55:14 +00:00
var name = RegistryHelper.getKeyOrThrow(BuiltInRegistries.ITEM, turtleItem);
var builder = ShapelessSpecBuilder.shapeless(RecipeCategory.REDSTONE, new ItemStack(turtleItem))
.group(name.withSuffix("_overlay").toString())
.unlockedBy("has_turtle", inventoryChange(turtleItem));
build.accept(builder);
builder
.requires(turtleItem)
2024-01-31 20:55:14 +00:00
.build(s -> new TurtleOverlayRecipe(s, new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/" + overlay)))
.save(add, name.withSuffix("_overlays/" + overlay));
}
}
2024-01-31 20:55:14 +00:00
private void basicRecipes(RecipeOutput add) {
ShapedRecipeBuilder
.shaped(RecipeCategory.REDSTONE, ModRegistry.Items.CABLE.get(), 6)
.pattern(" # ")
.pattern("#R#")
.pattern(" # ")
2022-11-09 10:59:05 +00:00
.define('#', ingredients.stone())
.define('R', ingredients.redstone())
.unlockedBy("has_computer", inventoryChange(COMPUTER))
.unlockedBy("has_modem", inventoryChange(WIRED_MODEM))
.save(add);
ShapedRecipeBuilder
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.COMPUTER_NORMAL.get())
.pattern("###")
.pattern("#R#")
.pattern("#G#")
2022-11-09 10:59:05 +00:00
.define('#', ingredients.stone())
.define('R', ingredients.redstone())
.define('G', ingredients.glassPane())
.unlockedBy("has_redstone", inventoryChange(itemPredicate(ingredients.redstone())))
.save(add);
ShapedRecipeBuilder
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.COMPUTER_ADVANCED.get())
.pattern("###")
.pattern("#R#")
.pattern("#G#")
2022-11-09 10:59:05 +00:00
.define('#', ingredients.goldIngot())
.define('R', ingredients.redstone())
.define('G', ingredients.glassPane())
.unlockedBy("has_components", inventoryChange(itemPredicate(ingredients.redstone()), itemPredicate(ingredients.goldIngot())))
.save(add);
2024-01-31 20:55:14 +00:00
ShapedSpecBuilder
.shaped(RecipeCategory.REDSTONE, ModRegistry.Items.COMPUTER_ADVANCED.get())
.pattern("###")
.pattern("#C#")
.pattern("# #")
2022-11-09 10:59:05 +00:00
.define('#', ingredients.goldIngot())
2022-11-21 19:08:23 +00:00
.define('C', ModRegistry.Items.COMPUTER_NORMAL.get())
2022-11-09 10:59:05 +00:00
.unlockedBy("has_components", inventoryChange(itemPredicate(ModRegistry.Items.COMPUTER_NORMAL.get()), itemPredicate(ingredients.goldIngot())))
.build(ComputerConvertRecipe::new)
2024-01-31 20:55:14 +00:00
.save(add, new ResourceLocation(ComputerCraftAPI.MOD_ID, "computer_advanced_upgrade"));
ShapedRecipeBuilder
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.COMPUTER_COMMAND.get())
.pattern("###")
.pattern("#R#")
.pattern("#G#")
2022-11-09 10:59:05 +00:00
.define('#', ingredients.goldIngot())
.define('R', Blocks.COMMAND_BLOCK)
.define('G', ingredients.glassPane())
.unlockedBy("has_components", inventoryChange(Blocks.COMMAND_BLOCK))
.save(add);
2024-01-31 20:55:14 +00:00
ShapedSpecBuilder
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.TURTLE_NORMAL.get())
.pattern("###")
.pattern("#C#")
.pattern("#I#")
2022-11-09 10:59:05 +00:00
.define('#', ingredients.ironIngot())
.define('C', ModRegistry.Items.COMPUTER_NORMAL.get())
2022-11-09 10:59:05 +00:00
.define('I', ingredients.woodenChest())
.unlockedBy("has_computer", inventoryChange(ModRegistry.Items.COMPUTER_NORMAL.get()))
.build(ComputerConvertRecipe::new)
2024-01-31 20:55:14 +00:00
.save(add);
2024-01-31 20:55:14 +00:00
ShapedSpecBuilder
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.TURTLE_ADVANCED.get())
.pattern("###")
.pattern("#C#")
.pattern("#I#")
2022-11-09 10:59:05 +00:00
.define('#', ingredients.goldIngot())
.define('C', ModRegistry.Items.COMPUTER_ADVANCED.get())
2022-11-09 10:59:05 +00:00
.define('I', ingredients.woodenChest())
.unlockedBy("has_computer", inventoryChange(ModRegistry.Items.COMPUTER_NORMAL.get()))
.build(ComputerConvertRecipe::new)
2024-01-31 20:55:14 +00:00
.save(add);
2024-01-31 20:55:14 +00:00
ShapedSpecBuilder
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.TURTLE_ADVANCED.get())
.pattern("###")
.pattern("#C#")
.pattern(" B ")
2022-11-09 10:59:05 +00:00
.define('#', ingredients.goldIngot())
2022-11-21 19:08:23 +00:00
.define('C', ModRegistry.Items.TURTLE_NORMAL.get())
2022-11-09 10:59:05 +00:00
.define('B', ingredients.goldBlock())
.unlockedBy("has_components", inventoryChange(itemPredicate(ModRegistry.Items.TURTLE_NORMAL.get()), itemPredicate(ingredients.goldIngot())))
.build(ComputerConvertRecipe::new)
2024-01-31 20:55:14 +00:00
.save(add, new ResourceLocation(ComputerCraftAPI.MOD_ID, "turtle_advanced_upgrade"));
ShapedRecipeBuilder
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.DISK_DRIVE.get())
.pattern("###")
.pattern("#R#")
.pattern("#R#")
2022-11-09 10:59:05 +00:00
.define('#', ingredients.stone())
.define('R', ingredients.redstone())
.unlockedBy("has_computer", inventoryChange(COMPUTER))
.save(add);
ShapedRecipeBuilder
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.MONITOR_NORMAL.get())
.pattern("###")
.pattern("#G#")
.pattern("###")
2022-11-09 10:59:05 +00:00
.define('#', ingredients.stone())
.define('G', ingredients.glassPane())
.unlockedBy("has_computer", inventoryChange(COMPUTER))
.save(add);
ShapedRecipeBuilder
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.MONITOR_ADVANCED.get(), 4)
.pattern("###")
.pattern("#G#")
.pattern("###")
2022-11-09 10:59:05 +00:00
.define('#', ingredients.goldIngot())
.define('G', ingredients.glassPane())
.unlockedBy("has_computer", inventoryChange(COMPUTER))
.save(add);
ShapedRecipeBuilder
.shaped(RecipeCategory.REDSTONE, ModRegistry.Items.POCKET_COMPUTER_NORMAL.get())
.pattern("###")
.pattern("#A#")
.pattern("#G#")
2022-11-09 10:59:05 +00:00
.define('#', ingredients.stone())
.define('A', Items.GOLDEN_APPLE)
.define('G', ingredients.glassPane())
.unlockedBy("has_computer", inventoryChange(COMPUTER))
2022-11-09 10:59:05 +00:00
.unlockedBy("has_apple", inventoryChange(Items.GOLDEN_APPLE))
.save(add);
ShapedRecipeBuilder
.shaped(RecipeCategory.REDSTONE, ModRegistry.Items.POCKET_COMPUTER_ADVANCED.get())
.pattern("###")
.pattern("#A#")
.pattern("#G#")
2022-11-09 10:59:05 +00:00
.define('#', ingredients.goldIngot())
.define('A', Items.GOLDEN_APPLE)
.define('G', ingredients.glassPane())
.unlockedBy("has_computer", inventoryChange(COMPUTER))
2022-11-09 10:59:05 +00:00
.unlockedBy("has_apple", inventoryChange(Items.GOLDEN_APPLE))
.save(add);
2024-01-31 20:55:14 +00:00
ShapedSpecBuilder
.shaped(RecipeCategory.REDSTONE, ModRegistry.Items.POCKET_COMPUTER_ADVANCED.get())
.pattern("###")
.pattern("#C#")
.pattern("# #")
2022-11-09 10:59:05 +00:00
.define('#', ingredients.goldIngot())
.define('C', ModRegistry.Items.POCKET_COMPUTER_NORMAL.get())
2022-11-09 10:59:05 +00:00
.unlockedBy("has_components", inventoryChange(itemPredicate(ModRegistry.Items.POCKET_COMPUTER_NORMAL.get()), itemPredicate(ingredients.goldIngot())))
.build(ComputerConvertRecipe::new)
2024-01-31 20:55:14 +00:00
.save(add, new ResourceLocation(ComputerCraftAPI.MOD_ID, "pocket_computer_advanced_upgrade"));
ShapedRecipeBuilder
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.PRINTER.get())
.pattern("###")
.pattern("#R#")
.pattern("#D#")
2022-11-09 10:59:05 +00:00
.define('#', ingredients.stone())
.define('R', ingredients.redstone())
.define('D', ingredients.dye())
.unlockedBy("has_computer", inventoryChange(COMPUTER))
.save(add);
ShapedRecipeBuilder
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.SPEAKER.get())
.pattern("###")
.pattern("#N#")
.pattern("#R#")
2022-11-09 10:59:05 +00:00
.define('#', ingredients.stone())
.define('N', Blocks.NOTE_BLOCK)
.define('R', ingredients.redstone())
.unlockedBy("has_computer", inventoryChange(COMPUTER))
.save(add);
ShapedRecipeBuilder
.shaped(RecipeCategory.REDSTONE, ModRegistry.Items.WIRED_MODEM.get())
.pattern("###")
.pattern("#R#")
.pattern("###")
2022-11-09 10:59:05 +00:00
.define('#', ingredients.stone())
.define('R', ingredients.redstone())
.unlockedBy("has_computer", inventoryChange(COMPUTER))
.unlockedBy("has_cable", inventoryChange(ModRegistry.Items.CABLE.get()))
.save(add);
ShapelessRecipeBuilder
.shapeless(RecipeCategory.REDSTONE, ModRegistry.Blocks.WIRED_MODEM_FULL.get())
.requires(ModRegistry.Items.WIRED_MODEM.get())
.unlockedBy("has_modem", inventoryChange(WIRED_MODEM))
.save(add, new ResourceLocation(ComputerCraftAPI.MOD_ID, "wired_modem_full_from"));
ShapelessRecipeBuilder
.shapeless(RecipeCategory.REDSTONE, ModRegistry.Items.WIRED_MODEM.get())
.requires(ModRegistry.Blocks.WIRED_MODEM_FULL.get())
.unlockedBy("has_modem", inventoryChange(WIRED_MODEM))
.save(add, new ResourceLocation(ComputerCraftAPI.MOD_ID, "wired_modem_full_to"));
ShapedRecipeBuilder
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.WIRELESS_MODEM_NORMAL.get())
.pattern("###")
.pattern("#E#")
.pattern("###")
2022-11-09 10:59:05 +00:00
.define('#', ingredients.stone())
.define('E', ingredients.enderPearl())
.unlockedBy("has_computer", inventoryChange(COMPUTER))
.save(add);
ShapedRecipeBuilder
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.WIRELESS_MODEM_ADVANCED.get())
.pattern("###")
.pattern("#E#")
.pattern("###")
2022-11-09 10:59:05 +00:00
.define('#', ingredients.goldIngot())
.define('E', Items.ENDER_EYE)
.unlockedBy("has_computer", inventoryChange(COMPUTER))
.unlockedBy("has_wireless", inventoryChange(ModRegistry.Blocks.WIRELESS_MODEM_NORMAL.get()))
.save(add);
2024-01-31 20:55:14 +00:00
ShapelessSpecBuilder
.shapeless(RecipeCategory.DECORATIONS, playerHead("Cloudhunter", "6d074736-b1e9-4378-a99b-bd8777821c9c"))
.requires(ItemTags.SKULLS)
.requires(ModRegistry.Items.MONITOR_NORMAL.get())
.unlockedBy("has_monitor", inventoryChange(ModRegistry.Items.MONITOR_NORMAL.get()))
.build()
2024-01-31 20:55:14 +00:00
.save(add, new ResourceLocation(ComputerCraftAPI.MOD_ID, "skull_cloudy"));
2024-01-31 20:55:14 +00:00
ShapelessSpecBuilder
.shapeless(RecipeCategory.DECORATIONS, playerHead("dan200", "f3c8d69b-0776-4512-8434-d1b2165909eb"))
.requires(ItemTags.SKULLS)
.requires(ModRegistry.Items.COMPUTER_ADVANCED.get())
.unlockedBy("has_computer", inventoryChange(ModRegistry.Items.COMPUTER_ADVANCED.get()))
.build()
2024-01-31 20:55:14 +00:00
.save(add, new ResourceLocation(ComputerCraftAPI.MOD_ID, "skull_dan200"));
2024-01-31 20:55:14 +00:00
ShapelessSpecBuilder
.shapeless(RecipeCategory.REDSTONE, ModRegistry.Items.PRINTED_PAGES.get())
.requires(ModRegistry.Items.PRINTED_PAGE.get(), 2)
2022-11-09 10:59:05 +00:00
.requires(ingredients.string())
.unlockedBy("has_printer", inventoryChange(ModRegistry.Blocks.PRINTER.get()))
2024-01-31 20:55:14 +00:00
.build(ImpostorShapelessRecipe::new)
.save(add);
2024-01-31 20:55:14 +00:00
ShapelessSpecBuilder
.shapeless(RecipeCategory.REDSTONE, ModRegistry.Items.PRINTED_BOOK.get())
2022-11-09 10:59:05 +00:00
.requires(ingredients.leather())
.requires(ModRegistry.Items.PRINTED_PAGE.get(), 1)
2022-11-09 10:59:05 +00:00
.requires(ingredients.string())
.unlockedBy("has_printer", inventoryChange(ModRegistry.Blocks.PRINTER.get()))
2024-01-31 20:55:14 +00:00
.build(ImpostorShapelessRecipe::new)
.save(add);
}
private static DyeColor ofColour(Colour colour) {
return DyeColor.byId(15 - colour.ordinal());
}
2024-01-31 20:55:14 +00:00
private static Criterion<InventoryChangeTrigger.TriggerInstance> inventoryChange(TagKey<Item> stack) {
return InventoryChangeTrigger.TriggerInstance.hasItems(itemPredicate(stack));
}
2024-01-31 20:55:14 +00:00
private static Criterion<InventoryChangeTrigger.TriggerInstance> inventoryChange(ItemLike... stack) {
return InventoryChangeTrigger.TriggerInstance.hasItems(stack);
}
2024-01-31 20:55:14 +00:00
private static Criterion<InventoryChangeTrigger.TriggerInstance> inventoryChange(ItemPredicate... items) {
return InventoryChangeTrigger.TriggerInstance.hasItems(items);
}
private static ItemPredicate itemPredicate(ItemLike item) {
return ItemPredicate.Builder.item().of(item).build();
}
private static ItemPredicate itemPredicate(TagKey<Item> item) {
return ItemPredicate.Builder.item().of(item).build();
}
2022-11-09 10:59:05 +00:00
private static ItemPredicate itemPredicate(Ingredient ingredient) {
var json = Ingredient.CODEC_NONEMPTY.encodeStart(JsonOps.INSTANCE, ingredient).getOrThrow();
2022-11-09 10:59:05 +00:00
if (!(json instanceof JsonObject object)) throw new IllegalStateException("Unknown ingredient " + json);
if (object.has("item")) {
var item = ItemStack.SIMPLE_ITEM_CODEC.parse(JsonOps.INSTANCE, object).getOrThrow();
2024-01-31 20:55:14 +00:00
return itemPredicate(item.getItem());
2022-11-09 10:59:05 +00:00
} else if (object.has("tag")) {
return itemPredicate(TagKey.create(Registries.ITEM, new ResourceLocation(GsonHelper.getAsString(object, "tag"))));
2022-11-09 10:59:05 +00:00
} else {
throw new IllegalArgumentException("Unknown ingredient " + json);
}
}
2024-01-31 20:55:14 +00:00
private static ItemStack playerHead(String name, String uuid) {
return DataComponentUtil.createStack(Items.PLAYER_HEAD, DataComponents.PROFILE, new ResolvableProfile(new GameProfile(UUID.fromString(uuid), name)));
}
2024-01-31 20:55:14 +00:00
private static void addSpecial(RecipeOutput add, Recipe<?> recipe) {
add.accept(RegistryHelper.getKeyOrThrow(BuiltInRegistries.RECIPE_SERIALIZER, recipe.getSerializer()), recipe, null);
}
}