Update to MC 1.20.6

- Update EMI and REI integration, and fix some issues with the upgrade
   crafting hooks.
 - Just use smooth stone for recipes, not #c:stone. We're mirroring
   redstone's crafting recipes here.
 - Some cleanup to printouts.
 - Remote upgrade data generators - these can be replaced with the
   standard registry data generators.
 - Remove the API's PlatformHelper - we no longer have any
   platform-specific code in the API.
This commit is contained in:
Jonathan Coates 2024-05-07 22:58:25 +01:00
parent 94c864759d
commit 2c0d8263d3
No known key found for this signature in database
GPG Key ID: B9E431FF07C98D06
70 changed files with 513 additions and 822 deletions

View File

@ -30,11 +30,6 @@ subsystems {
} }
} }
dependencies {
val libs = project.extensions.getByType<VersionCatalogsExtension>().named("libs")
implementation("net.neoforged:neoforge:${libs.findVersion("neoForge").get()}")
}
MinecraftConfigurations.setup(project) MinecraftConfigurations.setup(project)
extensions.configure(CCTweakedExtension::class.java) { extensions.configure(CCTweakedExtension::class.java) {

View File

@ -56,7 +56,6 @@ repositories {
includeGroup("mezz.jei") includeGroup("mezz.jei")
includeGroup("org.teavm") includeGroup("org.teavm")
includeModule("com.terraformersmc", "modmenu") includeModule("com.terraformersmc", "modmenu")
includeModule("me.lucko", "fabric-permissions-api")
} }
} }
} }

View File

@ -13,4 +13,4 @@ isUnstable=true
modVersion=1.110.3 modVersion=1.110.3
# Minecraft properties: We want to configure this here so we can read it in settings.gradle # Minecraft properties: We want to configure this here so we can read it in settings.gradle
mcVersion=1.20.5 mcVersion=1.20.6

View File

@ -7,14 +7,14 @@
# Minecraft # Minecraft
# MC version is specified in gradle.properties, as we need that in settings.gradle. # MC version is specified in gradle.properties, as we need that in settings.gradle.
# Remember to update corresponding versions in fabric.mod.json/neoforge.mods.toml # Remember to update corresponding versions in fabric.mod.json/neoforge.mods.toml
fabric-api = "0.97.6+1.20.5" fabric-api = "0.98.0+1.20.6"
fabric-loader = "0.15.10" fabric-loader = "0.15.10"
neoForge = "20.5.20-beta" neoForge = "20.6.48-beta"
neoForgeSpi = "8.0.1" neoForgeSpi = "8.0.1"
mixin = "0.8.5" mixin = "0.8.5"
parchment = "2024.04.14" parchment = "2024.05.01"
parchmentMc = "1.20.4" parchmentMc = "1.20.6"
yarn = "1.20.5+build.1" yarn = "1.20.6+build.1"
# Core dependencies (these versions are tied to the version Minecraft uses) # Core dependencies (these versions are tied to the version Minecraft uses)
fastutil = "8.5.12" fastutil = "8.5.12"
@ -36,14 +36,14 @@ kotlin-coroutines = "1.7.3"
nightConfig = "3.6.7" nightConfig = "3.6.7"
# Minecraft mods # Minecraft mods
emi = "1.0.30+1.20.4" emi = "1.1.5+1.20.6"
fabricPermissions = "0.3.20230723" fabricPermissions = "0.3.1"
iris = "1.6.14+1.20.4" iris = "1.6.14+1.20.4"
jei = "17.3.0.48" jei = "17.3.0.48"
modmenu = "9.0.0" modmenu = "9.0.0"
moreRed = "4.0.0.4" moreRed = "4.0.0.4"
oculus = "1.2.5" oculus = "1.2.5"
rei = "14.0.688" rei = "15.0.728"
rubidium = "0.6.1" rubidium = "0.6.1"
sodium = "mc1.20-0.4.10" sodium = "mc1.20-0.4.10"
mixinExtra = "0.3.5" mixinExtra = "0.3.5"

View File

@ -21,4 +21,16 @@ tasks.javadoc {
// Include the core-api in our javadoc export. This is wrong, but it means we can export a single javadoc dump. // Include the core-api in our javadoc export. This is wrong, but it means we can export a single javadoc dump.
source(project(":core-api").sourceSets.main.map { it.allJava }) source(project(":core-api").sourceSets.main.map { it.allJava })
options {
this as StandardJavadocDocletOptions
addBooleanOption("-allow-script-in-comments", true)
bottom(
"""
<script src="https://cdn.jsdelivr.net/npm/prismjs@v1.29.0/components/prism-core.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/prismjs@v1.29.0/plugins/autoloader/prism-autoloader.min.js"></script>
<link href=" https://cdn.jsdelivr.net/npm/prismjs@1.29.0/themes/prism.min.css " rel="stylesheet">
""".trimIndent(),
)
}
} }

View File

