1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-10-23 01:47:38 +00:00

Compare commits

...

17 Commits

Author SHA1 Message Date
Jonathan Coates
de078e3037 Merge branch 'mc-1.20.x' into mc-1.20.y 2024-05-28 18:46:19 +01:00
Jonathan Coates
209b1ddbf9 Bump CC:T to 1.111.0 2024-05-28 18:19:13 +01:00
Jonathan Coates
0c9f9a8652 Warn when Optifine is installed
We keep getting bug reports on 1.20.1 about an Optifine bug that causes
Forge's capabilities to not work (#1458). The cause of this bug is not
immediately visible to users, and can be very confusing when hit.

Optifine have not released a fix for this bug (despite it being reported
a year ago), and we continue to receive bug reports about it.

Nobody likes it when mods complain about other mods. So much Minecraft
drama can be traced back to this, and it's a slippery slope to go down.
I've tried to keep this as unobtrusive as possible — it's just a chat
message at world join, and it'll turn off if the bug is fixed.
2024-05-28 18:10:50 +01:00
Jonathan Coates
862d92785e Don't expose a menu provider for computers
We can't safely use this anyway (as custom data is not sent), so better
not to expose it at all. Fixes #1844.
2024-05-28 09:47:12 +01:00
Jonathan Coates
d48b85d50c Add r+/w+ support to io library 2024-05-26 10:16:33 +01:00
Jonathan Coates
4d619de357 Don't publish Gradle module metadata for common
Fixes #1842
2024-05-26 09:34:10 +01:00
Daniel Ratcliffe
57c289f173 Allow planks to be used for building in "adventure" 2024-05-25 10:04:45 +01:00
Jonathan Coates
f63f85921f Fix missing quotes in the settings example 2024-05-19 08:38:41 +01:00
Jonathan Coates
c7e49d1929 Use RecordItem.getDisplayName to get audio title
Rather than constructing the component manually. This should be more
compatible with mods that override getDisplayName.
2024-05-09 22:54:03 +01:00
Jonathan Coates
eb584aa94d Translate remaining item tags
These shouldn't appear in recipes, but just to stop Fabric complaining
:).
2024-05-09 18:47:28 +01:00
Jonathan Coates
ad70e2ad90 Make printout recipes a little more flexible
Rather than having one single hard-coded recipe, we now have separate
recipes for printed pages and printed books. These recipes are defined
in terms of

 - A list of ingredients (like shapeless recipes).
 - A result item.
 - An ingredient defining the acceptable page items (so printed page(s),
   but not books). This cannot overlap with any of the main ingredients.
 - The minimum number of printouts required.

We then override the shapeless recipe crafting logic to allow for
multiple printouts to appear.

It feels like it'd be nice to generalise this to a way of defining
shapeless recipes with variable-count ingredients (for instance, the
disk recipe could also be defined this way), but I don't think it's
worth it right now.

