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)
extensions.configure(CCTweakedExtension::class.java) {

View File

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

View File

@ -13,4 +13,4 @@ isUnstable=true
modVersion=1.110.3
# 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
# 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
fabric-api = "0.97.6+1.20.5"
fabric-api = "0.98.0+1.20.6"
fabric-loader = "0.15.10"
neoForge = "20.5.20-beta"
neoForge = "20.6.48-beta"
neoForgeSpi = "8.0.1"
mixin = "0.8.5"
parchment = "2024.04.14"
parchmentMc = "1.20.4"
yarn = "1.20.5+build.1"
parchment = "2024.05.01"
parchmentMc = "1.20.6"
yarn = "1.20.6+build.1"
# Core dependencies (these versions are tied to the version Minecraft uses)
fastutil = "8.5.12"
@ -36,14 +36,14 @@ kotlin-coroutines = "1.7.3"
nightConfig = "3.6.7"
# Minecraft mods
emi = "1.0.30+1.20.4"
fabricPermissions = "0.3.20230723"
emi = "1.1.5+1.20.6"
fabricPermissions = "0.3.1"
iris = "1.6.14+1.20.4"
jei = "17.3.0.48"
modmenu = "9.0.0"
moreRed = "4.0.0.4"
oculus = "1.2.5"
rei = "14.0.688"
rei = "15.0.728"
rubidium = "0.6.1"
sodium = "mc1.20-0.4.10"
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.
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;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.upgrades.UpgradeBase;
import dan200.computercraft.api.upgrades.UpgradeType;
import dan200.computercraft.impl.ComputerCraftAPIService;
import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.Level;
import javax.annotation.Nullable;
@ -21,11 +23,11 @@
* {@link UpgradeType} instance, which are then registered in a registry.
* <p>
* 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
* generators}.
* the upgrade automatically registered. It is recommended this is done via
* <a href="../upgrades/UpgradeType.html#datagen">data generators</a>.
*
* <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.
* static final DeferredRegister<UpgradeType<? extends IPocketUpgrade>> POCKET_UPGRADES = DeferredRegister.create(IPocketUpgrade.typeRegistry(), "my_mod");
*
@ -35,19 +37,19 @@
*
* // Then in your constructor
* POCKET_UPGRADES.register(bus);
* }</pre>
* }
* <p>
* 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}.
* <pre>{@code
* {@code data/<my_mod>/computercraft/pocket_upgrade/<my_upgrade_id>.json}.
* {@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 {
ResourceKey<Registry<IPocketUpgrade>> REGISTRY = ResourceKey.createRegistryKey(new ResourceLocation(ComputerCraftAPI.MOD_ID, "pocket_upgrade"));
/**
* 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;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.upgrades.UpgradeBase;
import dan200.computercraft.api.upgrades.UpgradeType;
@ -12,6 +13,7 @@
import net.minecraft.core.Registry;
import net.minecraft.core.component.DataComponentPatch;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import javax.annotation.Nullable;
@ -23,11 +25,11 @@
* {@link UpgradeType} instance, which are then registered in a registry.
* <p>
* 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
* generators}.
* the upgrade automatically registered. It is recommended this is done via
* <a href="../upgrades/UpgradeType.html#datagen">data generators</a>.
*
* <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.
* static final DeferredRegister<UpgradeType<? extends ITurtleUpgrade>> TURTLE_UPGRADES = DeferredRegister.create(ITurtleUpgrade.typeRegistry(), "my_mod");
*
@ -37,28 +39,38 @@
*
* // Then in your constructor
* TURTLE_UPGRADES.register(bus);
* }</pre>
* }
* <p>
* 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}}.
*
* <pre>{@code
* {@code data/<my_mod>/computercraft/turtle_upgrade/<my_upgrade_id>.json}.
* <p>
* {@snippet lang="json" :
* {
* "type": "my_mod:my_upgrade"
* }
* }</pre>
* <p>
* {@link TurtleUpgradeDataProvider} provides a data provider to aid with generating these JSON files.
* }
* <p>
* Finally, we need to register a model for our upgrade, see
* {@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 {
/**
* 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.
*

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.
*
* @see TurtleUpgradeDataProvider.ToolBuilder#consumeDurability(TurtleToolDurability)
* @see TurtleToolBuilder#consumeDurability(TurtleToolDurability)
*/
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 dan200.computercraft.api.pocket.IPocketUpgrade;
import dan200.computercraft.api.pocket.PocketUpgradeDataProvider;
import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.api.turtle.TurtleUpgradeDataProvider;
import dan200.computercraft.impl.upgrades.UpgradeTypeImpl;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.data.registries.RegistryPatchGenerator;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.Recipe;
import net.minecraft.world.level.storage.loot.functions.LootItemFunction;
@ -33,8 +32,35 @@
* {@link IPocketUpgrade#typeRegistry()}).
* <p>
* In order to register the actual upgrade, a JSON file referencing your upgrade type should be added to a datapack. It
* is recommended to do this via the data generators (see {@link TurtleUpgradeDataProvider} and
* {@link PocketUpgradeDataProvider}).
* is recommended to do this via the data generators.
*
* <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.
* @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.shared.ModRegistry;
import dan200.computercraft.shared.integration.RecipeModHelpers;
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
import dan200.computercraft.shared.turtle.items.TurtleItem;
import dev.emi.emi.api.EmiEntrypoint;
import dev.emi.emi.api.EmiPlugin;
import dev.emi.emi.api.EmiRegistry;
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 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_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)

View File

@ -11,14 +11,12 @@
import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.api.turtle.TurtleSide;
import dan200.computercraft.api.upgrades.UpgradeType;
import dan200.computercraft.impl.RegistryHelper;
import dan200.computercraft.impl.UpgradeManager;
import dan200.computercraft.shared.util.RegistryHelper;
import net.minecraft.client.Minecraft;
import net.minecraft.core.component.DataComponentPatch;
import net.minecraft.resources.ResourceLocation;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
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 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() {
}
@ -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) {
@SuppressWarnings("unchecked")
var modeller = (TurtleUpgradeModeller<ITurtleUpgrade>) modelCache.computeIfAbsent(upgrade, TurtleUpgradeModellers::getModeller);
return modeller.getModel(upgrade, access, side, access.getUpgradeData(side));
return getModeller(upgrade).getModel(upgrade, access, side, access.getUpgradeData(side));
}
public static TransformedModel getModel(ITurtleUpgrade upgrade, DataComponentPatch data, TurtleSide side) {
@SuppressWarnings("unchecked")
var modeller = (TurtleUpgradeModeller<ITurtleUpgrade>) modelCache.computeIfAbsent(upgrade, TurtleUpgradeModellers::getModeller);
return modeller.getModel(upgrade, null, side, data);
return getModeller(upgrade).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());
return modeller == null ? NULL_TURTLE_MODELLER : modeller;
return (TurtleUpgradeModeller<T>) (modeller == null ? NULL_TURTLE_MODELLER : modeller);
}
public static Stream<ResourceLocation> getDependencies() {

View File

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

View File

@ -1,7 +1,7 @@
{
"type": "minecraft:crafting_shaped",
"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#"],
"result": {"count": 1, "id": "computercraft:computer_normal"}
}

View File

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

View File

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

View File

@ -1,7 +1,11 @@
{
"type": "minecraft:crafting_shaped",
"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#"],
"result": {"count": 1, "id": "computercraft:pocket_computer_normal"}
}

