1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-03-26 21:36:58 +00:00

Update to Minecraft 1.19.3

Lots of minor changes, but nothing too nasty - just tedious.

Known bugs/issues:
 - REI and JEI haven't been updated at the time of writing, so our usage
   of their APIs may be incompatible.

 - Crash when opening the config UI in Fabric, as forgeconfigapi-port
   hasn't been updated yet.

Will hold off on doing a release until those mods have updated.
This commit is contained in:
Jonathan Coates 2022-12-08 19:45:02 +00:00
parent 3b42f22a4f
commit c3fe9f00d4
No known key found for this signature in database
GPG Key ID: B9E431FF07C98D06
373 changed files with 886 additions and 728 deletions
gradle.properties
gradle
projects
common-api/src
common/src
client
main
test/java/dan200/computercraft
testMod
fabric

@ -9,4 +9,4 @@ isUnstable=true
modVersion=1.102.0-SNAPSHOT
# Minecraft properties: We want to configure this here so we can read it in settings.gradle
mcVersion=1.19.2
mcVersion=1.19.3

@ -2,12 +2,12 @@
# Minecraft
# MC version is specified in gradle.properties, as we need that in settings.gradle.
fabric-api = "0.67.0+1.19.2"
fabric-loader = "0.14.10"
forge = "43.1.1"
fabric-api = "0.68.1+1.19.3"
fabric-loader = "0.14.11"
forge = "44.0.0"
forgeSpi = "6.0.0"
mixin = "0.8.5"
parchment = "2022.10.16"
parchment = "2022.11.27"
parchmentMc = "1.19.2"
# Normal dependencies
@ -28,16 +28,16 @@ slf4j = "1.7.36"
# Minecraft mods
forgeConfig = "4.2.7"
iris = "1.19.x-v1.4.0"
iris = "1.19.3-v1.4.6"
jei = "11.3.0.262"
modmenu = "4.1.0"
modmenu = "5.0.1"
oculus = "1.2.5"
rei = "9.1.550"
rubidium = "0.6.1"
sodium = "mc1.19.2-0.4.4"
sodium = "mc1.19.3-0.4.6"
# Testing
byteBuddy = "1.12.18"
byteBuddy = "1.12.19"
hamcrest = "2.2"
jqwik = "1.7.0"
junit = "5.9.1"
@ -100,6 +100,7 @@ sodium = { module = "maven.modrinth:sodium", version.ref = "sodium" }
# Testing
byteBuddyAgent = { module ="net.bytebuddy:byte-buddy-agent", version.ref = "byteBuddy" }
byteBuddy = { module ="net.bytebuddy:byte-buddy", version.ref = "byteBuddy" }
hamcrest = { module = "org.hamcrest:hamcrest", version.ref = "hamcrest" }
jqwik-api = { module = "net.jqwik:jqwik-api", version.ref = "jqwik" }
jqwik-engine = { module = "net.jqwik:jqwik-engine", version.ref = "jqwik" }
@ -143,10 +144,10 @@ kotlin = ["kotlin-stdlib", "kotlin-coroutines"]
# Minecraft
externalMods-common = ["jei-api", "forgeConfig", "nightConfig-core", "nightConfig-toml"]
externalMods-forge-compile = ["oculus", "jei-api"]
externalMods-forge-runtime = ["jei-forge"]
externalMods-forge-runtime = []
externalMods-fabric = ["fabric-loader", "fabric-api", "forgeConfig", "nightConfig-core", "nightConfig-toml"]
externalMods-fabric-compile = ["iris", "jei-api", "rei-api", "rei-builtin"]
externalMods-fabric-runtime = ["jei-fabric", "modmenu"]
externalMods-fabric-runtime = ["modmenu"]
# Testing
test = ["junit-jupiter-api", "junit-jupiter-params", "hamcrest", "jqwik-api"]