@ -4,12 +4,14 @@
package dan200.computercraft.api.pocket; package dan200.computercraft.api.pocket;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.upgrades.UpgradeBase; import dan200.computercraft.api.upgrades.UpgradeBase;
import dan200.computercraft.api.upgrades.UpgradeType; import dan200.computercraft.api.upgrades.UpgradeType;
import dan200.computercraft.impl.ComputerCraftAPIService; import dan200.computercraft.impl.ComputerCraftAPIService;
import net.minecraft.core.Registry; import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -21,11 +23,11 @@
* {@link UpgradeType} instance, which are then registered in a registry. * {@link UpgradeType} instance, which are then registered in a registry.
* <p> * <p>
* You then write a JSON file in your mod's {@literal data/} folder. This is then parsed when the world is loaded, and * 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 * the upgrade automatically registered. It is recommended this is done via
* generators}. * <a href="../upgrades/UpgradeType.html#datagen">data generators</a>.
* *
* <h2>Example</h2> * <h2>Example</h2>
* <pre>{@code * {@snippet lang="java" :
* // We use Forge's DeferredRegister to register our upgrade type. Fabric mods may register their type directly. * // We use Forge's DeferredRegister to register our upgrade type. Fabric mods may register their type directly.
* static final DeferredRegister<UpgradeType<? extends IPocketUpgrade>> POCKET_UPGRADES = DeferredRegister.create(IPocketUpgrade.typeRegistry(), "my_mod"); * static final DeferredRegister<UpgradeType<? extends IPocketUpgrade>> POCKET_UPGRADES = DeferredRegister.create(IPocketUpgrade.typeRegistry(), "my_mod");
* *
@ -35,19 +37,19 @@
* *
* // Then in your constructor * // Then in your constructor
* POCKET_UPGRADES.register(bus); * POCKET_UPGRADES.register(bus);
* }</pre> * }
* <p> * <p>
* We can then define a new upgrade using JSON by placing the following in * We can then define a new upgrade using JSON by placing the following in
* {@code data/<my_mod>/computercraft/pocket_upgrades/<my_upgrade_id>.json}. * {@code data/<my_mod>/computercraft/pocket_upgrade/<my_upgrade_id>.json}.
* <pre>{@code * {@snippet lang="json" :
* { * {
* "type": my_mod:my_upgrade", * "type": "my_mod:my_upgrade"
* }
* } * }
* }</pre>
* <p>
* {@link PocketUpgradeDataProvider} provides a data provider to aid with generating these JSON files.
*/ */
public interface IPocketUpgrade extends UpgradeBase { public interface IPocketUpgrade extends UpgradeBase {
ResourceKey<Registry<IPocketUpgrade>> REGISTRY = ResourceKey.createRegistryKey(new ResourceLocation(ComputerCraftAPI.MOD_ID, "pocket_upgrade"));
/** /**
* The registry key for pocket upgrade types. * The registry key for pocket upgrade types.
* *

View File

@ -1,30 +0,0 @@
// SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.api.pocket;
import dan200.computercraft.api.upgrades.UpgradeDataProvider;
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;
import java.util.function.Consumer;
/**
* A data provider to generate pocket computer upgrades.
* <p>
* This should be subclassed and registered to a {@link DataGenerator.PackGenerator}. Override the
* {@link #addUpgrades(Consumer)} function, construct each upgrade, and pass them off to the provided consumer to
* generate them.
*
* @see IPocketUpgrade
* @see UpgradeType
*/
public abstract class PocketUpgradeDataProvider extends UpgradeDataProvider<IPocketUpgrade> {
public PocketUpgradeDataProvider(PackOutput output) {
super(output, "Pocket Computer Upgrades", RegistryHelper.POCKET_UPGRADE, ComputerCraftAPIService.get().pocketUpgradeCodec());
}
}

View File

@ -4,6 +4,7 @@
package dan200.computercraft.api.turtle; package dan200.computercraft.api.turtle;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.upgrades.UpgradeBase; import dan200.computercraft.api.upgrades.UpgradeBase;
import dan200.computercraft.api.upgrades.UpgradeType; import dan200.computercraft.api.upgrades.UpgradeType;
@ -12,6 +13,7 @@
import net.minecraft.core.Registry; import net.minecraft.core.Registry;
import net.minecraft.core.component.DataComponentPatch; import net.minecraft.core.component.DataComponentPatch;
import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -23,11 +25,11 @@
* {@link UpgradeType} instance, which are then registered in a registry. * {@link UpgradeType} instance, which are then registered in a registry.
* <p> * <p>
* You then write a JSON file in your mod's {@literal data/} folder. This is then parsed when the world is loaded, and * 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 * the upgrade automatically registered. It is recommended this is done via
* generators}. * <a href="../upgrades/UpgradeType.html#datagen">data generators</a>.
* *
* <h2>Example</h2> * <h2>Example</h2>
* <pre>{@code * {@snippet lang="java" :
* // We use Forge's DeferredRegister to register our upgrade type. Fabric mods may register their type directly. * // We use Forge's DeferredRegister to register our upgrade type. Fabric mods may register their type directly.
* static final DeferredRegister<UpgradeType<? extends ITurtleUpgrade>> TURTLE_UPGRADES = DeferredRegister.create(ITurtleUpgrade.typeRegistry(), "my_mod"); * static final DeferredRegister<UpgradeType<? extends ITurtleUpgrade>> TURTLE_UPGRADES = DeferredRegister.create(ITurtleUpgrade.typeRegistry(), "my_mod");
* *
@ -37,28 +39,38 @@
* *
* // Then in your constructor * // Then in your constructor
* TURTLE_UPGRADES.register(bus); * TURTLE_UPGRADES.register(bus);
* }</pre> * }
* <p> * <p>
* We can then define a new upgrade using JSON by placing the following in * We can then define a new upgrade using JSON by placing the following in
* {@literal data/<my_mod>/computercraft/turtle_upgrades/<my_upgrade_id>.json}}. * {@code data/<my_mod>/computercraft/turtle_upgrade/<my_upgrade_id>.json}.
* * <p>
* <pre>{@code * {@snippet lang="json" :
* { * {
* "type": "my_mod:my_upgrade" * "type": "my_mod:my_upgrade"
* } * }
* }</pre> * }
* <p>
* {@link TurtleUpgradeDataProvider} provides a data provider to aid with generating these JSON files.
* <p> * <p>
* Finally, we need to register a model for our upgrade, see * Finally, we need to register a model for our upgrade, see
* {@link dan200.computercraft.api.client.turtle.TurtleUpgradeModeller} for more information. * {@link dan200.computercraft.api.client.turtle.TurtleUpgradeModeller} for more information.
*
* <pre>{@code
* // Register our model inside FMLClientSetupEvent
* ComputerCraftAPIClient.registerTurtleUpgradeModeller(MY_UPGRADE.get(), TurtleUpgradeModeller.flatItem())
* }</pre>
*/ */
public interface ITurtleUpgrade extends UpgradeBase { public interface ITurtleUpgrade extends UpgradeBase {
/**
* The registry in which turtle upgrades are stored.
*/
ResourceKey<Registry<ITurtleUpgrade>> REGISTRY = ResourceKey.createRegistryKey(new ResourceLocation(ComputerCraftAPI.MOD_ID, "turtle_upgrade"));
/**
* Create a {@link ResourceKey} for a turtle upgrade given a {@link ResourceLocation}.
* <p>
* This should only be called from within data generation code. Do not hard code references to your upgrades!
*
* @param id The id of the turtle upgrade.
* @return The upgrade registry key.
*/
static ResourceKey<ITurtleUpgrade> createKey(ResourceLocation id) {
return ResourceKey.create(REGISTRY, id);
}
/** /**
* The registry key for turtle upgrade types. * The registry key for turtle upgrade types.
* *

View File

@ -0,0 +1,157 @@
// SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.api.turtle;
import dan200.computercraft.api.ComputerCraftTags;
import dan200.computercraft.api.upgrades.UpgradeBase;
import dan200.computercraft.impl.ComputerCraftAPIService;
import dan200.computercraft.impl.upgrades.TurtleToolSpec;
import net.minecraft.core.component.DataComponents;
import net.minecraft.data.worldgen.BootstrapContext;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.TagKey;
import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.Block;
import javax.annotation.Nullable;
import java.util.Optional;
/**
* A builder for custom turtle tool upgrades.
* <p>
* This can be used from your <a href="../upgrades/UpgradeType.html#datagen">data generator</a> code in order to
* register turtle tools for your mod's tools.
*
* <h2>Example:</h2>
* {@snippet lang = "java":
* import net.minecraft.data.worldgen.BootstrapContext;
* import net.minecraft.resources.ResourceLocation;
* import net.minecraft.world.item.Items;
*
* public void registerTool(BootstrapContext<ITurtleUpgrade> upgrades) {
* TurtleToolBuilder.tool(new ResourceLocation("my_mod", "wooden_pickaxe"), Items.WOODEN_PICKAXE).register(upgrades);
* }
*}
*/
public final class TurtleToolBuilder {
private final ResourceKey<ITurtleUpgrade> id;
private final Item item;
private Component adjective;
private float damageMultiplier = TurtleToolSpec.DEFAULT_DAMAGE_MULTIPLIER;
private @Nullable TagKey<Block> breakable;
private boolean allowEnchantments = false;
private TurtleToolDurability consumeDurability = TurtleToolDurability.NEVER;
private TurtleToolBuilder(ResourceKey<ITurtleUpgrade> id, Item item) {
this.id = id;
adjective = Component.translatable(UpgradeBase.getDefaultAdjective(id.location()));
this.item = item;
}
public static TurtleToolBuilder tool(ResourceLocation id, Item item) {
return new TurtleToolBuilder(ITurtleUpgrade.createKey(id), item);
}
public static TurtleToolBuilder tool(ResourceKey<ITurtleUpgrade> id, Item item) {
return new TurtleToolBuilder(id, item);
}
/**
* Get the id for this turtle tool.
*
* @return The upgrade id.
*/
public ResourceKey<ITurtleUpgrade> id() {
return id;
}
/**
* Specify a custom adjective for this tool. By default this takes its adjective from the upgrade id.
*
* @param adjective The new adjective to use.
* @return The tool builder, for further use.
*/
public TurtleToolBuilder adjective(Component adjective) {
this.adjective = adjective;
return this;
}
/**
* The amount of damage a swing of this tool will do. This is multiplied by {@link Attributes#ATTACK_DAMAGE} to
* get the final damage.
*
* @param damageMultiplier The damage multiplier.
* @return The tool builder, for further use.
*/
public TurtleToolBuilder damageMultiplier(float damageMultiplier) {
this.damageMultiplier = damageMultiplier;
return this;
}
/**
* Indicate that this upgrade allows items which have been {@linkplain ItemStack#isEnchanted() enchanted} or have
* {@linkplain DataComponents#ATTRIBUTE_MODIFIERS custom attribute modifiers}.
*
* @return The tool builder, for further use.
*/
public TurtleToolBuilder allowEnchantments() {
allowEnchantments = true;
return this;
}
/**
* Set when the tool will consume durability.
*
* @param durability The durability predicate.
* @return The tool builder, for further use.
*/
public TurtleToolBuilder consumeDurability(TurtleToolDurability durability) {
consumeDurability = durability;
return this;
}
/**
* Provide a list of breakable blocks. If not given, the tool can break all blocks. If given, only blocks
* in this tag, those in {@link ComputerCraftTags.Blocks#TURTLE_ALWAYS_BREAKABLE} and "insta-mine" ones can
* be broken.
*
* @param breakable The tag containing all blocks breakable by this item.
* @return The tool builder, for further use.
* @see ComputerCraftTags.Blocks
*/
public TurtleToolBuilder breakable(TagKey<Block> breakable) {
this.breakable = breakable;
return this;
}
/**
* Build the turtle tool upgrade.
*
* @return The constructed upgrade.
*/
public ITurtleUpgrade build() {
return ComputerCraftAPIService.get().createTurtleTool(new TurtleToolSpec(
adjective,
item,
damageMultiplier,
allowEnchantments,
consumeDurability,
Optional.ofNullable(breakable)
));
}
/**
* Build this upgrade and register it for datagen.
*
* @param upgrades The registry this upgrade should be added to.
*/
public void register(BootstrapContext<ITurtleUpgrade> upgrades) {
upgrades.register(id(), build());
}
}

View File

@ -11,7 +11,7 @@
/** /**
* Indicates if an equipped turtle item will consume durability. * Indicates if an equipped turtle item will consume durability.
* *
* @see TurtleUpgradeDataProvider.ToolBuilder#consumeDurability(TurtleToolDurability) * @see TurtleToolBuilder#consumeDurability(TurtleToolDurability)
*/ */
public enum TurtleToolDurability implements StringRepresentable { public enum TurtleToolDurability implements StringRepresentable {
/** /**

View File

@ -1,151 +0,0 @@
// SPDX-FileCopyrightText: 2021 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.api.turtle;
import dan200.computercraft.api.ComputerCraftTags;
import dan200.computercraft.api.upgrades.UpgradeBase;
import dan200.computercraft.api.upgrades.UpgradeDataProvider;
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.data.DataGenerator;
import net.minecraft.data.PackOutput;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.TagKey;
import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.item.Item;
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;
/**
* A data provider to generate turtle upgrades.
* <p>
* This should be subclassed and registered to a {@link DataGenerator.PackGenerator}. Override the
* {@link #addUpgrades(Consumer)} function, construct each upgrade, and pass them off to the provided consumer to
* generate them.
*
* @see ITurtleUpgrade
*/
public abstract class TurtleUpgradeDataProvider extends UpgradeDataProvider<ITurtleUpgrade> {
public TurtleUpgradeDataProvider(PackOutput output) {
super(output, "Turtle Upgrades", RegistryHelper.TURTLE_UPGRADE, ComputerCraftAPIService.get().turtleUpgradeCodec());
}
/**
* Create a new turtle tool upgrade, such as a pickaxe or shovel.
*
* @param id The ID of this tool.
* @param item The item used for tool actions. Note, this doesn't inherit all properties of the tool, you may need
* to specify {@link ToolBuilder#damageMultiplier(float)} and {@link ToolBuilder#breakable(TagKey)}.
* @return A tool builder,
*/
public final ToolBuilder tool(ResourceLocation id, Item item) {
return new ToolBuilder(id, item);
}
/**
* A builder for custom turtle tool upgrades.
*
* @see #tool(ResourceLocation, Item)
*/
public final class ToolBuilder {
private final ResourceLocation id;
private final Item item;
private Component adjective;
private @Nullable Item craftingItem;
private float damageMultiplier = TurtleToolSpec.DEFAULT_DAMAGE_MULTIPLIER;
private @Nullable TagKey<Block> breakable;
private boolean allowEnchantments = false;
private TurtleToolDurability consumeDurability = TurtleToolDurability.NEVER;
ToolBuilder(ResourceLocation id, Item item) {
this.id = id;
adjective = Component.translatable(UpgradeBase.getDefaultAdjective(id));
this.item = item;
craftingItem = null;
}
/**
* Specify a custom adjective for this tool. By default this takes its adjective from the upgrade id.
*
* @param adjective The new adjective to use.
* @return The tool builder, for further use.
*/
public ToolBuilder adjective(Component adjective) {
this.adjective = adjective;
return this;
}
/**
* The amount of damage a swing of this tool will do. This is multiplied by {@link Attributes#ATTACK_DAMAGE} to
* get the final damage.
*
* @param damageMultiplier The damage multiplier.
* @return The tool builder, for further use.
*/
public ToolBuilder damageMultiplier(float damageMultiplier) {
this.damageMultiplier = damageMultiplier;
return this;
}
/**
* Indicate that this upgrade allows items which have been {@linkplain ItemStack#isEnchanted() enchanted} or have
* {@linkplain DataComponents#ATTRIBUTE_MODIFIERS custom attribute modifiers}.
*
* @return The tool builder, for further use.
*/
public ToolBuilder allowEnchantments() {
allowEnchantments = true;
return this;
}
/**
* Set when the tool will consume durability.
*
* @param durability The durability predicate.
* @return The tool builder, for further use.
*/
public ToolBuilder consumeDurability(TurtleToolDurability durability) {
consumeDurability = durability;
return this;
}
/**
* Provide a list of breakable blocks. If not given, the tool can break all blocks. If given, only blocks
* in this tag, those in {@link ComputerCraftTags.Blocks#TURTLE_ALWAYS_BREAKABLE} and "insta-mine" ones can
* be broken.
*
* @param breakable The tag containing all blocks breakable by this item.
* @return The tool builder, for further use.
* @see ComputerCraftTags.Blocks
*/
public ToolBuilder breakable(TagKey<Block> breakable) {
this.breakable = breakable;
return this;
}
/**
* Register this as an upgrade.
*
* @param add The callback given to {@link #addUpgrades(Consumer)}.
*/
public void add(Consumer<Upgrade<ITurtleUpgrade>> add) {
upgrade(id, ComputerCraftAPIService.get().createTurtleTool(new TurtleToolSpec(
adjective,
item,
damageMultiplier,
allowEnchantments,
consumeDurability,
Optional.ofNullable(breakable)
))).add(add);
}
}
}

View File

@ -1,154 +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 com.mojang.serialization.Codec;
import com.mojang.serialization.JsonOps;
import dan200.computercraft.impl.PlatformHelper;
import net.minecraft.Util;
import net.minecraft.core.Registry;
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 org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
/**
* A data generator/provider for turtle and pocket computer upgrades. This should not be extended directly, instead see
* the other subclasses.
*
* @param <T> The base class of upgrades.
*/
public abstract class UpgradeDataProvider<T extends UpgradeBase> implements DataProvider {
private final PackOutput output;
private final String name;
private final ResourceKey<Registry<T>> registryName;
private final Codec<T> codec;
private @Nullable Map<ResourceKey<T>, T> upgrades;
@ApiStatus.Internal
protected UpgradeDataProvider(PackOutput output, String name, ResourceKey<Registry<T>> registryName, Codec<T> codec) {
this.output = output;
this.name = name;
this.registryName = registryName;
this.codec = codec;
}
/**
* Add a new upgrade.
*
* @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.
*/
protected final Upgrade<T> upgrade(ResourceLocation id, T upgrade) {
return new Upgrade<>(id, upgrade, j -> {
});
}
/**
* Add all turtle or pocket computer upgrades.
* <p>
* <strong>Example usage:</strong>
* <pre>{@code
* protected void addUpgrades(Consumer<Upgrade<ITurtleUpgrade>> addUpgrade) {
* upgrade(new ResourceLocation("mymod", "speaker"), new TurtleSpeaker(new ItemStack(Items.NOTE_BLOCK))).add(addUpgrade);
* }
* }</pre>
*
* @param addUpgrade A callback used to register an upgrade.
*/
protected abstract void addUpgrades(Consumer<Upgrade<T>> addUpgrade);
@Override
public CompletableFuture<?> run(CachedOutput cache) {
var base = output.createPathProvider(PackOutput.Target.DATA_PACK, registryName.location().getNamespace() + "/" + registryName.location().getPath());
Map<ResourceKey<T>, T> upgrades = new LinkedHashMap<>();
List<CompletableFuture<?>> futures = new ArrayList<>();
addUpgrades(upgrade -> {
var id = ResourceKey.create(registryName, upgrade.id);
if (upgrades.containsKey(id)) throw new IllegalStateException("Duplicate upgrade " + upgrade.id);
var json = (JsonObject) codec.encodeStart(JsonOps.INSTANCE, upgrade.upgrade).getOrThrow();
upgrade.serialise.accept(json);
futures.add(DataProvider.saveStable(cache, json, base.json(upgrade.id)));
upgrades.put(id, upgrade.upgrade);
});
this.upgrades = Collections.unmodifiableMap(upgrades);
return Util.sequenceFailFast(futures);
}
@Override
public final String getName() {
return name;
}
/**
* Get all registered upgrades.
*
* @return The map of registered upgrades.
*/
public Map<ResourceKey<T>, 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 <T> The type of upgrade.
*/
public static final class Upgrade<T extends UpgradeBase> {
private final ResourceLocation id;
private final T upgrade;
private final Consumer<JsonObject> serialise;
private Upgrade(ResourceLocation id, T upgrade, Consumer<JsonObject> 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<Upgrade<T>> add) {
add.accept(this);
}
/**
* Return a new {@link Upgrade} which requires the given mod to be present.
* <p>
* This uses mod-loader-specific hooks (Forge's crafting conditions and Fabric's resource conditions). If using
* this in a multi-loader setup, you must generate resources separately for the two loaders.
*
* @param modId The id of the mod.
* @return A new upgrade instance.
*/
public Upgrade<T> requireMod(String modId) {
return new Upgrade<>(id, upgrade, json -> {
PlatformHelper.get().addRequiredModCondition(json, modId);
serialise.accept(json);
});
}
}
}

View File

@ -6,11 +6,10 @@
import com.mojang.serialization.MapCodec; import com.mojang.serialization.MapCodec;
import dan200.computercraft.api.pocket.IPocketUpgrade; import dan200.computercraft.api.pocket.IPocketUpgrade;
import dan200.computercraft.api.pocket.PocketUpgradeDataProvider;
import dan200.computercraft.api.turtle.ITurtleUpgrade; import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.api.turtle.TurtleUpgradeDataProvider;
import dan200.computercraft.impl.upgrades.UpgradeTypeImpl; import dan200.computercraft.impl.upgrades.UpgradeTypeImpl;
import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.data.registries.RegistryPatchGenerator;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.Recipe; import net.minecraft.world.item.crafting.Recipe;
import net.minecraft.world.level.storage.loot.functions.LootItemFunction; import net.minecraft.world.level.storage.loot.functions.LootItemFunction;
@ -33,8 +32,35 @@
* {@link IPocketUpgrade#typeRegistry()}). * {@link IPocketUpgrade#typeRegistry()}).
* <p> * <p>
* In order to register the actual upgrade, a JSON file referencing your upgrade type should be added to a datapack. It * 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 * is recommended to do this via the data generators.
* {@link PocketUpgradeDataProvider}). *
* <h2 id="datagen">Data Generation</h2>
* As turtle and pocket upgrades are just loaded using vanilla's dynamic loaders, one may use the same data generation
* tools as you would for any other dynamic registry.
* <p>
* This can typically be done by extending vanilla's built-in registries using {@link RegistryPatchGenerator}, and then
* writing out the new registries using {@code net.fabricmc.fabric.api.datagen.v1.provider.FabricDynamicRegistryProvider}
* on Fabric or {@code net.neoforged.neoforge.common.data.DatapackBuiltinEntriesProvider} on Forge.
* <p>
* {@snippet lang="java" :
* import dan200.computercraft.api.turtle.ITurtleUpgrade;
* import net.minecraft.Util;
* import net.minecraft.core.HolderLookup;
* import net.minecraft.core.RegistrySetBuilder;
* import net.minecraft.data.DataGenerator;
* import net.neoforged.neoforge.common.data.DatapackBuiltinEntriesProvider;
*
* import java.util.concurrent.CompletableFuture;
*
* public void generate(DataGenerator.PackGenerator output, CompletableFuture<HolderLookup.Provider> registries) {
* var newRegistries = RegistryPatchGenerator.createLookup(registries, Util.make(new RegistrySetBuilder(), builder -> {
* builder.add(ITurtleUpgrade.REGISTRY, upgrades -> {
* upgrades.register(ITurtleUpgrade.createKey(new ResourceLocation("my_mod", "my_upgrade")), new MyUpgrade());
* });
* }));
* output.addProvider(o -> new DatapackBuiltinEntriesProvider(o, newRegistries, Set.of("my_mod")));
* }
* }
* *
* @param <T> The upgrade subclass that this upgrade type represents. * @param <T> The upgrade subclass that this upgrade type represents.
* @see ITurtleUpgrade * @see ITurtleUpgrade

View File

@ -1,54 +0,0 @@
// SPDX-FileCopyrightText: 2022 The CC: Tweaked Developers
//
// SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.impl;
import com.google.gson.JsonObject;
import dan200.computercraft.api.upgrades.UpgradeDataProvider;
import org.jetbrains.annotations.ApiStatus;
import javax.annotation.Nullable;
/**
* Abstraction layer for Forge and Fabric. See implementations for more details.
* <p>
* Do <strong>NOT</strong> directly reference this class. It exists for internal use by the API.
*/
@ApiStatus.Internal
public interface PlatformHelper {
/**
* Get the current {@link PlatformHelper} instance.
*
* @return The current instance.
*/
static PlatformHelper get() {
var instance = Instance.INSTANCE;
return instance == null ? Services.raise(PlatformHelper.class, Instance.ERROR) : instance;
}
/**
* Add a resource condition which requires a mod to be loaded. This should be used by data providers such as
* {@link UpgradeDataProvider}.
*
* @param object The JSON object we're generating.
* @param modId The mod ID that we require.
*/
void addRequiredModCondition(JsonObject object, String modId);
final class Instance {
static final @Nullable PlatformHelper INSTANCE;
static final @Nullable Throwable ERROR;
static {
// We don't want class initialisation to fail here (as that results in confusing errors). Instead, capture
// the error and rethrow it when accessing. This should be JITted away in the common case.
var helper = Services.tryLoad(PlatformHelper.class);
INSTANCE = helper.instance();
ERROR = helper.error();
}
private Instance() {
}
}
}

View File

@ -6,12 +6,15 @@
import dan200.computercraft.api.turtle.TurtleSide; import dan200.computercraft.api.turtle.TurtleSide;
import dan200.computercraft.shared.ModRegistry; import dan200.computercraft.shared.ModRegistry;
import dan200.computercraft.shared.integration.RecipeModHelpers;
import dan200.computercraft.shared.pocket.items.PocketComputerItem; import dan200.computercraft.shared.pocket.items.PocketComputerItem;
import dan200.computercraft.shared.turtle.items.TurtleItem; import dan200.computercraft.shared.turtle.items.TurtleItem;
import dev.emi.emi.api.EmiEntrypoint; import dev.emi.emi.api.EmiEntrypoint;
import dev.emi.emi.api.EmiPlugin; import dev.emi.emi.api.EmiPlugin;
import dev.emi.emi.api.EmiRegistry; import dev.emi.emi.api.EmiRegistry;
import dev.emi.emi.api.stack.Comparison; import dev.emi.emi.api.stack.Comparison;
import dev.emi.emi.api.stack.EmiStack;
import net.minecraft.client.Minecraft;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import java.util.function.BiPredicate; import java.util.function.BiPredicate;
@ -25,6 +28,10 @@ public void register(EmiRegistry registry) {
registry.setDefaultComparison(ModRegistry.Items.POCKET_COMPUTER_NORMAL.get(), pocketComparison); registry.setDefaultComparison(ModRegistry.Items.POCKET_COMPUTER_NORMAL.get(), pocketComparison);
registry.setDefaultComparison(ModRegistry.Items.POCKET_COMPUTER_ADVANCED.get(), pocketComparison); registry.setDefaultComparison(ModRegistry.Items.POCKET_COMPUTER_ADVANCED.get(), pocketComparison);
for (var stack : RecipeModHelpers.getExtraStacks(Minecraft.getInstance().level.registryAccess())) {
registry.addEmiStack(EmiStack.of(stack));
}
} }
private static final Comparison turtleComparison = compareStacks((left, right) private static final Comparison turtleComparison = compareStacks((left, right)

View File

@ -11,14 +11,12 @@
import dan200.computercraft.api.turtle.ITurtleUpgrade; import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.api.turtle.TurtleSide; import dan200.computercraft.api.turtle.TurtleSide;
import dan200.computercraft.api.upgrades.UpgradeType; import dan200.computercraft.api.upgrades.UpgradeType;
import dan200.computercraft.impl.RegistryHelper; import dan200.computercraft.shared.util.RegistryHelper;
import dan200.computercraft.impl.UpgradeManager;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.core.component.DataComponentPatch; import net.minecraft.core.component.DataComponentPatch;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import java.util.Map; import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Stream; import java.util.stream.Stream;
@ -32,14 +30,6 @@ public final class TurtleUpgradeModellers {
private static final Map<UpgradeType<? extends ITurtleUpgrade>, TurtleUpgradeModeller<?>> turtleModels = new ConcurrentHashMap<>(); private static final Map<UpgradeType<? extends ITurtleUpgrade>, TurtleUpgradeModeller<?>> turtleModels = new ConcurrentHashMap<>();
private static volatile boolean fetchedModels; private static volatile boolean fetchedModels;
/**
* In order to avoid a double lookup of {@link ITurtleUpgrade} to {@link UpgradeManager.UpgradeWrapper} to
* {@link TurtleUpgradeModeller}, we maintain a cache here.
* <p>
* Turtle upgrades may be removed as part of datapack reloads, so we use a weak map to avoid the memory leak.
*/
private static final WeakHashMap<ITurtleUpgrade, TurtleUpgradeModeller<?>> modelCache = new WeakHashMap<>();
private TurtleUpgradeModellers() { private TurtleUpgradeModellers() {
} }
@ -57,20 +47,17 @@ public static <T extends ITurtleUpgrade> void register(UpgradeType<T> type, Turt
} }
public static TransformedModel getModel(ITurtleUpgrade upgrade, ITurtleAccess access, TurtleSide side) { public static TransformedModel getModel(ITurtleUpgrade upgrade, ITurtleAccess access, TurtleSide side) {
@SuppressWarnings("unchecked") return getModeller(upgrade).getModel(upgrade, access, side, access.getUpgradeData(side));
var modeller = (TurtleUpgradeModeller<ITurtleUpgrade>) modelCache.computeIfAbsent(upgrade, TurtleUpgradeModellers::getModeller);
return modeller.getModel(upgrade, access, side, access.getUpgradeData(side));
} }
public static TransformedModel getModel(ITurtleUpgrade upgrade, DataComponentPatch data, TurtleSide side) { public static TransformedModel getModel(ITurtleUpgrade upgrade, DataComponentPatch data, TurtleSide side) {
@SuppressWarnings("unchecked") return getModeller(upgrade).getModel(upgrade, null, side, data);
var modeller = (TurtleUpgradeModeller<ITurtleUpgrade>) modelCache.computeIfAbsent(upgrade, TurtleUpgradeModellers::getModeller);
return modeller.getModel(upgrade, null, side, data);
} }
private static TurtleUpgradeModeller<?> getModeller(ITurtleUpgrade upgrade) { @SuppressWarnings("unchecked")
private static <T extends ITurtleUpgrade> TurtleUpgradeModeller<T> getModeller(T upgrade) {
var modeller = turtleModels.get(upgrade.getType()); var modeller = turtleModels.get(upgrade.getType());
return modeller == null ? NULL_TURTLE_MODELLER : modeller; return (TurtleUpgradeModeller<T>) (modeller == null ? NULL_TURTLE_MODELLER : modeller);
} }
public static Stream<ResourceLocation> getDependencies() { public static Stream<ResourceLocation> getDependencies() {

View File

@ -1,7 +1,7 @@
{ {
"type": "minecraft:crafting_shaped", "type": "minecraft:crafting_shaped",
"category": "redstone", "category": "redstone",
"key": {"#": {"tag": "c:stones"}, "R": {"tag": "c:dusts/redstone"}}, "key": {"#": {"item": "minecraft:stone"}, "R": {"tag": "c:dusts/redstone"}},
"pattern": [" # ", "#R#", " # "], "pattern": [" # ", "#R#", " # "],
"result": {"count": 6, "id": "computercraft:cable"} "result": {"count": 6, "id": "computercraft:cable"}
} }

View File

@ -1,7 +1,7 @@
{ {
"type": "minecraft:crafting_shaped", "type": "minecraft:crafting_shaped",
"category": "redstone", "category": "redstone",
"key": {"#": {"tag": "c:stones"}, "G": {"tag": "c:glass_panes"}, "R": {"tag": "c:dusts/redstone"}}, "key": {"#": {"item": "minecraft:stone"}, "G": {"tag": "c:glass_panes"}, "R": {"tag": "c:dusts/redstone"}},
"pattern": ["###", "#R#", "#G#"], "pattern": ["###", "#R#", "#G#"],
"result": {"count": 1, "id": "computercraft:computer_normal"} "result": {"count": 1, "id": "computercraft:computer_normal"}
} }

View File

@ -1,7 +1,7 @@
{ {
"type": "minecraft:crafting_shaped", "type": "minecraft:crafting_shaped",
"category": "redstone", "category": "redstone",
"key": {"#": {"tag": "c:stones"}, "R": {"tag": "c:dusts/redstone"}}, "key": {"#": {"item": "minecraft:stone"}, "R": {"tag": "c:dusts/redstone"}},
"pattern": ["###", "#R#", "#R#"], "pattern": ["###", "#R#", "#R#"],
"result": {"count": 1, "id": "computercraft:disk_drive"} "result": {"count": 1, "id": "computercraft:disk_drive"}
} }

View File

@ -1,7 +1,7 @@
{ {
"type": "minecraft:crafting_shaped", "type": "minecraft:crafting_shaped",
"category": "redstone", "category": "redstone",
"key": {"#": {"tag": "c:stones"}, "G": {"tag": "c:glass_panes"}}, "key": {"#": {"item": "minecraft:stone"}, "G": {"tag": "c:glass_panes"}},
"pattern": ["###", "#G#", "###"], "pattern": ["###", "#G#", "###"],
"result": {"count": 1, "id": "computercraft:monitor_normal"} "result": {"count": 1, "id": "computercraft:monitor_normal"}
} }

View File

@ -1,7 +1,11 @@
{ {
"type": "minecraft:crafting_shaped", "type": "minecraft:crafting_shaped",
"category": "redstone", "category": "redstone",
"key": {"#": {"tag": "c:stones"}, "A": {"item": "minecraft:golden_apple"}, "G": {"tag": "c:glass_panes"}}, "key": {
"#": {"item": "minecraft:stone"},
"A": {"item": "minecraft:golden_apple"},
"G": {"tag": "c:glass_panes"}
},
"pattern": ["###", "#A#", "#G#"], "pattern": ["###", "#A#", "#G#"],
"result": {"count": 1, "id": "computercraft:pocket_computer_normal"} "result": {"count": 1, "id": "computercraft:pocket_computer_normal"}
} }

View File

@ -1,7 +1,7 @@
{ {
"type": "minecraft:crafting_shaped", "type": "minecraft:crafting_shaped",
"category": "redstone", "category": "redstone",
"key": {"#": {"tag": "c:stones"}, "D": {"tag": "c:dyes"}, "R": {"tag": "c:dusts/redstone"}}, "key": {"#": {"item": "minecraft:stone"}, "D": {"tag": "c:dyes"}, "R": {"tag": "c:dusts/redstone"}},
"pattern": ["###", "#R#", "#D#"], "pattern": ["###", "#R#", "#D#"],
"result": {"count": 1, "id": "computercraft:printer"} "result": {"count": 1, "id": "computercraft:printer"}
} }

View File

@ -1,7 +1,11 @@
{ {
"type": "minecraft:crafting_shaped", "type": "minecraft:crafting_shaped",
"category": "redstone", "category": "redstone",
"key": {"#": {"tag": "c:stones"}, "N": {"item": "minecraft:note_block"}, "R": {"tag": "c:dusts/redstone"}}, "key": {
"#": {"item": "minecraft:stone"},
"N": {"item": "minecraft:note_block"},
"R": {"tag": "c:dusts/redstone"}
},
"pattern": ["###", "#N#", "#R#"], "pattern": ["###", "#N#", "#R#"],
"result": {"count": 1, "id": "computercraft:speaker"} "result": {"count": 1, "id": "computercraft:speaker"}
} }

View File

@ -1,7 +1,7 @@
{ {
"type": "minecraft:crafting_shaped", "type": "minecraft:crafting_shaped",
"category": "redstone", "category": "redstone",
"key": {"#": {"tag": "c:stones"}, "R": {"tag": "c:dusts/redstone"}}, "key": {"#": {"item": "minecraft:stone"}, "R": {"tag": "c:dusts/redstone"}},
"pattern": ["###", "#R#", "###"], "pattern": ["###", "#R#", "###"],
"result": {"count": 1, "id": "computercraft:wired_modem"} "result": {"count": 1, "id": "computercraft:wired_modem"}
} }

View File

@ -5,7 +5,9 @@
package dan200.computercraft.data; package dan200.computercraft.data;
import com.mojang.serialization.Codec; import com.mojang.serialization.Codec;
import dan200.computercraft.shared.ModRegistry; import dan200.computercraft.api.pocket.IPocketUpgrade;
import dan200.computercraft.api.turtle.ITurtleUpgrade;
import net.minecraft.Util;
import net.minecraft.core.HolderLookup; import net.minecraft.core.HolderLookup;
import net.minecraft.core.RegistrySetBuilder; import net.minecraft.core.RegistrySetBuilder;
import net.minecraft.data.DataProvider; import net.minecraft.data.DataProvider;
@ -20,7 +22,6 @@
import java.util.Set; import java.util.Set;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer; import java.util.function.Consumer;
/** /**
@ -32,25 +33,25 @@ private DataProviders() {
} }
public static void add(GeneratorSink generator) { public static void add(GeneratorSink generator) {
var turtleUpgrades = generator.add(TurtleUpgradeProvider::new); var fullRegistryPatch = RegistryPatchGenerator.createLookup(
var pocketUpgrades = generator.add(PocketUpgradeProvider::new); generator.registries(),
Util.make(new RegistrySetBuilder(), builder -> {
builder.add(ITurtleUpgrade.REGISTRY, TurtleUpgradeProvider::addUpgrades);
builder.add(IPocketUpgrade.REGISTRY, PocketUpgradeProvider::addUpgrades);
}));
var fullRegistries = fullRegistryPatch.thenApply(RegistrySetBuilder.PatchedRegistries::full);
generator.add((out, registries) -> { generator.registries(fullRegistryPatch);
var builder = new RegistrySetBuilder(); generator.add(out -> new RecipeProvider(out, fullRegistries));
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); var blockTags = generator.blockTags(TagProvider::blockTags);
generator.itemTags(TagProvider::itemTags, blockTags); generator.itemTags(TagProvider::itemTags, blockTags);
generator.add((out, registries) -> new net.minecraft.data.loot.LootTableProvider(out, Set.of(), LootTableProvider.getTables(), registries)); generator.add(out -> new net.minecraft.data.loot.LootTableProvider(out, Set.of(), LootTableProvider.getTables(), fullRegistries));
generator.add(out -> new ModelProvider(out, BlockModelProvider::addBlockModels, ItemModelProvider::addItemModels)); generator.add(out -> new ModelProvider(out, BlockModelProvider::addBlockModels, ItemModelProvider::addItemModels));
generator.add(out -> new LanguageProvider(out, turtleUpgrades, pocketUpgrades)); generator.add(out -> new LanguageProvider(out, fullRegistries));
// Unfortunately we rely on some client-side classes in this code. We just load in the client side data provider // Unfortunately we rely on some client-side classes in this code. We just load in the client side data provider
// and invoke that. // and invoke that.
@ -63,9 +64,9 @@ public static void add(GeneratorSink generator) {
} }
public interface GeneratorSink { public interface GeneratorSink {
<T extends DataProvider> T add(DataProvider.Factory<T> factory); CompletableFuture<HolderLookup.Provider> registries();
<T extends DataProvider> T add(BiFunction<PackOutput, CompletableFuture<HolderLookup.Provider>, T> factory); <T extends DataProvider> T add(DataProvider.Factory<T> factory);
<T> void addFromCodec(String name, PackType type, String directory, Codec<T> codec, Consumer<BiConsumer<ResourceLocation, T>> output); <T> void addFromCodec(String name, PackType type, String directory, Codec<T> codec, Consumer<BiConsumer<ResourceLocation, T>> output);
@ -74,16 +75,10 @@ public interface GeneratorSink {
TagsProvider<Item> itemTags(Consumer<TagProvider.ItemTagConsumer> tags, TagsProvider<Block> blocks); TagsProvider<Item> itemTags(Consumer<TagProvider.ItemTagConsumer> tags, TagsProvider<Block> blocks);
/** /**
* Extend our registries with additional entries. * Build new dynamic registries and save them to a pack.
* *
* @param registries The existing registries. * @param registries The patched registries to write.
* @param patch The new registries to apply.
* @return The built registries.
*/ */
default CompletableFuture<RegistrySetBuilder.PatchedRegistries> createPatchedRegistries( void registries(CompletableFuture<RegistrySetBuilder.PatchedRegistries> registries);
CompletableFuture<HolderLookup.Provider> registries, RegistrySetBuilder patch
) {
return RegistryPatchGenerator.createLookup(registries, patch);
}
} }
} }

View File

@ -7,8 +7,8 @@
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.ComputerCraftTags; import dan200.computercraft.api.ComputerCraftTags;
import dan200.computercraft.api.pocket.PocketUpgradeDataProvider; import dan200.computercraft.api.pocket.IPocketUpgrade;
import dan200.computercraft.api.turtle.TurtleUpgradeDataProvider; import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.core.metrics.Metric; import dan200.computercraft.core.metrics.Metric;
import dan200.computercraft.core.metrics.Metrics; import dan200.computercraft.core.metrics.Metrics;
import dan200.computercraft.shared.ModRegistry; import dan200.computercraft.shared.ModRegistry;
@ -17,6 +17,7 @@
import dan200.computercraft.shared.computer.metrics.basic.AggregatedMetric; import dan200.computercraft.shared.computer.metrics.basic.AggregatedMetric;
import dan200.computercraft.shared.config.ConfigFile; import dan200.computercraft.shared.config.ConfigFile;
import dan200.computercraft.shared.config.ConfigSpec; import dan200.computercraft.shared.config.ConfigSpec;
import net.minecraft.core.HolderLookup;
import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.data.CachedOutput; import net.minecraft.data.CachedOutput;
import net.minecraft.data.DataProvider; import net.minecraft.data.DataProvider;
@ -34,27 +35,28 @@
public final class LanguageProvider implements DataProvider { public final class LanguageProvider implements DataProvider {
private final PackOutput output; private final PackOutput output;
private final TurtleUpgradeDataProvider turtleUpgrades; private final CompletableFuture<HolderLookup.Provider> registries;
private final PocketUpgradeDataProvider pocketUpgrades;
private final Map<String, String> translations = new HashMap<>(); private final Map<String, String> translations = new HashMap<>();
public LanguageProvider(PackOutput output, TurtleUpgradeDataProvider turtleUpgrades, PocketUpgradeDataProvider pocketUpgrades) { public LanguageProvider(PackOutput output, CompletableFuture<HolderLookup.Provider> registries) {
this.output = output; this.output = output;
this.turtleUpgrades = turtleUpgrades; this.registries = registries;
this.pocketUpgrades = pocketUpgrades;
} }
@Override @Override
public CompletableFuture<?> run(CachedOutput cachedOutput) { public CompletableFuture<?> run(CachedOutput cachedOutput) {
addTranslations(); addTranslations();
getExpectedKeys().forEach(x -> {
if (!translations.containsKey(x)) throw new IllegalStateException("No translation for " + x);
});
var json = new JsonObject(); return registries.thenCompose(registries -> {
for (var pair : translations.entrySet()) json.addProperty(pair.getKey(), pair.getValue()); getExpectedKeys(registries).forEach(x -> {
return DataProvider.saveStable(cachedOutput, json, output.getOutputFolder().resolve("assets/" + ComputerCraftAPI.MOD_ID + "/lang/en_us.json")); if (!translations.containsKey(x)) throw new IllegalStateException("No translation for " + x);
});
var json = new JsonObject();
for (var pair : translations.entrySet()) json.addProperty(pair.getKey(), pair.getValue());
return DataProvider.saveStable(cachedOutput, json, output.getOutputFolder().resolve("assets/" + ComputerCraftAPI.MOD_ID + "/lang/en_us.json"));
});
} }
@Override @Override
@ -280,7 +282,7 @@ private void addTranslations() {
addConfigEntry(ConfigSpec.uploadNagDelay, "Upload nag delay"); addConfigEntry(ConfigSpec.uploadNagDelay, "Upload nag delay");
} }
private Stream<String> getExpectedKeys() { private Stream<String> getExpectedKeys(HolderLookup.Provider registries) {
return Stream.of( return Stream.of(
BuiltInRegistries.BLOCK.holders() BuiltInRegistries.BLOCK.holders()
.filter(x -> x.key().location().getNamespace().equals(ComputerCraftAPI.MOD_ID)) .filter(x -> x.key().location().getNamespace().equals(ComputerCraftAPI.MOD_ID))
@ -288,8 +290,8 @@ private Stream<String> getExpectedKeys() {
BuiltInRegistries.ITEM.holders() BuiltInRegistries.ITEM.holders()
.filter(x -> x.key().location().getNamespace().equals(ComputerCraftAPI.MOD_ID)) .filter(x -> x.key().location().getNamespace().equals(ComputerCraftAPI.MOD_ID))
.map(x -> x.value().getDescriptionId()), .map(x -> x.value().getDescriptionId()),
turtleUpgrades.getGeneratedUpgrades().values().stream().flatMap(x -> getTranslationKeys(x.getAdjective())), registries.lookupOrThrow(ITurtleUpgrade.REGISTRY).listElements().flatMap(x -> getTranslationKeys(x.value().getAdjective())),
pocketUpgrades.getGeneratedUpgrades().values().stream().flatMap(x -> getTranslationKeys(x.getAdjective())), registries.lookupOrThrow(IPocketUpgrade.REGISTRY).listElements().flatMap(x -> getTranslationKeys(x.value().getAdjective())),
Metric.metrics().values().stream().map(x -> AggregatedMetric.TRANSLATION_PREFIX + x.name() + ".name"), Metric.metrics().values().stream().map(x -> AggregatedMetric.TRANSLATION_PREFIX + x.name() + ".name"),
ConfigSpec.serverSpec.entries().map(ConfigFile.Entry::translationKey), ConfigSpec.serverSpec.entries().map(ConfigFile.Entry::translationKey),
ConfigSpec.clientSpec.entries().map(ConfigFile.Entry::translationKey), ConfigSpec.clientSpec.entries().map(ConfigFile.Entry::translationKey),

View File

@ -5,7 +5,7 @@
package dan200.computercraft.data; package dan200.computercraft.data;
import com.google.gson.JsonElement; import com.google.gson.JsonElement;
import dan200.computercraft.impl.RegistryHelper; import dan200.computercraft.shared.util.RegistryHelper;
import net.minecraft.Util; import net.minecraft.Util;
import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.data.CachedOutput; import net.minecraft.data.CachedOutput;

View File

@ -6,30 +6,23 @@
import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.pocket.IPocketUpgrade; import dan200.computercraft.api.pocket.IPocketUpgrade;
import dan200.computercraft.api.pocket.PocketUpgradeDataProvider;
import dan200.computercraft.shared.pocket.peripherals.PocketModem; import dan200.computercraft.shared.pocket.peripherals.PocketModem;
import dan200.computercraft.shared.pocket.peripherals.PocketSpeaker; import dan200.computercraft.shared.pocket.peripherals.PocketSpeaker;
import net.minecraft.data.PackOutput; import net.minecraft.data.worldgen.BootstrapContext;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import java.util.function.Consumer;
import static dan200.computercraft.shared.ModRegistry.Items; import static dan200.computercraft.shared.ModRegistry.Items;
class PocketUpgradeProvider extends PocketUpgradeDataProvider { class PocketUpgradeProvider {
PocketUpgradeProvider(PackOutput output) { public static void addUpgrades(BootstrapContext<IPocketUpgrade> upgrades) {
super(output); upgrades.register(id("speaker"), new PocketSpeaker(new ItemStack(Items.SPEAKER.get())));
upgrades.register(id("wireless_modem_normal"), new PocketModem(new ItemStack(Items.WIRELESS_MODEM_NORMAL.get()), false));
upgrades.register(id("wireless_modem_advanced"), new PocketModem(new ItemStack(Items.WIRELESS_MODEM_ADVANCED.get()), true));
} }
@Override private static ResourceKey<IPocketUpgrade> id(String id) {
protected void addUpgrades(Consumer<Upgrade<IPocketUpgrade>> addUpgrade) { return ResourceKey.create(IPocketUpgrade.REGISTRY, new ResourceLocation(ComputerCraftAPI.MOD_ID, id));
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) {
return new ResourceLocation(ComputerCraftAPI.MOD_ID, id);
} }
} }

View File

@ -8,11 +8,13 @@
import com.mojang.authlib.GameProfile; import com.mojang.authlib.GameProfile;
import com.mojang.serialization.JsonOps; import com.mojang.serialization.JsonOps;
import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.pocket.IPocketUpgrade;
import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.api.upgrades.UpgradeData; import dan200.computercraft.api.upgrades.UpgradeData;
import dan200.computercraft.core.util.Colour; import dan200.computercraft.core.util.Colour;
import dan200.computercraft.data.recipe.ShapedSpecBuilder; import dan200.computercraft.data.recipe.ShapedSpecBuilder;
import dan200.computercraft.data.recipe.ShapelessSpecBuilder; import dan200.computercraft.data.recipe.ShapelessSpecBuilder;
import dan200.computercraft.impl.RegistryHelper; import dan200.computercraft.shared.util.RegistryHelper;
import dan200.computercraft.shared.ModRegistry; import dan200.computercraft.shared.ModRegistry;
import dan200.computercraft.shared.common.ClearColourRecipe; import dan200.computercraft.shared.common.ClearColourRecipe;
import dan200.computercraft.shared.common.ColourableRecipe; import dan200.computercraft.shared.common.ColourableRecipe;
@ -54,14 +56,12 @@
import net.minecraft.world.item.crafting.Ingredient; import net.minecraft.world.item.crafting.Ingredient;
import net.minecraft.world.item.crafting.Recipe; import net.minecraft.world.item.crafting.Recipe;
import net.minecraft.world.level.ItemLike; import net.minecraft.world.level.ItemLike;
import net.minecraft.world.level.block.Blocks;
import java.util.List; import java.util.List;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.function.Consumer; 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.COMPUTER;
import static dan200.computercraft.api.ComputerCraftTags.Items.WIRED_MODEM; import static dan200.computercraft.api.ComputerCraftTags.Items.WIRED_MODEM;
@ -119,7 +119,7 @@ private void diskColours(RecipeOutput output) {
.requires(Items.PAPER) .requires(Items.PAPER)
.requires(DyeItem.byColor(ofColour(colour))) .requires(DyeItem.byColor(ofColour(colour)))
.group("computercraft:disk") .group("computercraft:disk")
.unlockedBy("has_drive", inventoryChange(ModRegistry.Blocks.DISK_DRIVE.get())) .unlockedBy("has_drive", inventoryChange(ModRegistry.Items.DISK_DRIVE.get()))
.build(ImpostorShapelessRecipe::new) .build(ImpostorShapelessRecipe::new)
.save(output, new ResourceLocation(ComputerCraftAPI.MOD_ID, "disk_" + (colour.ordinal() + 1))); .save(output, new ResourceLocation(ComputerCraftAPI.MOD_ID, "disk_" + (colour.ordinal() + 1)));
} }
@ -139,7 +139,7 @@ private void turtleUpgrades(RecipeOutput add, HolderLookup.Provider registries)
for (var turtleItem : turtleItems()) { for (var turtleItem : turtleItems()) {
var name = RegistryHelper.getKeyOrThrow(BuiltInRegistries.ITEM, turtleItem); var name = RegistryHelper.getKeyOrThrow(BuiltInRegistries.ITEM, turtleItem);
registries.lookup(ModRegistry.TURTLE_UPGRADE).map(HolderLookup::listElements).orElse(Stream.empty()).forEach(upgradeHolder -> { registries.lookupOrThrow(ITurtleUpgrade.REGISTRY).listElements().forEach(upgradeHolder -> {
var upgrade = upgradeHolder.value(); var upgrade = upgradeHolder.value();
ShapedSpecBuilder ShapedSpecBuilder
.shaped(RecipeCategory.REDSTONE, DataComponentUtil.createStack(turtleItem, ModRegistry.DataComponents.RIGHT_TURTLE_UPGRADE.get(), UpgradeData.ofDefault(upgradeHolder))) .shaped(RecipeCategory.REDSTONE, DataComponentUtil.createStack(turtleItem, ModRegistry.DataComponents.RIGHT_TURTLE_UPGRADE.get(), UpgradeData.ofDefault(upgradeHolder)))
@ -171,7 +171,7 @@ private void pocketUpgrades(RecipeOutput add, HolderLookup.Provider registries)
for (var pocket : pocketComputerItems()) { for (var pocket : pocketComputerItems()) {
var name = RegistryHelper.getKeyOrThrow(BuiltInRegistries.ITEM, pocket).withPath(x -> x.replace("pocket_computer_", "pocket_")); var name = RegistryHelper.getKeyOrThrow(BuiltInRegistries.ITEM, pocket).withPath(x -> x.replace("pocket_computer_", "pocket_"));
registries.lookup(ModRegistry.POCKET_UPGRADE).map(HolderLookup::listElements).orElse(Stream.empty()).forEach(upgradeHolder -> { registries.lookupOrThrow(IPocketUpgrade.REGISTRY).listElements().forEach(upgradeHolder -> {
var upgrade = upgradeHolder.value(); var upgrade = upgradeHolder.value();
ShapedSpecBuilder ShapedSpecBuilder
.shaped(RecipeCategory.REDSTONE, DataComponentUtil.createStack(pocket, ModRegistry.DataComponents.POCKET_UPGRADE.get(), UpgradeData.ofDefault(upgradeHolder))) .shaped(RecipeCategory.REDSTONE, DataComponentUtil.createStack(pocket, ModRegistry.DataComponents.POCKET_UPGRADE.get(), UpgradeData.ofDefault(upgradeHolder)))
@ -238,25 +238,25 @@ private void basicRecipes(RecipeOutput add) {
.pattern(" # ") .pattern(" # ")
.pattern("#R#") .pattern("#R#")
.pattern(" # ") .pattern(" # ")
.define('#', ingredients.stone()) .define('#', Items.STONE)
.define('R', ingredients.redstone()) .define('R', ingredients.redstone())
.unlockedBy("has_computer", inventoryChange(COMPUTER)) .unlockedBy("has_computer", inventoryChange(COMPUTER))
.unlockedBy("has_modem", inventoryChange(WIRED_MODEM)) .unlockedBy("has_modem", inventoryChange(WIRED_MODEM))
.save(add); .save(add);
ShapedRecipeBuilder ShapedRecipeBuilder
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.COMPUTER_NORMAL.get()) .shaped(RecipeCategory.REDSTONE, ModRegistry.Items.COMPUTER_NORMAL.get())
.pattern("###") .pattern("###")
.pattern("#R#") .pattern("#R#")
.pattern("#G#") .pattern("#G#")
.define('#', ingredients.stone()) .define('#', Items.STONE)
.define('R', ingredients.redstone()) .define('R', ingredients.redstone())
.define('G', ingredients.glassPane()) .define('G', ingredients.glassPane())
.unlockedBy("has_redstone", inventoryChange(itemPredicate(ingredients.redstone()))) .unlockedBy("has_redstone", inventoryChange(itemPredicate(ingredients.redstone())))
.save(add); .save(add);
ShapedRecipeBuilder ShapedRecipeBuilder
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.COMPUTER_ADVANCED.get()) .shaped(RecipeCategory.REDSTONE, ModRegistry.Items.COMPUTER_ADVANCED.get())
.pattern("###") .pattern("###")
.pattern("#R#") .pattern("#R#")
.pattern("#G#") .pattern("#G#")
@ -278,18 +278,18 @@ private void basicRecipes(RecipeOutput add) {
.save(add, new ResourceLocation(ComputerCraftAPI.MOD_ID, "computer_advanced_upgrade")); .save(add, new ResourceLocation(ComputerCraftAPI.MOD_ID, "computer_advanced_upgrade"));
ShapedRecipeBuilder ShapedRecipeBuilder
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.COMPUTER_COMMAND.get()) .shaped(RecipeCategory.REDSTONE, ModRegistry.Items.COMPUTER_COMMAND.get())
.pattern("###") .pattern("###")
.pattern("#R#") .pattern("#R#")
.pattern("#G#") .pattern("#G#")
.define('#', ingredients.goldIngot()) .define('#', ingredients.goldIngot())
.define('R', Blocks.COMMAND_BLOCK) .define('R', Items.COMMAND_BLOCK)
.define('G', ingredients.glassPane()) .define('G', ingredients.glassPane())
.unlockedBy("has_components", inventoryChange(Blocks.COMMAND_BLOCK)) .unlockedBy("has_components", inventoryChange(Items.COMMAND_BLOCK))
.save(add); .save(add);
ShapedSpecBuilder ShapedSpecBuilder
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.TURTLE_NORMAL.get()) .shaped(RecipeCategory.REDSTONE, ModRegistry.Items.TURTLE_NORMAL.get())
.pattern("###") .pattern("###")
.pattern("#C#") .pattern("#C#")
.pattern("#I#") .pattern("#I#")
@ -301,7 +301,7 @@ private void basicRecipes(RecipeOutput add) {
.save(add); .save(add);
ShapedSpecBuilder ShapedSpecBuilder
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.TURTLE_ADVANCED.get()) .shaped(RecipeCategory.REDSTONE, ModRegistry.Items.TURTLE_ADVANCED.get())
.pattern("###") .pattern("###")
.pattern("#C#") .pattern("#C#")
.pattern("#I#") .pattern("#I#")
@ -313,7 +313,7 @@ private void basicRecipes(RecipeOutput add) {
.save(add); .save(add);
ShapedSpecBuilder ShapedSpecBuilder
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.TURTLE_ADVANCED.get()) .shaped(RecipeCategory.REDSTONE, ModRegistry.Items.TURTLE_ADVANCED.get())
.pattern("###") .pattern("###")
.pattern("#C#") .pattern("#C#")
.pattern(" B ") .pattern(" B ")
@ -325,27 +325,27 @@ private void basicRecipes(RecipeOutput add) {
.save(add, new ResourceLocation(ComputerCraftAPI.MOD_ID, "turtle_advanced_upgrade")); .save(add, new ResourceLocation(ComputerCraftAPI.MOD_ID, "turtle_advanced_upgrade"));
ShapedRecipeBuilder ShapedRecipeBuilder
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.DISK_DRIVE.get()) .shaped(RecipeCategory.REDSTONE, ModRegistry.Items.DISK_DRIVE.get())
.pattern("###") .pattern("###")
.pattern("#R#") .pattern("#R#")
.pattern("#R#") .pattern("#R#")
.define('#', ingredients.stone()) .define('#', Items.STONE)
.define('R', ingredients.redstone()) .define('R', ingredients.redstone())
.unlockedBy("has_computer", inventoryChange(COMPUTER)) .unlockedBy("has_computer", inventoryChange(COMPUTER))
.save(add); .save(add);
ShapedRecipeBuilder ShapedRecipeBuilder
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.MONITOR_NORMAL.get()) .shaped(RecipeCategory.REDSTONE, ModRegistry.Items.MONITOR_NORMAL.get())
.pattern("###") .pattern("###")
.pattern("#G#") .pattern("#G#")
.pattern("###") .pattern("###")
.define('#', ingredients.stone()) .define('#', Items.STONE)
.define('G', ingredients.glassPane()) .define('G', ingredients.glassPane())
.unlockedBy("has_computer", inventoryChange(COMPUTER)) .unlockedBy("has_computer", inventoryChange(COMPUTER))
.save(add); .save(add);
ShapedRecipeBuilder ShapedRecipeBuilder
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.MONITOR_ADVANCED.get(), 4) .shaped(RecipeCategory.REDSTONE, ModRegistry.Items.MONITOR_ADVANCED.get(), 4)
.pattern("###") .pattern("###")
.pattern("#G#") .pattern("#G#")
.pattern("###") .pattern("###")
@ -359,7 +359,7 @@ private void basicRecipes(RecipeOutput add) {
.pattern("###") .pattern("###")
.pattern("#A#") .pattern("#A#")
.pattern("#G#") .pattern("#G#")
.define('#', ingredients.stone()) .define('#', Items.STONE)
.define('A', Items.GOLDEN_APPLE) .define('A', Items.GOLDEN_APPLE)
.define('G', ingredients.glassPane()) .define('G', ingredients.glassPane())
.unlockedBy("has_computer", inventoryChange(COMPUTER)) .unlockedBy("has_computer", inventoryChange(COMPUTER))
@ -390,23 +390,23 @@ private void basicRecipes(RecipeOutput add) {
.save(add, new ResourceLocation(ComputerCraftAPI.MOD_ID, "pocket_computer_advanced_upgrade")); .save(add, new ResourceLocation(ComputerCraftAPI.MOD_ID, "pocket_computer_advanced_upgrade"));
ShapedRecipeBuilder ShapedRecipeBuilder
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.PRINTER.get()) .shaped(RecipeCategory.REDSTONE, ModRegistry.Items.PRINTER.get())
.pattern("###") .pattern("###")
.pattern("#R#") .pattern("#R#")
.pattern("#D#") .pattern("#D#")
.define('#', ingredients.stone()) .define('#', Items.STONE)
.define('R', ingredients.redstone()) .define('R', ingredients.redstone())
.define('D', ingredients.dye()) .define('D', ingredients.dye())
.unlockedBy("has_computer", inventoryChange(COMPUTER)) .unlockedBy("has_computer", inventoryChange(COMPUTER))
.save(add); .save(add);
ShapedRecipeBuilder ShapedRecipeBuilder
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.SPEAKER.get()) .shaped(RecipeCategory.REDSTONE, ModRegistry.Items.SPEAKER.get())
.pattern("###") .pattern("###")
.pattern("#N#") .pattern("#N#")
.pattern("#R#") .pattern("#R#")
.define('#', ingredients.stone()) .define('#', Items.STONE)
.define('N', Blocks.NOTE_BLOCK) .define('N', Items.NOTE_BLOCK)
.define('R', ingredients.redstone()) .define('R', ingredients.redstone())
.unlockedBy("has_computer", inventoryChange(COMPUTER)) .unlockedBy("has_computer", inventoryChange(COMPUTER))
.save(add); .save(add);
@ -416,42 +416,42 @@ private void basicRecipes(RecipeOutput add) {
.pattern("###") .pattern("###")
.pattern("#R#") .pattern("#R#")
.pattern("###") .pattern("###")
.define('#', ingredients.stone()) .define('#', Items.STONE)
.define('R', ingredients.redstone()) .define('R', ingredients.redstone())
.unlockedBy("has_computer", inventoryChange(COMPUTER)) .unlockedBy("has_computer", inventoryChange(COMPUTER))
.unlockedBy("has_cable", inventoryChange(ModRegistry.Items.CABLE.get())) .unlockedBy("has_cable", inventoryChange(ModRegistry.Items.CABLE.get()))
.save(add); .save(add);
ShapelessRecipeBuilder ShapelessRecipeBuilder
.shapeless(RecipeCategory.REDSTONE, ModRegistry.Blocks.WIRED_MODEM_FULL.get()) .shapeless(RecipeCategory.REDSTONE, ModRegistry.Items.WIRED_MODEM_FULL.get())
.requires(ModRegistry.Items.WIRED_MODEM.get()) .requires(ModRegistry.Items.WIRED_MODEM.get())
.unlockedBy("has_modem", inventoryChange(WIRED_MODEM)) .unlockedBy("has_modem", inventoryChange(WIRED_MODEM))
.save(add, new ResourceLocation(ComputerCraftAPI.MOD_ID, "wired_modem_full_from")); .save(add, new ResourceLocation(ComputerCraftAPI.MOD_ID, "wired_modem_full_from"));
ShapelessRecipeBuilder ShapelessRecipeBuilder
.shapeless(RecipeCategory.REDSTONE, ModRegistry.Items.WIRED_MODEM.get()) .shapeless(RecipeCategory.REDSTONE, ModRegistry.Items.WIRED_MODEM.get())
.requires(ModRegistry.Blocks.WIRED_MODEM_FULL.get()) .requires(ModRegistry.Items.WIRED_MODEM_FULL.get())
.unlockedBy("has_modem", inventoryChange(WIRED_MODEM)) .unlockedBy("has_modem", inventoryChange(WIRED_MODEM))
.save(add, new ResourceLocation(ComputerCraftAPI.MOD_ID, "wired_modem_full_to")); .save(add, new ResourceLocation(ComputerCraftAPI.MOD_ID, "wired_modem_full_to"));
ShapedRecipeBuilder ShapedRecipeBuilder
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.WIRELESS_MODEM_NORMAL.get()) .shaped(RecipeCategory.REDSTONE, ModRegistry.Items.WIRELESS_MODEM_NORMAL.get())
.pattern("###") .pattern("###")
.pattern("#E#") .pattern("#E#")
.pattern("###") .pattern("###")
.define('#', ingredients.stone()) .define('#', Items.STONE)
.define('E', ingredients.enderPearl()) .define('E', ingredients.enderPearl())
.unlockedBy("has_computer", inventoryChange(COMPUTER)) .unlockedBy("has_computer", inventoryChange(COMPUTER))
.save(add); .save(add);
ShapedRecipeBuilder ShapedRecipeBuilder
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.WIRELESS_MODEM_ADVANCED.get()) .shaped(RecipeCategory.REDSTONE, ModRegistry.Items.WIRELESS_MODEM_ADVANCED.get())
.pattern("###") .pattern("###")
.pattern("#E#") .pattern("#E#")
.pattern("###") .pattern("###")
.define('#', ingredients.goldIngot()) .define('#', ingredients.goldIngot())
.define('E', Items.ENDER_EYE) .define('E', Items.ENDER_EYE)
.unlockedBy("has_computer", inventoryChange(COMPUTER)) .unlockedBy("has_computer", inventoryChange(COMPUTER))
.unlockedBy("has_wireless", inventoryChange(ModRegistry.Blocks.WIRELESS_MODEM_NORMAL.get())) .unlockedBy("has_wireless", inventoryChange(ModRegistry.Items.WIRELESS_MODEM_NORMAL.get()))
.save(add); .save(add);
ShapelessSpecBuilder ShapelessSpecBuilder

View File

@ -5,7 +5,7 @@
package dan200.computercraft.data; package dan200.computercraft.data;
import dan200.computercraft.api.ComputerCraftTags; import dan200.computercraft.api.ComputerCraftTags;
import dan200.computercraft.impl.RegistryHelper; import dan200.computercraft.shared.util.RegistryHelper;
import dan200.computercraft.shared.ModRegistry; import dan200.computercraft.shared.ModRegistry;
import dan200.computercraft.shared.integration.ExternalModTags; import dan200.computercraft.shared.integration.ExternalModTags;
import net.minecraft.core.Registry; import net.minecraft.core.Registry;

View File

@ -5,45 +5,40 @@
package dan200.computercraft.data; package dan200.computercraft.data;
import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.ComputerCraftTags.Blocks; import dan200.computercraft.api.ComputerCraftTags;
import dan200.computercraft.api.turtle.ITurtleUpgrade; import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.api.turtle.TurtleUpgradeDataProvider;
import dan200.computercraft.shared.ModRegistry; import dan200.computercraft.shared.ModRegistry;
import dan200.computercraft.shared.turtle.upgrades.TurtleCraftingTable; import dan200.computercraft.shared.turtle.upgrades.TurtleCraftingTable;
import dan200.computercraft.shared.turtle.upgrades.TurtleModem; import dan200.computercraft.shared.turtle.upgrades.TurtleModem;
import dan200.computercraft.shared.turtle.upgrades.TurtleSpeaker; import dan200.computercraft.shared.turtle.upgrades.TurtleSpeaker;
import net.minecraft.data.PackOutput; import net.minecraft.data.worldgen.BootstrapContext;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items; import net.minecraft.world.item.Items;
import java.util.function.Consumer; import static dan200.computercraft.api.turtle.TurtleToolBuilder.tool;
class TurtleUpgradeProvider extends TurtleUpgradeDataProvider { class TurtleUpgradeProvider {
TurtleUpgradeProvider(PackOutput output) { public static void addUpgrades(BootstrapContext<ITurtleUpgrade> upgrades) {
super(output); upgrades.register(id("speaker"), new TurtleSpeaker(new ItemStack(ModRegistry.Items.SPEAKER.get())));
upgrades.register(vanilla("crafting_table"), new TurtleCraftingTable(new ItemStack(Items.CRAFTING_TABLE)));
upgrades.register(id("wireless_modem_normal"), new TurtleModem(new ItemStack(ModRegistry.Items.WIRELESS_MODEM_NORMAL.get()), false));
upgrades.register(id("wireless_modem_advanced"), new TurtleModem(new ItemStack(ModRegistry.Items.WIRELESS_MODEM_ADVANCED.get()), true));
tool(vanilla("diamond_axe").location(), Items.DIAMOND_AXE).damageMultiplier(6.0f).register(upgrades);
tool(vanilla("diamond_pickaxe"), Items.DIAMOND_PICKAXE).register(upgrades);
tool(vanilla("diamond_hoe"), Items.DIAMOND_HOE).breakable(ComputerCraftTags.Blocks.TURTLE_HOE_BREAKABLE).register(upgrades);
tool(vanilla("diamond_shovel"), Items.DIAMOND_SHOVEL).breakable(ComputerCraftTags.Blocks.TURTLE_SHOVEL_BREAKABLE).register(upgrades);
tool(vanilla("diamond_sword"), Items.DIAMOND_SWORD).breakable(ComputerCraftTags.Blocks.TURTLE_SWORD_BREAKABLE).damageMultiplier(9.0f).register(upgrades);
} }
@Override private static ResourceKey<ITurtleUpgrade> id(String id) {
protected void addUpgrades(Consumer<Upgrade<ITurtleUpgrade>> addUpgrade) { return ITurtleUpgrade.createKey(new ResourceLocation(ComputerCraftAPI.MOD_ID, id));
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"), 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) { private static ResourceKey<ITurtleUpgrade> vanilla(String id) {
return new ResourceLocation(ComputerCraftAPI.MOD_ID, id);
}
private static ResourceLocation vanilla(String id) {
// Naughty, please don't do this. Mostly here for some semblance of backwards compatibility. // Naughty, please don't do this. Mostly here for some semblance of backwards compatibility.
return new ResourceLocation("minecraft", id); return ITurtleUpgrade.createKey(new ResourceLocation("minecraft", id));
} }
} }

View File

@ -5,11 +5,10 @@
package dan200.computercraft.impl; package dan200.computercraft.impl;
import dan200.computercraft.api.pocket.IPocketUpgrade; import dan200.computercraft.api.pocket.IPocketUpgrade;
import dan200.computercraft.shared.ModRegistry;
public final class PocketUpgrades { public final class PocketUpgrades {
private static final UpgradeManager<IPocketUpgrade> registry = new UpgradeManager<>( private static final UpgradeManager<IPocketUpgrade> registry = new UpgradeManager<>(
IPocketUpgrade.typeRegistry(), ModRegistry.POCKET_UPGRADE, IPocketUpgrade::getType IPocketUpgrade.typeRegistry(), IPocketUpgrade.REGISTRY, IPocketUpgrade::getType
); );
private PocketUpgrades() { private PocketUpgrades() {

View File

@ -5,11 +5,10 @@
package dan200.computercraft.impl; package dan200.computercraft.impl;
import dan200.computercraft.api.turtle.ITurtleUpgrade; import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.shared.ModRegistry;
public final class TurtleUpgrades { public final class TurtleUpgrades {
private static final UpgradeManager<ITurtleUpgrade> registry = new UpgradeManager<>( private static final UpgradeManager<ITurtleUpgrade> registry = new UpgradeManager<>(
ITurtleUpgrade.typeRegistry(), ModRegistry.TURTLE_UPGRADE, ITurtleUpgrade::getType ITurtleUpgrade.typeRegistry(), ITurtleUpgrade.REGISTRY, ITurtleUpgrade::getType
); );
private TurtleUpgrades() { private TurtleUpgrades() {

View File

@ -18,7 +18,6 @@
import dan200.computercraft.api.upgrades.UpgradeType; import dan200.computercraft.api.upgrades.UpgradeType;
import dan200.computercraft.core.util.Colour; import dan200.computercraft.core.util.Colour;
import dan200.computercraft.impl.PocketUpgrades; import dan200.computercraft.impl.PocketUpgrades;
import dan200.computercraft.impl.RegistryHelper;
import dan200.computercraft.shared.command.UserLevel; import dan200.computercraft.shared.command.UserLevel;
import dan200.computercraft.shared.command.arguments.ComputerArgumentType; import dan200.computercraft.shared.command.arguments.ComputerArgumentType;
import dan200.computercraft.shared.command.arguments.RepeatArgumentType; import dan200.computercraft.shared.command.arguments.RepeatArgumentType;
@ -91,7 +90,6 @@
import net.minecraft.commands.synchronization.SingletonArgumentInfo; import net.minecraft.commands.synchronization.SingletonArgumentInfo;
import net.minecraft.core.Holder; import net.minecraft.core.Holder;
import net.minecraft.core.HolderLookup; import net.minecraft.core.HolderLookup;
import net.minecraft.core.Registry;
import net.minecraft.core.cauldron.CauldronInteraction; import net.minecraft.core.cauldron.CauldronInteraction;
import net.minecraft.core.component.DataComponentType; import net.minecraft.core.component.DataComponentType;
import net.minecraft.core.registries.Registries; import net.minecraft.core.registries.Registries;
@ -99,7 +97,6 @@
import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Component;
import net.minecraft.network.codec.ByteBufCodecs; import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec; import net.minecraft.network.codec.StreamCodec;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.flag.FeatureFlags; import net.minecraft.world.flag.FeatureFlags;
import net.minecraft.world.inventory.MenuType; import net.minecraft.world.inventory.MenuType;
@ -130,9 +127,6 @@ public final class ModRegistry {
private ModRegistry() { private ModRegistry() {
} }
public static final ResourceKey<Registry<ITurtleUpgrade>> TURTLE_UPGRADE = RegistryHelper.TURTLE_UPGRADE;
public static final ResourceKey<Registry<IPocketUpgrade>> POCKET_UPGRADE = RegistryHelper.POCKET_UPGRADE;
public static final class Blocks { public static final class Blocks {
static final RegistrationHelper<Block> REGISTRY = PlatformHelper.get().createRegistrationHelper(Registries.BLOCK); static final RegistrationHelper<Block> REGISTRY = PlatformHelper.get().createRegistrationHelper(Registries.BLOCK);
@ -602,7 +596,7 @@ public static void registerMainThread() {
private static void addTurtle(CreativeModeTab.Output out, TurtleItem turtle, HolderLookup.Provider registries) { private static void addTurtle(CreativeModeTab.Output out, TurtleItem turtle, HolderLookup.Provider registries) {
out.accept(new ItemStack(turtle)); out.accept(new ItemStack(turtle));
registries.lookupOrThrow(TURTLE_UPGRADE).listElements() registries.lookupOrThrow(ITurtleUpgrade.REGISTRY).listElements()
.filter(ModRegistry::isOurUpgrade) .filter(ModRegistry::isOurUpgrade)
.map(x -> DataComponentUtil.createStack(turtle, DataComponents.RIGHT_TURTLE_UPGRADE.get(), UpgradeData.ofDefault(x))) .map(x -> DataComponentUtil.createStack(turtle, DataComponents.RIGHT_TURTLE_UPGRADE.get(), UpgradeData.ofDefault(x)))
.forEach(out::accept); .forEach(out::accept);
@ -610,7 +604,7 @@ private static void addTurtle(CreativeModeTab.Output out, TurtleItem turtle, Hol
private static void addPocket(CreativeModeTab.Output out, PocketComputerItem pocket, HolderLookup.Provider registries) { private static void addPocket(CreativeModeTab.Output out, PocketComputerItem pocket, HolderLookup.Provider registries) {
out.accept(new ItemStack(pocket)); out.accept(new ItemStack(pocket));
registries.lookupOrThrow(POCKET_UPGRADE).listElements() registries.lookupOrThrow(IPocketUpgrade.REGISTRY).listElements()
.filter(ModRegistry::isOurUpgrade) .filter(ModRegistry::isOurUpgrade)
.map(x -> DataComponentUtil.createStack(pocket, DataComponents.POCKET_UPGRADE.get(), UpgradeData.ofDefault(x))).forEach(out::accept); .map(x -> DataComponentUtil.createStack(pocket, DataComponents.POCKET_UPGRADE.get(), UpgradeData.ofDefault(x))).forEach(out::accept);
} }

View File

@ -8,7 +8,7 @@
import com.mojang.brigadier.Message; import com.mojang.brigadier.Message;
import com.mojang.brigadier.arguments.ArgumentType; import com.mojang.brigadier.arguments.ArgumentType;
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
import dan200.computercraft.impl.RegistryHelper; import dan200.computercraft.shared.util.RegistryHelper;
import net.minecraft.commands.synchronization.ArgumentTypeInfo; import net.minecraft.commands.synchronization.ArgumentTypeInfo;
import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.FriendlyByteBuf;

View File

@ -4,7 +4,7 @@
package dan200.computercraft.shared.details; package dan200.computercraft.shared.details;
import dan200.computercraft.impl.RegistryHelper; import dan200.computercraft.shared.util.RegistryHelper;
import net.minecraft.core.Holder; import net.minecraft.core.Holder;
import net.minecraft.core.Registry; import net.minecraft.core.Registry;
import net.minecraft.tags.TagKey; import net.minecraft.tags.TagKey;

View File

@ -5,6 +5,8 @@
package dan200.computercraft.shared.integration; package dan200.computercraft.shared.integration;
import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.pocket.IPocketUpgrade;
import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.api.upgrades.UpgradeData; import dan200.computercraft.api.upgrades.UpgradeData;
import dan200.computercraft.shared.ModRegistry; import dan200.computercraft.shared.ModRegistry;
import dan200.computercraft.shared.pocket.items.PocketComputerItem; import dan200.computercraft.shared.pocket.items.PocketComputerItem;
@ -60,14 +62,14 @@ public static List<ItemStack> getExtraStacks(HolderLookup.Provider registries) {
List<ItemStack> upgradeItems = new ArrayList<>(); List<ItemStack> upgradeItems = new ArrayList<>();
for (var turtleSupplier : TURTLES) { for (var turtleSupplier : TURTLES) {
var turtle = turtleSupplier.get(); var turtle = turtleSupplier.get();
forEachRegistry(registries, ModRegistry.TURTLE_UPGRADE, upgrade -> forEachRegistry(registries, ITurtleUpgrade.REGISTRY, upgrade ->
upgradeItems.add(DataComponentUtil.createStack(turtle, ModRegistry.DataComponents.RIGHT_TURTLE_UPGRADE.get(), UpgradeData.ofDefault(upgrade))) upgradeItems.add(DataComponentUtil.createStack(turtle, ModRegistry.DataComponents.RIGHT_TURTLE_UPGRADE.get(), UpgradeData.ofDefault(upgrade)))
); );
} }
for (var pocketSupplier : POCKET_COMPUTERS) { for (var pocketSupplier : POCKET_COMPUTERS) {
var pocket = pocketSupplier.get(); var pocket = pocketSupplier.get();
forEachRegistry(registries, ModRegistry.POCKET_UPGRADE, upgrade -> forEachRegistry(registries, IPocketUpgrade.REGISTRY, upgrade ->
upgradeItems.add(DataComponentUtil.createStack(pocket, ModRegistry.DataComponents.POCKET_UPGRADE.get(), UpgradeData.ofDefault(upgrade))) upgradeItems.add(DataComponentUtil.createStack(pocket, ModRegistry.DataComponents.POCKET_UPGRADE.get(), UpgradeData.ofDefault(upgrade)))
); );
} }

View File

@ -57,7 +57,7 @@ private void setupCache() {
if (initialised) return; if (initialised) return;
initialised = true; initialised = true;
forEachRegistry(registries, ModRegistry.TURTLE_UPGRADE, holder -> { forEachRegistry(registries, ITurtleUpgrade.REGISTRY, holder -> {
var upgrade = holder.value(); var upgrade = holder.value();
var stack = upgrade.getCraftingItem(); var stack = upgrade.getCraftingItem();
if (stack.isEmpty()) return; if (stack.isEmpty()) return;
@ -67,7 +67,7 @@ private void setupCache() {
turtleUpgrades.add(info); turtleUpgrades.add(info);
}); });
forEachRegistry(registries, ModRegistry.POCKET_UPGRADE, holder -> { forEachRegistry(registries, IPocketUpgrade.REGISTRY, holder -> {
var upgrade = holder.value(); var upgrade = holder.value();
var stack = upgrade.getCraftingItem(); var stack = upgrade.getCraftingItem();
if (stack.isEmpty()) return; if (stack.isEmpty()) return;
@ -223,13 +223,13 @@ private static ItemStack turtleWith(ItemStack stack, @Nullable UpgradeData<ITurt
var newStack = stack.copyWithCount(1); var newStack = stack.copyWithCount(1);
newStack.set(ModRegistry.DataComponents.LEFT_TURTLE_UPGRADE.get(), left); newStack.set(ModRegistry.DataComponents.LEFT_TURTLE_UPGRADE.get(), left);
newStack.set(ModRegistry.DataComponents.RIGHT_TURTLE_UPGRADE.get(), right); newStack.set(ModRegistry.DataComponents.RIGHT_TURTLE_UPGRADE.get(), right);
return stack; return newStack;
} }
private static ItemStack pocketWith(ItemStack stack, @Nullable UpgradeData<IPocketUpgrade> back) { private static ItemStack pocketWith(ItemStack stack, @Nullable UpgradeData<IPocketUpgrade> back) {
var newStack = stack.copyWithCount(1); var newStack = stack.copyWithCount(1);
newStack.set(ModRegistry.DataComponents.POCKET_UPGRADE.get(), back); newStack.set(ModRegistry.DataComponents.POCKET_UPGRADE.get(), back);
return stack; return newStack;
} }
private T pocket(Ingredient upgrade, Ingredient pocketComputer, ItemStack result) { private T pocket(Ingredient upgrade, Ingredient pocketComputer, ItemStack result) {

View File

@ -8,7 +8,9 @@
import com.mojang.serialization.DataResult; import com.mojang.serialization.DataResult;
import com.mojang.serialization.codecs.RecordCodecBuilder; import com.mojang.serialization.codecs.RecordCodecBuilder;
import dan200.computercraft.core.terminal.Terminal; import dan200.computercraft.core.terminal.Terminal;
import dan200.computercraft.shared.ModRegistry;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import net.minecraft.core.component.DataComponentHolder;
import net.minecraft.network.codec.ByteBufCodecs; import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec; import net.minecraft.network.codec.StreamCodec;
@ -93,6 +95,10 @@ private static DataResult<List<Line>> validateLines(List<Line> lines) {
return DataResult.success(lines); return DataResult.success(lines);
} }
public static PrintoutData getOrEmpty(DataComponentHolder holder) {
return holder.getOrDefault(ModRegistry.DataComponents.PRINTOUT.get(), EMPTY);
}
/** /**
* Get the number of pages in this printout. * Get the number of pages in this printout.
* *

View File

@ -35,7 +35,7 @@ public PrintoutItem(Properties settings, Type type) {
@Override @Override
public void appendHoverText(ItemStack stack, TooltipContext context, List<Component> list, TooltipFlag options) { public void appendHoverText(ItemStack stack, TooltipContext context, List<Component> list, TooltipFlag options) {
var title = getTitle(stack); var title = PrintoutData.getOrEmpty(stack).title();
if (!title.isEmpty()) list.add(Component.literal(title)); if (!title.isEmpty()) list.add(Component.literal(title));
} }
@ -51,14 +51,4 @@ public InteractionResultHolder<ItemStack> use(Level world, Player player, Intera
public Type getType() { public Type getType() {
return type; return type;
} }
public static String getTitle(ItemStack stack) {
var nbt = stack.get(ModRegistry.DataComponents.PRINTOUT.get());
return nbt == null ? "" : nbt.title();
}
public static int getPageCount(ItemStack stack) {
var nbt = stack.get(ModRegistry.DataComponents.PRINTOUT.get());
return nbt == null ? 1 : nbt.pages();
}
} }

View File

@ -64,7 +64,7 @@ public ItemStack assemble(CraftingContainer inventory, HolderLookup.Provider reg
if (stack.getItem() instanceof PrintoutItem printout && printout.getType() != PrintoutItem.Type.BOOK) { if (stack.getItem() instanceof PrintoutItem printout && printout.getType() != PrintoutItem.Type.BOOK) {
if (printouts == null) printouts = new ItemStack[9]; if (printouts == null) printouts = new ItemStack[9];
printouts[numPrintouts] = stack; printouts[numPrintouts] = stack;
numPages += PrintoutItem.getPageCount(stack); numPages += PrintoutData.getOrEmpty(stack).pages();
numPrintouts++; numPrintouts++;
printoutFound = true; printoutFound = true;
} else if (stack.getItem() == Items.PAPER) { } else if (stack.getItem() == Items.PAPER) {
@ -104,7 +104,7 @@ public ItemStack assemble(CraftingContainer inventory, HolderLookup.Provider reg
} }
} }
var title = PrintoutItem.getTitle(printouts[0]); var title = PrintoutData.getOrEmpty(printouts[0]).title();
return DataComponentUtil.createStack( return DataComponentUtil.createStack(
leatherFound ? ModRegistry.Items.PRINTED_BOOK.get() : ModRegistry.Items.PRINTED_PAGES.get(), leatherFound ? ModRegistry.Items.PRINTED_BOOK.get() : ModRegistry.Items.PRINTED_PAGES.get(),

View File

@ -4,7 +4,7 @@
package dan200.computercraft.shared.peripheral.modem.wired; package dan200.computercraft.shared.peripheral.modem.wired;
import dan200.computercraft.impl.RegistryHelper; import dan200.computercraft.shared.util.RegistryHelper;
import dan200.computercraft.shared.ModRegistry; import dan200.computercraft.shared.ModRegistry;
import net.minecraft.Util; import net.minecraft.Util;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;

View File

@ -11,7 +11,6 @@
import dan200.computercraft.shared.container.BasicContainer; import dan200.computercraft.shared.container.BasicContainer;
import dan200.computercraft.shared.container.BasicWorldlyContainer; import dan200.computercraft.shared.container.BasicWorldlyContainer;
import dan200.computercraft.shared.media.items.PrintoutData; import dan200.computercraft.shared.media.items.PrintoutData;
import dan200.computercraft.shared.media.items.PrintoutItem;
import dan200.computercraft.shared.util.ColourUtils; import dan200.computercraft.shared.util.ColourUtils;
import dan200.computercraft.shared.util.DataComponentUtil; import dan200.computercraft.shared.util.DataComponentUtil;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
@ -172,8 +171,7 @@ static boolean isInk(ItemStack stack) {
static boolean isPaper(ItemStack stack) { static boolean isPaper(ItemStack stack) {
var item = stack.getItem(); var item = stack.getItem();
return item == Items.PAPER return item == Items.PAPER || item == ModRegistry.Items.PRINTED_PAGE.get();
|| (item instanceof PrintoutItem printout && printout.getType() == PrintoutItem.Type.PAGE);
} }
private boolean canInputPage() { private boolean canInputPage() {

View File

@ -4,11 +4,11 @@
package dan200.computercraft.shared.platform; package dan200.computercraft.shared.platform;
import com.google.gson.JsonObject;
import com.mojang.authlib.GameProfile; import com.mojang.authlib.GameProfile;
import com.mojang.brigadier.arguments.ArgumentType; import com.mojang.brigadier.arguments.ArgumentType;
import dan200.computercraft.api.network.wired.WiredElement; import dan200.computercraft.api.network.wired.WiredElement;
import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.impl.Services;
import dan200.computercraft.shared.config.ConfigFile; import dan200.computercraft.shared.config.ConfigFile;
import dan200.computercraft.shared.network.container.ContainerData; import dan200.computercraft.shared.network.container.ContainerData;
import dan200.computercraft.shared.util.InventoryUtil; import dan200.computercraft.shared.util.InventoryUtil;
@ -47,26 +47,19 @@
import java.util.function.Predicate; import java.util.function.Predicate;
/** /**
* This extends {@linkplain dan200.computercraft.impl.PlatformHelper the API's loader abstraction layer}, adding * Abstraction layer for Forge and Fabric. See implementations for more details.
* additional methods used by the actual mod.
*/ */
public interface PlatformHelper extends dan200.computercraft.impl.PlatformHelper { public interface PlatformHelper {
/** /**
* Get the current {@link PlatformHelper} instance. * Get the current {@link PlatformHelper} instance.
* *
* @return The current instance. * @return The current instance.
*/ */
static PlatformHelper get() { static PlatformHelper get() {
return (PlatformHelper) dan200.computercraft.impl.PlatformHelper.get(); var instance = Instance.INSTANCE;
return instance == null ? Services.raise(PlatformHelper.class, Instance.ERROR) : instance;
} }
/**
* Check if we're running in a development environment.
*
* @return If we're running in a development environment.
*/
boolean isDevelopmentEnvironment();
/** /**
* Create a new config builder. * Create a new config builder.
* *
@ -83,16 +76,6 @@ static PlatformHelper get() {
*/ */
<T> RegistrationHelper<T> createRegistrationHelper(ResourceKey<Registry<T>> registry); <T> RegistrationHelper<T> createRegistrationHelper(ResourceKey<Registry<T>> registry);
/**
* Determine if this resource should be loaded, based on platform-specific loot conditions.
* <p>
* This should only be called from the {@code apply} stage of a reload listener.
*
* @param object The root JSON object of this resource.
* @return If this resource should be loaded.
*/
boolean shouldLoadResource(JsonObject object);
/** /**
* Register a new argument type. * Register a new argument type.
* *
@ -328,4 +311,21 @@ default boolean isFakePlayer(ServerPlayer player) {
* @see ServerPlayerGameMode#useItemOn(ServerPlayer, Level, ItemStack, InteractionHand, BlockHitResult) * @see ServerPlayerGameMode#useItemOn(ServerPlayer, Level, ItemStack, InteractionHand, BlockHitResult)
*/ */
InteractionResult useOn(ServerPlayer player, ItemStack stack, BlockHitResult hit, Predicate<BlockState> canUseBlock); InteractionResult useOn(ServerPlayer player, ItemStack stack, BlockHitResult hit, Predicate<BlockState> canUseBlock);
final class Instance {
static final @Nullable PlatformHelper INSTANCE;
static final @Nullable Throwable ERROR;
static {
// We don't want class initialisation to fail here (as that results in confusing errors). Instead, capture
// the error and rethrow it when accessing. This should be JITted away in the common case.
var helper = Services.tryLoad(PlatformHelper.class);
INSTANCE = helper.instance();
ERROR = helper.error();
}
private Instance() {
}
}
} }

View File

@ -13,7 +13,6 @@
* @param redstone All {@link Items#REDSTONE} items. * @param redstone All {@link Items#REDSTONE} items.
* @param string All {@link Items#STRING} items. * @param string All {@link Items#STRING} items.
* @param leather All {@link Items#LEATHER} items. * @param leather All {@link Items#LEATHER} items.
* @param stone All {@link Items#STONE} items.
* @param glassPane All {@link Items#GLASS_PANE} items. * @param glassPane All {@link Items#GLASS_PANE} items.
* @param goldIngot All {@link Items#GOLD_INGOT} items. * @param goldIngot All {@link Items#GOLD_INGOT} items.
* @param goldBlock All {@link Items#GOLD_BLOCK} items. * @param goldBlock All {@link Items#GOLD_BLOCK} items.
@ -26,7 +25,6 @@ public record RecipeIngredients(
Ingredient redstone, Ingredient redstone,
Ingredient string, Ingredient string,
Ingredient leather, Ingredient leather,
Ingredient stone,
Ingredient glassPane, Ingredient glassPane,
Ingredient goldIngot, Ingredient goldIngot,
Ingredient goldBlock, Ingredient goldBlock,

View File

@ -6,7 +6,7 @@
import com.mojang.serialization.DataResult; import com.mojang.serialization.DataResult;
import com.mojang.serialization.MapCodec; import com.mojang.serialization.MapCodec;
import dan200.computercraft.impl.RegistryHelper; import dan200.computercraft.shared.util.RegistryHelper;
import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.StreamCodec; import net.minecraft.network.codec.StreamCodec;

View File

@ -2,11 +2,8 @@
// //
// SPDX-License-Identifier: MPL-2.0 // SPDX-License-Identifier: MPL-2.0
package dan200.computercraft.impl; package dan200.computercraft.shared.util;
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.Registry;
import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceKey;
@ -18,9 +15,6 @@
*/ */
@ApiStatus.Internal @ApiStatus.Internal
public final class RegistryHelper { public final class RegistryHelper {
public static final ResourceKey<Registry<ITurtleUpgrade>> TURTLE_UPGRADE = ResourceKey.createRegistryKey(new ResourceLocation(ComputerCraftAPI.MOD_ID, "turtle_upgrade"));
public static final ResourceKey<Registry<IPocketUpgrade>> POCKET_UPGRADE = ResourceKey.createRegistryKey(new ResourceLocation(ComputerCraftAPI.MOD_ID, "pocket_upgrade"));
private RegistryHelper() { private RegistryHelper() {
} }

View File

@ -6,7 +6,6 @@
import com.mojang.serialization.*; import com.mojang.serialization.*;
import com.mojang.serialization.codecs.KeyDispatchCodec; import com.mojang.serialization.codecs.KeyDispatchCodec;
import dan200.computercraft.impl.RegistryHelper;
import net.minecraft.core.Registry; import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceKey;

View File

@ -6,7 +6,6 @@
"uniforms": [ "uniforms": [
{ "name": "ModelViewMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] }, { "name": "ModelViewMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] },
{ "name": "ProjMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] }, { "name": "ProjMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] },
{ "name": "IViewRotMat", "type": "matrix3x3", "count": 9, "values": [ 1.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 1.0 ] },
{ "name": "ColorModulator", "type": "float", "count": 4, "values": [ 1.0, 1.0, 1.0, 1.0 ] }, { "name": "ColorModulator", "type": "float", "count": 4, "values": [ 1.0, 1.0, 1.0, 1.0 ] },
{ "name": "FogStart", "type": "float", "count": 1, "values": [ 0.0 ] }, { "name": "FogStart", "type": "float", "count": 1, "values": [ 0.0 ] },
{ "name": "FogEnd", "type": "float", "count": 1, "values": [ 1.0 ] }, { "name": "FogEnd", "type": "float", "count": 1, "values": [ 1.0 ] },

View File

@ -5,7 +5,6 @@
package dan200.computercraft; package dan200.computercraft;
import com.google.auto.service.AutoService; import com.google.auto.service.AutoService;
import com.google.gson.JsonObject;
import com.mojang.authlib.GameProfile; import com.mojang.authlib.GameProfile;
import com.mojang.brigadier.arguments.ArgumentType; import com.mojang.brigadier.arguments.ArgumentType;
import dan200.computercraft.api.network.wired.WiredElement; import dan200.computercraft.api.network.wired.WiredElement;
@ -48,13 +47,8 @@
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Predicate; import java.util.function.Predicate;
@AutoService({ PlatformHelper.class, dan200.computercraft.impl.PlatformHelper.class, ComputerCraftAPIService.class }) @AutoService({ PlatformHelper.class, ComputerCraftAPIService.class })
public class TestPlatformHelper extends AbstractComputerCraftAPI implements PlatformHelper { public class TestPlatformHelper extends AbstractComputerCraftAPI implements PlatformHelper {
@Override
public boolean isDevelopmentEnvironment() {
return true;
}
@Override @Override
public ConfigFile.Builder createConfigBuilder() { public ConfigFile.Builder createConfigBuilder() {
throw new UnsupportedOperationException("Cannot create config file inside tests"); throw new UnsupportedOperationException("Cannot create config file inside tests");
@ -65,16 +59,6 @@ public <T> RegistrationHelper<T> createRegistrationHelper(ResourceKey<Registry<T
throw new UnsupportedOperationException("Cannot query registry inside tests"); throw new UnsupportedOperationException("Cannot query registry inside tests");
} }
@Override
public boolean shouldLoadResource(JsonObject object) {
throw new UnsupportedOperationException("Cannot use resource conditions");
}
@Override
public void addRequiredModCondition(JsonObject object, String modId) {
throw new UnsupportedOperationException("Cannot use resource conditions");
}
@Override @Override
public <A extends ArgumentType<?>, T extends ArgumentTypeInfo.Template<A>, I extends ArgumentTypeInfo<A, T>> I registerArgumentTypeInfo(Class<A> klass, I info) { public <A extends ArgumentType<?>, T extends ArgumentTypeInfo.Template<A>, I extends ArgumentTypeInfo<A, T>> I registerArgumentTypeInfo(Class<A> klass, I info) {
throw new UnsupportedOperationException("Cannot register ArgumentTypeInfo inside tests"); throw new UnsupportedOperationException("Cannot register ArgumentTypeInfo inside tests");

View File

@ -17,7 +17,7 @@
import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.data.PrettyJsonWriter; import dan200.computercraft.data.PrettyJsonWriter;
import dan200.computercraft.gametest.core.TestHooks; import dan200.computercraft.gametest.core.TestHooks;
import dan200.computercraft.impl.RegistryHelper; import dan200.computercraft.shared.util.RegistryHelper;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.network.chat.Component; import net.minecraft.network.chat.Component;

View File

@ -4,7 +4,7 @@
package dan200.computercraft.export; package dan200.computercraft.export;
import dan200.computercraft.impl.RegistryHelper; import dan200.computercraft.shared.util.RegistryHelper;
import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.world.item.Item; import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;

View File

@ -233,7 +233,7 @@ fun Dig_consume_durability(helper: GameTestHelper) = helper.sequence {
val turtle = helper.getBlockEntity(BlockPos(2, 2, 2), ModRegistry.BlockEntities.TURTLE_NORMAL.get()).access val turtle = helper.getBlockEntity(BlockPos(2, 2, 2), ModRegistry.BlockEntities.TURTLE_NORMAL.get()).access
val upgrade = turtle.getUpgrade(TurtleSide.LEFT) val upgrade = turtle.getUpgrade(TurtleSide.LEFT)
assertEquals( assertEquals(
helper.level.registryAccess().registryOrThrow(ModRegistry.TURTLE_UPGRADE) helper.level.registryAccess().registryOrThrow(ITurtleUpgrade.REGISTRY)
.get(ResourceLocation("cctest", "wooden_pickaxe")), .get(ResourceLocation("cctest", "wooden_pickaxe")),
upgrade, upgrade,
"Upgrade is a wooden pickaxe", "Upgrade is a wooden pickaxe",
@ -262,7 +262,7 @@ fun Dig_breaks_tool(helper: GameTestHelper) = helper.sequence {
helper.assertUpgradeItem( helper.assertUpgradeItem(
ItemStack(Items.WOODEN_PICKAXE), ItemStack(Items.WOODEN_PICKAXE),
UpgradeData.ofDefault( UpgradeData.ofDefault(
helper.level.registryAccess().registryOrThrow(ModRegistry.TURTLE_UPGRADE) helper.level.registryAccess().registryOrThrow(ITurtleUpgrade.REGISTRY)
.getHolder(ResourceLocation("cctest", "wooden_pickaxe")).orElseThrow(), .getHolder(ResourceLocation("cctest", "wooden_pickaxe")).orElseThrow(),
), ),
) )
@ -283,7 +283,7 @@ fun Dig_enchanted_consume_durability(helper: GameTestHelper) = helper.sequence {
val turtle = helper.getBlockEntity(BlockPos(2, 2, 2), ModRegistry.BlockEntities.TURTLE_NORMAL.get()).access val turtle = helper.getBlockEntity(BlockPos(2, 2, 2), ModRegistry.BlockEntities.TURTLE_NORMAL.get()).access
val upgrade = turtle.getUpgrade(TurtleSide.LEFT) val upgrade = turtle.getUpgrade(TurtleSide.LEFT)
assertEquals( assertEquals(
helper.level.registryAccess().registryOrThrow(ModRegistry.TURTLE_UPGRADE) helper.level.registryAccess().registryOrThrow(ITurtleUpgrade.REGISTRY)
.get(ResourceLocation("cctest", "netherite_pickaxe")), .get(ResourceLocation("cctest", "netherite_pickaxe")),
upgrade, upgrade,
"Upgrade is a netherite pickaxe", "Upgrade is a netherite pickaxe",

View File

@ -5,8 +5,8 @@
package dan200.computercraft.gametest.api package dan200.computercraft.gametest.api
import dan200.computercraft.gametest.core.MinecraftExtensions import dan200.computercraft.gametest.core.MinecraftExtensions
import dan200.computercraft.impl.RegistryHelper
import dan200.computercraft.mixin.gametest.GameTestSequenceAccessor import dan200.computercraft.mixin.gametest.GameTestSequenceAccessor
import dan200.computercraft.shared.util.RegistryHelper
import net.minecraft.client.Minecraft import net.minecraft.client.Minecraft
import net.minecraft.client.Screenshot import net.minecraft.client.Screenshot
import net.minecraft.client.gui.screens.inventory.MenuAccess import net.minecraft.client.gui.screens.inventory.MenuAccess

View File

@ -6,10 +6,10 @@
import dan200.computercraft.api.peripheral.IPeripheral import dan200.computercraft.api.peripheral.IPeripheral
import dan200.computercraft.gametest.core.ManagedComputers import dan200.computercraft.gametest.core.ManagedComputers
import dan200.computercraft.impl.RegistryHelper
import dan200.computercraft.mixin.gametest.GameTestInfoAccessor import dan200.computercraft.mixin.gametest.GameTestInfoAccessor
import dan200.computercraft.mixin.gametest.GameTestSequenceAccessor import dan200.computercraft.mixin.gametest.GameTestSequenceAccessor
import dan200.computercraft.shared.platform.PlatformHelper import dan200.computercraft.shared.platform.PlatformHelper
import dan200.computercraft.shared.util.RegistryHelper
import dan200.computercraft.test.core.computer.LuaTaskContext import dan200.computercraft.test.core.computer.LuaTaskContext
import dan200.computercraft.test.shared.ItemStackMatcher.isStack import dan200.computercraft.test.shared.ItemStackMatcher.isStack
import net.minecraft.commands.arguments.blocks.BlockInput import net.minecraft.commands.arguments.blocks.BlockInput

View File

@ -7,15 +7,22 @@
import dan200.computercraft.api.turtle.TurtleSide; import dan200.computercraft.api.turtle.TurtleSide;
import dan200.computercraft.shared.ModRegistry; import dan200.computercraft.shared.ModRegistry;
import dan200.computercraft.shared.integration.RecipeModHelpers; import dan200.computercraft.shared.integration.RecipeModHelpers;
import dan200.computercraft.shared.platform.RegistryEntry;
import dan200.computercraft.shared.pocket.items.PocketComputerItem; import dan200.computercraft.shared.pocket.items.PocketComputerItem;
import dan200.computercraft.shared.turtle.items.TurtleItem; import dan200.computercraft.shared.turtle.items.TurtleItem;
import dev.architectury.event.EventResult; import dev.architectury.event.EventResult;
import me.shedaniel.rei.api.client.plugins.REIClientPlugin; import me.shedaniel.rei.api.client.plugins.REIClientPlugin;
import me.shedaniel.rei.api.client.registry.display.DisplayRegistry; import me.shedaniel.rei.api.client.registry.display.DisplayRegistry;
import me.shedaniel.rei.api.client.registry.entry.CollapsibleEntryRegistry;
import me.shedaniel.rei.api.client.registry.entry.EntryRegistry; import me.shedaniel.rei.api.client.registry.entry.EntryRegistry;
import me.shedaniel.rei.api.common.display.basic.BasicDisplay;
import me.shedaniel.rei.api.common.entry.comparison.ItemComparatorRegistry; import me.shedaniel.rei.api.common.entry.comparison.ItemComparatorRegistry;
import me.shedaniel.rei.api.common.entry.type.VanillaEntryTypes;
import me.shedaniel.rei.api.common.util.EntryStacks; import me.shedaniel.rei.api.common.util.EntryStacks;
import me.shedaniel.rei.plugin.common.BuiltinPlugin; import me.shedaniel.rei.plugin.common.BuiltinPlugin;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.component.DyedItemColor;
/** /**
* REI integration for ComputerCraft. * REI integration for ComputerCraft.
@ -40,15 +47,30 @@ public void registerItemComparators(ItemComparatorRegistry registry) {
var upgrade = PocketComputerItem.getUpgradeWithData(stack); var upgrade = PocketComputerItem.getUpgradeWithData(stack);
return upgrade == null ? 1 : upgrade.holder().key().location().hashCode(); return upgrade == null ? 1 : upgrade.holder().key().location().hashCode();
}, ModRegistry.Items.POCKET_COMPUTER_NORMAL.get(), ModRegistry.Items.POCKET_COMPUTER_ADVANCED.get()); }, ModRegistry.Items.POCKET_COMPUTER_NORMAL.get(), ModRegistry.Items.POCKET_COMPUTER_ADVANCED.get());
registry.register((context, stack) -> DyedItemColor.getOrDefault(stack, -1), ModRegistry.Items.DISK.get());
} }
@Override @Override
public void registerEntries(EntryRegistry registry) { public void registerEntries(EntryRegistry registry) {
for (var stack : RecipeModHelpers.getExtraStacks(RecipeModHelpers.getEmptyRegistryAccess())) { for (var stack : RecipeModHelpers.getExtraStacks(BasicDisplay.registryAccess())) {
registry.addEntry(EntryStacks.of(stack)); registry.addEntry(EntryStacks.of(stack));
} }
} }
@Override
public void registerCollapsibleEntries(CollapsibleEntryRegistry registry) {
addCollapsableGroup(registry, ModRegistry.Items.TURTLE_NORMAL);
addCollapsableGroup(registry, ModRegistry.Items.TURTLE_ADVANCED);
addCollapsableGroup(registry, ModRegistry.Items.POCKET_COMPUTER_NORMAL);
addCollapsableGroup(registry, ModRegistry.Items.POCKET_COMPUTER_ADVANCED);
}
private static void addCollapsableGroup(CollapsibleEntryRegistry registry, RegistryEntry<? extends Item> holder) {
var item = holder.get();
registry.group(holder.id(), new ItemStack(item).getDisplayName(), VanillaEntryTypes.ITEM, x -> x.getValue().is(item));
}
@Override @Override
public void registerDisplays(DisplayRegistry registry) { public void registerDisplays(DisplayRegistry registry) {
registry.registerDisplayGenerator(BuiltinPlugin.CRAFTING, new UpgradeDisplayGenerator()); registry.registerDisplayGenerator(BuiltinPlugin.CRAFTING, new UpgradeDisplayGenerator());

View File

@ -4,7 +4,6 @@
package dan200.computercraft.client.integration.rei; package dan200.computercraft.client.integration.rei;
import dan200.computercraft.shared.integration.RecipeModHelpers;
import dan200.computercraft.shared.integration.UpgradeRecipeGenerator; import dan200.computercraft.shared.integration.UpgradeRecipeGenerator;
import me.shedaniel.rei.api.client.registry.display.DynamicDisplayGenerator; import me.shedaniel.rei.api.client.registry.display.DynamicDisplayGenerator;
import me.shedaniel.rei.api.common.display.basic.BasicDisplay; import me.shedaniel.rei.api.common.display.basic.BasicDisplay;
@ -24,7 +23,7 @@
* Provides custom recipe and usage hints for pocket/turtle upgrades. * Provides custom recipe and usage hints for pocket/turtle upgrades.
*/ */
class UpgradeDisplayGenerator implements DynamicDisplayGenerator<DefaultCraftingDisplay<?>> { class UpgradeDisplayGenerator implements DynamicDisplayGenerator<DefaultCraftingDisplay<?>> {
private final UpgradeRecipeGenerator<DefaultCraftingDisplay<?>> resolver = new UpgradeRecipeGenerator<>(GeneratedShapedDisplay::new, RecipeModHelpers.getEmptyRegistryAccess()); private final UpgradeRecipeGenerator<DefaultCraftingDisplay<?>> resolver = new UpgradeRecipeGenerator<>(GeneratedShapedDisplay::new, BasicDisplay.registryAccess());
@Override @Override
public Optional<List<DefaultCraftingDisplay<?>>> getRecipeFor(EntryStack<?> entry) { public Optional<List<DefaultCraftingDisplay<?>>> getRecipeFor(EntryStack<?> entry) {

View File

@ -1,7 +1,7 @@
{ {
"type": "minecraft:crafting_shaped", "type": "minecraft:crafting_shaped",
"category": "redstone", "category": "redstone",
"key": {"#": {"tag": "c:stones"}, "E": {"item": "minecraft:ender_pearl"}}, "key": {"#": {"item": "minecraft:stone"}, "E": {"item": "minecraft:ender_pearl"}},
"pattern": ["###", "#E#", "###"], "pattern": ["###", "#E#", "###"],
"result": {"count": 1, "id": "computercraft:wireless_modem_normal"} "result": {"count": 1, "id": "computercraft:wireless_modem_normal"}
} }

View File

@ -5,41 +5,40 @@
package dan200.computercraft.data; package dan200.computercraft.data;
import com.mojang.serialization.Codec; 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.DataGeneratorEntrypoint;
import net.fabricmc.fabric.api.datagen.v1.FabricDataGenerator; import net.fabricmc.fabric.api.datagen.v1.FabricDataGenerator;
import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput; 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.FabricCodecDataProvider;
import net.fabricmc.fabric.api.datagen.v1.provider.FabricDynamicRegistryProvider;
import net.fabricmc.fabric.api.datagen.v1.provider.FabricTagProvider; import net.fabricmc.fabric.api.datagen.v1.provider.FabricTagProvider;
import net.fabricmc.fabric.api.event.registry.DynamicRegistries; import net.fabricmc.fabric.api.event.registry.DynamicRegistries;
import net.minecraft.core.*; import net.minecraft.core.Holder;
import net.minecraft.core.HolderLookup;
import net.minecraft.core.RegistrySetBuilder;
import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.data.DataProvider; import net.minecraft.data.DataProvider;
import net.minecraft.data.PackOutput; import net.minecraft.data.PackOutput;
import net.minecraft.data.tags.TagsProvider; import net.minecraft.data.tags.TagsProvider;
import net.minecraft.resources.RegistryDataLoader;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.packs.PackType; import net.minecraft.server.packs.PackType;
import net.minecraft.tags.TagKey; import net.minecraft.tags.TagKey;
import net.minecraft.world.item.Item; import net.minecraft.world.item.Item;
import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Block;
import java.util.Optional;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.stream.Stream;
public class FabricDataGenerators implements DataGeneratorEntrypoint { public class FabricDataGenerators implements DataGeneratorEntrypoint {
@Override @Override
public void onInitializeDataGenerator(FabricDataGenerator generator) { public void onInitializeDataGenerator(FabricDataGenerator generator) {
var pack = new PlatformGeneratorsImpl(generator.createPack()); var pack = new PlatformGeneratorsImpl(generator.createPack(), generator.getRegistries());
DataProviders.add(pack); DataProviders.add(pack);
} }
private record PlatformGeneratorsImpl(FabricDataGenerator.Pack generator) implements DataProviders.GeneratorSink { private record PlatformGeneratorsImpl(
FabricDataGenerator.Pack generator, CompletableFuture<HolderLookup.Provider> registries
) implements DataProviders.GeneratorSink {
public <T extends DataProvider> T addWithFabricOutput(FabricDataGenerator.Pack.Factory<T> factory) { public <T extends DataProvider> T addWithFabricOutput(FabricDataGenerator.Pack.Factory<T> factory) {
return generator.addProvider((FabricDataOutput p) -> new PrettyDataProvider<>(factory.create(p))).provider(); return generator.addProvider((FabricDataOutput p) -> new PrettyDataProvider<>(factory.create(p))).provider();
} }
@ -53,11 +52,6 @@ public <T extends DataProvider> T add(DataProvider.Factory<T> factory) {
return addWithFabricOutput(factory::create); return addWithFabricOutput(factory::create);
} }
@Override
public <T extends DataProvider> T add(BiFunction<PackOutput, CompletableFuture<HolderLookup.Provider>, T> factory) {
return addWithRegistries(factory::apply);
}
@Override @Override
public <T> void addFromCodec(String name, PackType type, String directory, Codec<T> codec, Consumer<BiConsumer<ResourceLocation, T>> output) { public <T> void addFromCodec(String name, PackType type, String directory, Codec<T> codec, Consumer<BiConsumer<ResourceLocation, T>> output) {
addWithRegistries((out, registries) -> { addWithRegistries((out, registries) -> {
@ -111,61 +105,24 @@ public void copy(TagKey<Block> block, TagKey<Item> item) {
} }
@Override @Override
public CompletableFuture<RegistrySetBuilder.PatchedRegistries> createPatchedRegistries(CompletableFuture<HolderLookup.Provider> registries, RegistrySetBuilder patch) { public void registries(CompletableFuture<RegistrySetBuilder.PatchedRegistries> registries) {
return registries.thenApply(oldRegistries -> { addWithFabricOutput(out -> new FabricDynamicRegistryProvider(out, registries.thenApply(RegistrySetBuilder.PatchedRegistries::patches)) {
var factory = new Cloner.Factory(); @Override
DynamicRegistries.getDynamicRegistries().forEach(registryData -> registryData.runWithArguments(factory::addCodec)); public String getName() {
return patch.buildPatch(RegistryAccess.fromRegistryOfRegistries(BuiltInRegistries.REGISTRY), new DynamicRegistryLookup(oldRegistries), factory); return "Registries";
}
@Override
protected void configure(HolderLookup.Provider registries, Entries entries) {
for (var reg : DynamicRegistries.getDynamicRegistries()) {
registries.lookupOrThrow(reg.key()).listElements().forEach(x -> register(entries, x));
}
}
private static <T> void register(Entries entries, Holder.Reference<T> reference) {
entries.add(reference.key(), reference.value());
}
}); });
} }
/**
* 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<ResourceKey<? extends Registry<?>>> listRegistries() {
return Stream.concat(
parent.listRegistries(),
DynamicRegistries.getDynamicRegistries().stream().map(RegistryDataLoader.RegistryData::key)
).distinct();
}
@Override
public <T> Optional<HolderLookup.RegistryLookup<T>> lookup(ResourceKey<? extends Registry<? extends T>> registryKey) {
return parent.lookup(registryKey).or(() -> Optional.of(new EmptyRegistry<>(registryKey)));
}
}
private record EmptyRegistry<T>(
ResourceKey<? extends Registry<? extends T>> key
) implements HolderLookup.RegistryLookup<T> {
@Override
public Lifecycle registryLifecycle() {
return Lifecycle.stable();
}
@Override
public Stream<Holder.Reference<T>> listElements() {
return Stream.empty();
}
@Override
public Stream<HolderSet.Named<T>> listTags() {
return Stream.empty();
}
@Override
public Optional<Holder.Reference<T>> get(ResourceKey<T> resourceKey) {
return Optional.empty();
}
@Override
public Optional<HolderSet.Named<T>> get(TagKey<T> tagKey) {
return Optional.empty();
}
}
} }
} }

View File

@ -8,6 +8,8 @@
import dan200.computercraft.api.detail.FabricDetailRegistries; import dan200.computercraft.api.detail.FabricDetailRegistries;
import dan200.computercraft.api.network.wired.WiredElementLookup; import dan200.computercraft.api.network.wired.WiredElementLookup;
import dan200.computercraft.api.peripheral.PeripheralLookup; import dan200.computercraft.api.peripheral.PeripheralLookup;
import dan200.computercraft.api.pocket.IPocketUpgrade;
import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.impl.Peripherals; import dan200.computercraft.impl.Peripherals;
import dan200.computercraft.impl.PocketUpgrades; import dan200.computercraft.impl.PocketUpgrades;
import dan200.computercraft.impl.TurtleUpgrades; import dan200.computercraft.impl.TurtleUpgrades;
@ -66,8 +68,8 @@ public static void init() {
FabricRegistryBuilder.createSimple(RecipeFunction.REGISTRY).attribute(RegistryAttribute.SYNCED).buildAndRegister(); FabricRegistryBuilder.createSimple(RecipeFunction.REGISTRY).attribute(RegistryAttribute.SYNCED).buildAndRegister();
DynamicRegistries.registerSynced(ModRegistry.TURTLE_UPGRADE, TurtleUpgrades.instance().upgradeCodec()); DynamicRegistries.registerSynced(ITurtleUpgrade.REGISTRY, TurtleUpgrades.instance().upgradeCodec());
DynamicRegistries.registerSynced(ModRegistry.POCKET_UPGRADE, PocketUpgrades.instance().upgradeCodec()); DynamicRegistries.registerSynced(IPocketUpgrade.REGISTRY, PocketUpgrades.instance().upgradeCodec());
ModRegistry.register(); ModRegistry.register();
ModRegistry.registerMainThread(); ModRegistry.registerMainThread();

View File

@ -5,11 +5,8 @@
package dan200.computercraft.shared.platform; package dan200.computercraft.shared.platform;
import com.google.auto.service.AutoService; import com.google.auto.service.AutoService;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.mojang.authlib.GameProfile; import com.mojang.authlib.GameProfile;
import com.mojang.brigadier.arguments.ArgumentType; import com.mojang.brigadier.arguments.ArgumentType;
import com.mojang.serialization.JsonOps;
import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.network.wired.WiredElement; import dan200.computercraft.api.network.wired.WiredElement;
import dan200.computercraft.api.network.wired.WiredElementLookup; import dan200.computercraft.api.network.wired.WiredElementLookup;
@ -27,14 +24,11 @@
import net.fabricmc.fabric.api.lookup.v1.block.BlockApiCache; import net.fabricmc.fabric.api.lookup.v1.block.BlockApiCache;
import net.fabricmc.fabric.api.lookup.v1.block.BlockApiLookup; import net.fabricmc.fabric.api.lookup.v1.block.BlockApiLookup;
import net.fabricmc.fabric.api.registry.FuelRegistry; import net.fabricmc.fabric.api.registry.FuelRegistry;
import net.fabricmc.fabric.api.resource.conditions.v1.ResourceCondition;
import net.fabricmc.fabric.api.resource.conditions.v1.ResourceConditions;
import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerFactory; import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerFactory;
import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerType; import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerType;
import net.fabricmc.fabric.api.tag.convention.v2.ConventionalItemTags; import net.fabricmc.fabric.api.tag.convention.v2.ConventionalItemTags;
import net.fabricmc.fabric.api.transfer.v1.item.InventoryStorage; import net.fabricmc.fabric.api.transfer.v1.item.InventoryStorage;
import net.fabricmc.fabric.api.transfer.v1.item.ItemStorage; import net.fabricmc.fabric.api.transfer.v1.item.ItemStorage;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.commands.synchronization.ArgumentTypeInfo; import net.minecraft.commands.synchronization.ArgumentTypeInfo;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
@ -49,7 +43,6 @@
import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayer;
import net.minecraft.tags.ItemTags; import net.minecraft.tags.ItemTags;
import net.minecraft.tags.TagKey; import net.minecraft.tags.TagKey;
import net.minecraft.util.GsonHelper;
import net.minecraft.world.Container; import net.minecraft.world.Container;
import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult; import net.minecraft.world.InteractionResult;
@ -79,13 +72,8 @@
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.function.Supplier; import java.util.function.Supplier;
@AutoService(dan200.computercraft.impl.PlatformHelper.class) @AutoService(PlatformHelper.class)
public class PlatformHelperImpl implements PlatformHelper { public class PlatformHelperImpl implements PlatformHelper {
@Override
public boolean isDevelopmentEnvironment() {
return FabricLoader.getInstance().isDevelopmentEnvironment();
}
@Override @Override
public ConfigFile.Builder createConfigBuilder() { public ConfigFile.Builder createConfigBuilder() {
return new FabricConfigFile.Builder(); return new FabricConfigFile.Builder();
@ -103,22 +91,6 @@ public <T> RegistrationHelper<T> createRegistrationHelper(ResourceKey<Registry<T
return new RegistrationHelperImpl<>(getRegistry(registry)); return new RegistrationHelperImpl<>(getRegistry(registry));
} }
@Override
public boolean shouldLoadResource(JsonObject object) {
return true; // Done by default in Fabric, so can be skipped
}
@Override
public void addRequiredModCondition(JsonObject object, String modId) {
var conditions = GsonHelper.getAsJsonArray(object, ResourceConditions.CONDITIONS_KEY, null);
if (conditions == null) {
conditions = new JsonArray();
object.add(ResourceConditions.CONDITIONS_KEY, conditions);
}
conditions.add(ResourceCondition.CODEC.encodeStart(JsonOps.INSTANCE, ResourceConditions.allModsLoaded(modId)).getOrThrow());
}
@Override @Override
public <A extends ArgumentType<?>, T extends ArgumentTypeInfo.Template<A>, I extends ArgumentTypeInfo<A, T>> I registerArgumentTypeInfo(Class<A> klass, I info) { public <A extends ArgumentType<?>, T extends ArgumentTypeInfo.Template<A>, I extends ArgumentTypeInfo<A, T>> I registerArgumentTypeInfo(Class<A> klass, I info) {
ArgumentTypeInfosAccessor.classMap().put(klass, info); ArgumentTypeInfosAccessor.classMap().put(klass, info);
@ -171,7 +143,6 @@ public RecipeIngredients getRecipeIngredients() {
Ingredient.of(ConventionalItemTags.REDSTONE_DUSTS), Ingredient.of(ConventionalItemTags.REDSTONE_DUSTS),
Ingredient.of(ConventionalItemTags.STRINGS), Ingredient.of(ConventionalItemTags.STRINGS),
Ingredient.of(Items.LEATHER), Ingredient.of(Items.LEATHER),
Ingredient.of(ConventionalItemTags.STONES),
Ingredient.of(ConventionalItemTags.GLASS_PANES), Ingredient.of(ConventionalItemTags.GLASS_PANES),
Ingredient.of(ConventionalItemTags.GOLD_INGOTS), Ingredient.of(ConventionalItemTags.GOLD_INGOTS),
Ingredient.of(ConventionalItemTags.STORAGE_BLOCKS_GOLD), Ingredient.of(ConventionalItemTags.STORAGE_BLOCKS_GOLD),

View File

@ -47,7 +47,7 @@
"depends": { "depends": {
"fabricloader": ">=0.15.10", "fabricloader": ">=0.15.10",
"fabric-api": ">=0.97.3", "fabric-api": ">=0.97.3",
"minecraft": ">=1.20.5 <1.20.7" "minecraft": "=1.20.6"
}, },
"accessWidener": "computercraft.accesswidener" "accessWidener": "computercraft.accesswidener"
} }

View File

@ -15,6 +15,11 @@ cct.inlineProject(":common-api")
dependencies { dependencies {
api(project(":core-api")) api(project(":core-api"))
// FIXME: This should be implementation (and in the common Forge config)
// but NeoGradle does weird things and we end up with two Forge deps on the
// classpath - https://github.com/neoforged/NeoGradle/issues/162.
compileOnly("net.neoforged:neoforge:${libs.versions.neoForge.get()}")
} }
tasks.javadoc { tasks.javadoc {

View File

@ -89,7 +89,7 @@ runs {
} }
val gameTestClient by registering { val gameTestClient by registering {
configure(runTypes.client) configure(runTypes.named("client"))
workingDirectory(file("run/testClient")) workingDirectory(file("run/testClient"))
configureForGameTest() configureForGameTest()
@ -121,6 +121,8 @@ dependencies {
compileOnly(libs.bundles.externalMods.forge.compile) compileOnly(libs.bundles.externalMods.forge.compile)
runtimeOnly(libs.bundles.externalMods.forge.runtime) { cct.exclude(this) } runtimeOnly(libs.bundles.externalMods.forge.runtime) { cct.exclude(this) }
implementation("net.neoforged:neoforge:${libs.versions.neoForge.get()}")
// Depend on our other projects. // Depend on our other projects.
api(commonClasses(project(":forge-api"))) { cct.exclude(this) } api(commonClasses(project(":forge-api"))) { cct.exclude(this) }
clientApi(clientClasses(project(":forge-api"))) { cct.exclude(this) } clientApi(clientClasses(project(":forge-api"))) { cct.exclude(this) }

View File

@ -1,6 +1,6 @@
{ {
"type": "computercraft:impostor_shapeless", "type": "computercraft:impostor_shapeless",
"category": "redstone", "category": "redstone",
"ingredients": [{"tag": "c:leather"}, {"item": "computercraft:printed_page"}, {"tag": "c:strings"}], "ingredients": [{"tag": "c:leathers"}, {"item": "computercraft:printed_page"}, {"tag": "c:strings"}],
"result": {"count": 1, "id": "computercraft:printed_book"} "result": {"count": 1, "id": "computercraft:printed_book"}
} }

View File

@ -1,7 +1,7 @@
{ {
"type": "minecraft:crafting_shaped", "type": "minecraft:crafting_shaped",
"category": "redstone", "category": "redstone",
"key": {"#": {"tag": "c:stones"}, "E": {"tag": "c:ender_pearls"}}, "key": {"#": {"item": "minecraft:stone"}, "E": {"tag": "c:ender_pearls"}},
"pattern": ["###", "#E#", "###"], "pattern": ["###", "#E#", "###"],
"result": {"count": 1, "id": "computercraft:wireless_modem_normal"} "result": {"count": 1, "id": "computercraft:wireless_modem_normal"}
} }

View File

@ -90,8 +90,8 @@ public static void registerRegistries(NewRegistryEvent event) {
@SubscribeEvent @SubscribeEvent
public static void registerDynamicRegistries(DataPackRegistryEvent.NewRegistry event) { public static void registerDynamicRegistries(DataPackRegistryEvent.NewRegistry event) {
event.dataPackRegistry(ModRegistry.TURTLE_UPGRADE, TurtleUpgrades.instance().upgradeCodec(), TurtleUpgrades.instance().upgradeCodec()); event.dataPackRegistry(ITurtleUpgrade.REGISTRY, TurtleUpgrades.instance().upgradeCodec(), TurtleUpgrades.instance().upgradeCodec());
event.dataPackRegistry(ModRegistry.POCKET_UPGRADE, PocketUpgrades.instance().upgradeCodec(), PocketUpgrades.instance().upgradeCodec()); event.dataPackRegistry(IPocketUpgrade.REGISTRY, PocketUpgrades.instance().upgradeCodec(), PocketUpgrades.instance().upgradeCodec());
} }
@SubscribeEvent @SubscribeEvent

View File

@ -7,6 +7,7 @@
import com.mojang.serialization.Codec; import com.mojang.serialization.Codec;
import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.api.ComputerCraftAPI;
import net.minecraft.core.HolderLookup; import net.minecraft.core.HolderLookup;
import net.minecraft.core.RegistrySetBuilder;
import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.data.DataGenerator; import net.minecraft.data.DataGenerator;
import net.minecraft.data.DataProvider; import net.minecraft.data.DataProvider;
@ -21,13 +22,13 @@
import net.neoforged.bus.api.SubscribeEvent; import net.neoforged.bus.api.SubscribeEvent;
import net.neoforged.fml.common.EventBusSubscriber; import net.neoforged.fml.common.EventBusSubscriber;
import net.neoforged.neoforge.common.data.BlockTagsProvider; import net.neoforged.neoforge.common.data.BlockTagsProvider;
import net.neoforged.neoforge.common.data.DatapackBuiltinEntriesProvider;
import net.neoforged.neoforge.common.data.ExistingFileHelper; import net.neoforged.neoforge.common.data.ExistingFileHelper;
import net.neoforged.neoforge.common.data.JsonCodecProvider; import net.neoforged.neoforge.common.data.JsonCodecProvider;
import net.neoforged.neoforge.data.event.GatherDataEvent; import net.neoforged.neoforge.data.event.GatherDataEvent;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer; import java.util.function.Consumer;
@EventBusSubscriber(bus = EventBusSubscriber.Bus.MOD) @EventBusSubscriber(bus = EventBusSubscriber.Bus.MOD)
@ -48,11 +49,6 @@ public <T extends DataProvider> T add(DataProvider.Factory<T> factory) {
return generator.addProvider(p -> new PrettyDataProvider<>(factory.create(p))).provider(); return generator.addProvider(p -> new PrettyDataProvider<>(factory.create(p))).provider();
} }
@Override
public <T extends DataProvider> T add(BiFunction<PackOutput, CompletableFuture<HolderLookup.Provider>, T> factory) {
return generator.addProvider(p -> new PrettyDataProvider<>(factory.apply(p, registries))).provider();
}
@Override @Override
public <T> void addFromCodec(String name, PackType type, String directory, Codec<T> codec, Consumer<BiConsumer<ResourceLocation, T>> output) { public <T> void addFromCodec(String name, PackType type, String directory, Codec<T> codec, Consumer<BiConsumer<ResourceLocation, T>> output) {
add(out -> { add(out -> {
@ -99,5 +95,10 @@ public void copy(TagKey<Block> block, TagKey<Item> item) {
} }
}); });
} }
@Override
public void registries(CompletableFuture<RegistrySetBuilder.PatchedRegistries> registries) {
add(out -> new DatapackBuiltinEntriesProvider(out, registries, null));
}
} }
} }

View File

@ -5,11 +5,8 @@
package dan200.computercraft.shared.platform; package dan200.computercraft.shared.platform;
import com.google.auto.service.AutoService; import com.google.auto.service.AutoService;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.mojang.authlib.GameProfile; import com.mojang.authlib.GameProfile;
import com.mojang.brigadier.arguments.ArgumentType; import com.mojang.brigadier.arguments.ArgumentType;
import com.mojang.serialization.JsonOps;
import dan200.computercraft.ComputerCraft; import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.network.wired.WiredElement; import dan200.computercraft.api.network.wired.WiredElement;
@ -32,7 +29,6 @@
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayer;
import net.minecraft.tags.TagKey; import net.minecraft.tags.TagKey;
import net.minecraft.util.GsonHelper;
import net.minecraft.world.Container; import net.minecraft.world.Container;
import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult; import net.minecraft.world.InteractionResult;
@ -54,16 +50,12 @@
import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
import net.neoforged.bus.api.Event; import net.neoforged.bus.api.Event;
import net.neoforged.fml.loading.FMLLoader;
import net.neoforged.neoforge.capabilities.BlockCapability; import net.neoforged.neoforge.capabilities.BlockCapability;
import net.neoforged.neoforge.capabilities.BlockCapabilityCache; import net.neoforged.neoforge.capabilities.BlockCapabilityCache;
import net.neoforged.neoforge.capabilities.Capabilities; import net.neoforged.neoforge.capabilities.Capabilities;
import net.neoforged.neoforge.common.CommonHooks; import net.neoforged.neoforge.common.CommonHooks;
import net.neoforged.neoforge.common.Tags; import net.neoforged.neoforge.common.Tags;
import net.neoforged.neoforge.common.ToolActions; import net.neoforged.neoforge.common.ToolActions;
import net.neoforged.neoforge.common.conditions.ConditionalOps;
import net.neoforged.neoforge.common.conditions.ICondition;
import net.neoforged.neoforge.common.conditions.ModLoadedCondition;
import net.neoforged.neoforge.common.extensions.IMenuTypeExtension; import net.neoforged.neoforge.common.extensions.IMenuTypeExtension;
import net.neoforged.neoforge.event.EventHooks; import net.neoforged.neoforge.event.EventHooks;
import net.neoforged.neoforge.items.wrapper.InvWrapper; import net.neoforged.neoforge.items.wrapper.InvWrapper;
@ -78,13 +70,8 @@
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.function.Supplier; import java.util.function.Supplier;
@AutoService(dan200.computercraft.impl.PlatformHelper.class) @AutoService(PlatformHelper.class)
public class PlatformHelperImpl implements PlatformHelper { public class PlatformHelperImpl implements PlatformHelper {
@Override
public boolean isDevelopmentEnvironment() {
return !FMLLoader.isProduction();
}
@Override @Override
public ConfigFile.Builder createConfigBuilder() { public ConfigFile.Builder createConfigBuilder() {
return new ForgeConfigFile.Builder(); return new ForgeConfigFile.Builder();
@ -95,23 +82,6 @@ public <T> RegistrationHelper<T> createRegistrationHelper(ResourceKey<Registry<T
return new RegistrationHelperImpl<>(DeferredRegister.create(registry, ComputerCraftAPI.MOD_ID)); return new RegistrationHelperImpl<>(DeferredRegister.create(registry, ComputerCraftAPI.MOD_ID));
} }
@Override
public boolean shouldLoadResource(JsonObject object) {
return ICondition.conditionsMatched(JsonOps.INSTANCE, object);
}
@Override
public void addRequiredModCondition(JsonObject object, String modId) {
// FIXME: Test this, though maybe this should be implemented a different way anyway?
var conditions = GsonHelper.getAsJsonArray(object, ConditionalOps.DEFAULT_CONDITIONS_KEY, null);
if (conditions == null) {
conditions = new JsonArray();
object.add(ConditionalOps.DEFAULT_CONDITIONS_KEY, conditions);
}
conditions.add(ICondition.CODEC.encodeStart(JsonOps.INSTANCE, new ModLoadedCondition(modId)).getOrThrow());
}
@Override @Override
public <A extends ArgumentType<?>, T extends ArgumentTypeInfo.Template<A>, I extends ArgumentTypeInfo<A, T>> I registerArgumentTypeInfo(Class<A> klass, I info) { public <A extends ArgumentType<?>, T extends ArgumentTypeInfo.Template<A>, I extends ArgumentTypeInfo<A, T>> I registerArgumentTypeInfo(Class<A> klass, I info) {
return ArgumentTypeInfos.registerByClass(klass, info); return ArgumentTypeInfos.registerByClass(klass, info);
@ -169,7 +139,6 @@ public RecipeIngredients getRecipeIngredients() {
Ingredient.of(Tags.Items.DUSTS_REDSTONE), Ingredient.of(Tags.Items.DUSTS_REDSTONE),
Ingredient.of(Tags.Items.STRINGS), Ingredient.of(Tags.Items.STRINGS),
Ingredient.of(Tags.Items.LEATHERS), Ingredient.of(Tags.Items.LEATHERS),
Ingredient.of(Tags.Items.STONES),
Ingredient.of(Tags.Items.GLASS_PANES), Ingredient.of(Tags.Items.GLASS_PANES),
Ingredient.of(Tags.Items.INGOTS_GOLD), Ingredient.of(Tags.Items.INGOTS_GOLD),
Ingredient.of(Tags.Items.STORAGE_BLOCKS_GOLD), Ingredient.of(Tags.Items.STORAGE_BLOCKS_GOLD),

View File

@ -26,7 +26,7 @@ CC: Tweaked is a fork of ComputerCraft, adding programmable computers, turtles a
[[dependencies.computercraft]] [[dependencies.computercraft]]
modId="neoforge" modId="neoforge"
type="required" type="required"
versionRange="[${neoVersion},20.6)" versionRange="[${neoVersion},20.7)"
ordering="NONE" ordering="NONE"
side="BOTH" side="BOTH"