View File

@ -1,7 +1,7 @@
{
"type": "minecraft:crafting_shaped",
"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#"],
"result": {"count": 1, "id": "computercraft:printer"}
}

View File

@ -1,7 +1,11 @@
{
"type": "minecraft:crafting_shaped",
"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#"],
"result": {"count": 1, "id": "computercraft:speaker"}
}

View File

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

View File

@ -5,7 +5,9 @@
package dan200.computercraft.data;
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.RegistrySetBuilder;
import net.minecraft.data.DataProvider;
@ -20,7 +22,6 @@
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
/**
@ -32,25 +33,25 @@ private DataProviders() {
}
public static void add(GeneratorSink generator) {
var turtleUpgrades = generator.add(TurtleUpgradeProvider::new);
var pocketUpgrades = generator.add(PocketUpgradeProvider::new);
var fullRegistryPatch = RegistryPatchGenerator.createLookup(
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) -> {
var builder = new RegistrySetBuilder();
builder.add(ModRegistry.TURTLE_UPGRADE, bs -> turtleUpgrades.getGeneratedUpgrades().forEach(bs::register));
builder.add(ModRegistry.POCKET_UPGRADE, bs -> pocketUpgrades.getGeneratedUpgrades().forEach(bs::register));
return new RecipeProvider(out, generator.createPatchedRegistries(registries, builder).thenApply(RegistrySetBuilder.PatchedRegistries::full));
});
generator.registries(fullRegistryPatch);
generator.add(out -> new RecipeProvider(out, fullRegistries));
var blockTags = generator.blockTags(TagProvider::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 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
// and invoke that.
@ -63,9 +64,9 @@ public static void add(GeneratorSink generator) {
}
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);
@ -74,16 +75,10 @@ public interface GeneratorSink {
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 patch The new registries to apply.
* @return The built registries.
* @param registries The patched registries to write.
*/
default CompletableFuture<RegistrySetBuilder.PatchedRegistries> createPatchedRegistries(
CompletableFuture<HolderLookup.Provider> registries, RegistrySetBuilder patch
) {
return RegistryPatchGenerator.createLookup(registries, patch);
}
void registries(CompletableFuture<RegistrySetBuilder.PatchedRegistries> registries);
}
}

View File

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

View File

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

View File

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

View File