@ -5,13 +5,11 @@
*/
package dan200.computercraft.api.client.turtle;
import com.mojang.math.Matrix4f;
import com.mojang.math.Transformation;
import dan200.computercraft.api.client.TransformedModel;
import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.api.turtle.TurtleSide;
import java.nio.FloatBuffer;
import org.joml.Matrix4f;
class TurtleUpgradeModellers {
private static final Transformation leftTransform = getMatrixFor(-0.40625f);
@ -19,12 +17,12 @@ class TurtleUpgradeModellers {
private static Transformation getMatrixFor(float offset) {
var matrix = new Matrix4f();
matrix.load(FloatBuffer.wrap(new float[]{
matrix.set(new float[]{
0.0f, 0.0f, -1.0f, 1.0f + offset,
1.0f, 0.0f, 0.0f, 0.0f,
0.0f, -1.0f, 0.0f, 1.0f,
0.0f, 0.0f, 0.0f, 1.0f,
}));
});
matrix.transpose();
return new Transformation(matrix);
}

@ -5,7 +5,7 @@
*/
package dan200.computercraft.api;
import net.minecraft.core.Registry;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.TagKey;
import net.minecraft.world.item.Item;
@ -22,7 +22,7 @@ public class ComputerCraftTags {
public static final TagKey<Item> MONITOR = make("monitor");
private static TagKey<Item> make(String name) {
return TagKey.create(Registry.ITEM_REGISTRY, new ResourceLocation(ComputerCraftAPI.MOD_ID, name));
return TagKey.create(Registries.ITEM, new ResourceLocation(ComputerCraftAPI.MOD_ID, name));
}
}
@ -53,7 +53,7 @@ public class ComputerCraftTags {
public static final TagKey<Block> TURTLE_HOE_BREAKABLE = make("turtle_hoe_harvestable");
private static TagKey<Block> make(String name) {
return TagKey.create(Registry.BLOCK_REGISTRY, new ResourceLocation(ComputerCraftAPI.MOD_ID, name));
return TagKey.create(Registries.BLOCK, new ResourceLocation(ComputerCraftAPI.MOD_ID, name));
}
}
}

@ -7,19 +7,21 @@ package dan200.computercraft.api.pocket;
import dan200.computercraft.api.upgrades.UpgradeDataProvider;
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}. Override the {@link #addUpgrades(Consumer)} function,
* construct each upgrade, and pass them off to the provided consumer to generate them.
* 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 PocketUpgradeSerialiser
*/
public abstract class PocketUpgradeDataProvider extends UpgradeDataProvider<IPocketUpgrade, PocketUpgradeSerialiser<?>> {
public PocketUpgradeDataProvider(DataGenerator generator) {
super(generator, "Pocket Computer Upgrades", "computercraft/pocket_upgrades", PocketUpgradeSerialiser.REGISTRY_ID);
public PocketUpgradeDataProvider(PackOutput output) {
super(output, "Pocket Computer Upgrades", "computercraft/pocket_upgrades", PocketUpgradeSerialiser.REGISTRY_ID);
}
}

@ -14,7 +14,7 @@ import net.minecraft.core.Registry;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.SimpleRecipeSerializer;
import net.minecraft.world.item.crafting.SimpleCraftingRecipeSerializer;
import java.util.function.BiFunction;
import java.util.function.Function;
@ -36,8 +36,8 @@ public interface PocketUpgradeSerialiser<T extends IPocketUpgrade> extends Upgra
ResourceKey<Registry<PocketUpgradeSerialiser<?>>> REGISTRY_ID = ResourceKey.createRegistryKey(new ResourceLocation(ComputerCraftAPI.MOD_ID, "pocket_upgrade_serialiser"));
/**
* Create an upgrade serialiser for a simple upgrade. This is similar to a {@link SimpleRecipeSerializer}, but for
* upgrades.
* Create an upgrade serialiser for a simple upgrade. This is similar to a {@link SimpleCraftingRecipeSerializer},
* but for upgrades.
* <p>
* If you might want to vary the item, it's suggested you use {@link #simpleWithCustomItem(BiFunction)} instead.
*

@ -9,8 +9,9 @@ import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.ComputerCraftTags;
import dan200.computercraft.api.upgrades.UpgradeDataProvider;
import dan200.computercraft.impl.PlatformHelper;
import net.minecraft.core.Registry;
import net.minecraft.core.registries.Registries;
import net.minecraft.data.DataGenerator;
import net.minecraft.data.PackOutput;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.TagKey;
import net.minecraft.world.entity.ai.attributes.Attributes;
@ -23,16 +24,17 @@ import java.util.function.Consumer;
/**
* A data provider to generate turtle upgrades.
* <p>
* This should be subclassed and registered to a {@link DataGenerator}. Override the {@link #addUpgrades(Consumer)} function,
* construct each upgrade, and pass them off to the provided consumer to generate them.
* 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 TurtleUpgradeSerialiser
*/
public abstract class TurtleUpgradeDataProvider extends UpgradeDataProvider<ITurtleUpgrade, TurtleUpgradeSerialiser<?>> {
private static final ResourceLocation TOOL_ID = new ResourceLocation(ComputerCraftAPI.MOD_ID, "tool");
public TurtleUpgradeDataProvider(DataGenerator generator) {
super(generator, "Turtle Upgrades", "computercraft/turtle_upgrades", TurtleUpgradeSerialiser.REGISTRY_ID);
public TurtleUpgradeDataProvider(PackOutput output) {
super(output, "Turtle Upgrades", "computercraft/turtle_upgrades", TurtleUpgradeSerialiser.REGISTRY_ID);
}
/**
@ -124,10 +126,10 @@ public abstract class TurtleUpgradeDataProvider extends UpgradeDataProvider<ITur
*/
public void add(Consumer<Upgrade<TurtleUpgradeSerialiser<?>>> add) {
add.accept(new Upgrade<>(id, serialiser, s -> {
s.addProperty("item", PlatformHelper.get().getRegistryKey(Registry.ITEM_REGISTRY, toolItem).toString());
s.addProperty("item", PlatformHelper.get().getRegistryKey(Registries.ITEM, toolItem).toString());
if (adjective != null) s.addProperty("adjective", adjective);
if (craftingItem != null) {
s.addProperty("craftItem", PlatformHelper.get().getRegistryKey(Registry.ITEM_REGISTRY, craftingItem).toString());
s.addProperty("craftItem", PlatformHelper.get().getRegistryKey(Registries.ITEM, craftingItem).toString());
}
if (damageMultiplier != null) s.addProperty("damageMultiplier", damageMultiplier);
if (breakable != null) s.addProperty("breakable", breakable.location().toString());

@ -15,7 +15,7 @@ import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.RecipeSerializer;
import net.minecraft.world.item.crafting.SimpleRecipeSerializer;
import net.minecraft.world.item.crafting.SimpleCraftingRecipeSerializer;
import java.util.function.BiFunction;
import java.util.function.Function;
@ -71,8 +71,8 @@ public interface TurtleUpgradeSerialiser<T extends ITurtleUpgrade> extends Upgra
ResourceKey<Registry<TurtleUpgradeSerialiser<?>>> REGISTRY_ID = ResourceKey.createRegistryKey(new ResourceLocation(ComputerCraftAPI.MOD_ID, "turtle_upgrade_serialiser"));
/**
* Create an upgrade serialiser for a simple upgrade. This is similar to a {@link SimpleRecipeSerializer}, but for
* upgrades.
* Create an upgrade serialiser for a simple upgrade. This is similar to a {@link SimpleCraftingRecipeSerializer},
* but for upgrades.
* <p>
* If you might want to vary the item, it's suggested you use {@link #simpleWithCustomItem(BiFunction)} instead.
*

@ -11,10 +11,12 @@ import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
import dan200.computercraft.impl.PlatformHelper;
import dan200.computercraft.impl.upgrades.SerialiserWithCraftingItem;
import dan200.computercraft.impl.upgrades.SimpleSerialiser;
import net.minecraft.Util;
import net.minecraft.core.Registry;
import net.minecraft.core.registries.Registries;
import net.minecraft.data.CachedOutput;
import net.minecraft.data.DataGenerator;
import net.minecraft.data.DataProvider;
import net.minecraft.data.PackOutput;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
@ -22,11 +24,11 @@ import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import javax.annotation.Nullable;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer;
import java.util.function.Function;
@ -40,15 +42,15 @@ import java.util.function.Function;
public abstract class UpgradeDataProvider<T extends UpgradeBase, R extends UpgradeSerialiser<? extends T>> implements DataProvider {
private static final Logger LOGGER = LogManager.getLogger();
private final DataGenerator generator;
private final PackOutput output;
private final String name;
private final String folder;
private final ResourceKey<Registry<R>> registry;
private @Nullable List<T> upgrades;
protected UpgradeDataProvider(DataGenerator generator, String name, String folder, ResourceKey<Registry<R>> registry) {
this.generator = generator;
protected UpgradeDataProvider(PackOutput output, String name, String folder, ResourceKey<Registry<R>> registry) {
this.output = output;
this.name = name;
this.folder = folder;
this.registry = registry;
@ -84,7 +86,7 @@ public abstract class UpgradeDataProvider<T extends UpgradeBase, R extends Upgra
}
return new Upgrade<>(id, serialiser, s ->
s.addProperty("item", PlatformHelper.get().getRegistryKey(Registry.ITEM_REGISTRY, item).toString())
s.addProperty("item", PlatformHelper.get().getRegistryKey(Registries.ITEM, item).toString())
);
}
@ -103,11 +105,12 @@ public abstract class UpgradeDataProvider<T extends UpgradeBase, R extends Upgra
protected abstract void addUpgrades(Consumer<Upgrade<R>> addUpgrade);
@Override
public final void run(CachedOutput cache) throws IOException {
var base = generator.getOutputFolder().resolve("data");
public final CompletableFuture<?> run(CachedOutput cache) {
var base = output.getOutputFolder().resolve("data");
Set<ResourceLocation> seen = new HashSet<>();
List<T> upgrades = new ArrayList<>();
List<CompletableFuture<?>> futures = new ArrayList<>();
addUpgrades(upgrade -> {
if (!seen.add(upgrade.id())) throw new IllegalStateException("Duplicate upgrade " + upgrade.id());
@ -115,11 +118,7 @@ public abstract class UpgradeDataProvider<T extends UpgradeBase, R extends Upgra
json.addProperty("type", PlatformHelper.get().getRegistryKey(registry, upgrade.serialiser()).toString());
upgrade.serialise().accept(json);
try {
DataProvider.saveStable(cache, json, base.resolve(upgrade.id().getNamespace() + "/" + folder + "/" + upgrade.id().getPath() + ".json"));
} catch (IOException e) {
LOGGER.error("Failed to save {} {}", name, upgrade.id(), e);
}
futures.add(DataProvider.saveStable(cache, json, base.resolve(upgrade.id().getNamespace() + "/" + folder + "/" + upgrade.id().getPath() + ".json")));
try {
var result = upgrade.serialiser().fromJson(upgrade.id(), json);
@ -130,6 +129,7 @@ public abstract class UpgradeDataProvider<T extends UpgradeBase, R extends Upgra
});
this.upgrades = upgrades;
return Util.sequenceFailFast(futures);
}
@Override

@ -29,7 +29,7 @@ import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
import net.minecraft.client.renderer.item.ClampedItemPropertyFunction;
import net.minecraft.client.renderer.item.ItemProperties;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.packs.resources.ResourceManager;
import net.minecraft.server.packs.resources.ResourceProvider;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
@ -177,7 +177,7 @@ public final class ClientRegistry {
<T extends BlockEntity> void register(BlockEntityType<? extends T> type, BlockEntityRendererProvider<T> provider);
}
public static void registerShaders(ResourceManager resources, BiConsumer<ShaderInstance, Consumer<ShaderInstance>> load) throws IOException {
public static void registerShaders(ResourceProvider resources, BiConsumer<ShaderInstance, Consumer<ShaderInstance>> load) throws IOException {
RenderTypes.registerShaders(resources, load);
}

@ -77,19 +77,12 @@ public abstract class AbstractComputerScreen<T extends AbstractComputerMenu> ext
@Override
protected void init() {
super.init();
minecraft.keyboardHandler.setSendRepeatsToGui(true);
terminal = addRenderableWidget(createTerminal());
ComputerSidebar.addButtons(this, menu::isOn, input, this::addRenderableWidget, leftPos, topPos + sidebarYOffset);
ComputerSidebar.addButtons(menu::isOn, input, this::addRenderableWidget, leftPos, topPos + sidebarYOffset);
setFocused(terminal);
}
@Override
public void removed() {
super.removed();
minecraft.keyboardHandler.setSendRepeatsToGui(false);
}
@Override
public void containerTick() {
super.containerTick();

@ -34,7 +34,7 @@ public final class ComputerScreen<T extends AbstractComputerMenu> extends Abstra
// Draw a border around the terminal
var terminal = getTerminal();
ComputerBorderRenderer.render(
stack.last().pose(), ComputerBorderRenderer.getTexture(family), terminal.x, terminal.y, getBlitOffset(),
stack.last().pose(), ComputerBorderRenderer.getTexture(family), terminal.getX(), terminal.getY(), getBlitOffset(),
FULL_BRIGHT_LIGHTMAP, terminal.getWidth(), terminal.getHeight()
);
ComputerSidebar.renderBackground(stack, leftPos, topPos + sidebarYOffset);

@ -46,7 +46,6 @@ public class NoTermComputerScreen<T extends AbstractComputerMenu> extends Screen
KeyMapping.releaseAll();
super.init();
minecraft.keyboardHandler.setSendRepeatsToGui(true);
terminal = addWidget(new TerminalWidget(terminalData, new ClientInputHandler(menu), 0, 0));
terminal.visible = false;
@ -54,12 +53,6 @@ public class NoTermComputerScreen<T extends AbstractComputerMenu> extends Screen
setFocused(terminal);
}
@Override
public final void removed() {
super.removed();
minecraft.keyboardHandler.setSendRepeatsToGui(false);
}
@Override
public final void tick() {
super.tick();

@ -74,8 +74,7 @@ public final class OptionScreen extends Screen {
var x = (width - buttonWidth) / 2;
for (var button : buttons) {
button.x = x;
button.y = y + textHeight;
button.setPosition(x, y + textHeight);
addRenderableWidget(button);
x += BUTTON_WIDTH + PADDING;
@ -105,11 +104,7 @@ public final class OptionScreen extends Screen {
}
public static AbstractWidget newButton(Component component, Button.OnPress clicked) {
return new Button(0, 0, BUTTON_WIDTH, BUTTON_HEIGHT, component, clicked);
}
public void disable() {
for (var widget : buttons) widget.active = false;
return Button.builder(component, clicked).bounds(0, 0, BUTTON_WIDTH, BUTTON_HEIGHT).build();
}
public Screen getOriginalScreen() {

@ -7,17 +7,15 @@ package dan200.computercraft.client.gui.widgets;
import com.mojang.blaze3d.vertex.PoseStack;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.client.gui.widgets.DynamicImageButton.HintedMessage;
import dan200.computercraft.client.render.ComputerBorderRenderer;
import dan200.computercraft.shared.computer.core.InputHandler;
import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
import net.minecraft.ChatFormatting;
import net.minecraft.client.gui.components.AbstractWidget;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import java.util.Arrays;
import java.util.Collections;
import java.util.function.BooleanSupplier;
import java.util.function.Consumer;
@ -44,29 +42,29 @@ public final class ComputerSidebar {
private ComputerSidebar() {
}
public static void addButtons(Screen screen, BooleanSupplier isOn, InputHandler input, Consumer<AbstractWidget> add, int x, int y) {
public static void addButtons(BooleanSupplier isOn, InputHandler input, Consumer<AbstractWidget> add, int x, int y) {
x += CORNERS_BORDER + 1;
y += CORNERS_BORDER + ICON_MARGIN;
var turnOn = new HintedMessage(
Component.translatable("gui.computercraft.tooltip.turn_off"),
Component.translatable("gui.computercraft.tooltip.turn_off.key")
);
var turnOff = new HintedMessage(Component.translatable("gui.computercraft.tooltip.turn_on"), (Component) null);
add.accept(new DynamicImageButton(
screen, x, y, ICON_WIDTH, ICON_HEIGHT, () -> isOn.getAsBoolean() ? 15 : 1, 1, ICON_TEX_Y_DIFF,
x, y, ICON_WIDTH, ICON_HEIGHT, () -> isOn.getAsBoolean() ? 15 : 1, 1, ICON_TEX_Y_DIFF,
TEXTURE, TEX_SIZE, TEX_SIZE, b -> toggleComputer(isOn, input),
() -> isOn.getAsBoolean() ? Arrays.asList(
Component.translatable("gui.computercraft.tooltip.turn_off"),
Component.translatable("gui.computercraft.tooltip.turn_off.key").withStyle(ChatFormatting.GRAY)
) : Collections.singletonList(
Component.translatable("gui.computercraft.tooltip.turn_on")
)
() -> isOn.getAsBoolean() ? turnOff : turnOn
));
y += ICON_HEIGHT + ICON_MARGIN * 2;
add.accept(new DynamicImageButton(
screen, x, y, ICON_WIDTH, ICON_HEIGHT, 29, 1, ICON_TEX_Y_DIFF,
x, y, ICON_WIDTH, ICON_HEIGHT, 29, 1, ICON_TEX_Y_DIFF,
TEXTURE, TEX_SIZE, TEX_SIZE, b -> input.queueEvent("terminate"),
Arrays.asList(
new HintedMessage(
Component.translatable("gui.computercraft.tooltip.terminate"),
Component.translatable("gui.computercraft.tooltip.terminate.key").withStyle(ChatFormatting.GRAY)
Component.translatable("gui.computercraft.tooltip.terminate.key")
)
));
}

@ -7,12 +7,13 @@ package dan200.computercraft.client.gui.widgets;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.ChatFormatting;
import net.minecraft.client.gui.components.Button;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.gui.components.Tooltip;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import java.util.List;
import javax.annotation.Nullable;
import java.util.function.IntSupplier;
import java.util.function.Supplier;
@ -21,42 +22,39 @@ import java.util.function.Supplier;
* dynamically.
*/
public class DynamicImageButton extends Button {
private final Screen screen;
private final ResourceLocation texture;
private final IntSupplier xTexStart;
private final int yTexStart;
private final int yDiffTex;
private final int textureWidth;
private final int textureHeight;
private final Supplier<List<Component>> tooltip;
private final Supplier<HintedMessage> message;
public DynamicImageButton(
Screen screen, int x, int y, int width, int height, int xTexStart, int yTexStart, int yDiffTex,
int x, int y, int width, int height, int xTexStart, int yTexStart, int yDiffTex,
ResourceLocation texture, int textureWidth, int textureHeight,
OnPress onPress, List<Component> tooltip
OnPress onPress, HintedMessage message
) {
this(
screen, x, y, width, height, () -> xTexStart, yTexStart, yDiffTex,
x, y, width, height, () -> xTexStart, yTexStart, yDiffTex,
texture, textureWidth, textureHeight,
onPress, () -> tooltip
onPress, () -> message
);
}
public DynamicImageButton(
Screen screen, int x, int y, int width, int height, IntSupplier xTexStart, int yTexStart, int yDiffTex,
int x, int y, int width, int height, IntSupplier xTexStart, int yTexStart, int yDiffTex,
ResourceLocation texture, int textureWidth, int textureHeight,
OnPress onPress, Supplier<List<Component>> tooltip
OnPress onPress, Supplier<HintedMessage> message
) {
super(x, y, width, height, Component.empty(), onPress);
this.screen = screen;
super(x, y, width, height, Component.empty(), onPress, DEFAULT_NARRATION);
this.textureWidth = textureWidth;
this.textureHeight = textureHeight;
this.xTexStart = xTexStart;
this.yTexStart = yTexStart;
this.yDiffTex = yDiffTex;
this.texture = texture;
this.tooltip = tooltip;
this.message = message;
}
@Override
@ -67,23 +65,29 @@ public class DynamicImageButton extends Button {
var yTex = yTexStart;
if (isHoveredOrFocused()) yTex += yDiffTex;
blit(stack, x, y, xTexStart.getAsInt(), yTex, width, height, textureWidth, textureHeight);
blit(stack, getX(), getY(), xTexStart.getAsInt(), yTex, width, height, textureWidth, textureHeight);
RenderSystem.enableDepthTest();
if (isHovered) renderToolTip(stack, mouseX, mouseY);
}
@Override
public Component getMessage() {
var tooltip = this.tooltip.get();
return tooltip.isEmpty() ? Component.empty() : tooltip.get(0);
return message.get().message;
}
@Override
public void renderToolTip(PoseStack stack, int mouseX, int mouseY) {
var tooltip = this.tooltip.get();
if (!tooltip.isEmpty()) {
screen.renderComponentTooltip(stack, tooltip, mouseX, mouseY);
public void render(PoseStack stack, int mouseX, int mouseY, float partialTicks) {
setTooltip(message.get().tooltip());
super.render(stack, mouseX, mouseY, partialTicks);
}
public record HintedMessage(Component message, Tooltip tooltip) {
public HintedMessage(Component message, @Nullable Component hint) {
this(
message,
hint == null
? Tooltip.create(message)
: Tooltip.create(Component.empty().append(message).append("\n").append(hint.copy().withStyle(ChatFormatting.GRAY)), hint)
);
}
}
}

@ -14,6 +14,7 @@ import dan200.computercraft.shared.computer.core.InputHandler;
import net.minecraft.SharedConstants;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.components.AbstractWidget;
import net.minecraft.client.gui.narration.NarratedElementType;
import net.minecraft.client.gui.narration.NarrationElementOutput;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.network.chat.Component;
@ -26,6 +27,8 @@ import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FON
import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_WIDTH;
public class TerminalWidget extends AbstractWidget {
private static final Component DESCRIPTION = Component.translatable("gui.computercraft.terminal");
private static final float TERMINATE_TIME = 0.5f;
private final Terminal terminal;
@ -48,7 +51,7 @@ public class TerminalWidget extends AbstractWidget {
private final BitSet keysDown = new BitSet(256);
public TerminalWidget(Terminal terminal, InputHandler computer, int x, int y) {
super(x, y, terminal.getWidth() * FONT_WIDTH + MARGIN * 2, terminal.getHeight() * FONT_HEIGHT + MARGIN * 2, Component.empty());
super(x, y, terminal.getWidth() * FONT_WIDTH + MARGIN * 2, terminal.getHeight() * FONT_HEIGHT + MARGIN * 2, DESCRIPTION);
this.terminal = terminal;
this.computer = computer;
@ -278,8 +281,8 @@ public class TerminalWidget extends AbstractWidget {
}
@Override
public void updateNarration(NarrationElementOutput output) {
// I'm not sure what the right option is here.
protected void updateWidgetNarration(NarrationElementOutput output) {
output.add(NarratedElementType.TITLE, getMessage());
}
public static int getWidth(int termWidth) {

@ -7,12 +7,12 @@ package dan200.computercraft.client.render;
import com.mojang.blaze3d.vertex.Tesselator;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.mojang.math.Matrix4f;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.resources.ResourceLocation;
import org.joml.Matrix4f;
public class ComputerBorderRenderer {
public static final ResourceLocation BACKGROUND_NORMAL = new ResourceLocation(ComputerCraftAPI.MOD_ID, "textures/gui/corners_normal.png");

@ -6,7 +6,7 @@
package dan200.computercraft.client.render;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.math.Vector3f;
import com.mojang.math.Axis;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.ItemInHandRenderer;
import net.minecraft.client.renderer.MultiBufferSource;
@ -66,7 +66,7 @@ public abstract class ItemMapLikeRenderer {
// If the player is not invisible then render a single arm
if (!minecraft.player.isInvisible()) {
transform.pushPose();
transform.mulPose(Vector3f.ZP.rotationDegrees(offset * 10f));
transform.mulPose(Axis.ZP.rotationDegrees(offset * 10f));
minecraft.getEntityRenderDispatcher().getItemInHandRenderer().renderPlayerArm(transform, render, combinedLight, equipProgress, swingProgress, side);
transform.popPose();
}
@ -81,8 +81,8 @@ public abstract class ItemMapLikeRenderer {
var f4 = 0.4f * Mth.sin(f1 * ((float) Math.PI * 2f));
var f5 = -0.3f * Mth.sin(swingProgress * (float) Math.PI);
transform.translate(offset * f3, f4 - 0.3f * f2, f5);
transform.mulPose(Vector3f.XP.rotationDegrees(f2 * -45f));
transform.mulPose(Vector3f.YP.rotationDegrees(offset * f2 * -30f));
transform.mulPose(Axis.XP.rotationDegrees(f2 * -45f));
transform.mulPose(Axis.YP.rotationDegrees(offset * f2 * -30f));
renderItem(transform, render, stack, combinedLight);
@ -114,17 +114,17 @@ public abstract class ItemMapLikeRenderer {
var pitchAngle = renderer.calculateMapTilt(pitch);
transform.translate(0, 0.04F + equipProgress * -1.2f + pitchAngle * -0.5f, -0.72f);
transform.mulPose(Vector3f.XP.rotationDegrees(pitchAngle * -85.0f));
transform.mulPose(Axis.XP.rotationDegrees(pitchAngle * -85.0f));
if (!minecraft.player.isInvisible()) {
transform.pushPose();
transform.mulPose(Vector3f.YP.rotationDegrees(90.0F));
transform.mulPose(Axis.YP.rotationDegrees(90.0F));
renderer.renderMapHand(transform, render, combinedLight, HumanoidArm.RIGHT);
renderer.renderMapHand(transform, render, combinedLight, HumanoidArm.LEFT);
transform.popPose();
}
var rX = Mth.sin(swingRt * (float) Math.PI);
transform.mulPose(Vector3f.XP.rotationDegrees(rX * 20.0F));
transform.mulPose(Axis.XP.rotationDegrees(rX * 20.0F));
transform.scale(2.0F, 2.0F, 2.0F);
renderItem(transform, render, stack, combinedLight);

@ -6,8 +6,7 @@
package dan200.computercraft.client.render;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.math.Matrix4f;
import com.mojang.math.Vector3f;
import com.mojang.math.Axis;
import dan200.computercraft.client.pocket.ClientPocketComputers;
import dan200.computercraft.client.render.text.FixedWidthFontRenderer;
import dan200.computercraft.core.util.Colour;
@ -15,6 +14,7 @@ import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.world.item.ItemStack;
import org.joml.Matrix4f;
import static dan200.computercraft.client.render.ComputerBorderRenderer.*;
import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_HEIGHT;
@ -43,8 +43,8 @@ public final class PocketItemRenderer extends ItemMapLikeRenderer {
// Setup various transformations. Note that these are partially adapted from the corresponding method
// in ItemRenderer
transform.pushPose();
transform.mulPose(Vector3f.YP.rotationDegrees(180f));
transform.mulPose(Vector3f.ZP.rotationDegrees(180f));
transform.mulPose(Axis.YP.rotationDegrees(180f));
transform.mulPose(Axis.ZP.rotationDegrees(180f));
transform.scale(0.5f, 0.5f, 0.5f);
var scale = 0.75f / Math.max(width + BORDER * 2, height + BORDER * 2 + LIGHT_HEIGHT);

@ -6,7 +6,7 @@
package dan200.computercraft.client.render;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.math.Vector3f;
import com.mojang.math.Axis;
import dan200.computercraft.shared.media.items.PrintoutItem;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.world.entity.EntityType;
@ -30,7 +30,7 @@ public final class PrintoutItemRenderer extends ItemMapLikeRenderer {
@Override
protected void renderItem(PoseStack transform, MultiBufferSource render, ItemStack stack, int light) {
transform.mulPose(Vector3f.XP.rotationDegrees(180f));
transform.mulPose(Axis.XP.rotationDegrees(180f));
transform.scale(0.42f, 0.42f, -0.42f);
transform.translate(-0.5f, -0.48f, 0.0f);
@ -42,7 +42,7 @@ public final class PrintoutItemRenderer extends ItemMapLikeRenderer {
// Move a little bit forward to ensure we're not clipping with the frame
transform.translate(0.0f, 0.0f, -0.001f);
transform.mulPose(Vector3f.ZP.rotationDegrees(180f));
transform.mulPose(Axis.ZP.rotationDegrees(180f));
transform.scale(0.95f, 0.95f, -0.95f);
transform.translate(-0.5f, -0.5f, 0.0f);

@ -7,11 +7,11 @@ package dan200.computercraft.client.render;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.mojang.math.Matrix4f;
import dan200.computercraft.client.render.text.FixedWidthFontRenderer;
import dan200.computercraft.core.terminal.Palette;
import dan200.computercraft.core.terminal.TextBuffer;
import net.minecraft.client.renderer.MultiBufferSource;
import org.joml.Matrix4f;
import static dan200.computercraft.client.render.text.FixedWidthFontRenderer.FONT_HEIGHT;
import static dan200.computercraft.shared.media.items.PrintoutItem.LINES_PER_PAGE;

@ -15,7 +15,7 @@ import net.minecraft.client.renderer.RenderStateShard;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.ShaderInstance;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.packs.resources.ResourceManager;
import net.minecraft.server.packs.resources.ResourceProvider;
import javax.annotation.Nullable;
import java.io.IOException;
@ -60,7 +60,7 @@ public class RenderTypes {
return Objects.requireNonNull(GameRenderer.getRendertypeTextShader(), "Text shader has not been registered");
}
public static void registerShaders(ResourceManager resources, BiConsumer<ShaderInstance, Consumer<ShaderInstance>> load) throws IOException {
public static void registerShaders(ResourceProvider resources, BiConsumer<ShaderInstance, Consumer<ShaderInstance>> load) throws IOException {
load.accept(
new MonitorTextureBufferShader(
resources,

@ -7,8 +7,8 @@ package dan200.computercraft.client.render;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.mojang.math.Axis;
import com.mojang.math.Transformation;
import com.mojang.math.Vector3f;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.turtle.TurtleSide;
import dan200.computercraft.client.platform.ClientPlatformHelper;
@ -37,8 +37,8 @@ import javax.annotation.Nullable;
import java.util.List;
public class TurtleBlockEntityRenderer implements BlockEntityRenderer<TurtleBlockEntity> {
private static final ModelResourceLocation NORMAL_TURTLE_MODEL = new ModelResourceLocation("computercraft:turtle_normal", "inventory");
private static final ModelResourceLocation ADVANCED_TURTLE_MODEL = new ModelResourceLocation("computercraft:turtle_advanced", "inventory");
private static final ModelResourceLocation NORMAL_TURTLE_MODEL = new ModelResourceLocation(ComputerCraftAPI.MOD_ID, "turtle_normal", "inventory");
private static final ModelResourceLocation ADVANCED_TURTLE_MODEL = new ModelResourceLocation(ComputerCraftAPI.MOD_ID, "turtle_advanced", "inventory");
private static final ResourceLocation COLOUR_TURTLE_MODEL = new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_colour");
private static final ResourceLocation ELF_OVERLAY_MODEL = new ResourceLocation(ComputerCraftAPI.MOD_ID, "block/turtle_elf_overlay");
@ -96,7 +96,7 @@ public class TurtleBlockEntityRenderer implements BlockEntityRenderer<TurtleBloc
transform.translate(offset.x, offset.y, offset.z);
transform.translate(0.5f, 0.5f, 0.5f);
transform.mulPose(Vector3f.YP.rotationDegrees(180.0f - yaw));
transform.mulPose(Axis.YP.rotationDegrees(180.0f - yaw));
if (label != null && (label.equals("Dinnerbone") || label.equals("Grumm"))) {
// Flip the model
transform.scale(1.0f, -1.0f, 1.0f);
@ -131,7 +131,7 @@ public class TurtleBlockEntityRenderer implements BlockEntityRenderer<TurtleBloc
var toolAngle = turtle.getToolRenderAngle(side, f);
transform.translate(0.0f, 0.5f, 0.5f);
transform.mulPose(Vector3f.XN.rotationDegrees(toolAngle));
transform.mulPose(Axis.XN.rotationDegrees(toolAngle));
transform.translate(0.0f, -0.5f, -0.5f);
var model = TurtleUpgradeModellers.getModel(upgrade, turtle.getAccess(), side);

@ -12,9 +12,7 @@ import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.Tesselator;
import com.mojang.blaze3d.vertex.VertexBuffer;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.mojang.math.Matrix3f;
import com.mojang.math.Matrix4f;
import com.mojang.math.Vector3f;
import com.mojang.math.Axis;
import dan200.computercraft.client.FrameInfo;
import dan200.computercraft.client.integration.ShaderMod;
import dan200.computercraft.client.render.RenderTypes;
@ -28,10 +26,11 @@ import dan200.computercraft.shared.peripheral.monitor.ClientMonitor;
import dan200.computercraft.shared.peripheral.monitor.MonitorBlockEntity;
import dan200.computercraft.shared.peripheral.monitor.MonitorRenderer;
import dan200.computercraft.shared.util.DirectionUtil;
import net.minecraft.Util;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer;
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
import org.joml.Matrix3f;
import org.joml.Matrix4f;
import org.lwjgl.opengl.GL;
import org.lwjgl.opengl.GL11;
import org.lwjgl.opengl.GL20;
@ -56,7 +55,7 @@ public class MonitorBlockEntityRenderer implements BlockEntityRenderer<MonitorBl
*/
private static final float MARGIN = (float) (MonitorBlockEntity.RENDER_MARGIN * 1.1);
private static final Matrix3f IDENTITY_NORMAL = Util.make(new Matrix3f(), Matrix3f::setIdentity);
private static final Matrix3f IDENTITY_NORMAL = new Matrix3f().identity();
private static @Nullable ByteBuffer backingBuffer;
@ -100,8 +99,8 @@ public class MonitorBlockEntityRenderer implements BlockEntityRenderer<MonitorBl
originPos.getZ() - monitorPos.getZ() + 0.5
);
transform.mulPose(Vector3f.YN.rotationDegrees(yaw));
transform.mulPose(Vector3f.XP.rotationDegrees(pitch));
transform.mulPose(Axis.YN.rotationDegrees(yaw));
transform.mulPose(Axis.XP.rotationDegrees(pitch));
transform.translate(
-0.5 + MonitorBlockEntity.RENDER_BORDER + MonitorBlockEntity.RENDER_MARGIN,
origin.getHeight() - 0.5 - (MonitorBlockEntity.RENDER_BORDER + MonitorBlockEntity.RENDER_MARGIN) + 0,

@ -7,14 +7,14 @@ package dan200.computercraft.client.render.monitor;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.mojang.math.Matrix3f;
import com.mojang.math.Matrix4f;
import dan200.computercraft.shared.peripheral.monitor.MonitorBlockEntity;
import net.minecraft.client.Camera;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.core.Direction;
import net.minecraft.world.phys.BlockHitResult;
import org.joml.Matrix3f;
import org.joml.Matrix4f;
import java.util.EnumSet;

@ -7,14 +7,14 @@ package dan200.computercraft.client.render.text;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.mojang.math.Matrix4f;
import com.mojang.math.Vector3f;
import dan200.computercraft.client.FrameInfo;
import dan200.computercraft.core.terminal.Palette;
import dan200.computercraft.core.terminal.Terminal;
import dan200.computercraft.core.terminal.TextBuffer;
import dan200.computercraft.core.util.Colour;
import net.minecraft.resources.ResourceLocation;
import org.joml.Matrix4f;
import org.joml.Vector3f;
import static dan200.computercraft.client.render.RenderTypes.FULL_BRIGHT_LIGHTMAP;
@ -197,13 +197,13 @@ public final class FixedWidthFontRenderer {
// Render the foreground with a slight offset. By calling .translate() on the matrix itself, we're translating
// in screen space, rather than in model/view space.
// It's definitely not perfect, but better than z fighting!
var transformBackup = emitter.poseMatrix().copy();
var transformBackup = new Matrix4f(emitter.poseMatrix());
emitter.poseMatrix().translate(new Vector3f(0, 0, Z_OFFSET));
drawTerminalForeground(emitter, x, y, terminal);
drawCursor(emitter, x, y, terminal);
emitter.poseMatrix().load(transformBackup);
emitter.poseMatrix().set(transformBackup);
}
public static void drawEmptyTerminal(QuadEmitter emitter, float x, float y, float width, float height) {

@ -9,8 +9,8 @@ import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.BufferUploader;
import com.mojang.blaze3d.vertex.VertexBuffer;
import com.mojang.blaze3d.vertex.VertexFormat;
import com.mojang.math.Matrix4f;
import net.minecraft.client.renderer.ShaderInstance;
import org.joml.Matrix4f;
import org.lwjgl.opengl.GL15;
import org.lwjgl.opengl.GL15C;
import org.lwjgl.opengl.GL45C;

@ -6,15 +6,15 @@
package dan200.computercraft.mixin.client;
import dan200.computercraft.client.ClientHooks;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.client.multiplayer.ClientPacketListener;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(LocalPlayer.class)
class LocalPlayerMixin {
@Inject(method = "commandUnsigned", at = @At("HEAD"), cancellable = true)
@Mixin(ClientPacketListener.class)
class ClientPacketListenerMixin {
@Inject(method = "sendUnsignedCommand", at = @At("HEAD"), cancellable = true)
void commandUnsigned(String message, CallbackInfoReturnable<Boolean> ci) {
if (ClientHooks.onChatMessage(message)) ci.setReturnValue(true);
}

@ -7,7 +7,7 @@
"defaultRequire": 1
},
"client": [
"LocalPlayerMixin"
"ClientPacketListenerMixin"
],
"refmap": "client-computercraft.refmap.json"
}

@ -5,62 +5,50 @@
*/
package dan200.computercraft.data;
import com.mojang.datafixers.util.Pair;
import net.minecraft.data.DataGenerator;
import net.minecraft.data.DataProvider;
import net.minecraft.data.PackOutput;
import net.minecraft.data.loot.LootTableProvider.SubProviderEntry;
import net.minecraft.data.models.BlockModelGenerators;
import net.minecraft.data.models.ItemModelGenerators;
import net.minecraft.data.recipes.FinishedRecipe;
import net.minecraft.data.tags.TagsProvider;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.storage.loot.LootTable;
import net.minecraft.world.level.storage.loot.parameters.LootContextParamSet;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Supplier;
/**
* All data providers for ComputerCraft. We require a mod-loader abstraction {@link DataProviders.GeneratorFactory} to
* handle the slight differences between how Forge and Fabric expose Minecraft's data providers.
* All data providers for ComputerCraft. We require a mod-loader abstraction {@link GeneratorSink} (instead of
* {@link PackOutput})to handle the slight differences between how Forge and Fabric expose Minecraft's data providers.
*/
public final class DataProviders {
private DataProviders() {
}
public static void add(DataGenerator generator, GeneratorFactory generators, boolean includeServer, boolean includeClient) {
var turtleUpgrades = new TurtleUpgradeProvider(generator);
var pocketUpgrades = new PocketUpgradeProvider(generator);
public static void add(GeneratorSink generator) {
var turtleUpgrades = generator.add(TurtleUpgradeProvider::new);
var pocketUpgrades = generator.add(PocketUpgradeProvider::new);
generator.add(out -> new RecipeProvider(out, turtleUpgrades, pocketUpgrades));
generator.addProvider(includeServer, turtleUpgrades);
generator.addProvider(includeServer, pocketUpgrades);
generator.addProvider(includeServer, generators.recipes(new RecipeProvider(turtleUpgrades, pocketUpgrades)::addRecipes));
var blockTags = generator.blockTags(TagProvider::blockTags);
generator.itemTags(TagProvider::itemTags, blockTags);
var blockTags = generators.blockTags(TagProvider::blockTags);
generator.addProvider(includeServer, blockTags);
generator.addProvider(includeServer, generators.itemTags(TagProvider::itemTags, blockTags));
generator.lootTable(LootTableProvider.getTables());
for (var provider : generators.lootTable(LootTableProvider.getTables())) {
generator.addProvider(includeServer, provider);
}
generator.models(BlockModelProvider::addBlockModels, ItemModelProvider::addItemModels);
generator.addProvider(includeClient, generators.models(BlockModelProvider::addBlockModels, ItemModelProvider::addItemModels));
generator.addProvider(includeServer, new LanguageProvider(generator, turtleUpgrades, pocketUpgrades));
generator.add(out -> new LanguageProvider(out, turtleUpgrades, pocketUpgrades));
}
interface GeneratorFactory {
DataProvider recipes(Consumer<Consumer<FinishedRecipe>> recipes);
interface GeneratorSink {
<T extends DataProvider> T add(DataProvider.Factory<T> factory);
List<DataProvider> lootTable(List<Pair<Supplier<Consumer<BiConsumer<ResourceLocation, LootTable.Builder>>>, LootContextParamSet>> tables);
void lootTable(List<SubProviderEntry> tables);
TagsProvider<Block> blockTags(Consumer<TagProvider.TagConsumer<Block>> tags);
TagsProvider<Item> itemTags(Consumer<TagProvider.ItemTagConsumer> tags, TagsProvider<Block> blocks);
DataProvider models(Consumer<BlockModelGenerators> blocks, Consumer<ItemModelGenerators> items);
void models(Consumer<BlockModelGenerators> blocks, Consumer<ItemModelGenerators> items);
}
}

@ -18,35 +18,35 @@ import dan200.computercraft.shared.ModRegistry;
import dan200.computercraft.shared.computer.metrics.basic.Aggregate;
import dan200.computercraft.shared.computer.metrics.basic.AggregatedMetric;
import dan200.computercraft.shared.config.ConfigSpec;
import dan200.computercraft.shared.platform.Registries;
import dan200.computercraft.shared.platform.RegistryWrappers;
import net.minecraft.data.CachedOutput;
import net.minecraft.data.DataGenerator;
import net.minecraft.data.DataProvider;
import net.minecraft.data.PackOutput;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.block.Block;
import net.minecraftforge.common.ForgeConfigSpec;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Stream;
public final class LanguageProvider implements DataProvider {
private final DataGenerator generator;
private final PackOutput output;
private final TurtleUpgradeDataProvider turtleUpgrades;
private final PocketUpgradeDataProvider pocketUpgrades;
private final Map<String, String> translations = new HashMap<>();
public LanguageProvider(DataGenerator generator, TurtleUpgradeDataProvider turtleUpgrades, PocketUpgradeDataProvider pocketUpgrades) {
this.generator = generator;
public LanguageProvider(PackOutput output, TurtleUpgradeDataProvider turtleUpgrades, PocketUpgradeDataProvider pocketUpgrades) {
this.output = output;
this.turtleUpgrades = turtleUpgrades;
this.pocketUpgrades = pocketUpgrades;
}
@Override
public void run(CachedOutput cachedOutput) throws IOException {
public CompletableFuture<?> run(CachedOutput cachedOutput) {
addTranslations();
getExpectedKeys().forEach(x -> {
if (!translations.containsKey(x)) throw new IllegalStateException("No translation for " + x);
@ -54,7 +54,7 @@ public final class LanguageProvider implements DataProvider {
var json = new JsonObject();
for (var pair : translations.entrySet()) json.addProperty(pair.getKey(), pair.getValue());
DataProvider.saveStable(cachedOutput, json, generator.getOutputFolder().resolve("assets/" + ComputerCraftAPI.MOD_ID + "/lang/en_us.json"));
return DataProvider.saveStable(cachedOutput, json, output.getOutputFolder().resolve("assets/" + ComputerCraftAPI.MOD_ID + "/lang/en_us.json"));
}
@Override
@ -190,6 +190,7 @@ public final class LanguageProvider implements DataProvider {
add(AggregatedMetric.TRANSLATION_PREFIX + Aggregate.COUNT.id(), "%s (count)");
// Additional UI elements
add("gui.computercraft.terminal", "Computer terminal");
add("gui.computercraft.tooltip.copy", "Copy to clipboard");
add("gui.computercraft.tooltip.computer_id", "Computer ID: %s");
add("gui.computercraft.tooltip.disk_id", "Disk ID: %s");
@ -266,11 +267,11 @@ public final class LanguageProvider implements DataProvider {
private Stream<String> getExpectedKeys() {
return Stream.of(
Registries.BLOCKS.stream()
.filter(x -> Registries.BLOCKS.getKey(x).getNamespace().equals(ComputerCraftAPI.MOD_ID))
RegistryWrappers.BLOCKS.stream()
.filter(x -> RegistryWrappers.BLOCKS.getKey(x).getNamespace().equals(ComputerCraftAPI.MOD_ID))
.map(Block::getDescriptionId),
Registries.ITEMS.stream()
.filter(x -> Registries.ITEMS.getKey(x).getNamespace().equals(ComputerCraftAPI.MOD_ID))
RegistryWrappers.ITEMS.stream()
.filter(x -> RegistryWrappers.ITEMS.getKey(x).getNamespace().equals(ComputerCraftAPI.MOD_ID))
.map(Item::getDescriptionId),
turtleUpgrades.getGeneratedUpgrades().stream().map(UpgradeBase::getUnlocalisedAdjective),
pocketUpgrades.getGeneratedUpgrades().stream().map(UpgradeBase::getUnlocalisedAdjective),

@ -5,7 +5,6 @@
*/
package dan200.computercraft.data;
import com.mojang.datafixers.util.Pair;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.shared.CommonHooks;
import dan200.computercraft.shared.ModRegistry;
@ -15,6 +14,7 @@ import dan200.computercraft.shared.data.PlayerCreativeLootCondition;
import dan200.computercraft.shared.peripheral.modem.wired.CableBlock;
import dan200.computercraft.shared.peripheral.modem.wired.CableModemVariant;
import net.minecraft.advancements.critereon.StatePropertiesPredicate;
import net.minecraft.data.loot.LootTableProvider.SubProviderEntry;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.storage.loot.LootPool;
@ -23,7 +23,6 @@ import net.minecraft.world.level.storage.loot.entries.DynamicLoot;
import net.minecraft.world.level.storage.loot.entries.LootItem;
import net.minecraft.world.level.storage.loot.entries.LootPoolEntryContainer;
import net.minecraft.world.level.storage.loot.functions.CopyNameFunction;
import net.minecraft.world.level.storage.loot.parameters.LootContextParamSet;
import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets;
import net.minecraft.world.level.storage.loot.predicates.AlternativeLootItemCondition;
import net.minecraft.world.level.storage.loot.predicates.ExplosionCondition;
@ -33,14 +32,13 @@ import net.minecraft.world.level.storage.loot.providers.number.ConstantValue;
import java.util.List;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Supplier;
class LootTableProvider {
public static List<Pair<Supplier<Consumer<BiConsumer<ResourceLocation, LootTable.Builder>>>, LootContextParamSet>> getTables() {
public static List<SubProviderEntry> getTables() {
return List.of(
Pair.of(() -> LootTableProvider::registerBlocks, LootContextParamSets.BLOCK),
Pair.of(() -> LootTableProvider::registerGeneric, LootContextParamSets.ALL_PARAMS)
new SubProviderEntry(() -> LootTableProvider::registerBlocks, LootContextParamSets.BLOCK),
new SubProviderEntry(() -> LootTableProvider::registerGeneric, LootContextParamSets.ALL_PARAMS)
);
}

@ -6,10 +6,11 @@
package dan200.computercraft.data;
import com.google.gson.JsonElement;
import dan200.computercraft.shared.platform.Registries;
import dan200.computercraft.shared.platform.RegistryWrappers;
import net.minecraft.Util;
import net.minecraft.data.CachedOutput;
import net.minecraft.data.DataGenerator;
import net.minecraft.data.DataProvider;
import net.minecraft.data.PackOutput;
import net.minecraft.data.models.BlockModelGenerators;
import net.minecraft.data.models.ItemModelGenerators;
import net.minecraft.data.models.blockstates.BlockStateGenerator;
@ -18,14 +19,10 @@ import net.minecraft.data.models.model.ModelLocationUtils;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.block.Block;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.nio.file.Path;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
@ -37,24 +34,22 @@ import java.util.function.Supplier;
* Please don't sue me Mojang. Or at least make these changes to vanilla before doing so!
*/
public class ModelProvider implements DataProvider {
private static final Logger LOG = LoggerFactory.getLogger(ModelProvider.class);
private final DataGenerator.PathProvider blockStatePath;
private final DataGenerator.PathProvider modelPath;
private final PackOutput.PathProvider blockStatePath;
private final PackOutput.PathProvider modelPath;
private final Consumer<BlockModelGenerators> blocks;
private final Consumer<ItemModelGenerators> items;
public ModelProvider(DataGenerator generator, Consumer<BlockModelGenerators> blocks, Consumer<ItemModelGenerators> items) {
blockStatePath = generator.createPathProvider(DataGenerator.Target.RESOURCE_PACK, "blockstates");
modelPath = generator.createPathProvider(DataGenerator.Target.RESOURCE_PACK, "models");
public ModelProvider(PackOutput output, Consumer<BlockModelGenerators> blocks, Consumer<ItemModelGenerators> items) {
blockStatePath = output.createPathProvider(PackOutput.Target.RESOURCE_PACK, "blockstates");
modelPath = output.createPathProvider(PackOutput.Target.RESOURCE_PACK, "models");
this.blocks = blocks;
this.items = items;
}
@Override
public void run(CachedOutput output) {
public CompletableFuture<?> run(CachedOutput output) {
Map<Block, BlockStateGenerator> blockStates = new HashMap<>();
Consumer<BlockStateGenerator> addBlockState = generator -> {
var block = generator.getBlock();
@ -73,7 +68,7 @@ public class ModelProvider implements DataProvider {
blocks.accept(new BlockModelGenerators(addBlockState, addModel, explicitItems::add));
items.accept(new ItemModelGenerators(addModel));
for (var block : Registries.BLOCKS) {
for (var block : RegistryWrappers.BLOCKS) {
if (!blockStates.containsKey(block)) continue;
var item = Item.BY_BLOCK.get(block);
@ -85,18 +80,17 @@ public class ModelProvider implements DataProvider {
}
}
saveCollection(output, blockStates, x -> blockStatePath.json(Registries.BLOCKS.getKey(x)));
saveCollection(output, models, modelPath::json);
List<CompletableFuture<?>> futures = new ArrayList<>();
saveCollection(output, futures, blockStates, x -> blockStatePath.json(RegistryWrappers.BLOCKS.getKey(x)));
saveCollection(output, futures, models, modelPath::json);
return Util.sequenceFailFast(futures);
}
private <T> void saveCollection(CachedOutput output, Map<T, ? extends Supplier<JsonElement>> items, Function<T, Path> getLocation) {
private <T> void saveCollection(CachedOutput output, List<CompletableFuture<?>> futures, Map<T, ? extends Supplier<JsonElement>> items, Function<T, Path> getLocation) {
for (Map.Entry<T, ? extends Supplier<JsonElement>> entry : items.entrySet()) {
var path = getLocation.apply(entry.getKey());
try {
DataProvider.saveStable(output, entry.getValue().get(), path);
} catch (Exception exception) {
LOG.error("Couldn't save {}", path, exception);
}
futures.add(DataProvider.saveStable(output, entry.getValue().get(), path));
}
}

@ -8,7 +8,7 @@ package dan200.computercraft.data;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.pocket.PocketUpgradeDataProvider;
import dan200.computercraft.api.pocket.PocketUpgradeSerialiser;
import net.minecraft.data.DataGenerator;
import net.minecraft.data.PackOutput;
import net.minecraft.resources.ResourceLocation;
import java.util.function.Consumer;
@ -17,8 +17,8 @@ import static dan200.computercraft.shared.ModRegistry.Items;
import static dan200.computercraft.shared.ModRegistry.PocketUpgradeSerialisers;
class PocketUpgradeProvider extends PocketUpgradeDataProvider {
PocketUpgradeProvider(DataGenerator generator) {
super(generator);
PocketUpgradeProvider(PackOutput output) {
super(output);
}
@Override

@ -15,16 +15,14 @@ import dan200.computercraft.shared.common.IColouredItem;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.platform.PlatformHelper;
import dan200.computercraft.shared.platform.RecipeIngredients;
import dan200.computercraft.shared.platform.Registries;
import dan200.computercraft.shared.platform.RegistryWrappers;
import dan200.computercraft.shared.pocket.items.PocketComputerItemFactory;
import dan200.computercraft.shared.turtle.items.TurtleItemFactory;
import net.minecraft.advancements.critereon.InventoryChangeTrigger;
import net.minecraft.advancements.critereon.ItemPredicate;
import net.minecraft.core.Registry;
import net.minecraft.data.recipes.FinishedRecipe;
import net.minecraft.data.recipes.ShapedRecipeBuilder;
import net.minecraft.data.recipes.ShapelessRecipeBuilder;
import net.minecraft.data.recipes.SpecialRecipeBuilder;
import net.minecraft.core.registries.Registries;
import net.minecraft.data.PackOutput;
import net.minecraft.data.recipes.*;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.TagKey;
@ -36,7 +34,7 @@ import net.minecraft.world.item.Items;
import net.minecraft.world.item.crafting.Ingredient;
import net.minecraft.world.item.crafting.RecipeSerializer;
import net.minecraft.world.item.crafting.ShapedRecipe;
import net.minecraft.world.item.crafting.SimpleRecipeSerializer;
import net.minecraft.world.item.crafting.SimpleCraftingRecipeSerializer;
import net.minecraft.world.level.ItemLike;
import net.minecraft.world.level.block.Blocks;
@ -46,17 +44,19 @@ import java.util.function.Consumer;
import static dan200.computercraft.api.ComputerCraftTags.Items.COMPUTER;
import static dan200.computercraft.api.ComputerCraftTags.Items.WIRED_MODEM;
class RecipeProvider {
class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
private final RecipeIngredients ingredients = PlatformHelper.get().getRecipeIngredients();
private final TurtleUpgradeDataProvider turtleUpgrades;
private final PocketUpgradeDataProvider pocketUpgrades;
RecipeProvider(TurtleUpgradeDataProvider turtleUpgrades, PocketUpgradeDataProvider pocketUpgrades) {
RecipeProvider(PackOutput output, TurtleUpgradeDataProvider turtleUpgrades, PocketUpgradeDataProvider pocketUpgrades) {
super(output);
this.turtleUpgrades = turtleUpgrades;
this.pocketUpgrades = pocketUpgrades;
}
public void addRecipes(Consumer<FinishedRecipe> add) {
@Override
public void buildRecipes(Consumer<FinishedRecipe> add) {
basicRecipes(add);
diskColours(add);
pocketUpgrades(add);
@ -77,7 +77,7 @@ class RecipeProvider {
private void diskColours(Consumer<FinishedRecipe> add) {
for (var colour : Colour.VALUES) {
ShapelessRecipeBuilder
.shapeless(ModRegistry.Items.DISK.get())
.shapeless(RecipeCategory.REDSTONE, ModRegistry.Items.DISK.get())
.requires(ingredients.redstone())
.requires(Items.PAPER)
.requires(DyeItem.byColor(ofColour(colour)))
@ -106,7 +106,7 @@ class RecipeProvider {
for (var upgrade : turtleUpgrades.getGeneratedUpgrades()) {
var result = TurtleItemFactory.create(-1, null, -1, family, null, upgrade, -1, null);
ShapedRecipeBuilder
.shaped(result.getItem())
.shaped(RecipeCategory.REDSTONE, result.getItem())
.group(String.format("%s:turtle_%s", ComputerCraftAPI.MOD_ID, nameId))
.pattern("#T")
.define('T', base.getItem())
@ -138,7 +138,7 @@ class RecipeProvider {
for (var upgrade : pocketUpgrades.getGeneratedUpgrades()) {
var result = PocketComputerItemFactory.create(-1, null, -1, family, upgrade);
ShapedRecipeBuilder
.shaped(result.getItem())
.shaped(RecipeCategory.REDSTONE, result.getItem())
.group(String.format("%s:pocket_%s", ComputerCraftAPI.MOD_ID, nameId))
.pattern("#")
.pattern("P")
@ -158,7 +158,7 @@ class RecipeProvider {
private void basicRecipes(Consumer<FinishedRecipe> add) {
ShapedRecipeBuilder
.shaped(ModRegistry.Items.CABLE.get(), 6)
.shaped(RecipeCategory.REDSTONE, ModRegistry.Items.CABLE.get(), 6)
.pattern(" # ")
.pattern("#R#")
.pattern(" # ")
@ -169,7 +169,7 @@ class RecipeProvider {
.save(add);
ShapedRecipeBuilder
.shaped(ModRegistry.Blocks.COMPUTER_NORMAL.get())
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.COMPUTER_NORMAL.get())
.pattern("###")
.pattern("#R#")
.pattern("#G#")
@ -180,7 +180,7 @@ class RecipeProvider {
.save(add);
ShapedRecipeBuilder
.shaped(ModRegistry.Blocks.COMPUTER_ADVANCED.get())
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.COMPUTER_ADVANCED.get())
.pattern("###")
.pattern("#R#")
.pattern("#G#")
@ -191,7 +191,7 @@ class RecipeProvider {
.save(add);
ShapedRecipeBuilder
.shaped(ModRegistry.Items.COMPUTER_ADVANCED.get())
.shaped(RecipeCategory.REDSTONE, ModRegistry.Items.COMPUTER_ADVANCED.get())
.pattern("###")
.pattern("#C#")
.pattern("# #")
@ -204,7 +204,7 @@ class RecipeProvider {
);
ShapedRecipeBuilder
.shaped(ModRegistry.Blocks.COMPUTER_COMMAND.get())
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.COMPUTER_COMMAND.get())
.pattern("###")
.pattern("#R#")
.pattern("#G#")
@ -215,7 +215,7 @@ class RecipeProvider {
.save(add);
ShapedRecipeBuilder
.shaped(ModRegistry.Blocks.TURTLE_NORMAL.get())
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.TURTLE_NORMAL.get())
.pattern("###")
.pattern("#C#")
.pattern("#I#")
@ -226,7 +226,7 @@ class RecipeProvider {
.save(RecipeWrapper.wrap(ModRegistry.RecipeSerializers.TURTLE.get(), add).withExtraData(family(ComputerFamily.NORMAL)));
ShapedRecipeBuilder
.shaped(ModRegistry.Blocks.TURTLE_ADVANCED.get())
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.TURTLE_ADVANCED.get())
.pattern("###")
.pattern("#C#")
.pattern("#I#")
@ -237,7 +237,7 @@ class RecipeProvider {
.save(RecipeWrapper.wrap(ModRegistry.RecipeSerializers.TURTLE.get(), add).withExtraData(family(ComputerFamily.ADVANCED)));
ShapedRecipeBuilder
.shaped(ModRegistry.Blocks.TURTLE_ADVANCED.get())
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.TURTLE_ADVANCED.get())
.pattern("###")
.pattern("#C#")
.pattern(" B ")
@ -251,7 +251,7 @@ class RecipeProvider {
);
ShapedRecipeBuilder
.shaped(ModRegistry.Blocks.DISK_DRIVE.get())
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.DISK_DRIVE.get())
.pattern("###")
.pattern("#R#")
.pattern("#R#")
@ -261,7 +261,7 @@ class RecipeProvider {
.save(add);
ShapedRecipeBuilder
.shaped(ModRegistry.Blocks.MONITOR_NORMAL.get())
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.MONITOR_NORMAL.get())
.pattern("###")
.pattern("#G#")
.pattern("###")
@ -271,7 +271,7 @@ class RecipeProvider {
.save(add);
ShapedRecipeBuilder
.shaped(ModRegistry.Blocks.MONITOR_ADVANCED.get(), 4)
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.MONITOR_ADVANCED.get(), 4)
.pattern("###")
.pattern("#G#")
.pattern("###")
@ -281,7 +281,7 @@ class RecipeProvider {
.save(add);
ShapedRecipeBuilder
.shaped(ModRegistry.Items.POCKET_COMPUTER_NORMAL.get())
.shaped(RecipeCategory.REDSTONE, ModRegistry.Items.POCKET_COMPUTER_NORMAL.get())
.pattern("###")
.pattern("#A#")
.pattern("#G#")
@ -293,7 +293,7 @@ class RecipeProvider {
.save(add);
ShapedRecipeBuilder
.shaped(ModRegistry.Items.POCKET_COMPUTER_ADVANCED.get())
.shaped(RecipeCategory.REDSTONE, ModRegistry.Items.POCKET_COMPUTER_ADVANCED.get())
.pattern("###")
.pattern("#A#")
.pattern("#G#")
@ -305,7 +305,7 @@ class RecipeProvider {
.save(add);
ShapedRecipeBuilder
.shaped(ModRegistry.Items.POCKET_COMPUTER_ADVANCED.get())
.shaped(RecipeCategory.REDSTONE, ModRegistry.Items.POCKET_COMPUTER_ADVANCED.get())
.pattern("###")
.pattern("#C#")
.pattern("# #")
@ -318,7 +318,7 @@ class RecipeProvider {
);
ShapedRecipeBuilder
.shaped(ModRegistry.Blocks.PRINTER.get())
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.PRINTER.get())
.pattern("###")
.pattern("#R#")
.pattern("#D#")
@ -329,7 +329,7 @@ class RecipeProvider {
.save(add);
ShapedRecipeBuilder
.shaped(ModRegistry.Blocks.SPEAKER.get())
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.SPEAKER.get())
.pattern("###")
.pattern("#N#")
.pattern("#R#")
@ -340,7 +340,7 @@ class RecipeProvider {
.save(add);
ShapedRecipeBuilder
.shaped(ModRegistry.Items.WIRED_MODEM.get())
.shaped(RecipeCategory.REDSTONE, ModRegistry.Items.WIRED_MODEM.get())
.pattern("###")
.pattern("#R#")
.pattern("###")
@ -351,18 +351,18 @@ class RecipeProvider {
.save(add);
ShapelessRecipeBuilder
.shapeless(ModRegistry.Blocks.WIRED_MODEM_FULL.get())
.shapeless(RecipeCategory.REDSTONE, ModRegistry.Blocks.WIRED_MODEM_FULL.get())
.requires(ModRegistry.Items.WIRED_MODEM.get())
.unlockedBy("has_modem", inventoryChange(WIRED_MODEM))
.save(add, new ResourceLocation(ComputerCraftAPI.MOD_ID, "wired_modem_full_from"));
ShapelessRecipeBuilder
.shapeless(ModRegistry.Items.WIRED_MODEM.get())
.shapeless(RecipeCategory.REDSTONE, ModRegistry.Items.WIRED_MODEM.get())
.requires(ModRegistry.Blocks.WIRED_MODEM_FULL.get())
.unlockedBy("has_modem", inventoryChange(WIRED_MODEM))
.save(add, new ResourceLocation(ComputerCraftAPI.MOD_ID, "wired_modem_full_to"));
ShapedRecipeBuilder
.shaped(ModRegistry.Blocks.WIRELESS_MODEM_NORMAL.get())
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.WIRELESS_MODEM_NORMAL.get())
.pattern("###")
.pattern("#E#")
.pattern("###")
@ -372,7 +372,7 @@ class RecipeProvider {
.save(add);
ShapedRecipeBuilder
.shaped(ModRegistry.Blocks.WIRELESS_MODEM_ADVANCED.get())
.shaped(RecipeCategory.REDSTONE, ModRegistry.Blocks.WIRELESS_MODEM_ADVANCED.get())
.pattern("###")
.pattern("#E#")
.pattern("###")
@ -383,7 +383,7 @@ class RecipeProvider {
.save(add);
ShapelessRecipeBuilder
.shapeless(Items.PLAYER_HEAD)
.shapeless(RecipeCategory.DECORATIONS, Items.PLAYER_HEAD)
.requires(ingredients.head())
.requires(ModRegistry.Items.MONITOR_NORMAL.get())
.unlockedBy("has_monitor", inventoryChange(ModRegistry.Items.MONITOR_NORMAL.get()))
@ -394,7 +394,7 @@ class RecipeProvider {
);
ShapelessRecipeBuilder
.shapeless(Items.PLAYER_HEAD)
.shapeless(RecipeCategory.DECORATIONS, Items.PLAYER_HEAD)
.requires(ingredients.head())
.requires(ModRegistry.Items.COMPUTER_ADVANCED.get())
.unlockedBy("has_computer", inventoryChange(ModRegistry.Items.COMPUTER_ADVANCED.get()))
@ -405,14 +405,14 @@ class RecipeProvider {
);
ShapelessRecipeBuilder
.shapeless(ModRegistry.Items.PRINTED_PAGES.get())
.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()))
.save(RecipeWrapper.wrap(ModRegistry.RecipeSerializers.IMPOSTOR_SHAPELESS.get(), add));
ShapelessRecipeBuilder
.shapeless(ModRegistry.Items.PRINTED_BOOK.get())
.shapeless(RecipeCategory.REDSTONE, ModRegistry.Items.PRINTED_BOOK.get())
.requires(ingredients.leather())
.requires(ModRegistry.Items.PRINTED_PAGE.get(), 1)
.requires(ingredients.string())
@ -451,7 +451,7 @@ class RecipeProvider {
if (object.has("item")) {
return itemPredicate(ShapedRecipe.itemFromJson(object));
} else if (object.has("tag")) {
return itemPredicate(TagKey.create(Registry.ITEM_REGISTRY, new ResourceLocation(GsonHelper.getAsString(object, "tag"))));
return itemPredicate(TagKey.create(Registries.ITEM, new ResourceLocation(GsonHelper.getAsString(object, "tag"))));
} else {
throw new IllegalArgumentException("Unknown ingredient " + json);
}
@ -471,7 +471,7 @@ class RecipeProvider {
return json -> json.addProperty("family", family.toString());
}
private static void addSpecial(Consumer<FinishedRecipe> add, SimpleRecipeSerializer<?> special) {
SpecialRecipeBuilder.special(special).save(add, Registries.RECIPE_SERIALIZERS.getKey(special).toString());
private static void addSpecial(Consumer<FinishedRecipe> add, SimpleCraftingRecipeSerializer<?> special) {
SpecialRecipeBuilder.special(special).save(add, RegistryWrappers.RECIPE_SERIALIZERS.getKey(special).toString());
}
}

@ -7,10 +7,12 @@ package dan200.computercraft.data;
import dan200.computercraft.api.ComputerCraftTags;
import dan200.computercraft.shared.ModRegistry;
import dan200.computercraft.shared.platform.RegistryWrappers;
import net.minecraft.data.tags.ItemTagsProvider;
import net.minecraft.data.tags.TagsProvider;
import net.minecraft.tags.BlockTags;
import net.minecraft.tags.ItemTags;
import net.minecraft.tags.TagBuilder;
import net.minecraft.tags.TagKey;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.block.Block;
@ -91,7 +93,25 @@ class TagProvider {
* @param <T> The type of object we're providing tags for.
*/
public interface TagConsumer<T> {
TagsProvider.TagAppender<T> tag(TagKey<T> tag);
TagAppender<T> tag(TagKey<T> tag);
}
public record TagAppender<T>(RegistryWrappers.RegistryWrapper<T> registry, TagBuilder builder) {
public TagAppender<T> add(T object) {
builder.addElement(registry.getKey(object));
return this;
}
@SafeVarargs
public final TagAppender<T> add(T... objects) {
for (var object : objects) add(object);
return this;
}
public TagAppender<T> addTag(TagKey<T> tag) {
builder.addTag(tag.location());
return this;
}
}
/**

@ -9,7 +9,7 @@ import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.ComputerCraftTags.Blocks;
import dan200.computercraft.api.turtle.TurtleUpgradeDataProvider;
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
import net.minecraft.data.DataGenerator;
import net.minecraft.data.PackOutput;
import net.minecraft.resources.ResourceLocation;
import java.util.function.Consumer;
@ -18,8 +18,8 @@ import static dan200.computercraft.shared.ModRegistry.Items;
import static dan200.computercraft.shared.ModRegistry.TurtleSerialisers;
class TurtleUpgradeProvider extends TurtleUpgradeDataProvider {
TurtleUpgradeProvider(DataGenerator generator) {
super(generator);
TurtleUpgradeProvider(PackOutput output) {
super(output);
}
@Override

@ -1,24 +0,0 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.mixin;
import net.minecraft.world.item.CreativeModeTab;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Mutable;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(CreativeModeTab.class)
public interface CreativeModeTabAccessor {
@Accessor("langId")
String computercraft$langId();
@Final
@Mutable
@Accessor("TABS")
static void computercraft$setTabs(CreativeModeTab[] tabs) {
}
}

@ -12,6 +12,9 @@ import dan200.computercraft.api.detail.VanillaDetailRegistries;
import dan200.computercraft.api.media.IMedia;
import dan200.computercraft.api.pocket.PocketUpgradeSerialiser;
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
import dan200.computercraft.core.util.Colour;
import dan200.computercraft.impl.PocketUpgrades;
import dan200.computercraft.impl.TurtleUpgrades;
import dan200.computercraft.shared.command.arguments.ComputerArgumentType;
import dan200.computercraft.shared.command.arguments.ComputersArgumentType;
import dan200.computercraft.shared.command.arguments.RepeatArgumentType;
@ -75,16 +78,14 @@ import dan200.computercraft.shared.util.ImpostorShapelessRecipe;
import net.minecraft.commands.synchronization.ArgumentTypeInfo;
import net.minecraft.commands.synchronization.SingletonArgumentInfo;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Registry;
import net.minecraft.core.cauldron.CauldronInteraction;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.core.registries.Registries;
import net.minecraft.network.chat.Component;
import net.minecraft.world.inventory.MenuType;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.RecordItem;
import net.minecraft.world.item.*;
import net.minecraft.world.item.crafting.CustomRecipe;
import net.minecraft.world.item.crafting.RecipeSerializer;
import net.minecraft.world.item.crafting.SimpleRecipeSerializer;
import net.minecraft.world.item.crafting.SimpleCraftingRecipeSerializer;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
@ -94,7 +95,6 @@ import net.minecraft.world.level.material.Material;
import net.minecraft.world.level.storage.loot.predicates.LootItemConditionType;
import java.util.function.BiFunction;
import java.util.function.Function;
/**
* Registers ComputerCraft's registry entries and additional objects, such as {@link CauldronInteraction}s and
@ -107,7 +107,7 @@ public final class ModRegistry {
}
public static final class Blocks {
static final RegistrationHelper<Block> REGISTRY = PlatformHelper.get().createRegistrationHelper(Registry.BLOCK_REGISTRY);
static final RegistrationHelper<Block> REGISTRY = PlatformHelper.get().createRegistrationHelper(Registries.BLOCK);
private static BlockBehaviour.Properties properties() {
return BlockBehaviour.Properties.of(Material.STONE).strength(2);
@ -162,7 +162,7 @@ public final class ModRegistry {
}
public static class BlockEntities {
static final RegistrationHelper<BlockEntityType<?>> REGISTRY = PlatformHelper.get().createRegistrationHelper(Registry.BLOCK_ENTITY_TYPE_REGISTRY);
static final RegistrationHelper<BlockEntityType<?>> REGISTRY = PlatformHelper.get().createRegistrationHelper(Registries.BLOCK_ENTITY_TYPE);
private static <T extends BlockEntity> RegistryEntry<BlockEntityType<T>> ofBlock(RegistryEntry<? extends Block> block, BiFunction<BlockPos, BlockState, T> factory) {
return REGISTRY.register(block.id().getPath(), () -> PlatformHelper.get().createBlockEntityType(factory, block.get()));
@ -203,10 +203,10 @@ public final class ModRegistry {
}
public static final class Items {
static final RegistrationHelper<Item> REGISTRY = PlatformHelper.get().createRegistrationHelper(Registry.ITEM_REGISTRY);
static final RegistrationHelper<Item> REGISTRY = PlatformHelper.get().createRegistrationHelper(Registries.ITEM);
private static Item.Properties properties() {
return new Item.Properties().tab(PlatformHelper.get().getCreativeTab());
return new Item.Properties();
}
private static <B extends Block, I extends Item> RegistryEntry<I> ofBlock(RegistryEntry<B> parent, BiFunction<B, Item.Properties, I> supplier) {
@ -279,7 +279,7 @@ public final class ModRegistry {
}
public static class Menus {
static final RegistrationHelper<MenuType<?>> REGISTRY = PlatformHelper.get().createRegistrationHelper(Registry.MENU_REGISTRY);
static final RegistrationHelper<MenuType<?>> REGISTRY = PlatformHelper.get().createRegistrationHelper(Registries.MENU);
public static final RegistryEntry<MenuType<ComputerMenuWithoutInventory>> COMPUTER = REGISTRY.register("computer",
() -> ContainerData.toType(ComputerContainerData::new, (id, inv, data) -> new ComputerMenuWithoutInventory(Menus.COMPUTER.get(), id, inv, data)));
@ -307,7 +307,7 @@ public final class ModRegistry {
}
static class ArgumentTypes {
static final RegistrationHelper<ArgumentTypeInfo<?, ?>> REGISTRY = PlatformHelper.get().createRegistrationHelper(Registry.COMMAND_ARGUMENT_TYPE_REGISTRY);
static final RegistrationHelper<ArgumentTypeInfo<?, ?>> REGISTRY = PlatformHelper.get().createRegistrationHelper(Registries.COMMAND_ARGUMENT_TYPE);
@SuppressWarnings("unchecked")
private static <T extends ArgumentType<?>> void registerUnsafe(String name, Class<T> type, ArgumentTypeInfo<?, ?> serializer) {
@ -331,7 +331,7 @@ public final class ModRegistry {
}
public static class LootItemConditionTypes {
static final RegistrationHelper<LootItemConditionType> REGISTRY = PlatformHelper.get().createRegistrationHelper(Registry.LOOT_ITEM_REGISTRY);
static final RegistrationHelper<LootItemConditionType> REGISTRY = PlatformHelper.get().createRegistrationHelper(Registries.LOOT_CONDITION_TYPE);
public static final RegistryEntry<LootItemConditionType> BLOCK_NAMED = REGISTRY.register("block_named",
() -> ConstantLootConditionSerializer.type(BlockNamedEntityLootCondition.INSTANCE));
@ -344,18 +344,18 @@ public final class ModRegistry {
}
public static class RecipeSerializers {
static final RegistrationHelper<RecipeSerializer<?>> REGISTRY = PlatformHelper.get().createRegistrationHelper(Registry.RECIPE_SERIALIZER_REGISTRY);
static final RegistrationHelper<RecipeSerializer<?>> REGISTRY = PlatformHelper.get().createRegistrationHelper(Registries.RECIPE_SERIALIZER);
private static <T extends CustomRecipe> RegistryEntry<SimpleRecipeSerializer<T>> simple(String name, Function<ResourceLocation, T> factory) {
return REGISTRY.register(name, () -> new SimpleRecipeSerializer<>(factory));
private static <T extends CustomRecipe> RegistryEntry<SimpleCraftingRecipeSerializer<T>> simple(String name, SimpleCraftingRecipeSerializer.Factory<T> factory) {
return REGISTRY.register(name, () -> new SimpleCraftingRecipeSerializer<>(factory));
}
public static final RegistryEntry<SimpleRecipeSerializer<ColourableRecipe>> DYEABLE_ITEM = simple("colour", ColourableRecipe::new);
public static final RegistryEntry<SimpleCraftingRecipeSerializer<ColourableRecipe>> DYEABLE_ITEM = simple("colour", ColourableRecipe::new);
public static final RegistryEntry<TurtleRecipe.Serializer> TURTLE = REGISTRY.register("turtle", TurtleRecipe.Serializer::new);
public static final RegistryEntry<SimpleRecipeSerializer<TurtleUpgradeRecipe>> TURTLE_UPGRADE = simple("turtle_upgrade", TurtleUpgradeRecipe::new);
public static final RegistryEntry<SimpleRecipeSerializer<PocketComputerUpgradeRecipe>> POCKET_COMPUTER_UPGRADE = simple("pocket_computer_upgrade", PocketComputerUpgradeRecipe::new);
public static final RegistryEntry<SimpleRecipeSerializer<PrintoutRecipe>> PRINTOUT = simple("printout", PrintoutRecipe::new);
public static final RegistryEntry<SimpleRecipeSerializer<DiskRecipe>> DISK = simple("disk", DiskRecipe::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<SimpleCraftingRecipeSerializer<DiskRecipe>> DISK = simple("disk", DiskRecipe::new);
public static final RegistryEntry<ComputerUpgradeRecipe.Serializer> COMPUTER_UPGRADE = REGISTRY.register("computer_upgrade", ComputerUpgradeRecipe.Serializer::new);
public static final RegistryEntry<ImpostorRecipe.Serializer> IMPOSTOR_SHAPED = REGISTRY.register("impostor_shaped", ImpostorRecipe.Serializer::new);
public static final RegistryEntry<ImpostorShapelessRecipe.Serializer> IMPOSTOR_SHAPELESS = REGISTRY.register("impostor_shapeless", ImpostorShapelessRecipe.Serializer::new);
@ -396,4 +396,58 @@ public final class ModRegistry {
CauldronInteraction.WATER.put(ModRegistry.Items.TURTLE_NORMAL.get(), TurtleItem.CAULDRON_INTERACTION);
CauldronInteraction.WATER.put(ModRegistry.Items.TURTLE_ADVANCED.get(), TurtleItem.CAULDRON_INTERACTION);
}
/**
* Configure a {@link CreativeModeTab.Builder} to contain all of ComputerCraft's items.
*
* @param builder The builder to configure.
* @return The same building, for calling {@link CreativeModeTab.Builder#build()} on.
*/
public static CreativeModeTab.Builder registerCreativeTab(CreativeModeTab.Builder builder) {
return builder
.icon(() -> new ItemStack(Items.COMPUTER_NORMAL.get()))
.title(Component.translatable("itemGroup.computercraft"))
.displayItems((flags, out, isOp) -> {
out.accept(new ItemStack(Items.COMPUTER_NORMAL.get()));
out.accept(new ItemStack(Items.COMPUTER_ADVANCED.get()));
if (isOp) out.accept(new ItemStack(Items.COMPUTER_COMMAND.get()));
addTurtle(out, Items.TURTLE_NORMAL.get());
addTurtle(out, Items.TURTLE_ADVANCED.get());
addPocket(out, Items.POCKET_COMPUTER_NORMAL.get());
addPocket(out, Items.POCKET_COMPUTER_ADVANCED.get());
out.accept(Items.WIRELESS_MODEM_NORMAL.get());
out.accept(Items.WIRELESS_MODEM_ADVANCED.get());
out.accept(Items.CABLE.get());
out.accept(Items.WIRED_MODEM.get());
out.accept(Items.WIRED_MODEM_FULL.get());
out.accept(Items.MONITOR_NORMAL.get());
out.accept(Items.MONITOR_ADVANCED.get());
out.accept(Items.SPEAKER.get());
out.accept(Items.PRINTER.get());
out.accept(Items.PRINTED_PAGE.get());
out.accept(Items.PRINTED_PAGES.get());
out.accept(Items.PRINTED_BOOK.get());
out.accept(Items.DISK_DRIVE.get());
for (var colour = 0; colour < 16; colour++) {
out.accept(DiskItem.createFromIDAndColour(-1, null, Colour.VALUES[colour].getHex()));
}
});
}
private static void addTurtle(CreativeModeTab.Output out, TurtleItem turtle) {
out.accept(turtle.create(-1, null, -1, null, null, 0, null));
TurtleUpgrades.getVanillaUpgrades()
.map(x -> turtle.create(-1, null, -1, null, x, 0, null))
.forEach(out::accept);
}
private static void addPocket(CreativeModeTab.Output out, PocketComputerItem pocket) {
out.accept(pocket.create(-1, null, -1, null));
PocketUpgrades.getVanillaUpgrades().map(x -> pocket.create(-1, null, -1, x)).forEach(out::accept);
}
}

@ -9,7 +9,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.shared.platform.Registries;
import dan200.computercraft.shared.platform.RegistryWrappers;
import net.minecraft.commands.synchronization.ArgumentTypeInfo;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.chat.Component;
@ -25,7 +25,7 @@ public class ArgumentUtils {
public static <A extends ArgumentType<?>> JsonObject serializeToJson(ArgumentTypeInfo.Template<A> template) {
var object = new JsonObject();
object.addProperty("type", "argument");
object.addProperty("parser", Registries.COMMAND_ARGUMENT_TYPES.getKey(template.type()).toString());
object.addProperty("parser", RegistryWrappers.COMMAND_ARGUMENT_TYPES.getKey(template.type()).toString());
var properties = new JsonObject();
serializeToJson(properties, template.type(), template);
@ -45,12 +45,12 @@ public class ArgumentUtils {
@SuppressWarnings("unchecked")
private static <A extends ArgumentType<?>, T extends ArgumentTypeInfo.Template<A>> void serializeToNetwork(FriendlyByteBuf buffer, ArgumentTypeInfo<A, T> type, ArgumentTypeInfo.Template<A> template) {
Registries.writeId(buffer, Registries.COMMAND_ARGUMENT_TYPES, type);
RegistryWrappers.writeId(buffer, RegistryWrappers.COMMAND_ARGUMENT_TYPES, type);
type.serializeToNetwork((T) template, buffer);
}
public static ArgumentTypeInfo.Template<?> deserialize(FriendlyByteBuf buffer) {
var type = Registries.readId(buffer, Registries.COMMAND_ARGUMENT_TYPES);
var type = RegistryWrappers.readId(buffer, RegistryWrappers.COMMAND_ARGUMENT_TYPES);
Objects.requireNonNull(type, "Unknown argument type");
return type.deserializeFromNetwork(buffer);
}

@ -11,13 +11,14 @@ import dan200.computercraft.shared.util.ColourUtils;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.inventory.CraftingContainer;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.CraftingBookCategory;
import net.minecraft.world.item.crafting.CustomRecipe;
import net.minecraft.world.item.crafting.RecipeSerializer;
import net.minecraft.world.level.Level;
public final class ColourableRecipe extends CustomRecipe {
public ColourableRecipe(ResourceLocation id) {
super(id);
public ColourableRecipe(ResourceLocation id, CraftingBookCategory category) {
super(id, category);
}
@Override

@ -15,7 +15,7 @@ import dan200.computercraft.shared.computer.blocks.CommandComputerBlockEntity;
import dan200.computercraft.shared.util.NBTUtil;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Registry;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
@ -255,7 +255,7 @@ public class CommandAPI implements ILuaAPI {
var dimensionId = ResourceLocation.tryParse(id.get());
if (dimensionId == null) throw new LuaException("Invalid dimension name");
Level level = currentLevel.getServer().getLevel(ResourceKey.create(Registry.DIMENSION_REGISTRY, dimensionId));
Level level = currentLevel.getServer().getLevel(ResourceKey.create(Registries.DIMENSION, dimensionId));
if (level == null) throw new LuaException("Unknown dimension");
return level;

@ -10,6 +10,7 @@ import net.minecraft.core.NonNullList;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.inventory.CraftingContainer;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.CraftingBookCategory;
import net.minecraft.world.item.crafting.Ingredient;
import net.minecraft.world.item.crafting.ShapedRecipe;
import net.minecraft.world.level.Level;
@ -20,8 +21,8 @@ import net.minecraft.world.level.Level;
public abstract class ComputerConvertRecipe extends ShapedRecipe {
private final String group;
public ComputerConvertRecipe(ResourceLocation identifier, String group, int width, int height, NonNullList<Ingredient> ingredients, ItemStack result) {
super(identifier, group, width, height, ingredients, result);
public ComputerConvertRecipe(ResourceLocation identifier, String group, CraftingBookCategory category, int width, int height, NonNullList<Ingredient> ingredients, ItemStack result) {
super(identifier, group, category, width, height, ingredients, result);
this.group = group;
}

@ -13,14 +13,15 @@ import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.GsonHelper;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.CraftingBookCategory;
import net.minecraft.world.item.crafting.Ingredient;
import net.minecraft.world.item.crafting.RecipeSerializer;
public abstract class ComputerFamilyRecipe extends ComputerConvertRecipe {
private final ComputerFamily family;
public ComputerFamilyRecipe(ResourceLocation identifier, String group, int width, int height, NonNullList<Ingredient> ingredients, ItemStack result, ComputerFamily family) {
super(identifier, group, width, height, ingredients, result);
public ComputerFamilyRecipe(ResourceLocation identifier, String group, CraftingBookCategory category, int width, int height, NonNullList<Ingredient> ingredients, ItemStack result, ComputerFamily family) {
super(identifier, group, category, width, height, ingredients, result);
this.family = family;
}
@ -29,31 +30,33 @@ public abstract class ComputerFamilyRecipe extends ComputerConvertRecipe {
}
public abstract static class Serializer<T extends ComputerFamilyRecipe> implements RecipeSerializer<T> {
protected abstract T create(ResourceLocation identifier, String group, int width, int height, NonNullList<Ingredient> ingredients, ItemStack result, ComputerFamily family);
protected abstract T create(ResourceLocation identifier, String group, CraftingBookCategory category, int width, int height, NonNullList<Ingredient> ingredients, ItemStack result, ComputerFamily family);
@Override
public T fromJson(ResourceLocation identifier, JsonObject json) {
var group = GsonHelper.getAsString(json, "group", "");
var category = CraftingBookCategory.CODEC.byName(GsonHelper.getAsString(json, "category", null), CraftingBookCategory.MISC);
var family = RecipeUtil.getFamily(json, "family");
var template = RecipeUtil.getTemplate(json);
var result = itemStackFromJson(GsonHelper.getAsJsonObject(json, "result"));
return create(identifier, group, template.width(), template.height(), template.ingredients(), result, family);
return create(identifier, group, category, template.width(), template.height(), template.ingredients(), result, family);
}
@Override
public T fromNetwork(ResourceLocation identifier, FriendlyByteBuf buf) {
var width = buf.readVarInt();
var height = buf.readVarInt();
var group = buf.readUtf(Short.MAX_VALUE);
var group = buf.readUtf();
var category = buf.readEnum(CraftingBookCategory.class);
var ingredients = NonNullList.withSize(width * height, Ingredient.EMPTY);
for (var i = 0; i < ingredients.size(); i++) ingredients.set(i, Ingredient.fromNetwork(buf));
var result = buf.readItem();
var family = buf.readEnum(ComputerFamily.class);
return create(identifier, group, width, height, ingredients, result, family);
return create(identifier, group, category, width, height, ingredients, result, family);
}
@Override
@ -61,6 +64,7 @@ public abstract class ComputerFamilyRecipe extends ComputerConvertRecipe {
buf.writeVarInt(recipe.getWidth());
buf.writeVarInt(recipe.getHeight());
buf.writeUtf(recipe.getGroup());
buf.writeEnum(recipe.category());
for (var ingredient : recipe.getIngredients()) ingredient.toNetwork(buf);
buf.writeItem(recipe.getResultItem());
buf.writeEnum(recipe.getFamily());

@ -11,12 +11,13 @@ import dan200.computercraft.shared.computer.items.IComputerItem;
import net.minecraft.core.NonNullList;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.CraftingBookCategory;
import net.minecraft.world.item.crafting.Ingredient;
import net.minecraft.world.item.crafting.RecipeSerializer;
public final class ComputerUpgradeRecipe extends ComputerFamilyRecipe {
private ComputerUpgradeRecipe(ResourceLocation identifier, String group, int width, int height, NonNullList<Ingredient> ingredients, ItemStack result, ComputerFamily family) {
super(identifier, group, width, height, ingredients, result, family);
private ComputerUpgradeRecipe(ResourceLocation identifier, String group, CraftingBookCategory category, int width, int height, NonNullList<Ingredient> ingredients, ItemStack result, ComputerFamily family) {
super(identifier, group, category, width, height, ingredients, result, family);
}
@Override
@ -31,8 +32,8 @@ public final class ComputerUpgradeRecipe extends ComputerFamilyRecipe {
public static class Serializer extends ComputerFamilyRecipe.Serializer<ComputerUpgradeRecipe> {
@Override
protected ComputerUpgradeRecipe create(ResourceLocation identifier, String group, int width, int height, NonNullList<Ingredient> ingredients, ItemStack result, ComputerFamily family) {
return new ComputerUpgradeRecipe(identifier, group, width, height, ingredients, result, family);
protected ComputerUpgradeRecipe create(ResourceLocation identifier, String group, CraftingBookCategory category, int width, int height, NonNullList<Ingredient> ingredients, ItemStack result, ComputerFamily family) {
return new ComputerUpgradeRecipe(identifier, group, category, width, height, ingredients, result, family);
}
}
}

@ -6,7 +6,7 @@
package dan200.computercraft.shared.details;
import dan200.computercraft.api.detail.BlockReference;
import dan200.computercraft.shared.platform.Registries;
import dan200.computercraft.shared.platform.RegistryWrappers;
import net.minecraft.world.level.block.state.properties.Property;
import java.util.HashMap;
@ -16,7 +16,7 @@ public class BlockDetails {
public static void fillBasic(Map<? super String, Object> data, BlockReference block) {
var state = block.state();
data.put("name", DetailHelpers.getId(Registries.BLOCKS, state.getBlock()));
data.put("name", DetailHelpers.getId(RegistryWrappers.BLOCKS, state.getBlock()));
Map<Object, Object> stateTable = new HashMap<>();
for (Map.Entry<Property<?>, ? extends Comparable<?>> entry : state.getValues().entrySet()) {

@ -5,7 +5,7 @@
*/
package dan200.computercraft.shared.details;
import dan200.computercraft.shared.platform.Registries;
import dan200.computercraft.shared.platform.RegistryWrappers;
import net.minecraft.core.Holder;
import net.minecraft.tags.TagKey;
@ -25,7 +25,7 @@ public final class DetailHelpers {
return tags.collect(Collectors.toMap(x -> x.location().toString(), x -> true));
}
public static <T> String getId(Registries.RegistryWrapper<T> registry, T entry) {
public static <T> String getId(RegistryWrappers.RegistryWrapper<T> registry, T entry) {
return registry.getKey(entry).toString();
}
}

@ -6,13 +6,13 @@
package dan200.computercraft.shared.details;
import com.google.gson.JsonParseException;
import dan200.computercraft.mixin.CreativeModeTabAccessor;
import dan200.computercraft.shared.platform.PlatformHelper;
import dan200.computercraft.shared.platform.Registries;
import dan200.computercraft.shared.platform.RegistryWrappers;
import dan200.computercraft.shared.util.NBTUtil;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.Tag;
import net.minecraft.network.chat.Component;
import net.minecraft.world.item.CreativeModeTabs;
import net.minecraft.world.item.EnchantedBookItem;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.enchantment.EnchantmentHelper;
@ -25,7 +25,7 @@ import java.util.*;
*/
public class ItemDetails {
public static <T extends Map<? super String, Object>> T fillBasicSafe(T data, ItemStack stack) {
data.put("name", DetailHelpers.getId(Registries.ITEMS, stack.getItem()));
data.put("name", DetailHelpers.getId(RegistryWrappers.ITEMS, stack.getItem()));
data.put("count", stack.getCount());
return data;
}
@ -98,14 +98,15 @@ public class ItemDetails {
private static List<Map<String, Object>> getItemGroups(ItemStack stack) {
List<Map<String, Object>> groups = new ArrayList<>(1);
for (var group : PlatformHelper.get().getCreativeTabs(stack)) {
if (group == null) continue;
CreativeModeTabs.tabs().stream().filter(x -> x.contains(stack)).forEach(group -> {
Map<String, Object> groupData = new HashMap<>(2);
groupData.put("id", ((CreativeModeTabAccessor) group).computercraft$langId());
var id = PlatformHelper.get().getCreativeTabId(group);
if (id != null) groupData.put("id", id.toString());
groupData.put("displayName", group.getDisplayName().getString());
groups.add(groupData);
}
});
return groups;
}
@ -152,7 +153,7 @@ public class ItemDetails {
var enchantment = entry.getKey();
var level = entry.getValue();
var enchant = new HashMap<String, Object>(3);
enchant.put("name", DetailHelpers.getId(Registries.ENCHANTMENTS, enchantment));
enchant.put("name", DetailHelpers.getId(RegistryWrappers.ENCHANTMENTS, enchantment));
enchant.put("level", level);
enchant.put("displayName", enchantment.getFullname(level).getString());
enchants.add(enchant);

@ -20,6 +20,7 @@ import net.minecraft.core.NonNullList;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.CraftingBookCategory;
import net.minecraft.world.item.crafting.CraftingRecipe;
import net.minecraft.world.item.crafting.Ingredient;
import net.minecraft.world.item.crafting.ShapedRecipe;
@ -233,11 +234,11 @@ public class UpgradeRecipeGenerator<T> {
}
private T pocket(Ingredient upgrade, Ingredient pocketComputer, ItemStack result) {
return wrap.apply(new ShapedRecipe(POCKET_UPGRADE, "", 1, 2, NonNullList.of(Ingredient.EMPTY, upgrade, pocketComputer), result));
return wrap.apply(new ShapedRecipe(POCKET_UPGRADE, "", CraftingBookCategory.MISC, 1, 2, NonNullList.of(Ingredient.EMPTY, upgrade, pocketComputer), result));
}
private T turtle(Ingredient left, Ingredient right, ItemStack result) {
return wrap.apply(new ShapedRecipe(TURTLE_UPGRADE, "", 2, 1, NonNullList.of(Ingredient.EMPTY, left, right), result));
return wrap.apply(new ShapedRecipe(TURTLE_UPGRADE, "", CraftingBookCategory.MISC, 2, 1, NonNullList.of(Ingredient.EMPTY, left, right), result));
}
private class UpgradeInfo {

@ -15,11 +15,9 @@ import dan200.computercraft.shared.common.IColouredItem;
import dan200.computercraft.shared.config.Config;
import net.minecraft.ChatFormatting;
import net.minecraft.core.BlockPos;
import net.minecraft.core.NonNullList;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.CreativeModeTab;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.TooltipFlag;
@ -44,14 +42,6 @@ public class DiskItem extends Item implements IMedia, IColouredItem {
return stack;
}
@Override
public void fillItemCategory(CreativeModeTab tabs, NonNullList<ItemStack> list) {
if (!allowedIn(tabs)) return;
for (var colour = 0; colour < 16; colour++) {
list.add(createFromIDAndColour(-1, null, Colour.VALUES[colour].getHex()));
}
}
@Override
public void appendHoverText(ItemStack stack, @Nullable Level world, List<Component> list, TooltipFlag options) {
if (options.isAdvanced()) {

@ -13,11 +13,9 @@ import dan200.computercraft.core.filesystem.SubMount;
import dan200.computercraft.core.util.Colour;
import dan200.computercraft.shared.ModRegistry;
import net.minecraft.core.BlockPos;
import net.minecraft.core.NonNullList;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.CreativeModeTab;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.TooltipFlag;
@ -37,10 +35,6 @@ public class TreasureDiskItem extends Item implements IMedia {
super(settings);
}
@Override
public void fillItemCategory(CreativeModeTab group, NonNullList<ItemStack> stacks) {
}
@Override
public void appendHoverText(ItemStack stack, @Nullable Level world, List<Component> list, TooltipFlag tooltipOptions) {
var label = getTitle(stack);

@ -15,6 +15,7 @@ import net.minecraft.resources.ResourceLocation;
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;
@ -23,8 +24,8 @@ import net.minecraft.world.level.Level;
public class DiskRecipe extends CustomRecipe {
private final Ingredient redstone;
public DiskRecipe(ResourceLocation id) {
super(id);
public DiskRecipe(ResourceLocation id, CraftingBookCategory category) {
super(id, category);
redstone = PlatformHelper.get().getRecipeIngredients().redstone();
}

@ -12,6 +12,7 @@ import net.minecraft.resources.ResourceLocation;
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;
@ -21,8 +22,8 @@ public final class PrintoutRecipe extends CustomRecipe {
private final Ingredient leather;
private final Ingredient string;
public PrintoutRecipe(ResourceLocation id) {
super(id);
public PrintoutRecipe(ResourceLocation id, CraftingBookCategory category) {
super(id, category);
var ingredients = PlatformHelper.get().getRecipeIngredients();
leather = ingredients.leather();

@ -7,7 +7,7 @@ package dan200.computercraft.shared.network.client;
import dan200.computercraft.shared.network.NetworkMessage;
import dan200.computercraft.shared.peripheral.diskdrive.DiskDriveBlockEntity;
import dan200.computercraft.shared.platform.Registries;
import dan200.computercraft.shared.platform.RegistryWrappers;
import net.minecraft.core.BlockPos;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.sounds.SoundEvent;
@ -41,14 +41,14 @@ public class PlayRecordClientMessage implements NetworkMessage<ClientNetworkCont
public PlayRecordClientMessage(FriendlyByteBuf buf) {
pos = buf.readBlockPos();
soundEvent = buf.readBoolean() ? Registries.readKey(buf, Registries.SOUND_EVENTS) : null;
soundEvent = buf.readBoolean() ? RegistryWrappers.readKey(buf, RegistryWrappers.SOUND_EVENTS) : null;
name = buf.readBoolean() ? buf.readUtf(Short.MAX_VALUE) : null;
}
@Override
public void toBytes(FriendlyByteBuf buf) {
buf.writeBlockPos(pos);
writeOptional(buf, soundEvent, (b, e) -> Registries.writeKey(b, Registries.SOUND_EVENTS, e));
writeOptional(buf, soundEvent, (b, e) -> RegistryWrappers.writeKey(b, RegistryWrappers.SOUND_EVENTS, e));
writeOptional(buf, name, FriendlyByteBuf::writeUtf);
}

@ -27,10 +27,10 @@ public class SpeakerPlayClientMessage implements NetworkMessage<ClientNetworkCon
private final float volume;
private final float pitch;
public SpeakerPlayClientMessage(UUID source, SpeakerPosition pos, ResourceLocation event, float volume, float pitch) {
public SpeakerPlayClientMessage(UUID source, SpeakerPosition pos, ResourceLocation sound, float volume, float pitch) {
this.source = source;
this.pos = pos.asMessage();
sound = event;
this.sound = sound;
this.volume = volume;
this.pitch = pitch;
}

@ -12,7 +12,7 @@ import dan200.computercraft.api.lua.MethodResult;
import dan200.computercraft.api.peripheral.IComputerAccess;
import dan200.computercraft.api.peripheral.IDynamicPeripheral;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.shared.platform.Registries;
import dan200.computercraft.shared.platform.RegistryWrappers;
import net.minecraft.world.level.block.entity.BlockEntity;
import javax.annotation.Nullable;
@ -26,7 +26,7 @@ class GenericPeripheral implements IDynamicPeripheral {
private final List<SaturatedMethod> methods;
GenericPeripheral(BlockEntity tile, @Nullable String name, Set<String> additionalTypes, List<SaturatedMethod> methods) {
var type = Registries.BLOCK_ENTITY_TYPES.getKey(tile.getType());
var type = RegistryWrappers.BLOCK_ENTITY_TYPES.getKey(tile.getType());
this.tile = tile;
this.type = name != null ? name : type.toString();
this.additionalTypes = additionalTypes;

@ -6,15 +6,12 @@
package dan200.computercraft.shared.peripheral.modem.wired;
import dan200.computercraft.shared.ModRegistry;
import dan200.computercraft.shared.platform.Registries;
import dan200.computercraft.shared.platform.RegistryWrappers;
import net.minecraft.Util;
import net.minecraft.core.BlockPos;
import net.minecraft.core.NonNullList;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.CreativeModeTab;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.state.BlockState;
@ -51,15 +48,10 @@ public abstract class CableBlockItem extends BlockItem {
return placeAt(world, pos, correctConnections(world, pos, state));
}
@Override
public void fillItemCategory(CreativeModeTab group, NonNullList<ItemStack> list) {
if (allowedIn(group)) list.add(new ItemStack(this));
}
@Override
public String getDescriptionId() {
if (translationKey == null) {
translationKey = Util.makeDescriptionId("block", Registries.ITEMS.getKey(this));
translationKey = Util.makeDescriptionId("block", RegistryWrappers.ITEMS.getKey(this));
}
return translationKey;
}

@ -18,13 +18,14 @@ import dan200.computercraft.shared.network.client.SpeakerMoveClientMessage;
import dan200.computercraft.shared.network.client.SpeakerPlayClientMessage;
import dan200.computercraft.shared.network.client.SpeakerStopClientMessage;
import dan200.computercraft.shared.platform.PlatformHelper;
import dan200.computercraft.shared.platform.Registries;
import dan200.computercraft.shared.util.PauseAwareTimer;
import net.minecraft.ResourceLocationException;
import net.minecraft.core.BlockPos;
import net.minecraft.network.protocol.game.ClientboundCustomSoundPacket;
import net.minecraft.core.Holder;
import net.minecraft.network.protocol.game.ClientboundSoundPacket;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.level.block.state.properties.NoteBlockInstrument;
@ -64,11 +65,11 @@ public abstract class SpeakerPeripheral implements IPeripheral {
private long lastPlayTime;
private final List<PendingSound> pendingNotes = new ArrayList<>();
private final List<PendingSound<Holder<SoundEvent>>> pendingNotes = new ArrayList<>();
private final Object lock = new Object();
private boolean shouldStop;
private @Nullable PendingSound pendingSound = null;
private @Nullable PendingSound<ResourceLocation> pendingSound = null;
private @Nullable DfpwmState dfpwmState;
public void update() {
@ -85,7 +86,7 @@ public abstract class SpeakerPeripheral implements IPeripheral {
lastPlayTime = clock;
server.getPlayerList().broadcast(
null, pos.x, pos.y, pos.z, sound.volume * 16, level.dimension(),
new ClientboundCustomSoundPacket(sound.location, SoundSource.RECORDS, pos, sound.volume, sound.pitch, level.getRandom().nextLong())
new ClientboundSoundPacket(sound.sound, SoundSource.RECORDS, pos.x, pos.y, pos.z, sound.volume, sound.pitch, level.getRandom().nextLong())
);
}
pendingNotes.clear();
@ -96,7 +97,7 @@ public abstract class SpeakerPeripheral implements IPeripheral {
// dfpwmState will only ever transition from having a buffer to not having a buffer on the main thread (so this
// method), so we don't need to bother locking that.
boolean shouldStop;
PendingSound sound;
PendingSound<ResourceLocation> sound;
DfpwmState dfpwmState;
synchronized (lock) {
sound = pendingSound;
@ -122,7 +123,7 @@ public abstract class SpeakerPeripheral implements IPeripheral {
if (sound != null) {
lastPlayTime = clock;
PlatformHelper.get().sendToAllAround(
new SpeakerPlayClientMessage(getSource(), position, sound.location, sound.volume, sound.pitch),
new SpeakerPlayClientMessage(getSource(), position, sound.sound, sound.volume, sound.pitch),
(ServerLevel) level, pos, sound.volume * 16
);
syncedPosition(position);
@ -218,7 +219,7 @@ public abstract class SpeakerPeripheral implements IPeripheral {
synchronized (pendingNotes) {
if (pendingNotes.size() >= Config.maxNotesPerTick) return false;
pendingNotes.add(new PendingSound(Registries.SOUND_EVENTS.getKey(instrument.getSoundEvent()), volume, (float) Math.pow(2.0, (pitch - 12.0) / 12.0)));
pendingNotes.add(new PendingSound<>(instrument.getSoundEvent(), volume, (float) Math.pow(2.0, (pitch - 12.0) / 12.0)));
}
return true;
}
@ -260,7 +261,7 @@ public abstract class SpeakerPeripheral implements IPeripheral {
synchronized (lock) {
if (dfpwmState != null && dfpwmState.isPlaying()) return false;
dfpwmState = null;
pendingSound = new PendingSound(identifier, volume, pitch);
pendingSound = new PendingSound<>(identifier, volume, pitch);
return true;
}
}
@ -360,6 +361,6 @@ public abstract class SpeakerPeripheral implements IPeripheral {
}
}
private record PendingSound(ResourceLocation location, float volume, float pitch) {
private record PendingSound<T>(T sound, float volume, float pitch) {
}
}

@ -67,13 +67,6 @@ public interface PlatformHelper extends dan200.computercraft.impl.PlatformHelper
return (PlatformHelper) dan200.computercraft.impl.PlatformHelper.get();
}
/**
* Get ComputerCraft's creative tab all its items should appear under.
*
* @return The creative tab.
*/
CreativeModeTab getCreativeTab();
/**
* Wrap a Minecraft registry in our own abstraction layer.
*
@ -81,7 +74,7 @@ public interface PlatformHelper extends dan200.computercraft.impl.PlatformHelper
* @param <T> The type of object stored in this registry.
* @return The wrapped registry.
*/
<T> Registries.RegistryWrapper<T> wrap(ResourceKey<Registry<T>> registry);
<T> RegistryWrappers.RegistryWrapper<T> wrap(ResourceKey<Registry<T>> registry);
/**
* Create a registration helper for a specific registry.
@ -262,12 +255,13 @@ public interface PlatformHelper extends dan200.computercraft.impl.PlatformHelper
int getBurnTime(ItemStack stack);
/**
* Get the creative tabs this stack belongs to.
* Get a unique identifier for this creative tab.
*
* @param stack The current item.
* @return The creative tabs the item belongs to.
* @param tab The tab to get
* @return The unique identifier, or {@code null} if not available.
*/
Collection<CreativeModeTab> getCreativeTabs(ItemStack stack);
@Nullable
ResourceLocation getCreativeTabId(CreativeModeTab tab);
/**
* Get the "container" item to be returned after crafting. For instance, crafting with a lava bucket should return

@ -7,6 +7,7 @@ package dan200.computercraft.shared.platform;
import net.minecraft.commands.synchronization.ArgumentTypeInfo;
import net.minecraft.core.Registry;
import net.minecraft.core.registries.Registries;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.sounds.SoundEvent;
@ -25,16 +26,16 @@ import java.util.stream.StreamSupport;
/**
* Mimics {@link Registry} but using {@link PlatformHelper}'s recipe abstractions.
*/
public final class Registries {
public static final RegistryWrapper<Item> ITEMS = PlatformHelper.get().wrap(Registry.ITEM_REGISTRY);
public static final RegistryWrapper<Block> BLOCKS = PlatformHelper.get().wrap(Registry.BLOCK_REGISTRY);
public static final RegistryWrapper<BlockEntityType<?>> BLOCK_ENTITY_TYPES = PlatformHelper.get().wrap(Registry.BLOCK_ENTITY_TYPE_REGISTRY);
public static final RegistryWrapper<Fluid> FLUIDS = PlatformHelper.get().wrap(Registry.FLUID_REGISTRY);
public static final RegistryWrapper<Enchantment> ENCHANTMENTS = PlatformHelper.get().wrap(Registry.ENCHANTMENT_REGISTRY);
public static final RegistryWrapper<ArgumentTypeInfo<?, ?>> COMMAND_ARGUMENT_TYPES = PlatformHelper.get().wrap(Registry.COMMAND_ARGUMENT_TYPE_REGISTRY);
public static final RegistryWrapper<SoundEvent> SOUND_EVENTS = PlatformHelper.get().wrap(Registry.SOUND_EVENT_REGISTRY);
public static final RegistryWrapper<RecipeSerializer<?>> RECIPE_SERIALIZERS = PlatformHelper.get().wrap(Registry.RECIPE_SERIALIZER_REGISTRY);
public static final RegistryWrapper<MenuType<?>> MENU = PlatformHelper.get().wrap(Registry.MENU_REGISTRY);
public final class RegistryWrappers {
public static final RegistryWrapper<Item> ITEMS = PlatformHelper.get().wrap(Registries.ITEM);
public static final RegistryWrapper<Block> BLOCKS = PlatformHelper.get().wrap(Registries.BLOCK);
public static final RegistryWrapper<BlockEntityType<?>> BLOCK_ENTITY_TYPES = PlatformHelper.get().wrap(Registries.BLOCK_ENTITY_TYPE);
public static final RegistryWrapper<Fluid> FLUIDS = PlatformHelper.get().wrap(Registries.FLUID);
public static final RegistryWrapper<Enchantment> ENCHANTMENTS = PlatformHelper.get().wrap(Registries.ENCHANTMENT);
public static final RegistryWrapper<ArgumentTypeInfo<?, ?>> COMMAND_ARGUMENT_TYPES = PlatformHelper.get().wrap(Registries.COMMAND_ARGUMENT_TYPE);
public static final RegistryWrapper<SoundEvent> SOUND_EVENTS = PlatformHelper.get().wrap(Registries.SOUND_EVENT);
public static final RegistryWrapper<RecipeSerializer<?>> RECIPE_SERIALIZERS = PlatformHelper.get().wrap(Registries.RECIPE_SERIALIZER);
public static final RegistryWrapper<MenuType<?>> MENU = PlatformHelper.get().wrap(Registries.MENU);
public interface RegistryWrapper<T> extends Iterable<T> {
int getId(T object);
@ -53,7 +54,7 @@ public final class Registries {
}
}
private Registries() {
private RegistryWrappers() {
}
public static <K> void writeId(FriendlyByteBuf buf, RegistryWrapper<K> registry, K object) {

@ -24,7 +24,6 @@ import dan200.computercraft.shared.pocket.core.PocketServerComputer;
import dan200.computercraft.shared.pocket.inventory.PocketComputerMenuProvider;
import dan200.computercraft.shared.util.IDAssigner;
import net.minecraft.ChatFormatting;
import net.minecraft.core.NonNullList;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.server.MinecraftServer;
@ -36,7 +35,6 @@ import net.minecraft.world.InteractionResultHolder;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.CreativeModeTab;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.TooltipFlag;
@ -70,13 +68,6 @@ public class PocketComputerItem extends Item implements IComputerItem, IMedia, I
return result;
}
@Override
public void fillItemCategory(CreativeModeTab group, NonNullList<ItemStack> stacks) {
if (!allowedIn(group)) return;
stacks.add(create(-1, null, -1, null));
PocketUpgrades.getVanillaUpgrades().map(x -> create(-1, null, -1, x)).forEach(stacks::add);
}
private boolean tick(ItemStack stack, Level world, Entity entity, PocketServerComputer computer) {
var upgrade = getUpgrade(stack);

@ -14,13 +14,14 @@ import dan200.computercraft.shared.pocket.items.PocketComputerItemFactory;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.inventory.CraftingContainer;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.CraftingBookCategory;
import net.minecraft.world.item.crafting.CustomRecipe;
import net.minecraft.world.item.crafting.RecipeSerializer;
import net.minecraft.world.level.Level;
public final class PocketComputerUpgradeRecipe extends CustomRecipe {
public PocketComputerUpgradeRecipe(ResourceLocation identifier) {
super(identifier);
public PocketComputerUpgradeRecipe(ResourceLocation identifier, CraftingBookCategory category) {
super(identifier, category);
}
@Override

@ -14,12 +14,10 @@ import dan200.computercraft.shared.common.IColouredItem;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import dan200.computercraft.shared.computer.items.AbstractComputerItem;
import dan200.computercraft.shared.turtle.blocks.TurtleBlock;
import net.minecraft.core.NonNullList;
import net.minecraft.core.cauldron.CauldronInteraction;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.item.CreativeModeTab;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.LayeredCauldronBlock;
@ -56,16 +54,6 @@ public class TurtleItem extends AbstractComputerItem implements ITurtleItem {
return stack;
}
@Override
public void fillItemCategory(CreativeModeTab group, NonNullList<ItemStack> list) {
if (!allowedIn(group)) return;
list.add(create(-1, null, -1, null, null, 0, null));
TurtleUpgrades.getVanillaUpgrades()
.map(x -> create(-1, null, -1, null, x, 0, null))
.forEach(list::add);
}
@Override
public Component getName(ItemStack stack) {
var baseString = getDescriptionId(stack);

@ -13,12 +13,13 @@ import dan200.computercraft.shared.turtle.items.TurtleItemFactory;
import net.minecraft.core.NonNullList;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.CraftingBookCategory;
import net.minecraft.world.item.crafting.Ingredient;
import net.minecraft.world.item.crafting.RecipeSerializer;
public final class TurtleRecipe extends ComputerFamilyRecipe {
public TurtleRecipe(ResourceLocation identifier, String group, int width, int height, NonNullList<Ingredient> ingredients, ItemStack result, ComputerFamily family) {
super(identifier, group, width, height, ingredients, result, family);
public TurtleRecipe(ResourceLocation identifier, String group, CraftingBookCategory category, int width, int height, NonNullList<Ingredient> ingredients, ItemStack result, ComputerFamily family) {
super(identifier, group, category, width, height, ingredients, result, family);
}
@Override
@ -36,8 +37,8 @@ public final class TurtleRecipe extends ComputerFamilyRecipe {
public static class Serializer extends ComputerFamilyRecipe.Serializer<TurtleRecipe> {
@Override
protected TurtleRecipe create(ResourceLocation identifier, String group, int width, int height, NonNullList<Ingredient> ingredients, ItemStack result, ComputerFamily family) {
return new TurtleRecipe(identifier, group, width, height, ingredients, result, family);
protected TurtleRecipe create(ResourceLocation identifier, String group, CraftingBookCategory category, int width, int height, NonNullList<Ingredient> ingredients, ItemStack result, ComputerFamily family) {
return new TurtleRecipe(identifier, group, category, width, height, ingredients, result, family);
}
}
}

@ -15,13 +15,14 @@ import dan200.computercraft.shared.turtle.items.TurtleItemFactory;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.inventory.CraftingContainer;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.CraftingBookCategory;
import net.minecraft.world.item.crafting.CustomRecipe;
import net.minecraft.world.item.crafting.RecipeSerializer;
import net.minecraft.world.level.Level;
public final class TurtleUpgradeRecipe extends CustomRecipe {
public TurtleUpgradeRecipe(ResourceLocation id) {
super(id);
public TurtleUpgradeRecipe(ResourceLocation id, CraftingBookCategory category) {
super(id, category);
}
@Override

@ -8,8 +8,8 @@ package dan200.computercraft.shared.turtle.upgrades;
import com.google.gson.JsonObject;
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
import dan200.computercraft.api.upgrades.UpgradeBase;
import dan200.computercraft.shared.platform.Registries;
import net.minecraft.core.Registry;
import dan200.computercraft.shared.platform.RegistryWrappers;
import net.minecraft.core.registries.Registries;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.TagKey;
@ -33,7 +33,7 @@ public final class TurtleToolSerialiser implements TurtleUpgradeSerialiser<Turtl
TagKey<Block> breakable = null;
if (object.has("breakable")) {
var tag = new ResourceLocation(GsonHelper.getAsString(object, "breakable"));
breakable = TagKey.create(Registry.BLOCK_REGISTRY, tag);
breakable = TagKey.create(Registries.BLOCK, tag);
}
return new TurtleTool(id, adjective, craftingItem, new ItemStack(toolItem), damageMultiplier, breakable);
@ -42,20 +42,20 @@ public final class TurtleToolSerialiser implements TurtleUpgradeSerialiser<Turtl
@Override
public TurtleTool fromNetwork(ResourceLocation id, FriendlyByteBuf buffer) {
var adjective = buffer.readUtf();
var craftingItem = Registries.readId(buffer, Registries.ITEMS);
var craftingItem = RegistryWrappers.readId(buffer, RegistryWrappers.ITEMS);
var toolItem = buffer.readItem();
// damageMultiplier and breakable aren't used by the client, but we need to construct the upgrade exactly
// as otherwise syncing on an SP world will overwrite the (shared) upgrade registry with an invalid upgrade!
var damageMultiplier = buffer.readFloat();
var breakable = buffer.readBoolean() ? TagKey.create(Registry.BLOCK_REGISTRY, buffer.readResourceLocation()) : null;
var breakable = buffer.readBoolean() ? TagKey.create(Registries.BLOCK, buffer.readResourceLocation()) : null;
return new TurtleTool(id, adjective, craftingItem, toolItem, damageMultiplier, breakable);
}
@Override
public void toNetwork(FriendlyByteBuf buffer, TurtleTool upgrade) {
buffer.writeUtf(upgrade.getUnlocalisedAdjective());
Registries.writeId(buffer, Registries.ITEMS, upgrade.getCraftingItem().getItem());
RegistryWrappers.writeId(buffer, RegistryWrappers.ITEMS, upgrade.getCraftingItem().getItem());
buffer.writeItem(upgrade.item);
buffer.writeFloat(upgrade.damageMulitiplier);
buffer.writeBoolean(upgrade.breakable != null);

@ -6,7 +6,7 @@
package dan200.computercraft.shared.util;
import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.shared.platform.Registries;
import dan200.computercraft.shared.platform.RegistryWrappers;
import net.minecraft.ResourceLocationException;
import net.minecraft.resources.ResourceLocation;
@ -32,7 +32,7 @@ public final class ArgumentHelpers {
}
}
public static <T> T getRegistryEntry(String name, String typeName, Registries.RegistryWrapper<T> registry) throws LuaException {
public static <T> T getRegistryEntry(String name, String typeName, RegistryWrappers.RegistryWrapper<T> registry) throws LuaException {
ResourceLocation id;
try {
id = new ResourceLocation(name);

@ -13,6 +13,7 @@ import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.GsonHelper;
import net.minecraft.world.inventory.CraftingContainer;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.CraftingBookCategory;
import net.minecraft.world.item.crafting.Ingredient;
import net.minecraft.world.item.crafting.RecipeSerializer;
import net.minecraft.world.item.crafting.ShapedRecipe;
@ -21,8 +22,8 @@ import net.minecraft.world.level.Level;
public final class ImpostorRecipe extends ShapedRecipe {
private final String group;
private ImpostorRecipe(ResourceLocation id, String group, int width, int height, NonNullList<Ingredient> ingredients, ItemStack result) {
super(id, group, width, height, ingredients, result);
private ImpostorRecipe(ResourceLocation id, String group, CraftingBookCategory category, int width, int height, NonNullList<Ingredient> ingredients, ItemStack result) {
super(id, group, category, width, height, ingredients, result);
this.group = group;
}
@ -50,9 +51,10 @@ public final class ImpostorRecipe extends ShapedRecipe {
@Override
public ImpostorRecipe fromJson(ResourceLocation identifier, JsonObject json) {
var group = GsonHelper.getAsString(json, "group", "");
var category = CraftingBookCategory.CODEC.byName(GsonHelper.getAsString(json, "category", null), CraftingBookCategory.MISC);
var recipe = RecipeSerializer.SHAPED_RECIPE.fromJson(identifier, json);
var result = ShapedRecipe.itemStackFromJson(GsonHelper.getAsJsonObject(json, "result"));
return new ImpostorRecipe(identifier, group, recipe.getWidth(), recipe.getHeight(), recipe.getIngredients(), result);
return new ImpostorRecipe(identifier, group, category, recipe.getWidth(), recipe.getHeight(), recipe.getIngredients(), result);
}
@Override
@ -60,10 +62,11 @@ public final class ImpostorRecipe extends ShapedRecipe {
var width = buf.readVarInt();
var height = buf.readVarInt();
var group = buf.readUtf(Short.MAX_VALUE);
var category = buf.readEnum(CraftingBookCategory.class);
var items = NonNullList.withSize(width * height, Ingredient.EMPTY);
for (var k = 0; k < items.size(); k++) items.set(k, Ingredient.fromNetwork(buf));
var result = buf.readItem();
return new ImpostorRecipe(identifier, group, width, height, items, result);
return new ImpostorRecipe(identifier, group, category, width, height, items, result);
}
@Override
@ -71,6 +74,7 @@ public final class ImpostorRecipe extends ShapedRecipe {
buf.writeVarInt(recipe.getWidth());
buf.writeVarInt(recipe.getHeight());
buf.writeUtf(recipe.getGroup());
buf.writeEnum(recipe.category());
for (var ingredient : recipe.getIngredients()) ingredient.toNetwork(buf);
buf.writeItem(recipe.getResultItem());
}

@ -15,17 +15,14 @@ import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.GsonHelper;
import net.minecraft.world.inventory.CraftingContainer;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.Ingredient;
import net.minecraft.world.item.crafting.RecipeSerializer;
import net.minecraft.world.item.crafting.ShapedRecipe;
import net.minecraft.world.item.crafting.ShapelessRecipe;
import net.minecraft.world.item.crafting.*;
import net.minecraft.world.level.Level;
public final class ImpostorShapelessRecipe extends ShapelessRecipe {
private final String group;
private ImpostorShapelessRecipe(ResourceLocation id, String group, ItemStack result, NonNullList<Ingredient> ingredients) {
super(id, group, result, ingredients);
private ImpostorShapelessRecipe(ResourceLocation id, String group, CraftingBookCategory category, ItemStack result, NonNullList<Ingredient> ingredients) {
super(id, group, category, result, ingredients);
this.group = group;
}
@ -52,7 +49,8 @@ public final class ImpostorShapelessRecipe extends ShapelessRecipe {
public static final class Serializer implements RecipeSerializer<ImpostorShapelessRecipe> {
@Override
public ImpostorShapelessRecipe fromJson(ResourceLocation id, JsonObject json) {
var s = GsonHelper.getAsString(json, "group", "");
var group = GsonHelper.getAsString(json, "group", "");
var category = CraftingBookCategory.CODEC.byName(GsonHelper.getAsString(json, "category", null), CraftingBookCategory.MISC);
var ingredients = readIngredients(GsonHelper.getAsJsonArray(json, "ingredients"));
if (ingredients.isEmpty()) throw new JsonParseException("No ingredients for shapeless recipe");
@ -61,7 +59,7 @@ public final class ImpostorShapelessRecipe extends ShapelessRecipe {
}
var result = ShapedRecipe.itemStackFromJson(GsonHelper.getAsJsonObject(json, "result"));
return new ImpostorShapelessRecipe(id, s, result, ingredients);
return new ImpostorShapelessRecipe(id, group, category, result, ingredients);
}
private NonNullList<Ingredient> readIngredients(JsonArray arrays) {
@ -76,19 +74,21 @@ public final class ImpostorShapelessRecipe extends ShapelessRecipe {
@Override
public ImpostorShapelessRecipe fromNetwork(ResourceLocation id, FriendlyByteBuf buffer) {
var s = buffer.readUtf(32767);
var i = buffer.readVarInt();
var items = NonNullList.withSize(i, Ingredient.EMPTY);
var group = buffer.readUtf();
var category = buffer.readEnum(CraftingBookCategory.class);
var count = buffer.readVarInt();
var items = NonNullList.withSize(count, Ingredient.EMPTY);
for (var j = 0; j < items.size(); j++) items.set(j, Ingredient.fromNetwork(buffer));
var result = buffer.readItem();
return new ImpostorShapelessRecipe(id, s, result, items);
return new ImpostorShapelessRecipe(id, group, category, result, items);
}
@Override
public void toNetwork(FriendlyByteBuf buffer, ImpostorShapelessRecipe recipe) {
buffer.writeUtf(recipe.getGroup());
buffer.writeEnum(recipe.category());
buffer.writeVarInt(recipe.getIngredients().size());
for (var ingredient : recipe.getIngredients()) ingredient.toNetwork(buffer);

@ -4,6 +4,7 @@ accessWidener v1 named
# that we actually use
accessible method net/minecraft/client/renderer/item/ItemProperties register (Lnet/minecraft/world/item/Item;Lnet/minecraft/resources/ResourceLocation;Lnet/minecraft/client/renderer/item/ClampedItemPropertyFunction;)V
accessible class net/minecraft/world/item/CreativeModeTab$Output
# Containers
accessible class net/minecraft/world/inventory/MenuType$MenuSupplier
@ -12,6 +13,7 @@ accessible class net/minecraft/client/gui/screens/MenuScreens$ScreenConstructor
accessible method net/minecraft/client/gui/screens/MenuScreens register (Lnet/minecraft/world/inventory/MenuType;Lnet/minecraft/client/gui/screens/MenuScreens$ScreenConstructor;)V
# Data generators
accessible class net/minecraft/data/loot/LootTableProvider$SubProviderEntry
accessible class net/minecraft/data/tags/TagsProvider$TagAppender
accessible field net/minecraft/data/models/BlockModelGenerators blockStateOutput Ljava/util/function/Consumer;
accessible field net/minecraft/data/models/BlockModelGenerators modelOutput Ljava/util/function/BiConsumer;

@ -8,7 +8,6 @@
},
"mixins": [
"CacheUpdaterMixin",
"CreativeModeTabAccessor",
"ExplosionAccessor"
],
"refmap": "computercraft.refmap.json"

@ -59,7 +59,7 @@ import java.util.function.Function;
@AutoService({ PlatformHelper.class, dan200.computercraft.impl.PlatformHelper.class, ComputerCraftAPIService.class })
public class TestPlatformHelper extends AbstractComputerCraftAPI implements PlatformHelper {
@Override
public <T> Registries.RegistryWrapper<T> wrap(ResourceKey<Registry<T>> registry) {
public <T> RegistryWrappers.RegistryWrapper<T> wrap(ResourceKey<Registry<T>> registry) {
throw new UnsupportedOperationException("Cannot query registry inside tests");
}
@ -113,11 +113,6 @@ public class TestPlatformHelper extends AbstractComputerCraftAPI implements Plat
throw new UnsupportedOperationException("Cannot send NetworkMessages inside tests");
}
@Override
public CreativeModeTab getCreativeTab() {
throw new UnsupportedOperationException("Cannot get creative tab inside tests");
}
@Override
public List<TagKey<Item>> getDyeTags() {
throw new UnsupportedOperationException("Cannot query tags inside tests");
@ -153,9 +148,11 @@ public class TestPlatformHelper extends AbstractComputerCraftAPI implements Plat
throw new UnsupportedOperationException("Cannot interact with the world inside tests");
}
@Nullable
@Override
public Collection<CreativeModeTab> getCreativeTabs(ItemStack stack) {
throw new UnsupportedOperationException("Cannot get creative tabs inside tests");
public ResourceLocation getCreativeTabId(CreativeModeTab tab) {
return null;
}
@Override

@ -7,15 +7,15 @@ package dan200.computercraft.shared.computer.core;
import dan200.computercraft.api.filesystem.Mount;
import net.minecraft.Util;
import net.minecraft.server.packs.FolderPackResources;
import net.minecraft.server.packs.PackType;
import net.minecraft.server.packs.PathPackResources;
import net.minecraft.server.packs.resources.ReloadableResourceManager;
import net.minecraft.util.Unit;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
@ -32,7 +32,7 @@ public class ResourceMountTest {
var manager = new ReloadableResourceManager(PackType.SERVER_DATA);
var done = new CompletableFuture<Unit>();
manager.createReload(Util.backgroundExecutor(), Util.backgroundExecutor(), done, List.of(
new FolderPackResources(new File("../core/src/main/resources"))
new PathPackResources("resources", Path.of("../core/src/main/resources"), false)
));
mount = ResourceMount.get("computercraft", "lua/rom", manager);

@ -17,7 +17,7 @@ import com.mojang.brigadier.builder.RequiredArgumentBuilder;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.data.PrettyJsonWriter;
import dan200.computercraft.gametest.core.TestHooks;
import dan200.computercraft.shared.platform.Registries;
import dan200.computercraft.shared.platform.RegistryWrappers;
import net.minecraft.client.Minecraft;
import net.minecraft.network.chat.Component;
import net.minecraft.world.item.Item;
@ -74,14 +74,14 @@ public class Exporter {
Set<Item> items = new HashSet<>();
// First find all CC items
for (var item : Registries.ITEMS) {
if (Registries.ITEMS.getKey(item).getNamespace().equals(ComputerCraftAPI.MOD_ID)) items.add(item);
for (var item : RegistryWrappers.ITEMS) {
if (RegistryWrappers.ITEMS.getKey(item).getNamespace().equals(ComputerCraftAPI.MOD_ID)) items.add(item);
}
// Now find all CC recipes.
for (var recipe : Minecraft.getInstance().level.getRecipeManager().getAllRecipesFor(RecipeType.CRAFTING)) {
var result = recipe.getResultItem();
if (!Registries.ITEMS.getKey(result.getItem()).getNamespace().equals(ComputerCraftAPI.MOD_ID)) {
if (!RegistryWrappers.ITEMS.getKey(result.getItem()).getNamespace().equals(ComputerCraftAPI.MOD_ID)) {
continue;
}
if (result.hasTag()) {
@ -122,7 +122,7 @@ public class Exporter {
renderer.setupState();
for (var item : items) {
var stack = new ItemStack(item);
var location = Registries.ITEMS.getKey(item);
var location = RegistryWrappers.ITEMS.getKey(item);
dump.itemNames.put(location.toString(), stack.getHoverName().getString());
renderer.captureRender(itemDir.resolve(location.getNamespace()).resolve(location.getPath() + ".png"),

@ -8,9 +8,9 @@ package dan200.computercraft.export;
import com.mojang.blaze3d.pipeline.TextureTarget;
import com.mojang.blaze3d.platform.NativeImage;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.math.Matrix4f;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.FogRenderer;
import org.joml.Matrix4f;
import org.lwjgl.opengl.GL12;
import javax.annotation.Nullable;
@ -37,7 +37,7 @@ public class ImageRenderer implements AutoCloseable {
public void setupState() {
projectionMatrix = RenderSystem.getProjectionMatrix();
RenderSystem.setProjectionMatrix(Matrix4f.orthographic(0, 16, 0, 16, 1000, 3000));
RenderSystem.setProjectionMatrix(new Matrix4f().identity().ortho(0, 16, 0, 16, 1000, 3000));
var transform = RenderSystem.getModelViewStack();
transform.pushPose();

@ -5,7 +5,7 @@
*/
package dan200.computercraft.export;
import dan200.computercraft.shared.platform.Registries;
import dan200.computercraft.shared.platform.RegistryWrappers;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
@ -23,7 +23,7 @@ public class JsonDump {
public int count;
public Recipe(ItemStack output) {
this.output = Registries.ITEMS.getKey(output.getItem()).toString();
this.output = RegistryWrappers.ITEMS.getKey(output.getItem()).toString();
count = output.getCount();
}
@ -38,7 +38,7 @@ public class JsonDump {
if (!canonicalItem.contains(item)) continue;
trackedItems.add(item);
inputs[pos] = new String[]{ Registries.ITEMS.getKey(item).toString() };
inputs[pos] = new String[]{ RegistryWrappers.ITEMS.getKey(item).toString() };
return;
}
@ -46,7 +46,7 @@ public class JsonDump {
for (var i = 0; i < items.length; i++) {
var item = items[i].getItem();
trackedItems.add(item);
itemIds[i] = Registries.ITEMS.getKey(item).toString();
itemIds[i] = RegistryWrappers.ITEMS.getKey(item).toString();
}
Arrays.sort(itemIds);

@ -19,7 +19,7 @@ class Loot_Test {
/**
* Test that the loot tables will spawn in treasure disks.
*/
@GameTest(template = Structures.DEFAULT)
@GameTest(template = Structures.DEFAULT, required = false) // FIXME: We may need to inject this as a datapack instead
fun Chest_contains_disk(context: GameTestHelper) = context.sequence {
thenExecute {
val pos = BlockPos(2, 2, 2)

@ -2,7 +2,7 @@ package dan200.computercraft.gametest.api
import dan200.computercraft.gametest.core.MinecraftExtensions
import dan200.computercraft.mixin.gametest.GameTestSequenceAccessor
import dan200.computercraft.shared.platform.Registries
import dan200.computercraft.shared.platform.RegistryWrappers
import net.minecraft.client.Minecraft
import net.minecraft.client.Screenshot
import net.minecraft.client.gui.screens.inventory.MenuAccess
@ -117,7 +117,7 @@ class ClientTestHelper {
* Get the currently open [AbstractContainerMenu], ensuring it is of a specific type.
*/
fun <T : AbstractContainerMenu> getOpenMenu(type: MenuType<T>): T {
fun getName(type: MenuType<*>) = Registries.MENU.getKey(type)
fun getName(type: MenuType<*>) = RegistryWrappers.MENU.getKey(type)
val screen = minecraft.screen
@Suppress("UNCHECKED_CAST")

@ -10,7 +10,7 @@ import dan200.computercraft.mixin.gametest.GameTestHelperAccessor
import dan200.computercraft.mixin.gametest.GameTestInfoAccessor
import dan200.computercraft.mixin.gametest.GameTestSequenceAccessor
import dan200.computercraft.shared.platform.PlatformHelper
import dan200.computercraft.shared.platform.Registries
import dan200.computercraft.shared.platform.RegistryWrappers
import dan200.computercraft.test.core.computer.LuaTaskContext
import dan200.computercraft.test.shared.ItemStackMatcher.isStack
import net.minecraft.commands.arguments.blocks.BlockInput
@ -154,7 +154,7 @@ fun GameTestHelper.assertBlockIs(pos: BlockPos, predicate: (BlockState) -> Boole
fun <T : Comparable<T>> GameTestHelper.assertBlockHas(pos: BlockPos, property: Property<T>, value: T, message: String = "") {
val state = getBlockState(pos)
if (!state.hasProperty(property)) {
val id = Registries.BLOCKS.getKey(state.block)
val id = RegistryWrappers.BLOCKS.getKey(state.block)
fail(message, "block $id does not have property ${property.name}", pos)
} else if (state.getValue(property) != value) {
fail(message, "${property.name} is ${state.getValue(property)}, expected $value", pos)
@ -227,7 +227,7 @@ fun GameTestHelper.assertExactlyItems(vararg expected: ItemStack, message: Strin
}
}
private fun getName(type: BlockEntityType<*>): ResourceLocation = Registries.BLOCK_ENTITY_TYPES.getKey(type)!!
private fun getName(type: BlockEntityType<*>): ResourceLocation = RegistryWrappers.BLOCK_ENTITY_TYPES.getKey(type)!!
/**
* Get a [BlockEntity] of a specific type.

@ -10,18 +10,16 @@ import net.minecraft.client.gui.screens.Screen
import net.minecraft.client.gui.screens.TitleScreen
import net.minecraft.client.tutorial.TutorialSteps
import net.minecraft.core.BlockPos
import net.minecraft.core.Registry
import net.minecraft.core.RegistryAccess
import net.minecraft.gametest.framework.*
import net.minecraft.server.MinecraftServer
import net.minecraft.sounds.SoundSource
import net.minecraft.util.RandomSource
import net.minecraft.world.Difficulty
import net.minecraft.world.level.DataPackConfig
import net.minecraft.world.level.GameRules
import net.minecraft.world.level.GameType
import net.minecraft.world.level.LevelSettings
import net.minecraft.world.level.WorldDataConfiguration
import net.minecraft.world.level.block.Rotation
import net.minecraft.world.level.levelgen.WorldOptions
import net.minecraft.world.level.levelgen.presets.WorldPresets
import org.slf4j.Logger
import org.slf4j.LoggerFactory
@ -73,8 +71,8 @@ object ClientTestHooks {
minecraft.options.tutorialStep = TutorialSteps.NONE
minecraft.options.renderDistance().set(6)
minecraft.options.gamma().set(1.0)
minecraft.options.setSoundCategoryVolume(SoundSource.MUSIC, 0.0f)
minecraft.options.setSoundCategoryVolume(SoundSource.AMBIENT, 0.0f)
minecraft.options.getSoundSourceOptionInstance(SoundSource.MUSIC).set(0.0)
minecraft.options.getSoundSourceOptionInstance(SoundSource.AMBIENT).set(0.0)
if (minecraft.levelSource.levelExists(LEVEL_NAME)) {
LOG.info("World already exists, opening.")
@ -86,16 +84,11 @@ object ClientTestHooks {
rules.getRule(GameRules.RULE_DAYLIGHT).set(false, null)
rules.getRule(GameRules.RULE_WEATHER_CYCLE).set(false, null)
val registries = RegistryAccess.builtinCopy().freeze()
minecraft.createWorldOpenFlows().createFreshLevel(
LEVEL_NAME,
LevelSettings("Test Level", GameType.CREATIVE, false, Difficulty.EASY, true, rules, DataPackConfig.DEFAULT),
registries,
registries
.registryOrThrow(Registry.WORLD_PRESET_REGISTRY)
.getHolderOrThrow(WorldPresets.FLAT).value()
.createWorldGenSettings(RandomSource.create().nextLong(), false, false),
)
LevelSettings("Test Level", GameType.CREATIVE, false, Difficulty.EASY, true, rules, WorldDataConfiguration.DEFAULT),
WorldOptions(WorldOptions.randomSeed(), false, false),
) { WorldPresets.createNormalWorldDimensions(it) }
}
}

@ -71,6 +71,7 @@ dependencies {
testModImplementation(testFixtures(project(":core")))
testModImplementation(testFixtures(project(":fabric")))
testImplementation(libs.byteBuddy)
testImplementation(libs.byteBuddyAgent)
testImplementation(libs.bundles.test)
testRuntimeOnly(libs.bundles.testRuntime)

@ -8,14 +8,14 @@ package dan200.computercraft.client.model;
import com.mojang.blaze3d.vertex.DefaultVertexFormat;
import com.mojang.blaze3d.vertex.VertexFormat;
import com.mojang.blaze3d.vertex.VertexFormatElement;
import com.mojang.math.Matrix4f;
import com.mojang.math.Transformation;
import com.mojang.math.Vector4f;
import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.core.Direction;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.block.state.BlockState;
import org.joml.Matrix4f;
import org.joml.Vector4f;
import javax.annotation.Nullable;
import java.util.ArrayList;
@ -64,8 +64,7 @@ public class TransformedBakedModel extends CustomBakedModel {
// Transform the position
var pos = new Vector4f(x, y, z, 1);
pos.transform(transformation);
pos.perspectiveDivide();
transformation.transformProject(pos);
vertexData[start] = Float.floatToRawIntBits(pos.x());
vertexData[start + 1] = Float.floatToRawIntBits(pos.y());

@ -5,7 +5,6 @@
*/
package dan200.computercraft.client.model.turtle;
import com.mojang.datafixers.util.Pair;
import dan200.computercraft.api.ComputerCraftAPI;
import net.fabricmc.fabric.api.client.model.ModelProviderException;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
@ -16,7 +15,9 @@ import net.minecraft.util.GsonHelper;
import javax.annotation.Nullable;
import java.io.IOException;
import java.util.*;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.function.Function;
public final class TurtleModelLoader {
@ -57,15 +58,13 @@ public final class TurtleModelLoader {
}
@Override
public Collection<Material> getMaterials(Function<ResourceLocation, UnbakedModel> modelGetter, Set<Pair<String, String>> missingTextureErrors) {
Set<Material> materials = new HashSet<>();
materials.addAll(modelGetter.apply(model).getMaterials(modelGetter, missingTextureErrors));
materials.addAll(modelGetter.apply(COLOUR_TURTLE_MODEL).getMaterials(modelGetter, missingTextureErrors));
return materials;
public void resolveParents(Function<ResourceLocation, UnbakedModel> function) {
function.apply(model).resolveParents(function);
function.apply(COLOUR_TURTLE_MODEL).resolveParents(function);
}
@Override
public BakedModel bake(ModelBakery bakery, Function<Material, TextureAtlasSprite> spriteGetter, ModelState transform, ResourceLocation location) {
public BakedModel bake(ModelBaker bakery, Function<Material, TextureAtlasSprite> spriteGetter, ModelState transform, ResourceLocation location) {
var mainModel = bakery.bake(model, transform);
if (mainModel == null) throw new NullPointerException(model + " failed to bake");

@ -8,7 +8,7 @@ package dan200.computercraft.mixin.client;
import dan200.computercraft.client.ClientRegistry;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.client.renderer.ShaderInstance;
import net.minecraft.server.packs.resources.ResourceManager;
import net.minecraft.server.packs.resources.ResourceProvider;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
@ -29,7 +29,7 @@ class GameRendererMixin {
@Inject(method = "reloadShaders", at = @At(value = "TAIL"))
@SuppressWarnings("UnusedMethod")
private void onReloadShaders(ResourceManager resourceManager, CallbackInfo ci) {
private void onReloadShaders(ResourceProvider resourceManager, CallbackInfo ci) {
try {
ClientRegistry.registerShaders(resourceManager, (shader, callback) -> {
shaders.put(shader.getName(), shader);

@ -20,7 +20,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
class ItemFrameRendererMixin {
@Inject(
method = "render(Lnet/minecraft/world/entity/decoration/ItemFrame;FFLcom/mojang/blaze3d/vertex/PoseStack;Lnet/minecraft/client/renderer/MultiBufferSource;I)V",
at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/vertex/PoseStack;mulPose(Lcom/mojang/math/Quaternion;)V", ordinal = 2, shift = At.Shift.AFTER),
at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/vertex/PoseStack;mulPose(Lorg/joml/Quaternionf;)V", ordinal = 2, shift = At.Shift.AFTER),
cancellable = true
)
@SuppressWarnings("UnusedMethod")

@ -166,6 +166,7 @@
"gui.computercraft.config.upload_nag_delay": "Upload nag delay",
"gui.computercraft.config.upload_nag_delay.tooltip": "The delay in seconds after which we'll notify about unhandled imports. Set to 0 to disable.\nRange: 0 ~ 60",
"gui.computercraft.pocket_computer_overlay": "Pocket computer open. Press ESC to close.",
"gui.computercraft.terminal": "Computer terminal",
"gui.computercraft.tooltip.computer_id": "Computer ID: %s",
"gui.computercraft.tooltip.copy": "Copy to clipboard",
"gui.computercraft.tooltip.disk_id": "Disk ID: %s",

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