This solves some of the issues in #1755. Disk recipes have not been
changed yet.
2024-05-09 18:47:22 +01:00
Jonathan Coates
2c0d8263d3 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.
2024-05-07 22:59:53 +01:00
Jonathan Coates
1e214f329e Build docs for all MC versions 2024-05-06 09:59:09 +01:00
Jonathan Coates
de930c8d09 Split up turtle textures (#1813)
Turtles currently read their textures from a single 128x128 sprite
sheet. Most of this texture is unused which means we end up wasting a
lot of the block texture atlas[^1].

This change splits up the turtle textures into individual 32x32
textures[^2], one for each side, and then an additional backpack
texture.

I'm very sorry to any resource pack artists out there. The
tools/update-resources.py script will update existing packs, but does
not (currently) handle non-standard resolutions.

[^1]: It used to be worse: https://github.com/dan200/ComputerCraft/issues/145

[^2]: Turtle textures are a bit weird, in that they mostly *look* 16x16,
  but have some detail in places.
2024-04-30 20:58:07 +00:00
Jonathan Coates
94c864759d Ignore enchantments/attributes on the original item
Turtle tools were not equippable, as we considered the stack enchanted
due to the item's base attribute modifiers. We now only check the
component patch for enchantments/attribute modifiers.

This also removes the craftItem property of tools - this hasn't worked
since we added support for enchanted tools!

Fixes #1810
2024-04-29 21:07:04 +01:00
Weblate
735e7ce09b Translations for Italian
Co-authored-by: Alessandro <ale.proto00@gmail.com>
2024-04-29 19:00:14 +00:00
Jonathan Coates
6e9799316a Update ErrorProne 2024-04-28 18:32:19 +01:00
161 changed files with 973 additions and 1122 deletions

View File

@@ -3,8 +3,7 @@ name: Build documentation
on:
push:
branches:
- mc-1.19.x
- mc-1.20.x
- mc-*
jobs:
make_doc:

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")
}
}
}
@@ -95,9 +94,8 @@ sourceSets.all {
check("InlineMeSuggester", CheckSeverity.OFF) // Minecraft uses @Deprecated liberally
// Too many false positives right now. Maybe we need an indirection for it later on.
check("ReferenceEquality", CheckSeverity.OFF)
check("UnusedVariable", CheckSeverity.OFF) // Too many false positives with records.
check("EnumOrdinal", CheckSeverity.OFF) // For now. We could replace most of these with EnumMap.
check("OperatorPrecedence", CheckSeverity.OFF) // For now.
check("AlreadyChecked", CheckSeverity.OFF) // Seems to be broken?
check("NonOverridingEquals", CheckSeverity.OFF) // Peripheral.equals makes this hard to avoid
check("FutureReturnValueIgnored", CheckSeverity.OFF) // Too many false positives with Netty

View File

@@ -225,12 +225,12 @@ abstract class CCTweakedExtension(
* where possible.
*/
fun downloadFile(label: String, url: String): File {
val url = URI(url)
val path = File(url.path)
val uri = URI(url)
val path = File(uri.path)
project.repositories.ivy {
name = label
setUrl(URI(url.scheme, url.userInfo, url.host, url.port, path.parent, null, null))
setUrl(URI(uri.scheme, uri.userInfo, uri.host, uri.port, path.parent, null, null))
patternLayout {
artifact("[artifact].[ext]")
}

View File

@@ -98,7 +98,6 @@ abstract class MergeTrees : DefaultTask() {
}
val sharedFiles = files.entries.asSequence().filter { (_, v) -> v.found == sources.size }.map { (k, _) -> k }.toList()
println(sharedFiles)
// Copy shared files to the common directory
fsOperations.sync {

View File

@@ -10,7 +10,7 @@ kotlin.jvm.target.validation.mode=error
# Mod properties
isUnstable=true
modVersion=1.110.3
modVersion=1.111.0
# 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"
@@ -58,7 +58,7 @@ jmh = "1.37"
cctJavadoc = "1.8.2"
checkstyle = "10.14.1"
curseForgeGradle = "1.1.18"
errorProne-core = "2.23.0"
errorProne-core = "2.27.0"
errorProne-plugin = "3.1.0"
fabric-loom = "1.6.7"
githubRelease = "2.5.2"

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 @@ import javax.annotation.Nullable;
* {@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 @@ import javax.annotation.Nullable;
*
* // 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.Direction;
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 @@ import javax.annotation.Nullable;
* {@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 @@ import javax.annotation.Nullable;
*
* // 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 @@ import net.minecraft.world.item.ItemStack;
/**
* 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,164 +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 toolItem;
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 toolItem) {
this.id = id;
adjective = Component.translatable(UpgradeBase.getDefaultAdjective(id));
this.toolItem = toolItem;
craftingItem = null;
}
/**
* Specify a custom adjective for this tool. By default this takes its adjective from the tool item.
*
* @param adjective The new adjective to use.
* @return The tool builder, for further use.
*/
public ToolBuilder adjective(Component adjective) {
this.adjective = adjective;
return this;
}
/**
* Specify a custom item which is used to craft this upgrade. By default this is the same as the provided tool
* item, but you may wish to override it.
*
* @param craftingItem The item used to craft this upgrade.
* @return The tool builder, for further use.
*/
public ToolBuilder craftingItem(Item craftingItem) {
this.craftingItem = craftingItem;
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,
Optional.ofNullable(craftingItem),
toolItem,
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 @@ package dan200.computercraft.api.upgrades;
import com.mojang.serialization.MapCodec;
import dan200.computercraft.api.pocket.IPocketUpgrade;
import dan200.computercraft.api.pocket.PocketUpgradeDataProvider;
import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.api.turtle.TurtleUpgradeDataProvider;
import dan200.computercraft.impl.upgrades.UpgradeTypeImpl;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.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 @@ import java.util.function.Function;
* {@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

@@ -22,8 +22,7 @@ import java.util.Optional;
* The template for a turtle tool.
*
* @param adjective The adjective for this tool.
* @param craftItem The item used to craft this tool.
* @param toolItem The actual tool used.
* @param item The tool used.
* @param damageMultiplier The damage multiplier for this tool.
* @param allowEnchantments Whether to allow enchantments.
* @param consumeDurability When to consume durability.
@@ -31,8 +30,7 @@ import java.util.Optional;
*/
public record TurtleToolSpec(
Component adjective,
Optional<Item> craftItem,
Item toolItem,
Item item,
float damageMultiplier,
boolean allowEnchantments,
TurtleToolDurability consumeDurability,
@@ -42,8 +40,7 @@ public record TurtleToolSpec(
public static final MapCodec<TurtleToolSpec> CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
ComponentSerialization.CODEC.fieldOf("adjective").forGetter(TurtleToolSpec::adjective),
BuiltInRegistries.ITEM.byNameCodec().optionalFieldOf("craftingItem").forGetter(TurtleToolSpec::craftItem),
BuiltInRegistries.ITEM.byNameCodec().fieldOf("item").forGetter(TurtleToolSpec::toolItem),
BuiltInRegistries.ITEM.byNameCodec().fieldOf("item").forGetter(TurtleToolSpec::item),
Codec.FLOAT.optionalFieldOf("damageMultiplier", DEFAULT_DAMAGE_MULTIPLIER).forGetter(TurtleToolSpec::damageMultiplier),
Codec.BOOL.optionalFieldOf("allowEnchantments", false).forGetter(TurtleToolSpec::allowEnchantments),
TurtleToolDurability.CODEC.optionalFieldOf("consumeDurability", TurtleToolDurability.NEVER).forGetter(TurtleToolSpec::consumeDurability),

View File

@@ -129,3 +129,5 @@ val runData by tasks.registering(MergeTrees::class) {
}
}
}
tasks.withType(GenerateModuleMetadata::class).configureEach { isEnabled = false }

View File

@@ -6,12 +6,15 @@ package dan200.computercraft.client.integration.emi;
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 class EMIComputerCraft implements EmiPlugin {
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

@@ -27,7 +27,6 @@ import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer;
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
import net.minecraft.world.phys.AABB;
import org.joml.Matrix3f;
import org.joml.Matrix4f;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL20;
@@ -48,8 +47,6 @@ public class MonitorBlockEntityRenderer implements BlockEntityRenderer<MonitorBl
*/
private static final float MARGIN = (float) (MonitorBlockEntity.RENDER_MARGIN * 1.1);
private static final Matrix3f IDENTITY_NORMAL = new Matrix3f().identity();
private static @Nullable ByteBuffer backingBuffer;
private static long lastFrame = -1;

View File

@@ -11,6 +11,7 @@ import dan200.computercraft.api.turtle.ITurtleAccess;
import dan200.computercraft.api.turtle.TurtleSide;
import dan200.computercraft.shared.ModRegistry;
import dan200.computercraft.shared.turtle.upgrades.TurtleModem;
import dan200.computercraft.shared.util.DataComponentUtil;
import net.minecraft.core.component.DataComponentPatch;
import net.minecraft.resources.ResourceLocation;
import org.jetbrains.annotations.Nullable;
@@ -23,8 +24,7 @@ import java.util.stream.Stream;
public class TurtleModemModeller implements TurtleUpgradeModeller<TurtleModem> {
@Override
public TransformedModel getModel(TurtleModem upgrade, @Nullable ITurtleAccess turtle, TurtleSide side, DataComponentPatch data) {
var component = data.get(ModRegistry.DataComponents.ON.get());
var active = component != null && component.isPresent() && component.get();
var active = DataComponentUtil.isPresent(data, ModRegistry.DataComponents.ON.get(), x -> x);
var models = upgrade.advanced() ? ModemModels.ADVANCED : ModemModels.NORMAL;
return side == TurtleSide.LEFT

View File

@@ -11,14 +11,12 @@ import dan200.computercraft.api.turtle.ITurtleAccess;
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 final class TurtleUpgradeModellers {
}
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

@@ -206,8 +206,10 @@
"item.computercraft.treasure_disk": "Floppy Disk",
"itemGroup.computercraft": "ComputerCraft",
"tag.item.computercraft.computer": "Computers",
"tag.item.computercraft.dyeable": "Dyable items",
"tag.item.computercraft.monitor": "Monitors",
"tag.item.computercraft.turtle": "Turtles",
"tag.item.computercraft.turtle_can_place": "Turtle-placeable items",
"tag.item.computercraft.wired_modem": "Wired modems",
"tracking_field.computercraft.avg": "%s (avg)",
"tracking_field.computercraft.computer_tasks.name": "Tasks",

View File

@@ -1 +1,12 @@
{"parent": "computercraft:block/turtle_base", "textures": {"texture": "computercraft:block/turtle_advanced"}}
{
"parent": "computercraft:block/turtle_base",
"textures": {
"back": "computercraft:block/turtle_advanced_back",
"backpack": "computercraft:block/turtle_advanced_backpack",
"bottom": "computercraft:block/turtle_advanced_bottom",
"front": "computercraft:block/turtle_advanced_front",
"left": "computercraft:block/turtle_advanced_left",
"right": "computercraft:block/turtle_advanced_right",
"top": "computercraft:block/turtle_advanced_top"
}
}

View File

@@ -1 +1,12 @@
{"parent": "computercraft:block/turtle_base", "textures": {"texture": "computercraft:block/turtle_normal"}}
{
"parent": "computercraft:block/turtle_base",
"textures": {
"back": "computercraft:block/turtle_normal_back",
"backpack": "computercraft:block/turtle_normal_backpack",
"bottom": "computercraft:block/turtle_normal_bottom",
"front": "computercraft:block/turtle_normal_front",
"left": "computercraft:block/turtle_normal_left",
"right": "computercraft:block/turtle_normal_right",
"top": "computercraft:block/turtle_normal_top"
}
}

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,10 +1,12 @@
{
"type": "computercraft:impostor_shapeless",
"type": "computercraft:printout",
"category": "redstone",
"ingredients": [
"ingredients": [{"tag": "c:strings"}],
"min_printouts": 2,
"printout": [
{"item": "computercraft:printed_page"},
{"item": "computercraft:printed_page"},
{"tag": "c:strings"}
{"item": "computercraft:printed_pages"},
{"item": "minecraft:paper"}
],
"result": {"count": 1, "id": "computercraft:printed_pages"}
}

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 +0,0 @@
{"type": "computercraft:printout", "category": "misc"}

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

@@ -38,6 +38,9 @@ import static net.minecraft.data.models.model.TextureMapping.getBlockTexture;
class BlockModelProvider {
private static final TextureSlot CURSOR = TextureSlot.create("cursor");
private static final TextureSlot LEFT = TextureSlot.create("left");
private static final TextureSlot RIGHT = TextureSlot.create("right");
private static final TextureSlot BACKPACK = TextureSlot.create("backpack");
private static final ModelTemplate COMPUTER_ON = new ModelTemplate(
Optional.of(new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/computer_on")),
@@ -58,7 +61,7 @@ class BlockModelProvider {
private static final ModelTemplate TURTLE = new ModelTemplate(
Optional.of(new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_base")),
Optional.empty(),
TextureSlot.TEXTURE
TextureSlot.FRONT, TextureSlot.BACK, TextureSlot.TOP, TextureSlot.BOTTOM, LEFT, RIGHT, BACKPACK
);
private static final ModelTemplate TURTLE_UPGRADE_LEFT = new ModelTemplate(
Optional.of(new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_upgrade_base_left")),
@@ -167,7 +170,16 @@ class BlockModelProvider {
}
private static void registerTurtle(BlockModelGenerators generators, TurtleBlock block) {
var model = TURTLE.create(block, TextureMapping.defaultTexture(block), generators.modelOutput);
var model = TURTLE.create(block, new TextureMapping()
.put(TextureSlot.FRONT, getBlockTexture(block, "_front"))
.put(TextureSlot.BACK, getBlockTexture(block, "_back"))
.put(TextureSlot.TOP, getBlockTexture(block, "_top"))
.put(TextureSlot.BOTTOM, getBlockTexture(block, "_bottom"))
.put(LEFT, getBlockTexture(block, "_left"))
.put(RIGHT, getBlockTexture(block, "_right"))
.put(BACKPACK, getBlockTexture(block, "_backpack")),
generators.modelOutput
);
generators.blockStateOutput.accept(
MultiVariantGenerator.multiVariant(block, Variant.variant().with(VariantProperties.MODEL, model))
.with(createHorizontalFacingDispatch())

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 net.minecraft.world.level.block.Block;
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 @@ public final class 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 final class DataProviders {
}
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 final class DataProviders {
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 @@ package dan200.computercraft.data;
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.Aggregate;
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 @@ import java.util.stream.Stream;
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
@@ -105,6 +107,8 @@ public final class LanguageProvider implements DataProvider {
add(ComputerCraftTags.Items.TURTLE, "Turtles");
add(ComputerCraftTags.Items.WIRED_MODEM, "Wired modems");
add(ComputerCraftTags.Items.MONITOR, "Monitors");
add(ComputerCraftTags.Items.DYEABLE, "Dyable items");
add(ComputerCraftTags.Items.TURTLE_CAN_PLACE, "Turtle-placeable items");
// Turtle/pocket upgrades
add("upgrade.minecraft.diamond_sword.adjective", "Melee");
@@ -280,7 +284,7 @@ public final class LanguageProvider implements DataProvider {
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 +292,8 @@ public final class LanguageProvider implements DataProvider {
BuiltInRegistries.ITEM.holders()
.filter(x -> x.key().location().getNamespace().equals(ComputerCraftAPI.MOD_ID))
.map(x -> x.value().getDescriptionId()),
turtleUpgrades.getGeneratedUpgrades().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 @@ package dan200.computercraft.data;
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.google.gson.JsonObject;
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.CraftingBookCategory;
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;
@@ -98,7 +98,6 @@ final class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
turtleUpgrades(add, registries);
turtleOverlays(add);
addSpecial(add, new PrintoutRecipe(CraftingBookCategory.MISC));
addSpecial(add, new DiskRecipe(CraftingBookCategory.MISC));
addSpecial(add, new ColourableRecipe(CraftingBookCategory.MISC));
addSpecial(add, new ClearColourRecipe(CraftingBookCategory.MISC));
@@ -119,7 +118,7 @@ final class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
.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 +138,7 @@ final class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
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 +170,7 @@ final class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
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 +237,25 @@ final class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
.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 +277,18 @@ final class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
.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 +300,7 @@ final class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
.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 +312,7 @@ final class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
.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 +324,27 @@ final class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
.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 +358,7 @@ final class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
.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 +389,23 @@ final class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
.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 +415,42 @@ final class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
.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
@@ -470,21 +469,25 @@ final class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
.build()
.save(add, new ResourceLocation(ComputerCraftAPI.MOD_ID, "skull_dan200"));
var pages = Ingredient.of(
ModRegistry.Items.PRINTED_PAGE.get(),
ModRegistry.Items.PRINTED_PAGES.get(),
Items.PAPER
);
ShapelessSpecBuilder
.shapeless(RecipeCategory.REDSTONE, ModRegistry.Items.PRINTED_PAGES.get())
.requires(ModRegistry.Items.PRINTED_PAGE.get(), 2)
.requires(ingredients.string())
.unlockedBy("has_printer", inventoryChange(ModRegistry.Blocks.PRINTER.get()))
.build(ImpostorShapelessRecipe::new)
.unlockedBy("has_printer", inventoryChange(ModRegistry.Items.PRINTER.get()))
.build(x -> new PrintoutRecipe(x, pages, 2))
.save(add);
ShapelessSpecBuilder
.shapeless(RecipeCategory.REDSTONE, ModRegistry.Items.PRINTED_BOOK.get())
.requires(ingredients.leather())
.requires(ModRegistry.Items.PRINTED_PAGE.get(), 1)
.requires(ingredients.string())
.unlockedBy("has_printer", inventoryChange(ModRegistry.Blocks.PRINTER.get()))
.build(ImpostorShapelessRecipe::new)
.unlockedBy("has_printer", inventoryChange(ModRegistry.Items.PRINTER.get()))
.build(x -> new PrintoutRecipe(x, pages, 1))
.save(add);
}

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

@@ -13,7 +13,6 @@ import net.minecraft.tags.TagKey;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.Ingredient;
import net.minecraft.world.item.crafting.ShapelessRecipe;
import net.minecraft.world.level.ItemLike;
/**
@@ -61,6 +60,6 @@ public final class ShapelessSpecBuilder extends AbstractRecipeBuilder<ShapelessS
}
public FinishedRecipe build() {
return build(spec -> new ShapelessRecipe(spec.properties().group(), spec.properties().category(), spec.result(), spec.ingredients()));
return build(ShapelessRecipeSpec::create);
}
}

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.UpgradeData;
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.ArgumentTypeInfo;
import net.minecraft.commands.synchronization.SingletonArgumentInfo;
import net.minecraft.core.Holder;
import net.minecraft.core.HolderLookup;
import net.minecraft.core.Registry;
import net.minecraft.core.cauldron.CauldronInteraction;
import net.minecraft.core.component.DataComponentType;
import net.minecraft.core.registries.Registries;
@@ -99,7 +97,6 @@ import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.chat.Component;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.flag.FeatureFlags;
import net.minecraft.world.inventory.MenuType;
@@ -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);
@@ -255,12 +249,16 @@ public final class ModRegistry {
public static final RegistryEntry<TreasureDiskItem> TREASURE_DISK =
REGISTRY.register("treasure_disk", () -> new TreasureDiskItem(properties().stacksTo(1)));
private static Item.Properties printoutProperties() {
return properties().stacksTo(1).component(DataComponents.PRINTOUT.get(), PrintoutData.EMPTY);
}
public static final RegistryEntry<PrintoutItem> PRINTED_PAGE = REGISTRY.register("printed_page",
() -> new PrintoutItem(properties().stacksTo(1), PrintoutItem.Type.PAGE));
() -> new PrintoutItem(printoutProperties(), PrintoutItem.Type.PAGE));
public static final RegistryEntry<PrintoutItem> PRINTED_PAGES = REGISTRY.register("printed_pages",
() -> new PrintoutItem(properties().stacksTo(1), PrintoutItem.Type.PAGES));
() -> new PrintoutItem(printoutProperties(), PrintoutItem.Type.PAGES));
public static final RegistryEntry<PrintoutItem> PRINTED_BOOK = REGISTRY.register("printed_book",
() -> new PrintoutItem(properties().stacksTo(1), PrintoutItem.Type.BOOK));
() -> new PrintoutItem(printoutProperties(), PrintoutItem.Type.BOOK));
public static final RegistryEntry<BlockItem> SPEAKER = ofBlock(Blocks.SPEAKER, BlockItem::new);
public static final RegistryEntry<BlockItem> DISK_DRIVE = ofBlock(Blocks.DISK_DRIVE, BlockItem::new);
@@ -494,7 +492,7 @@ public final class ModRegistry {
public static final RegistryEntry<SimpleCraftingRecipeSerializer<ClearColourRecipe>> DYEABLE_ITEM_CLEAR = simple("clear_colour", ClearColourRecipe::new);
public static final RegistryEntry<SimpleCraftingRecipeSerializer<TurtleUpgradeRecipe>> TURTLE_UPGRADE = simple("turtle_upgrade", TurtleUpgradeRecipe::new);
public static final RegistryEntry<SimpleCraftingRecipeSerializer<PocketComputerUpgradeRecipe>> POCKET_COMPUTER_UPGRADE = simple("pocket_computer_upgrade", PocketComputerUpgradeRecipe::new);
public static final RegistryEntry<SimpleCraftingRecipeSerializer<PrintoutRecipe>> PRINTOUT = simple("printout", PrintoutRecipe::new);
public static final RegistryEntry<RecipeSerializer<PrintoutRecipe>> PRINTOUT = register("printout", PrintoutRecipe.CODEC, PrintoutRecipe.STREAM_CODEC);
public static final RegistryEntry<SimpleCraftingRecipeSerializer<DiskRecipe>> DISK = simple("disk", DiskRecipe::new);
}
@@ -566,8 +564,8 @@ public final class ModRegistry {
public static void register() {
Blocks.REGISTRY.register();
BlockEntities.REGISTRY.register();
Items.REGISTRY.register();
DataComponents.REGISTRY.register();
Items.REGISTRY.register();
TurtleUpgradeTypes.REGISTRY.register();
PocketUpgradeTypes.REGISTRY.register();
Menus.REGISTRY.register();
@@ -602,7 +600,7 @@ public final class ModRegistry {
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 +608,7 @@ public final class ModRegistry {
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.google.gson.JsonObject;
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

@@ -253,7 +253,6 @@ public record ComputerSelector(
}
private static final class Builder {
private OptionalInt instanceId = OptionalInt.empty();
private @Nullable UUID instanceUuid = null;
private OptionalInt computerId = OptionalInt.empty();
private @Nullable String label;

View File

@@ -5,16 +5,13 @@
package dan200.computercraft.shared.computer.blocks;
import dan200.computercraft.annotations.ForgeOverride;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.shared.common.IBundledRedstoneBlock;
import dan200.computercraft.shared.network.container.ComputerContainerData;
import dan200.computercraft.shared.platform.RegistryEntry;
import dan200.computercraft.shared.util.BlockEntityHelpers;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.MenuProvider;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.BlockGetter;
@@ -34,8 +31,6 @@ import net.minecraft.world.phys.BlockHitResult;
import javax.annotation.Nullable;
public abstract class AbstractComputerBlock<T extends AbstractComputerBlockEntity> extends HorizontalDirectionalBlock implements IBundledRedstoneBlock, EntityBlock {
private static final ResourceLocation DROP = new ResourceLocation(ComputerCraftAPI.MOD_ID, "computer");
protected final RegistryEntry<BlockEntityType<T>> type;
private final BlockEntityTicker<T> serverTicker = (level, pos, state, computer) -> computer.serverTick();
@@ -154,12 +149,6 @@ public abstract class AbstractComputerBlock<T extends AbstractComputerBlockEntit
return super.updateShape(state, direction, neighborState, level, pos, neighborPos);
}
@Nullable
@Override
protected MenuProvider getMenuProvider(BlockState state, Level level, BlockPos pos) {
return level.getBlockEntity(pos) instanceof AbstractComputerBlockEntity computer ? computer : null;
}
@Override
@Nullable
public <U extends BlockEntity> BlockEntityTicker<U> getTicker(Level level, BlockState state, BlockEntityType<U> type) {

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 final class RecipeModHelpers {
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 @@ public class UpgradeRecipeGenerator<T> {
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 @@ public class UpgradeRecipeGenerator<T> {
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 @@ public class UpgradeRecipeGenerator<T> {
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.Codec;
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 @@ public record PrintoutData(String title, 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 class PrintoutItem extends Item {
@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 class PrintoutItem extends Item {
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

@@ -5,7 +5,6 @@
package dan200.computercraft.shared.media.items;
import dan200.computercraft.api.media.IMedia;
import net.minecraft.network.chat.Component;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.RecordItem;
@@ -13,7 +12,7 @@ import net.minecraft.world.item.RecordItem;
import javax.annotation.Nullable;
/**
* An implementation of IMedia for ItemRecords.
* An implementation of {@link IMedia} for {@link RecordItem}.
*/
public final class RecordMedia implements IMedia {
public static final RecordMedia INSTANCE = new RecordMedia();
@@ -29,16 +28,12 @@ public final class RecordMedia implements IMedia {
@Override
public @Nullable String getAudioTitle(ItemStack stack) {
var item = stack.getItem();
if (!(item instanceof RecordItem)) return null;
return Component.translatable(item.getDescriptionId() + ".desc").getString();
return item instanceof RecordItem record ? record.getDisplayName().getString() : null;
}
@Override
public @Nullable SoundEvent getAudio(ItemStack stack) {
var item = stack.getItem();
if (!(item instanceof RecordItem)) return null;
return ((RecordItem) item).getSound();
return item instanceof RecordItem record ? record.getSound() : null;
}
}

View File

@@ -4,115 +4,141 @@
package dan200.computercraft.shared.media.recipes;
import com.mojang.serialization.MapCodec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import dan200.computercraft.shared.ModRegistry;
import dan200.computercraft.shared.media.items.PrintoutData;
import dan200.computercraft.shared.media.items.PrintoutItem;
import dan200.computercraft.shared.platform.PlatformHelper;
import dan200.computercraft.shared.util.DataComponentUtil;
import dan200.computercraft.shared.recipe.RecipeProperties;
import dan200.computercraft.shared.recipe.ShapelessRecipeSpec;
import net.minecraft.core.HolderLookup;
import net.minecraft.core.NonNullList;
import net.minecraft.network.RegistryFriendlyByteBuf;
import net.minecraft.network.codec.ByteBufCodecs;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.util.ExtraCodecs;
import net.minecraft.world.entity.player.StackedContents;
import net.minecraft.world.inventory.CraftingContainer;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.item.crafting.CraftingBookCategory;
import net.minecraft.world.item.crafting.CustomRecipe;
import net.minecraft.world.item.crafting.Ingredient;
import net.minecraft.world.item.crafting.RecipeSerializer;
import net.minecraft.world.item.crafting.ShapelessRecipe;
import net.minecraft.world.level.Level;
import java.util.ArrayList;
import java.util.List;
public final class PrintoutRecipe extends CustomRecipe {
private final Ingredient leather;
private final Ingredient string;
/**
* A recipe for combining one or more printed pages together.
* <p>
* This behaves similarly to a {@link ShapelessRecipe}, but allows a variable number of pages to appear as ingredients.
*
* @see PrintoutItem
* @see PrintoutData
*/
public final class PrintoutRecipe extends ShapelessRecipe {
public static final MapCodec<PrintoutRecipe> CODEC = RecordCodecBuilder.mapCodec(instance -> instance.group(
ShapelessRecipeSpec.CODEC.forGetter(PrintoutRecipe::toSpec),
Ingredient.CODEC_NONEMPTY.fieldOf("printout").forGetter(x -> x.printout),
ExtraCodecs.POSITIVE_INT.fieldOf("min_printouts").forGetter(x -> x.minPrintouts)
).apply(instance, PrintoutRecipe::new));
public PrintoutRecipe(CraftingBookCategory category) {
super(category);
public static final StreamCodec<RegistryFriendlyByteBuf, PrintoutRecipe> STREAM_CODEC = StreamCodec.composite(
ShapelessRecipeSpec.STREAM_CODEC, PrintoutRecipe::toSpec,
Ingredient.CONTENTS_STREAM_CODEC, x -> x.printout,
ByteBufCodecs.VAR_INT, x -> x.minPrintouts,
PrintoutRecipe::new
);
var ingredients = PlatformHelper.get().getRecipeIngredients();
leather = ingredients.leather();
string = ingredients.string();
private final NonNullList<Ingredient> ingredients;
private final Ingredient printout;
private final int minPrintouts;
private final ShapelessRecipe innerRecipe;
private final ItemStack result;
/**
* Construct a new {@link PrintoutRecipe}.
*
* @param spec The base {@link ShapelessRecipeSpec} for this recipe.
* @param printout The items that will be treated as printed pages.
* @param minPrintouts The minimum number of pages required.
*/
public PrintoutRecipe(
ShapelessRecipeSpec spec, Ingredient printout, int minPrintouts
) {
// We use the full list of ingredients in the recipe itself, so that it behaves sensibly with recipe mods.
super(spec.properties().group(), spec.properties().category(), spec.result(), concat(spec.ingredients(), printout, minPrintouts));
this.ingredients = spec.ingredients();
this.printout = printout;
this.minPrintouts = minPrintouts;
this.result = spec.result();
// However, when testing whether the recipe matches, we only want to use the non-printout ingredients. To do
// that, we create a hidden recipe with the main ingredients.
this.innerRecipe = spec.create();
}
private static NonNullList<Ingredient> concat(NonNullList<Ingredient> first, Ingredient pages, int pagesRequired) {
var result = NonNullList.withSize(first.size() + pagesRequired, Ingredient.EMPTY);
var idx = 0;
for (var ingredient : first) result.set(idx++, ingredient);
for (var i = 0; i < pagesRequired; i++) result.set(idx++, pages);
return result;
}
private ShapelessRecipeSpec toSpec() {
return new ShapelessRecipeSpec(RecipeProperties.of(this), ingredients, result);
}
@Override
public boolean canCraftInDimensions(int x, int y) {
return x >= 3 && y >= 3;
}
public boolean matches(CraftingContainer inv, Level world) {
var stackedContents = new StackedContents();
@Override
public ItemStack getResultItem(HolderLookup.Provider registryAccess) {
return new ItemStack(ModRegistry.Items.PRINTED_PAGES.get());
}
var inputs = 0;
var printouts = 0;
var pages = 0;
var hasPrintout = false;
@Override
public boolean matches(CraftingContainer inventory, Level world) {
return !assemble(inventory, world.registryAccess()).isEmpty();
}
for (var j = 0; j < inv.getContainerSize(); ++j) {
var stack = inv.getItem(j);
if (stack.isEmpty()) continue;
if (printout.test(stack)) {
printouts++;
@Override
public ItemStack assemble(CraftingContainer inventory, HolderLookup.Provider registryAccess) {
// See if we match the recipe, and extract the input disk ID and dye colour
var numPages = 0;
var numPrintouts = 0;
ItemStack[] printouts = null;
var stringFound = false;
var leatherFound = false;
var printoutFound = false;
for (var y = 0; y < inventory.getHeight(); y++) {
for (var x = 0; x < inventory.getWidth(); x++) {
var stack = inventory.getItem(x + y * inventory.getWidth());
if (!stack.isEmpty()) {
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);
numPrintouts++;
printoutFound = true;
} else if (stack.getItem() == Items.PAPER) {
if (printouts == null) {
printouts = new ItemStack[9];
}
printouts[numPrintouts] = stack;
numPages++;
numPrintouts++;
} else if (string.test(stack) && !stringFound) {
stringFound = true;
} else if (leather.test(stack) && !leatherFound) {
leatherFound = true;
} else {
return ItemStack.EMPTY;
}
}
}
}
// Build some pages with what was passed in
if (numPages <= PrintoutData.MAX_PAGES && stringFound && printoutFound && numPrintouts >= (leatherFound ? 1 : 2)) {
if (printouts == null) throw new IllegalStateException("Printouts must be non-null");
var lines = new PrintoutData.Line[numPages * PrintoutData.LINES_PER_PAGE];
var line = 0;
for (var printout = 0; printout < numPrintouts; printout++) {
var pageText = printouts[printout].get(ModRegistry.DataComponents.PRINTOUT.get());
if (pageText != null) {
// Add a printout
for (var pageLine : pageText.lines()) lines[line++] = pageLine;
var printout = stack.get(ModRegistry.DataComponents.PRINTOUT.get());
if (printout == null) {
pages++;
} else {
// Add a blank page
for (var pageLine = 0; pageLine < PrintoutData.LINES_PER_PAGE; pageLine++) {
lines[line++] = PrintoutData.Line.EMPTY;
}
hasPrintout = true;
pages += printout.pages();
}
} else {
inputs++;
stackedContents.accountStack(stack, 1);
}
var title = PrintoutItem.getTitle(printouts[0]);
return DataComponentUtil.createStack(
leatherFound ? ModRegistry.Items.PRINTED_BOOK.get() : ModRegistry.Items.PRINTED_PAGES.get(),
ModRegistry.DataComponents.PRINTOUT.get(), new PrintoutData(title, List.of(lines))
);
}
return ItemStack.EMPTY;
return hasPrintout && printouts >= minPrintouts && pages <= PrintoutData.MAX_PAGES
&& inputs == ingredients.size() && stackedContents.canCraft(innerRecipe, null);
}
@Override
public ItemStack assemble(CraftingContainer inv, HolderLookup.Provider registries) {
List<PrintoutData> data = new ArrayList<>();
for (var j = 0; j < inv.getContainerSize(); ++j) {
var stack = inv.getItem(j);
if (!stack.isEmpty() && printout.test(stack)) data.add(PrintoutData.getOrEmpty(stack));
}
if (data.isEmpty()) throw new IllegalStateException("Printouts must be non-null");
var lines = data.stream().flatMap(x -> x.lines().stream()).toList();
var result = super.assemble(inv, registries);
result.set(ModRegistry.DataComponents.PRINTOUT.get(), new PrintoutData(data.getFirst().title(), lines));
return result;
}
@Override

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.computer.terminal.NetworkedTerminal;
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 @@ public final class PrinterBlockEntity extends AbstractContainerBlockEntity imple
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.Consumer;
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 @@ public interface PlatformHelper extends dan200.computercraft.impl.PlatformHelper
*/
<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 @@ public interface PlatformHelper extends dan200.computercraft.impl.PlatformHelper
* @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 @@ import net.minecraft.world.item.crafting.Ingredient;
* @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 @@ package dan200.computercraft.shared.recipe;
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

@@ -49,4 +49,13 @@ public record ShapelessRecipeSpec(RecipeProperties properties, NonNullList<Ingre
ItemStack.STREAM_CODEC, ShapelessRecipeSpec::result,
ShapelessRecipeSpec::new
);
/**
* Create a basic {@link ShapelessRecipe} from this spec.
*
* @return The newly constructed recipe.
*/
public ShapelessRecipe create() {
return new ShapelessRecipe(properties().group(), properties().category(), result(), ingredients());
}
}

View File

@@ -14,6 +14,7 @@ import dan200.computercraft.shared.platform.PlatformHelper;
import dan200.computercraft.shared.turtle.TurtleUtil;
import dan200.computercraft.shared.turtle.core.TurtlePlaceCommand;
import dan200.computercraft.shared.turtle.core.TurtlePlayer;
import dan200.computercraft.shared.util.DataComponentUtil;
import dan200.computercraft.shared.util.DropConsumer;
import dan200.computercraft.shared.util.WorldUtil;
import net.minecraft.core.BlockPos;
@@ -49,37 +50,26 @@ public class TurtleTool extends AbstractTurtleUpgrade {
private static final TurtleCommandResult INEFFECTIVE = TurtleCommandResult.failure("Cannot break block with this tool");
final TurtleToolSpec spec;
final ItemStack item;
final float damageMulitiplier;
final boolean allowEnchantments;
final TurtleToolDurability consumeDurability;
final @Nullable TagKey<Block> breakable;
public TurtleTool(TurtleToolSpec spec) {
super(TurtleUpgradeType.TOOL, spec.adjective(), new ItemStack(spec.craftItem().orElse(spec.toolItem())));
super(TurtleUpgradeType.TOOL, spec.adjective(), new ItemStack(spec.item()));
this.spec = spec;
item = new ItemStack(spec.toolItem());
this.damageMulitiplier = spec.damageMultiplier();
this.allowEnchantments = spec.allowEnchantments();
this.consumeDurability = spec.consumeDurability();
this.breakable = spec.breakable().orElse(null);
}
@Override
public boolean isItemSuitable(ItemStack stack) {
if (consumeDurability == TurtleToolDurability.NEVER && stack.isDamaged()) return false;
if (!allowEnchantments && isEnchanted(stack)) return false;
if (spec.consumeDurability() == TurtleToolDurability.NEVER && stack.isDamaged()) return false;
if (!spec.allowEnchantments() && isEnchanted(stack)) return false;
return true;
}
private static boolean isEnchanted(ItemStack stack) {
var enchantments = stack.get(DataComponents.ENCHANTMENTS);
if (enchantments != null && !enchantments.isEmpty()) return true;
var modifiers = stack.get(DataComponents.ATTRIBUTE_MODIFIERS);
if (modifiers != null && !modifiers.modifiers().isEmpty()) return true;
return false;
// Only check whether the stack has been modified. We ignore components on the original item.
var patch = stack.getComponentsPatch();
return DataComponentUtil.isPresent(patch, DataComponents.ENCHANTMENTS, x -> !x.isEmpty())
|| DataComponentUtil.isPresent(patch, DataComponents.ATTRIBUTE_MODIFIERS, x -> !x.modifiers().isEmpty());
}
@Override
@@ -100,9 +90,7 @@ public class TurtleTool extends AbstractTurtleUpgrade {
}
private void setToolStack(ITurtleAccess turtle, TurtleSide side, ItemStack oldStack, ItemStack stack) {
var upgradeData = turtle.getUpgradeData(side);
var useDurability = switch (consumeDurability) {
var useDurability = switch (spec.consumeDurability()) {
case NEVER -> false;
case WHEN_ENCHANTED -> isEnchanted(oldStack);
case ALWAYS -> true;
@@ -116,7 +104,7 @@ public class TurtleTool extends AbstractTurtleUpgrade {
}
// If the tool has changed, no clue what's going on.
if (stack.getItem() != item.getItem()) return;
if (stack.getItem() != spec.item()) return;
turtle.setUpgradeData(side, stack.getComponentsPatch());
}
@@ -218,7 +206,7 @@ public class TurtleTool extends AbstractTurtleUpgrade {
* @see Player#attack(Entity)
*/
private boolean attack(ServerPlayer player, Direction direction, Entity entity) {
var baseDamage = (float) player.getAttributeValue(Attributes.ATTACK_DAMAGE) * damageMulitiplier;
var baseDamage = (float) player.getAttributeValue(Attributes.ATTACK_DAMAGE) * spec.damageMultiplier();
var bonusDamage = EnchantmentHelper.getDamageBonus(player.getItemInHand(InteractionHand.MAIN_HAND), entity.getType());
var damage = baseDamage + bonusDamage;
if (damage <= 0) return false;

View File

@@ -5,6 +5,7 @@
package dan200.computercraft.shared.util;
import net.minecraft.core.component.DataComponentHolder;
import net.minecraft.core.component.DataComponentPatch;
import net.minecraft.core.component.DataComponentType;
import net.minecraft.core.component.DataComponents;
import net.minecraft.network.chat.Component;
@@ -13,6 +14,7 @@ import net.minecraft.world.level.ItemLike;
import org.jetbrains.annotations.Contract;
import javax.annotation.Nullable;
import java.util.function.Predicate;
/**
* Utilities for working with {@linkplain DataComponentType data components}.
@@ -43,4 +45,18 @@ public class DataComponentUtil {
public static <T> ItemStack createStack(ItemLike item, DataComponentType<T> type, @Nullable T value) {
return set(new ItemStack(item), type, value);
}
/**
* Check a component is present in a {@link DataComponentPatch} and matches the supplied predicate.
*
* @param patch The current component patch.
* @param component The component type.
* @param check The predicate to check against.
* @param <T> The type of component.
* @return Whether the component is present in this patch, and matches the supplied predicate.
*/
public static <T> boolean isPresent(DataComponentPatch patch, DataComponentType<T> component, Predicate<T> check) {
var value = patch.get(component);
return value != null && value.isPresent() && check.test(value.get());
}
}

View File

@@ -33,7 +33,6 @@ public final class IDAssigner {
private final Path idFile;
private final Path newIdFile;
private boolean atomicMove = true;
private @Nullable Map<String, Integer> ids;
public IDAssigner(Path path) {

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 @@ import org.jetbrains.annotations.ApiStatus;
*/
@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 @@ package dan200.computercraft.shared.util;
import com.mojang.serialization.*;
import com.mojang.serialization.codecs.KeyDispatchCodec;
import dan200.computercraft.impl.RegistryHelper;
import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceKey;

View File

@@ -4,6 +4,7 @@
package dan200.computercraft.shared.util;
import com.google.errorprone.annotations.Keep;
import net.minecraft.core.BlockPos;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.level.ChunkLevel;
@@ -132,6 +133,7 @@ public final class TickScheduler {
/**
* The current state of this token.
*/
@Keep
private volatile State $state = State.IDLE;
public Token(BlockEntity owner) {

View File

@@ -218,5 +218,18 @@
"upgrade.minecraft.diamond_hoe.adjective": "Contadina",
"upgrade.minecraft.diamond_pickaxe.adjective": "Minatrice",
"upgrade.minecraft.diamond_shovel.adjective": "Scavatrice",
"upgrade.minecraft.diamond_sword.adjective": "Da Combattimento"
"upgrade.minecraft.diamond_sword.adjective": "Da Combattimento",
"tag.item.computercraft.computer": "Computer",
"tag.item.computercraft.wired_modem": "Modem cablati",
"argument.computercraft.computer.distance": "Distanza dall'entità",
"argument.computercraft.computer.family": "Famiglia computer",
"argument.computercraft.computer.id": "ID computer",
"argument.computercraft.computer.instance": "ID istanza unica",
"argument.computercraft.computer.label": "Etichetta computer",
"argument.computercraft.unknown_computer_family": "Famiglia computer '%s' sconosciuta",
"gui.computercraft.config.disabled_generic_methods": "Metodi generici disattivati",
"gui.computercraft.config.disabled_generic_methods.tooltip": "Una lista di metodi generici o sorgenti di metodi da disattivare. I metodi generici sono\nmetodi aggiunti a blocchi/entità blocchi quando non c'è un provider della periferica esplicito.\nQuesto include metodi dell'inventario (ad es. inventory.getItemDetail, inventory.pushItems) e,\nse su Forge, i metodi fluid_storage e energy_storage.\nI metodi in questa lista possono essere sia un gruppo intero di metodi (computer:inventory)\no un singolo metodo (computer:inventory#pushItems).\n",
"tag.item.computercraft.monitor": "Monitor",
"tag.item.computercraft.turtle": "Tartarughe",
"tracking_field.computercraft.java_allocation.name": "Allocazioni Java"
}

View File

@@ -2,30 +2,30 @@
"parent": "block/block",
"render_type": "translucent",
"textures": {
"particle": "#texture"
"particle": "#front"
},
"elements": [
{
"from": [ 2, 2, 2 ],
"to": [ 14, 14, 13 ],
"faces": {
"down": { "uv": [ 5.75, 2.75, 2.75, 0 ], "texture": "#texture" },
"up": { "uv": [ 8.75, 0, 5.75, 2.75 ], "texture": "#texture" },
"north": { "uv": [ 11.5, 5.75, 8.5, 2.75 ], "texture": "#texture" },
"south": { "uv": [ 5.75, 5.75, 2.75, 2.75 ], "texture": "#texture" },
"west": { "uv": [ 8.5, 5.75, 5.75, 2.75 ], "texture": "#texture" },
"east": { "uv": [ 2.75, 5.75, 0, 2.75 ], "texture": "#texture" }
"down": { "uv": [ 0, 0, 12, 11 ], "texture": "#bottom" },
"up": { "uv": [ 0, 0, 12, 11 ], "texture": "#top" },
"north": { "uv": [ 0, 0, 12, 12 ], "texture": "#front" },
"south": { "uv": [ 0, 0, 12, 12 ], "texture": "#back" },
"west": { "uv": [ 0, 0, 11, 12 ], "texture": "#left" },
"east": { "uv": [ 0, 0, 11, 12 ], "texture": "#right" }
}
},
{
"from": [ 3, 6, 13 ],
"to": [ 13, 13, 15 ],
"faces": {
"down": { "uv": [ 11.75, 0.5, 9.25, 0 ], "texture": "#texture" },
"up": { "uv": [ 14.25, 0, 11.75, 0.5 ], "texture": "#texture" },
"south": { "uv": [ 11.75, 2.25, 9.25, 0.5 ], "texture": "#texture" },
"west": { "uv": [ 12.25, 2.25, 11.75, 0.5 ], "texture": "#texture" },
"east": { "uv": [ 9.25, 2.25, 8.75, 0.5 ], "texture": "#texture" }
"down": { "uv": [ 2, 9, 12, 11 ], "texture": "#backpack" },
"up": { "uv": [ 2, 0, 12, 2 ], "texture": "#backpack" },
"south": { "uv": [ 2, 2, 12, 9 ], "texture": "#backpack" },
"west": { "uv": [ 0, 2, 2, 9 ], "texture": "#backpack" },
"east": { "uv": [ 12, 2, 14, 9 ], "texture": "#backpack" }
}
}
]

View File

@@ -1,53 +1,67 @@
{
"parent": "computercraft:block/turtle_base",
"textures": {
"texture": "computercraft:block/turtle_colour"
"texture": "computercraft:block/turtle_colour",
"body_back": "computercraft:block/turtle_colour_body_back",
"body_backpack": "computercraft:block/turtle_colour_body_backpack",
"body_bottom": "computercraft:block/turtle_colour_body_bottom",
"body_front": "computercraft:block/turtle_colour_body_front",
"body_left": "computercraft:block/turtle_colour_body_left",
"body_right": "computercraft:block/turtle_colour_body_right",
"body_top": "computercraft:block/turtle_colour_body_top",
"frame_back": "computercraft:block/turtle_colour_frame_back",
"frame_backpack": "computercraft:block/turtle_colour_frame_backpack",
"frame_bottom": "computercraft:block/turtle_colour_frame_bottom",
"frame_front": "computercraft:block/turtle_colour_frame_front",
"frame_left": "computercraft:block/turtle_colour_frame_left",
"frame_right": "computercraft:block/turtle_colour_frame_right",
"frame_top": "computercraft:block/turtle_colour_frame_top"
},
"elements": [
{
"from": [ 2, 2, 2 ],
"to": [ 14, 14, 13 ],
"faces": {
"down": { "uv": [ 5.75, 8.5, 2.75, 5.75 ], "texture": "#texture", "tintindex": 0 },
"up": { "uv": [ 8.75, 5.75, 5.75, 8.5 ], "texture": "#texture", "tintindex": 0 },
"north": { "uv": [ 11.5, 11.5, 8.5, 8.5 ], "texture": "#texture", "tintindex": 0 },
"south": { "uv": [ 5.75, 11.5, 2.75, 8.5 ], "texture": "#texture", "tintindex": 0 },
"west": { "uv": [ 8.5, 11.5, 5.75, 8.555 ], "texture": "#texture", "tintindex": 0 },
"east": { "uv": [ 2.75, 11.5, 0, 8.5 ], "texture": "#texture", "tintindex": 0 }
"down": { "uv": [ 0, 0, 12, 11 ], "texture": "#body_bottom", "tintindex": 0 },
"up": { "uv": [ 0, 0, 12, 11 ], "texture": "#body_top", "tintindex": 0 },
"north": { "uv": [ 0, 0, 12, 12 ], "texture": "#body_front", "tintindex": 0 },
"south": { "uv": [ 0, 0, 12, 12 ], "texture": "#body_back", "tintindex": 0 },
"west": { "uv": [ 0, 0, 11, 12 ], "texture": "#body_left", "tintindex": 0 },
"east": { "uv": [ 0, 0, 11, 12 ], "texture": "#body_right", "tintindex": 0 }
}
},
{
"from": [ 3, 6, 13 ],
"to": [ 13, 13, 15 ],
"faces": {
"down": { "uv": [ 11.75, 6.25, 9.25, 5.75 ], "texture": "#texture", "tintindex": 0 },
"up": { "uv": [ 14.25, 5.75, 11.75, 6.25 ], "texture": "#texture", "tintindex": 0 },
"south": { "uv": [ 11.75, 8, 9.25, 6.25 ], "texture": "#texture", "tintindex": 0 },
"west": { "uv": [ 12.25, 8, 11.75, 6.25 ], "texture": "#texture", "tintindex": 0 },
"east": { "uv": [ 9.25, 8, 8.75, 6.25 ], "texture": "#texture", "tintindex": 0 }
"down": { "uv": [ 2, 9, 12, 11 ], "texture": "#body_backpack", "tintindex": 0 },
"up": { "uv": [ 2, 0, 12, 2 ], "texture": "#body_backpack", "tintindex": 0 },
"south": { "uv": [ 2, 2, 12, 9 ], "texture": "#body_backpack", "tintindex": 0 },
"west": { "uv": [ 0, 2, 2, 9 ], "texture": "#body_backpack", "tintindex": 0 },
"east": { "uv": [ 12, 2, 14, 9 ], "texture": "#body_backpack", "tintindex": 0 }
}
},
{
"from": [ 2, 2, 2 ],
"to": [ 14, 14, 13 ],
"faces": {
"down": { "uv": [ 5.75, 2.75, 2.75, 0 ], "texture": "#texture" },
"up": { "uv": [ 8.75, 0, 5.75, 2.75 ], "texture": "#texture" },
"north": { "uv": [ 11.5, 5.75, 8.5, 2.75 ], "texture": "#texture" },
"south": { "uv": [ 5.75, 5.75, 2.75, 2.75 ], "texture": "#texture" },
"west": { "uv": [ 8.5, 5.75, 5.75, 2.75 ], "texture": "#texture" },
"east": { "uv": [ 2.75, 5.75, 0, 2.75 ], "texture": "#texture" }
"down": { "uv": [ 0, 0, 12, 11 ], "texture": "#frame_bottom" },
"up": { "uv": [ 0, 0, 12, 11 ], "texture": "#frame_top" },
"north": { "uv": [ 0, 0, 12, 12 ], "texture": "#frame_front" },
"south": { "uv": [ 0, 0, 12, 12 ], "texture": "#frame_back" },
"west": { "uv": [ 0, 0, 11, 12 ], "texture": "#frame_left" },
"east": { "uv": [ 0, 0, 11, 12 ], "texture": "#frame_right" }
}
},
{
"from": [ 3, 6, 13 ],
"to": [ 13, 13, 15 ],
"faces": {
"down": { "uv": [ 11.75, 0.5, 9.25, 0 ], "texture": "#texture" },
"up": { "uv": [ 14.25, 0, 11.75, 0.5 ], "texture": "#texture" },
"south": { "uv": [ 11.75, 2.25, 9.25, 0.5 ], "texture": "#texture" },
"west": { "uv": [ 12.25, 2.25, 11.75, 0.5 ], "texture": "#texture" },
"east": { "uv": [ 9.25, 2.25, 8.75, 0.5 ], "texture": "#texture" }
"down": { "uv": [ 2, 9, 12, 11 ], "texture": "#frame_backpack" },
"up": { "uv": [ 2, 0, 12, 2 ], "texture": "#frame_backpack" },
"south": { "uv": [ 2, 2, 12, 9 ], "texture": "#frame_backpack" },
"west": { "uv": [ 0, 2, 2, 9 ], "texture": "#frame_backpack" },
"east": { "uv": [ 12, 2, 14, 9 ], "texture": "#frame_backpack" }
}
}
]

View File

@@ -1,6 +1,35 @@
{
"parent": "computercraft:block/turtle_overlay",
"parent": "block/block",
"textures": {
"texture": "computercraft:block/turtle_elf_overlay"
}
}
"back": "computercraft:block/turtle_elf_overlay_back",
"backpack": "computercraft:block/turtle_elf_overlay_backpack",
"front": "computercraft:block/turtle_elf_overlay_front",
"left": "computercraft:block/turtle_elf_overlay_left",
"right": "computercraft:block/turtle_elf_overlay_right",
"top": "computercraft:block/turtle_elf_overlay_top"
},
"elements": [
{
"from": [ 2, 2, 2 ],
"to": [ 14, 14, 13 ],
"faces": {
"up": { "uv": [ 0, 0, 12, 11 ], "texture": "#top" },
"north": { "uv": [ 0, 0, 12, 12 ], "texture": "#front" },
"south": { "uv": [ 0, 0, 12, 12 ], "texture": "#back" },
"west": { "uv": [ 0, 0, 11, 12 ], "texture": "#left" },
"east": { "uv": [ 0, 0, 11, 12 ], "texture": "#right" }
}
},
{
"from": [ 3, 6, 13 ],
"to": [ 13, 13, 15 ],
"faces": {
"down": { "uv": [ 2, 9, 12, 11 ], "texture": "#backpack" },
"up": { "uv": [ 2, 0, 12, 2 ], "texture": "#backpack" },
"south": { "uv": [ 2, 2, 12, 9 ], "texture": "#backpack" },
"west": { "uv": [ 0, 2, 2, 9 ], "texture": "#backpack" },
"east": { "uv": [ 12, 2, 14, 9 ], "texture": "#backpack" }
}
}
]
}

View File

@@ -1,43 +0,0 @@
{
"parent": "block/block",
"textures": {
"particle": "#texture"
},
"elements": [
{
"from": [ 2, 2, 2 ],
"to": [ 14, 14, 13 ],
"faces": {
"down": { "uv": [ 2.75, 0, 5.75, 2.75 ], "texture": "#texture" },
"up": { "uv": [ 5.75, 0, 8.75, 2.75 ], "texture": "#texture" },
"north": { "uv": [ 8.5, 5.75, 11.5, 2.75 ], "texture": "#texture" },
"south": { "uv": [ 2.75, 5.75, 5.75, 2.75 ], "texture": "#texture" },
"west": { "uv": [ 0, 5.75, 2.75, 2.75 ], "texture": "#texture" },
"east": { "uv": [ 5.75, 5.75, 8.5, 2.75 ], "texture": "#texture" }
}
},
{
"from": [ 3, 6, 13 ],
"to": [ 13, 13, 15 ],
"faces": {
"down": { "uv": [ 9.25, 0, 11.75, 0.5 ], "texture": "#texture" },
"up": { "uv": [ 11.75, 0, 14.25, 0.5 ], "texture": "#texture" },
"south": { "uv": [ 9.25, 2.25, 11.75, 0.5 ], "texture": "#texture" },
"west": { "uv": [ 8.75, 2.25, 9.25, 0.5 ], "texture": "#texture" },
"east": { "uv": [ 11.75, 2.25, 12.25, 0.5 ], "texture": "#texture" }
}
},
{
"from": [ 1.5, 1.5, 1.5 ],
"to": [ 14.5, 14.5, 13.5 ],
"faces": {
"down": { "uv": [ 2.75, 8, 5.75, 10.75 ], "texture": "#texture" },
"up": { "uv": [ 5.75, 8, 8.75, 10.75 ], "texture": "#texture" },
"north": { "uv": [ 8.5, 13.75, 11.5, 10.75 ], "texture": "#texture" },
"south": { "uv": [ 2.75, 13.75, 5.75, 10.75 ], "texture": "#texture" },
"west": { "uv": [ 0, 13.75, 2.75, 10.75 ], "texture": "#texture" },
"east": { "uv": [ 5.75, 13.75, 8.5, 10.75 ], "texture": "#texture" }
}
}
]
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 533 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 489 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 421 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 495 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 502 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 517 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 490 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 258 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 229 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 361 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 247 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 292 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 168 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 235 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 191 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 233 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 185 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 338 B

Some files were not shown because too many files have changed in this diff Show More