@ -8,11 +8,13 @@
import com.mojang.authlib.GameProfile;
import com.mojang.serialization.JsonOps;
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.core.util.Colour;
import dan200.computercraft.data.recipe.ShapedSpecBuilder;
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.common.ClearColourRecipe;
import dan200.computercraft.shared.common.ColourableRecipe;
@ -54,14 +56,12 @@
import net.minecraft.world.item.crafting.Ingredient;
import net.minecraft.world.item.crafting.Recipe;
import net.minecraft.world.level.ItemLike;
import net.minecraft.world.level.block.Blocks;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.function.Consumer;
import java.util.stream.Stream;
import static dan200.computercraft.api.ComputerCraftTags.Items.COMPUTER;
import static dan200.computercraft.api.ComputerCraftTags.Items.WIRED_MODEM;
@ -119,7 +119,7 @@ private void diskColours(RecipeOutput output) {
.requires(Items.PAPER)
.requires(DyeItem.byColor(ofColour(colour)))
.group("computercraft:disk")
.unlockedBy("has_drive", inventoryChange(ModRegistry.Blocks.DISK_DRIVE.get()))
.unlockedBy("has_drive", inventoryChange(ModRegistry.Items.DISK_DRIVE.get()))
.build(ImpostorShapelessRecipe::new)
.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()) {
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();
ShapedSpecBuilder
.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()) {
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();
ShapedSpecBuilder
.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("#R#")
.pattern(" # ")
.define('#', ingredients.stone())
.define('#', Items.STONE)
.define('R', ingredients.redstone())
.unlockedBy("has_computer", inventoryChange(COMPUTER))
.unlockedBy("has_modem", inventoryChange(WIRED_MODEM))
.save(add);
ShapedRecipeBuilder
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.COMPUTER_NORMAL.get())
.shaped(RecipeCategory.REDSTONE, ModRegistry.Items.COMPUTER_NORMAL.get())
.pattern("###")
.pattern("#R#")
.pattern("#G#")
.define('#', ingredients.stone())
.define('#', Items.STONE)
.define('R', ingredients.redstone())
.define('G', ingredients.glassPane())
.unlockedBy("has_redstone", inventoryChange(itemPredicate(ingredients.redstone())))
.save(add);
ShapedRecipeBuilder
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.COMPUTER_ADVANCED.get())
.shaped(RecipeCategory.REDSTONE, ModRegistry.Items.COMPUTER_ADVANCED.get())
.pattern("###")
.pattern("#R#")
.pattern("#G#")
@ -278,18 +278,18 @@ private void basicRecipes(RecipeOutput add) {
.save(add, new ResourceLocation(ComputerCraftAPI.MOD_ID, "computer_advanced_upgrade"));
ShapedRecipeBuilder
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.COMPUTER_COMMAND.get())
.shaped(RecipeCategory.REDSTONE, ModRegistry.Items.COMPUTER_COMMAND.get())
.pattern("###")
.pattern("#R#")
.pattern("#G#")
.define('#', ingredients.goldIngot())
.define('R', Blocks.COMMAND_BLOCK)
.define('R', Items.COMMAND_BLOCK)
.define('G', ingredients.glassPane())
.unlockedBy("has_components", inventoryChange(Blocks.COMMAND_BLOCK))
.unlockedBy("has_components", inventoryChange(Items.COMMAND_BLOCK))
.save(add);
ShapedSpecBuilder
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.TURTLE_NORMAL.get())
.shaped(RecipeCategory.REDSTONE, ModRegistry.Items.TURTLE_NORMAL.get())
.pattern("###")
.pattern("#C#")
.pattern("#I#")
@ -301,7 +301,7 @@ private void basicRecipes(RecipeOutput add) {
.save(add);
ShapedSpecBuilder
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.TURTLE_ADVANCED.get())
.shaped(RecipeCategory.REDSTONE, ModRegistry.Items.TURTLE_ADVANCED.get())
.pattern("###")
.pattern("#C#")
.pattern("#I#")
@ -313,7 +313,7 @@ private void basicRecipes(RecipeOutput add) {
.save(add);
ShapedSpecBuilder
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.TURTLE_ADVANCED.get())
.shaped(RecipeCategory.REDSTONE, ModRegistry.Items.TURTLE_ADVANCED.get())
.pattern("###")
.pattern("#C#")
.pattern(" B ")
@ -325,27 +325,27 @@ private void basicRecipes(RecipeOutput add) {
.save(add, new ResourceLocation(ComputerCraftAPI.MOD_ID, "turtle_advanced_upgrade"));
ShapedRecipeBuilder
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.DISK_DRIVE.get())
.shaped(RecipeCategory.REDSTONE, ModRegistry.Items.DISK_DRIVE.get())
.pattern("###")
.pattern("#R#")
.pattern("#R#")
.define('#', ingredients.stone())
.define('#', Items.STONE)
.define('R', ingredients.redstone())
.unlockedBy("has_computer", inventoryChange(COMPUTER))
.save(add);
ShapedRecipeBuilder
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.MONITOR_NORMAL.get())
.shaped(RecipeCategory.REDSTONE, ModRegistry.Items.MONITOR_NORMAL.get())
.pattern("###")
.pattern("#G#")
.pattern("###")
.define('#', ingredients.stone())
.define('#', Items.STONE)
.define('G', ingredients.glassPane())
.unlockedBy("has_computer", inventoryChange(COMPUTER))
.save(add);
ShapedRecipeBuilder
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.MONITOR_ADVANCED.get(), 4)
.shaped(RecipeCategory.REDSTONE, ModRegistry.Items.MONITOR_ADVANCED.get(), 4)
.pattern("###")
.pattern("#G#")
.pattern("###")
@ -359,7 +359,7 @@ private void basicRecipes(RecipeOutput add) {
.pattern("###")
.pattern("#A#")
.pattern("#G#")
.define('#', ingredients.stone())
.define('#', Items.STONE)
.define('A', Items.GOLDEN_APPLE)
.define('G', ingredients.glassPane())
.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"));
ShapedRecipeBuilder
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.PRINTER.get())
.shaped(RecipeCategory.REDSTONE, ModRegistry.Items.PRINTER.get())
.pattern("###")
.pattern("#R#")
.pattern("#D#")
.define('#', ingredients.stone())
.define('#', Items.STONE)
.define('R', ingredients.redstone())
.define('D', ingredients.dye())
.unlockedBy("has_computer", inventoryChange(COMPUTER))
.save(add);
ShapedRecipeBuilder
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.SPEAKER.get())
.shaped(RecipeCategory.REDSTONE, ModRegistry.Items.SPEAKER.get())
.pattern("###")
.pattern("#N#")
.pattern("#R#")
.define('#', ingredients.stone())
.define('N', Blocks.NOTE_BLOCK)
.define('#', Items.STONE)
.define('N', Items.NOTE_BLOCK)
.define('R', ingredients.redstone())
.unlockedBy("has_computer", inventoryChange(COMPUTER))
.save(add);
@ -416,42 +416,42 @@ private void basicRecipes(RecipeOutput add) {
.pattern("###")
.pattern("#R#")
.pattern("###")
.define('#', ingredients.stone())
.define('#', Items.STONE)
.define('R', ingredients.redstone())
.unlockedBy("has_computer", inventoryChange(COMPUTER))
.unlockedBy("has_cable", inventoryChange(ModRegistry.Items.CABLE.get()))
.save(add);
ShapelessRecipeBuilder
.shapeless(RecipeCategory.REDSTONE, ModRegistry.Blocks.WIRED_MODEM_FULL.get())
.shapeless(RecipeCategory.REDSTONE, ModRegistry.Items.WIRED_MODEM_FULL.get())
.requires(ModRegistry.Items.WIRED_MODEM.get())
.unlockedBy("has_modem", inventoryChange(WIRED_MODEM))
.save(add, new ResourceLocation(ComputerCraftAPI.MOD_ID, "wired_modem_full_from"));
ShapelessRecipeBuilder
.shapeless(RecipeCategory.REDSTONE, ModRegistry.Items.WIRED_MODEM.get())
.requires(ModRegistry.Blocks.WIRED_MODEM_FULL.get())
.requires(ModRegistry.Items.WIRED_MODEM_FULL.get())
.unlockedBy("has_modem", inventoryChange(WIRED_MODEM))
.save(add, new ResourceLocation(ComputerCraftAPI.MOD_ID, "wired_modem_full_to"));
ShapedRecipeBuilder
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.WIRELESS_MODEM_NORMAL.get())
.shaped(RecipeCategory.REDSTONE, ModRegistry.Items.WIRELESS_MODEM_NORMAL.get())
.pattern("###")
.pattern("#E#")
.pattern("###")
.define('#', ingredients.stone())
.define('#', Items.STONE)
.define('E', ingredients.enderPearl())
.unlockedBy("has_computer", inventoryChange(COMPUTER))
.save(add);
ShapedRecipeBuilder
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.WIRELESS_MODEM_ADVANCED.get())
.shaped(RecipeCategory.REDSTONE, ModRegistry.Items.WIRELESS_MODEM_ADVANCED.get())
.pattern("###")
.pattern("#E#")
.pattern("###")
.define('#', ingredients.goldIngot())
.define('E', Items.ENDER_EYE)
.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);
ShapelessSpecBuilder

View File

@ -5,7 +5,7 @@
package dan200.computercraft.data;
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.integration.ExternalModTags;
import net.minecraft.core.Registry;

View File

@ -5,45 +5,40 @@
package dan200.computercraft.data;
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.TurtleUpgradeDataProvider;
import dan200.computercraft.shared.ModRegistry;
import dan200.computercraft.shared.turtle.upgrades.TurtleCraftingTable;
import dan200.computercraft.shared.turtle.upgrades.TurtleModem;
import dan200.computercraft.shared.turtle.upgrades.TurtleSpeaker;
import net.minecraft.data.PackOutput;
import net.minecraft.data.worldgen.BootstrapContext;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import java.util.function.Consumer;
import static dan200.computercraft.api.turtle.TurtleToolBuilder.tool;
class TurtleUpgradeProvider extends TurtleUpgradeDataProvider {
TurtleUpgradeProvider(PackOutput output) {
super(output);
class TurtleUpgradeProvider {
public static void addUpgrades(BootstrapContext<ITurtleUpgrade> upgrades) {
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
protected void addUpgrades(Consumer<Upgrade<ITurtleUpgrade>> addUpgrade) {
upgrade(id("speaker"), new TurtleSpeaker(new ItemStack(ModRegistry.Items.SPEAKER.get()))).add(addUpgrade);
upgrade(vanilla("crafting_table"), new TurtleCraftingTable(new ItemStack(Items.CRAFTING_TABLE))).add(addUpgrade);
upgrade(id("wireless_modem_normal"), new TurtleModem(new ItemStack(ModRegistry.Items.WIRELESS_MODEM_NORMAL.get()), false)).add(addUpgrade);
upgrade(id("wireless_modem_advanced"), new TurtleModem(new ItemStack(ModRegistry.Items.WIRELESS_MODEM_ADVANCED.get()), true)).add(addUpgrade);
tool(vanilla("diamond_axe"), 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 ResourceKey<ITurtleUpgrade> id(String id) {
return ITurtleUpgrade.createKey(new ResourceLocation(ComputerCraftAPI.MOD_ID, id));
}
private static ResourceLocation id(String id) {
return new ResourceLocation(ComputerCraftAPI.MOD_ID, id);
}
private static ResourceLocation vanilla(String id) {
private static ResourceKey<ITurtleUpgrade> vanilla(String id) {
// 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;
import dan200.computercraft.api.pocket.IPocketUpgrade;
import dan200.computercraft.shared.ModRegistry;
public final class PocketUpgrades {
private static final UpgradeManager<IPocketUpgrade> registry = new UpgradeManager<>(
IPocketUpgrade.typeRegistry(), ModRegistry.POCKET_UPGRADE, IPocketUpgrade::getType
IPocketUpgrade.typeRegistry(), IPocketUpgrade.REGISTRY, IPocketUpgrade::getType
);
private PocketUpgrades() {

View File

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

View File

@ -18,7 +18,6 @@
import dan200.computercraft.api.upgrades.UpgradeType;
import dan200.computercraft.core.util.Colour;
import dan200.computercraft.impl.PocketUpgrades;
import dan200.computercraft.impl.RegistryHelper;
import dan200.computercraft.shared.command.UserLevel;
import dan200.computercraft.shared.command.arguments.ComputerArgumentType;
import dan200.computercraft.shared.command.arguments.RepeatArgumentType;
@ -91,7 +90,6 @@
import net.minecraft.commands.synchronization.SingletonArgumentInfo;
import net.minecraft.core.Holder;
import net.minecraft.core.HolderLookup;
import net.minecraft.core.Registry;
import net.minecraft.core.cauldron.CauldronInteraction;
import net.minecraft.core.component.DataComponentType;
import net.minecraft.core.registries.Registries;
@ -99,7 +97,6 @@
import net.minecraft.network.chat.Component;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.flag.FeatureFlags;
import net.minecraft.world.inventory.MenuType;
@ -130,9 +127,6 @@ public final class 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 {
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) {
out.accept(new ItemStack(turtle));
registries.lookupOrThrow(TURTLE_UPGRADE).listElements()
registries.lookupOrThrow(ITurtleUpgrade.REGISTRY).listElements()
.filter(ModRegistry::isOurUpgrade)
.map(x -> DataComponentUtil.createStack(turtle, DataComponents.RIGHT_TURTLE_UPGRADE.get(), UpgradeData.ofDefault(x)))
.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) {
out.accept(new ItemStack(pocket));
registries.lookupOrThrow(POCKET_UPGRADE).listElements()
registries.lookupOrThrow(IPocketUpgrade.REGISTRY).listElements()
.filter(ModRegistry::isOurUpgrade)
.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.arguments.ArgumentType;
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.core.registries.BuiltInRegistries;
import net.minecraft.network.FriendlyByteBuf;

View File

@ -4,7 +4,7 @@
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.Registry;
import net.minecraft.tags.TagKey;

View File

@ -5,6 +5,8 @@
package dan200.computercraft.shared.integration;
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.shared.ModRegistry;
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
@ -60,14 +62,14 @@ public static List<ItemStack> getExtraStacks(HolderLookup.Provider registries) {
List<ItemStack> upgradeItems = new ArrayList<>();
for (var turtleSupplier : TURTLES) {
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)))
);
}
for (var pocketSupplier : POCKET_COMPUTERS) {
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)))
);
}

View File

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

View File

@ -8,7 +8,9 @@
import com.mojang.serialization.DataResult;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import dan200.computercraft.core.terminal.Terminal;
import dan200.computercraft.shared.ModRegistry;
import io.netty.buffer.ByteBuf;
import net.minecraft.core.component.DataComponentHolder;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
@ -93,6 +95,10 @@ private static DataResult<List<Line>> validateLines(List<Line> 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.
*

View File

@ -35,7 +35,7 @@ public PrintoutItem(Properties settings, Type type) {
@Override
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));
}
@ -51,14 +51,4 @@ public InteractionResultHolder<ItemStack> use(Level world, Player player, Intera
public Type getType() {
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 (printouts == null) printouts = new ItemStack[9];
printouts[numPrintouts] = stack;
numPages += PrintoutItem.getPageCount(stack);
numPages += PrintoutData.getOrEmpty(stack).pages();
numPrintouts++;
printoutFound = true;
} 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(
leatherFound ? ModRegistry.Items.PRINTED_BOOK.get() : ModRegistry.Items.PRINTED_PAGES.get(),

View File

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

View File

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

View File

@ -4,11 +4,11 @@
package dan200.computercraft.shared.platform;
import com.google.gson.JsonObject;
import com.mojang.authlib.GameProfile;
import com.mojang.brigadier.arguments.ArgumentType;
import dan200.computercraft.api.network.wired.WiredElement;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.impl.Services;
import dan200.computercraft.shared.config.ConfigFile;
import dan200.computercraft.shared.network.container.ContainerData;
import dan200.computercraft.shared.util.InventoryUtil;
@ -47,26 +47,19 @@
import java.util.function.Predicate;
/**
* This extends {@linkplain dan200.computercraft.impl.PlatformHelper the API's loader abstraction layer}, adding
* additional methods used by the actual mod.
* Abstraction layer for Forge and Fabric. See implementations for more details.
*/
public interface PlatformHelper extends dan200.computercraft.impl.PlatformHelper {
public interface PlatformHelper {
/**
* Get the current {@link PlatformHelper} instance.
*
* @return The current instance.
*/
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.
*
@ -83,16 +76,6 @@ static PlatformHelper get() {
*/
<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.
*
@ -328,4 +311,21 @@ default boolean isFakePlayer(ServerPlayer player) {
* @see ServerPlayerGameMode#useItemOn(ServerPlayer, Level, ItemStack, InteractionHand, BlockHitResult)
*/
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 string All {@link Items#STRING} items.
* @param leather All {@link Items#LEATHER} items.
* @param stone All {@link Items#STONE} items.
* @param glassPane All {@link Items#GLASS_PANE} items.
* @param goldIngot All {@link Items#GOLD_INGOT} items.
* @param goldBlock All {@link Items#GOLD_BLOCK} items.
@ -26,7 +25,6 @@ public record RecipeIngredients(
Ingredient redstone,
Ingredient string,
Ingredient leather,
Ingredient stone,
Ingredient glassPane,
Ingredient goldIngot,
Ingredient goldBlock,

View File

@ -6,7 +6,7 @@
import com.mojang.serialization.DataResult;
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.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.StreamCodec;

View File

@ -2,11 +2,8 @@
//
// 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.registries.BuiltInRegistries;
import net.minecraft.resources.ResourceKey;
@ -18,9 +15,6 @@
*/
@ApiStatus.Internal
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() {
}

View File

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

View File

@ -6,7 +6,6 @@
"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": "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": "FogStart", "type": "float", "count": 1, "values": [ 0.0 ] },
{ "name": "FogEnd", "type": "float", "count": 1, "values": [ 1.0 ] },

View File

@ -5,7 +5,6 @@
package dan200.computercraft;
import com.google.auto.service.AutoService;
import com.google.gson.JsonObject;
import com.mojang.authlib.GameProfile;
import com.mojang.brigadier.arguments.ArgumentType;
import dan200.computercraft.api.network.wired.WiredElement;
@ -48,13 +47,8 @@
import java.util.function.Consumer;
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 {
@Override
public boolean isDevelopmentEnvironment() {
return true;
}
@Override
public ConfigFile.Builder createConfigBuilder() {
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");
}
@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
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");

View File

@ -17,7 +17,7 @@
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.data.PrettyJsonWriter;
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.core.registries.BuiltInRegistries;
import net.minecraft.network.chat.Component;

View File

@ -4,7 +4,7 @@
package dan200.computercraft.export;
import dan200.computercraft.impl.RegistryHelper;
import dan200.computercraft.shared.util.RegistryHelper;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.world.item.Item;
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 upgrade = turtle.getUpgrade(TurtleSide.LEFT)
assertEquals(
helper.level.registryAccess().registryOrThrow(ModRegistry.TURTLE_UPGRADE)
helper.level.registryAccess().registryOrThrow(ITurtleUpgrade.REGISTRY)
.get(ResourceLocation("cctest", "wooden_pickaxe")),
upgrade,
"Upgrade is a wooden pickaxe",
@ -262,7 +262,7 @@ fun Dig_breaks_tool(helper: GameTestHelper) = helper.sequence {
helper.assertUpgradeItem(
ItemStack(Items.WOODEN_PICKAXE),
UpgradeData.ofDefault(
helper.level.registryAccess().registryOrThrow(ModRegistry.TURTLE_UPGRADE)
helper.level.registryAccess().registryOrThrow(ITurtleUpgrade.REGISTRY)
.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 upgrade = turtle.getUpgrade(TurtleSide.LEFT)
assertEquals(
helper.level.registryAccess().registryOrThrow(ModRegistry.TURTLE_UPGRADE)
helper.level.registryAccess().registryOrThrow(ITurtleUpgrade.REGISTRY)
.get(ResourceLocation("cctest", "netherite_pickaxe")),
upgrade,
"Upgrade is a netherite pickaxe",

View File

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

View File

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

View File

@ -7,15 +7,22 @@
import dan200.computercraft.api.turtle.TurtleSide;
import dan200.computercraft.shared.ModRegistry;
import dan200.computercraft.shared.integration.RecipeModHelpers;
import dan200.computercraft.shared.platform.RegistryEntry;
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
import dan200.computercraft.shared.turtle.items.TurtleItem;
import dev.architectury.event.EventResult;
import me.shedaniel.rei.api.client.plugins.REIClientPlugin;
import me.shedaniel.rei.api.client.registry.display.DisplayRegistry;
import me.shedaniel.rei.api.client.registry.entry.CollapsibleEntryRegistry;
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.type.VanillaEntryTypes;
import me.shedaniel.rei.api.common.util.EntryStacks;
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.
@ -40,15 +47,30 @@ public void registerItemComparators(ItemComparatorRegistry registry) {
var upgrade = PocketComputerItem.getUpgradeWithData(stack);
return upgrade == null ? 1 : upgrade.holder().key().location().hashCode();
}, ModRegistry.Items.POCKET_COMPUTER_NORMAL.get(), ModRegistry.Items.POCKET_COMPUTER_ADVANCED.get());
registry.register((context, stack) -> DyedItemColor.getOrDefault(stack, -1), ModRegistry.Items.DISK.get());
}
@Override
public void registerEntries(EntryRegistry registry) {
for (var stack : RecipeModHelpers.getExtraStacks(RecipeModHelpers.getEmptyRegistryAccess())) {
for (var stack : RecipeModHelpers.getExtraStacks(BasicDisplay.registryAccess())) {
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
public void registerDisplays(DisplayRegistry registry) {
registry.registerDisplayGenerator(BuiltinPlugin.CRAFTING, new UpgradeDisplayGenerator());

View File

@ -4,7 +4,6 @@
package dan200.computercraft.client.integration.rei;
import dan200.computercraft.shared.integration.RecipeModHelpers;
import dan200.computercraft.shared.integration.UpgradeRecipeGenerator;
import me.shedaniel.rei.api.client.registry.display.DynamicDisplayGenerator;
import me.shedaniel.rei.api.common.display.basic.BasicDisplay;
@ -24,7 +23,7 @@
* Provides custom recipe and usage hints for pocket/turtle upgrades.
*/
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
public Optional<List<DefaultCraftingDisplay<?>>> getRecipeFor(EntryStack<?> entry) {

View File

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

View File

@ -5,41 +5,40 @@
package dan200.computercraft.data;
import com.mojang.serialization.Codec;
import com.mojang.serialization.Lifecycle;
import net.fabricmc.fabric.api.datagen.v1.DataGeneratorEntrypoint;
import net.fabricmc.fabric.api.datagen.v1.FabricDataGenerator;
import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput;
import net.fabricmc.fabric.api.datagen.v1.provider.FabricCodecDataProvider;
import net.fabricmc.fabric.api.datagen.v1.provider.FabricDynamicRegistryProvider;
import net.fabricmc.fabric.api.datagen.v1.provider.FabricTagProvider;
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.data.DataProvider;
import net.minecraft.data.PackOutput;
import net.minecraft.data.tags.TagsProvider;
import net.minecraft.resources.RegistryDataLoader;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.packs.PackType;
import net.minecraft.tags.TagKey;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.block.Block;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.stream.Stream;
public class FabricDataGenerators implements DataGeneratorEntrypoint {
@Override
public void onInitializeDataGenerator(FabricDataGenerator generator) {
var pack = new PlatformGeneratorsImpl(generator.createPack());
var pack = new PlatformGeneratorsImpl(generator.createPack(), generator.getRegistries());
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) {
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);
}
@Override
public <T extends DataProvider> T add(BiFunction<PackOutput, CompletableFuture<HolderLookup.Provider>, T> factory) {
return addWithRegistries(factory::apply);
}
@Override
public <T> void addFromCodec(String name, PackType type, String directory, Codec<T> codec, Consumer<BiConsumer<ResourceLocation, T>> output) {
addWithRegistries((out, registries) -> {
@ -111,61 +105,24 @@ public void copy(TagKey<Block> block, TagKey<Item> item) {
}
@Override
public CompletableFuture<RegistrySetBuilder.PatchedRegistries> createPatchedRegistries(CompletableFuture<HolderLookup.Provider> registries, RegistrySetBuilder patch) {
return registries.thenApply(oldRegistries -> {
var factory = new Cloner.Factory();
DynamicRegistries.getDynamicRegistries().forEach(registryData -> registryData.runWithArguments(factory::addCodec));
return patch.buildPatch(RegistryAccess.fromRegistryOfRegistries(BuiltInRegistries.REGISTRY), new DynamicRegistryLookup(oldRegistries), factory);
public void registries(CompletableFuture<RegistrySetBuilder.PatchedRegistries> registries) {
addWithFabricOutput(out -> new FabricDynamicRegistryProvider(out, registries.thenApply(RegistrySetBuilder.PatchedRegistries::patches)) {
@Override
public String getName() {
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.network.wired.WiredElementLookup;
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.PocketUpgrades;
import dan200.computercraft.impl.TurtleUpgrades;
@ -66,8 +68,8 @@ public static void init() {
FabricRegistryBuilder.createSimple(RecipeFunction.REGISTRY).attribute(RegistryAttribute.SYNCED).buildAndRegister();
DynamicRegistries.registerSynced(ModRegistry.TURTLE_UPGRADE, TurtleUpgrades.instance().upgradeCodec());
DynamicRegistries.registerSynced(ModRegistry.POCKET_UPGRADE, PocketUpgrades.instance().upgradeCodec());
DynamicRegistries.registerSynced(ITurtleUpgrade.REGISTRY, TurtleUpgrades.instance().upgradeCodec());
DynamicRegistries.registerSynced(IPocketUpgrade.REGISTRY, PocketUpgrades.instance().upgradeCodec());
ModRegistry.register();
ModRegistry.registerMainThread();

View File

@ -5,11 +5,8 @@
package dan200.computercraft.shared.platform;
import com.google.auto.service.AutoService;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.mojang.authlib.GameProfile;
import com.mojang.brigadier.arguments.ArgumentType;
import com.mojang.serialization.JsonOps;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.network.wired.WiredElement;
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.BlockApiLookup;
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.ExtendedScreenHandlerType;
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.ItemStorage;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.commands.synchronization.ArgumentTypeInfo;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
@ -49,7 +43,6 @@
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.tags.ItemTags;
import net.minecraft.tags.TagKey;
import net.minecraft.util.GsonHelper;
import net.minecraft.world.Container;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
@ -79,13 +72,8 @@
import java.util.function.Predicate;
import java.util.function.Supplier;
@AutoService(dan200.computercraft.impl.PlatformHelper.class)
@AutoService(PlatformHelper.class)
public class PlatformHelperImpl implements PlatformHelper {
@Override
public boolean isDevelopmentEnvironment() {
return FabricLoader.getInstance().isDevelopmentEnvironment();
}
@Override
public ConfigFile.Builder createConfigBuilder() {
return new FabricConfigFile.Builder();
@ -103,22 +91,6 @@ public <T> RegistrationHelper<T> createRegistrationHelper(ResourceKey<Registry<T
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
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);
@ -171,7 +143,6 @@ public RecipeIngredients getRecipeIngredients() {
Ingredient.of(ConventionalItemTags.REDSTONE_DUSTS),
Ingredient.of(ConventionalItemTags.STRINGS),
Ingredient.of(Items.LEATHER),
Ingredient.of(ConventionalItemTags.STONES),
Ingredient.of(ConventionalItemTags.GLASS_PANES),
Ingredient.of(ConventionalItemTags.GOLD_INGOTS),
Ingredient.of(ConventionalItemTags.STORAGE_BLOCKS_GOLD),

View File

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

View File

@ -15,6 +15,11 @@ cct.inlineProject(":common-api")
dependencies {
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 {

View File

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

View File

@ -1,6 +1,6 @@
{
"type": "computercraft:impostor_shapeless",
"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"}
}

View File

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

View File

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

View File

@ -7,6 +7,7 @@
import com.mojang.serialization.Codec;
import dan200.computercraft.api.ComputerCraftAPI;
import net.minecraft.core.HolderLookup;
import net.minecraft.core.RegistrySetBuilder;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.data.DataGenerator;
import net.minecraft.data.DataProvider;
@ -21,13 +22,13 @@
import net.neoforged.bus.api.SubscribeEvent;
import net.neoforged.fml.common.EventBusSubscriber;
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.JsonCodecProvider;
import net.neoforged.neoforge.data.event.GatherDataEvent;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
@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();
}
@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
public <T> void addFromCodec(String name, PackType type, String directory, Codec<T> codec, Consumer<BiConsumer<ResourceLocation, T>> output) {
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;
import com.google.auto.service.AutoService;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.mojang.authlib.GameProfile;
import com.mojang.brigadier.arguments.ArgumentType;
import com.mojang.serialization.JsonOps;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.network.wired.WiredElement;
@ -32,7 +29,6 @@
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.tags.TagKey;
import net.minecraft.util.GsonHelper;
import net.minecraft.world.Container;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
@ -54,16 +50,12 @@
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.Vec3;
import net.neoforged.bus.api.Event;
import net.neoforged.fml.loading.FMLLoader;
import net.neoforged.neoforge.capabilities.BlockCapability;
import net.neoforged.neoforge.capabilities.BlockCapabilityCache;
import net.neoforged.neoforge.capabilities.Capabilities;
import net.neoforged.neoforge.common.CommonHooks;
import net.neoforged.neoforge.common.Tags;
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.event.EventHooks;
import net.neoforged.neoforge.items.wrapper.InvWrapper;
@ -78,13 +70,8 @@
import java.util.function.Predicate;
import java.util.function.Supplier;
@AutoService(dan200.computercraft.impl.PlatformHelper.class)
@AutoService(PlatformHelper.class)
public class PlatformHelperImpl implements PlatformHelper {
@Override
public boolean isDevelopmentEnvironment() {
return !FMLLoader.isProduction();
}
@Override
public ConfigFile.Builder createConfigBuilder() {
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));
}
@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
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);
@ -169,7 +139,6 @@ public RecipeIngredients getRecipeIngredients() {
Ingredient.of(Tags.Items.DUSTS_REDSTONE),
Ingredient.of(Tags.Items.STRINGS),
Ingredient.of(Tags.Items.LEATHERS),
Ingredient.of(Tags.Items.STONES),
Ingredient.of(Tags.Items.GLASS_PANES),
Ingredient.of(Tags.Items.INGOTS_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]]
modId="neoforge"
type="required"
versionRange="[${neoVersion},20.6)"
versionRange="[${neoVersion},20.7)"
ordering="NONE"
side="BOTH"