mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-01-10 09:20:28 +00:00
Load turtle overlays from a registry
- Add a new computercraft:turtle_overlay dynamic registry, which stores turtle overlays. Turtle overlays are just a model id and an (optional) boolean flag, which specifies whether this overlay is compatible with the elf/christmas model. - Change the computercraft:overlay component to accept a Holder<TurtleOverlay> (instead of just a model ID). This accepts both an overlay ID or an inline overlay object (e.g. you can do cc:turtle_normal[computercraft:overlay={model:"foo"}]. - Update turtle model and BE rendering code to render both the overlay and elf (if compatible). Fixes #1663. - Ideally we'd automatically load all models listed in the overlay registry. However, resource loading happens separately to datapacks, so we can't link the two. Instead, we add a new assets/computercraft/extra_models.json file that lists any additional models that should be loaded and baked. This file includes all built-in overlay models, but external resource packs and/or mods can easily extend it.
This commit is contained in:
parent
1a1623075f
commit
f10e401aea
@ -25,6 +25,7 @@ import dan200.computercraft.shared.computer.core.ComputerState;
|
||||
import dan200.computercraft.shared.computer.core.ServerContext;
|
||||
import dan200.computercraft.shared.computer.inventory.AbstractComputerMenu;
|
||||
import dan200.computercraft.shared.media.items.DiskItem;
|
||||
import dan200.computercraft.shared.turtle.TurtleOverlay;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.color.item.ItemColor;
|
||||
@ -53,6 +54,7 @@ import net.minecraft.world.level.ItemLike;
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Supplier;
|
||||
@ -143,15 +145,14 @@ public final class ClientRegistry {
|
||||
register.accept(GuiSprites.initialise(minecraft.getTextureManager()));
|
||||
}
|
||||
|
||||
private static final String[] EXTRA_MODELS = new String[]{
|
||||
"block/turtle_colour",
|
||||
"block/turtle_elf_overlay",
|
||||
"block/turtle_rainbow_overlay",
|
||||
"block/turtle_trans_overlay",
|
||||
private static final ResourceLocation[] EXTRA_MODELS = {
|
||||
TurtleOverlay.ELF_MODEL,
|
||||
TurtleBlockEntityRenderer.COLOUR_TURTLE_MODEL,
|
||||
};
|
||||
|
||||
public static void registerExtraModels(Consumer<ResourceLocation> register) {
|
||||
for (var model : EXTRA_MODELS) register.accept(ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, model));
|
||||
public static void registerExtraModels(Consumer<ResourceLocation> register, Collection<ResourceLocation> extraModels) {
|
||||
for (var model : EXTRA_MODELS) register.accept(model);
|
||||
extraModels.forEach(register);
|
||||
TurtleUpgradeModellers.getDependencies().forEach(register);
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,68 @@
|
||||
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.client.model;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.mojang.serialization.Codec;
|
||||
import com.mojang.serialization.JsonOps;
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.shared.turtle.TurtleOverlay;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.packs.resources.ResourceManager;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* A list of extra models to load on the client.
|
||||
* <p>
|
||||
* This is largely intended for use with {@linkplain TurtleOverlay turtle overlays}. As overlays are stored in a dynamic
|
||||
* registry, they are not available when resources are loaded, and so we need a way to request the overlays' models be
|
||||
* loaded.
|
||||
*
|
||||
* @param models The models to load.
|
||||
*/
|
||||
public record ExtraModels(List<ResourceLocation> models) {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(ExtraModels.class);
|
||||
private static final Gson GSON = new Gson();
|
||||
|
||||
/**
|
||||
* The path where the extra models are listed.
|
||||
*/
|
||||
public static final ResourceLocation PATH = ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "extra_models.json");
|
||||
|
||||
/**
|
||||
* The coded used to store the extra model file.
|
||||
*/
|
||||
public static final Codec<ExtraModels> CODEC = ResourceLocation.CODEC.listOf().xmap(ExtraModels::new, ExtraModels::models);
|
||||
|
||||
/**
|
||||
* Get the list of all extra models to load.
|
||||
*
|
||||
* @param resources The current resource manager.
|
||||
* @return A set of all resources to load.
|
||||
*/
|
||||
public static Collection<ResourceLocation> loadAll(ResourceManager resources) {
|
||||
Set<ResourceLocation> out = new HashSet<>();
|
||||
|
||||
for (var path : resources.getResourceStack(PATH)) {
|
||||
ExtraModels models;
|
||||
try (var stream = path.openAsReader()) {
|
||||
models = ExtraModels.CODEC.parse(JsonOps.INSTANCE, GSON.fromJson(stream, JsonElement.class)).getOrThrow(JsonParseException::new);
|
||||
} catch (IOException | RuntimeException e) {
|
||||
LOG.error("Failed to load extra models from {}", path.sourcePackId());
|
||||
continue;
|
||||
}
|
||||
|
||||
out.addAll(models.models());
|
||||
}
|
||||
|
||||
return Collections.unmodifiableCollection(out);
|
||||
}
|
||||
}
|
@ -12,13 +12,14 @@ import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import dan200.computercraft.api.turtle.TurtleSide;
|
||||
import dan200.computercraft.api.upgrades.UpgradeData;
|
||||
import dan200.computercraft.client.platform.ClientPlatformHelper;
|
||||
import dan200.computercraft.client.render.TurtleBlockEntityRenderer;
|
||||
import dan200.computercraft.client.turtle.TurtleUpgradeModellers;
|
||||
import dan200.computercraft.shared.turtle.TurtleOverlay;
|
||||
import dan200.computercraft.shared.turtle.items.TurtleItem;
|
||||
import dan200.computercraft.shared.util.DataComponentUtil;
|
||||
import dan200.computercraft.shared.util.Holiday;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.resources.model.BakedModel;
|
||||
import net.minecraft.client.resources.model.ModelManager;
|
||||
import net.minecraft.core.component.DataComponents;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
@ -52,7 +53,7 @@ public final class TurtleModelParts<T> {
|
||||
boolean colour,
|
||||
@Nullable UpgradeData<ITurtleUpgrade> leftUpgrade,
|
||||
@Nullable UpgradeData<ITurtleUpgrade> rightUpgrade,
|
||||
@Nullable ResourceLocation overlay,
|
||||
@Nullable TurtleOverlay overlay,
|
||||
boolean christmas,
|
||||
boolean flip
|
||||
) {
|
||||
@ -113,10 +114,10 @@ public final class TurtleModelParts<T> {
|
||||
var parts = new ArrayList<BakedModel>(4);
|
||||
parts.add(transform(combo.colour() ? colourModel : familyModel, transformation));
|
||||
|
||||
var overlayModelLocation = TurtleBlockEntityRenderer.getTurtleOverlayModel(combo.overlay(), combo.christmas());
|
||||
if (overlayModelLocation != null) {
|
||||
parts.add(transform(ClientPlatformHelper.get().getModel(modelManager, overlayModelLocation), transformation));
|
||||
}
|
||||
if (combo.overlay() != null) addPart(parts, modelManager, transformation, combo.overlay().model());
|
||||
|
||||
var showChristmas = TurtleOverlay.showElfOverlay(combo.overlay(), combo.christmas());
|
||||
if (showChristmas) addPart(parts, modelManager, transformation, TurtleOverlay.ELF_MODEL);
|
||||
|
||||
addUpgrade(parts, transformation, TurtleSide.LEFT, combo.leftUpgrade());
|
||||
addUpgrade(parts, transformation, TurtleSide.RIGHT, combo.rightUpgrade());
|
||||
@ -124,6 +125,10 @@ public final class TurtleModelParts<T> {
|
||||
return parts;
|
||||
}
|
||||
|
||||
private void addPart(List<BakedModel> parts, ModelManager modelManager, Transformation transformation, ResourceLocation model) {
|
||||
parts.add(transform(ClientPlatformHelper.get().getModel(modelManager, model), transformation));
|
||||
}
|
||||
|
||||
private void addUpgrade(List<BakedModel> parts, Transformation transformation, TurtleSide side, @Nullable UpgradeData<ITurtleUpgrade> upgrade) {
|
||||
if (upgrade == null) return;
|
||||
var model = TurtleUpgradeModellers.getModel(upgrade.upgrade(), upgrade.data(), side);
|
||||
|
@ -11,6 +11,7 @@ import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.api.turtle.TurtleSide;
|
||||
import dan200.computercraft.client.platform.ClientPlatformHelper;
|
||||
import dan200.computercraft.client.turtle.TurtleUpgradeModellers;
|
||||
import dan200.computercraft.shared.turtle.TurtleOverlay;
|
||||
import dan200.computercraft.shared.turtle.blocks.TurtleBlockEntity;
|
||||
import dan200.computercraft.shared.util.Holiday;
|
||||
import net.minecraft.client.Minecraft;
|
||||
@ -28,8 +29,7 @@ import net.minecraft.world.phys.HitResult;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class TurtleBlockEntityRenderer implements BlockEntityRenderer<TurtleBlockEntity> {
|
||||
private static final ResourceLocation COLOUR_TURTLE_MODEL = ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "block/turtle_colour");
|
||||
private static final ResourceLocation ELF_OVERLAY_MODEL = ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "block/turtle_elf_overlay");
|
||||
public static final ResourceLocation COLOUR_TURTLE_MODEL = ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "block/turtle_colour");
|
||||
|
||||
private final BlockEntityRenderDispatcher renderer;
|
||||
private final Font font;
|
||||
@ -39,12 +39,6 @@ public class TurtleBlockEntityRenderer implements BlockEntityRenderer<TurtleBloc
|
||||
font = context.getFont();
|
||||
}
|
||||
|
||||
public static @Nullable ResourceLocation getTurtleOverlayModel(@Nullable ResourceLocation overlay, boolean christmas) {
|
||||
if (overlay != null) return overlay;
|
||||
if (christmas) return ELF_OVERLAY_MODEL;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void render(TurtleBlockEntity turtle, float partialTicks, PoseStack transform, MultiBufferSource buffers, int lightmapCoord, int overlayLight) {
|
||||
transform.pushPose();
|
||||
@ -99,10 +93,11 @@ public class TurtleBlockEntityRenderer implements BlockEntityRenderer<TurtleBloc
|
||||
}
|
||||
|
||||
// Render the overlay
|
||||
var overlayModel = getTurtleOverlayModel(overlay, Holiday.getCurrent() == Holiday.CHRISTMAS);
|
||||
if (overlayModel != null) {
|
||||
renderModel(transform, buffers, lightmapCoord, overlayLight, overlayModel, null);
|
||||
}
|
||||
if (overlay != null) renderModel(transform, buffers, lightmapCoord, overlayLight, overlay.model(), null);
|
||||
|
||||
// And the Christmas overlay.
|
||||
var showChristmas = TurtleOverlay.showElfOverlay(overlay, Holiday.getCurrent() == Holiday.CHRISTMAS);
|
||||
if (showChristmas) renderModel(transform, buffers, lightmapCoord, overlayLight, TurtleOverlay.ELF_MODEL, null);
|
||||
|
||||
// Render the upgrades
|
||||
renderUpgrade(transform, buffers, lightmapCoord, overlayLight, turtle, TurtleSide.LEFT, partialTicks);
|
||||
|
@ -6,15 +6,18 @@ package dan200.computercraft.data.client;
|
||||
|
||||
import dan200.computercraft.client.gui.GuiSprites;
|
||||
import dan200.computercraft.data.DataProviders;
|
||||
import dan200.computercraft.shared.turtle.TurtleOverlay;
|
||||
import dan200.computercraft.shared.turtle.inventory.UpgradeSlot;
|
||||
import net.minecraft.client.renderer.texture.atlas.SpriteSource;
|
||||
import net.minecraft.client.renderer.texture.atlas.SpriteSources;
|
||||
import net.minecraft.client.renderer.texture.atlas.sources.SingleFile;
|
||||
import net.minecraft.core.HolderLookup;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.packs.PackType;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
@ -26,7 +29,7 @@ public final class ClientDataProviders {
|
||||
private ClientDataProviders() {
|
||||
}
|
||||
|
||||
public static void add(DataProviders.GeneratorSink generator) {
|
||||
public static void add(DataProviders.GeneratorSink generator, CompletableFuture<HolderLookup.Provider> registries) {
|
||||
generator.addFromCodec("Block atlases", PackType.CLIENT_RESOURCES, "atlases", SpriteSources.FILE_CODEC, out -> {
|
||||
out.accept(ResourceLocation.withDefaultNamespace("blocks"), List.of(
|
||||
new SingleFile(UpgradeSlot.LEFT_UPGRADE, Optional.empty()),
|
||||
@ -44,5 +47,12 @@ public final class ClientDataProviders {
|
||||
GuiSprites.COMPUTER_COLOUR.textures()
|
||||
).flatMap(x -> x).<SpriteSource>map(x -> new SingleFile(x, Optional.empty())).toList());
|
||||
});
|
||||
|
||||
generator.add(pack -> new ExtraModelsProvider(pack, registries) {
|
||||
@Override
|
||||
public Stream<ResourceLocation> getModels(HolderLookup.Provider registries) {
|
||||
return registries.lookupOrThrow(TurtleOverlay.REGISTRY).listElements().map(x -> x.value().model());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,52 @@
|
||||
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.data.client;
|
||||
|
||||
import com.mojang.serialization.JsonOps;
|
||||
import dan200.computercraft.client.model.ExtraModels;
|
||||
import net.minecraft.core.HolderLookup;
|
||||
import net.minecraft.data.CachedOutput;
|
||||
import net.minecraft.data.DataProvider;
|
||||
import net.minecraft.data.PackOutput;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* A data provider to generate {@link ExtraModels}.
|
||||
*/
|
||||
abstract class ExtraModelsProvider implements DataProvider {
|
||||
private final Path path;
|
||||
private final CompletableFuture<HolderLookup.Provider> registries;
|
||||
|
||||
ExtraModelsProvider(PackOutput output, CompletableFuture<HolderLookup.Provider> registries) {
|
||||
path = output.getOutputFolder(PackOutput.Target.RESOURCE_PACK).resolve(ExtraModels.PATH.getNamespace()).resolve(ExtraModels.PATH.getPath());
|
||||
this.registries = registries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a stream of models to load.
|
||||
*
|
||||
* @param registries The current registries.
|
||||
* @return The collection of extra models to load.
|
||||
*/
|
||||
public abstract Stream<ResourceLocation> getModels(HolderLookup.Provider registries);
|
||||
|
||||
@Override
|
||||
public final CompletableFuture<?> run(CachedOutput output) {
|
||||
return registries.thenCompose(registries -> {
|
||||
var models = new ExtraModels(getModels(registries).sorted().toList());
|
||||
var json = ExtraModels.CODEC.encodeStart(JsonOps.INSTANCE, models).getOrThrow(IllegalStateException::new);
|
||||
return DataProvider.saveStable(output, json, path);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String getName() {
|
||||
return "Extra Models";
|
||||
}
|
||||
}
|
1
projects/common/src/generated/resources/assets/computercraft/extra_models.json
generated
Normal file
1
projects/common/src/generated/resources/assets/computercraft/extra_models.json
generated
Normal file
@ -0,0 +1 @@
|
||||
["computercraft:block/turtle_rainbow_overlay", "computercraft:block/turtle_trans_overlay"]
|
@ -3,7 +3,7 @@
|
||||
"criteria": {
|
||||
"has_dye": {"conditions": {"items": [{"items": "#c:dyes"}]}, "trigger": "minecraft:inventory_changed"},
|
||||
"has_the_recipe": {
|
||||
"conditions": {"recipe": "computercraft:turtle_advanced_overlays/turtle_trans_overlay"},
|
||||
"conditions": {"recipe": "computercraft:turtle_advanced_overlays/rainbow_flag"},
|
||||
"trigger": "minecraft:recipe_unlocked"
|
||||
},
|
||||
"has_turtle": {
|
||||
@ -12,5 +12,5 @@
|
||||
}
|
||||
},
|
||||
"requirements": [["has_the_recipe", "has_turtle", "has_dye"]],
|
||||
"rewards": {"recipes": ["computercraft:turtle_advanced_overlays/turtle_trans_overlay"]}
|
||||
"rewards": {"recipes": ["computercraft:turtle_advanced_overlays/rainbow_flag"]}
|
||||
}
|
@ -3,7 +3,7 @@
|
||||
"criteria": {
|
||||
"has_dye": {"conditions": {"items": [{"items": "#c:dyes"}]}, "trigger": "minecraft:inventory_changed"},
|
||||
"has_the_recipe": {
|
||||
"conditions": {"recipe": "computercraft:turtle_advanced_overlays/turtle_rainbow_overlay"},
|
||||
"conditions": {"recipe": "computercraft:turtle_advanced_overlays/trans_flag"},
|
||||
"trigger": "minecraft:recipe_unlocked"
|
||||
},
|
||||
"has_turtle": {
|
||||
@ -12,5 +12,5 @@
|
||||
}
|
||||
},
|
||||
"requirements": [["has_the_recipe", "has_turtle", "has_dye"]],
|
||||
"rewards": {"recipes": ["computercraft:turtle_advanced_overlays/turtle_rainbow_overlay"]}
|
||||
"rewards": {"recipes": ["computercraft:turtle_advanced_overlays/trans_flag"]}
|
||||
}
|
@ -3,7 +3,7 @@
|
||||
"criteria": {
|
||||
"has_dye": {"conditions": {"items": [{"items": "#c:dyes"}]}, "trigger": "minecraft:inventory_changed"},
|
||||
"has_the_recipe": {
|
||||
"conditions": {"recipe": "computercraft:turtle_normal_overlays/turtle_trans_overlay"},
|
||||
"conditions": {"recipe": "computercraft:turtle_normal_overlays/rainbow_flag"},
|
||||
"trigger": "minecraft:recipe_unlocked"
|
||||
},
|
||||
"has_turtle": {
|
||||
@ -12,5 +12,5 @@
|
||||
}
|
||||
},
|
||||
"requirements": [["has_the_recipe", "has_turtle", "has_dye"]],
|
||||
"rewards": {"recipes": ["computercraft:turtle_normal_overlays/turtle_trans_overlay"]}
|
||||
"rewards": {"recipes": ["computercraft:turtle_normal_overlays/rainbow_flag"]}
|
||||
}
|
@ -3,7 +3,7 @@
|
||||
"criteria": {
|
||||
"has_dye": {"conditions": {"items": [{"items": "#c:dyes"}]}, "trigger": "minecraft:inventory_changed"},
|
||||
"has_the_recipe": {
|
||||
"conditions": {"recipe": "computercraft:turtle_normal_overlays/turtle_rainbow_overlay"},
|
||||
"conditions": {"recipe": "computercraft:turtle_normal_overlays/trans_flag"},
|
||||
"trigger": "minecraft:recipe_unlocked"
|
||||
},
|
||||
"has_turtle": {
|
||||
@ -12,5 +12,5 @@
|
||||
}
|
||||
},
|
||||
"requirements": [["has_the_recipe", "has_turtle", "has_dye"]],
|
||||
"rewards": {"recipes": ["computercraft:turtle_normal_overlays/turtle_rainbow_overlay"]}
|
||||
"rewards": {"recipes": ["computercraft:turtle_normal_overlays/trans_flag"]}
|
||||
}
|
@ -0,0 +1 @@
|
||||
{"model": "computercraft:block/turtle_rainbow_overlay", "show_elf_overlay": true}
|
@ -0,0 +1 @@
|
||||
{"model": "computercraft:block/turtle_trans_overlay", "show_elf_overlay": true}
|
@ -20,7 +20,7 @@
|
||||
{"item": "computercraft:turtle_advanced"}
|
||||
],
|
||||
"result": {
|
||||
"components": {"computercraft:overlay": "computercraft:block/turtle_rainbow_overlay"},
|
||||
"components": {"computercraft:overlay": "computercraft:rainbow_flag"},
|
||||
"count": 1,
|
||||
"id": "computercraft:turtle_advanced"
|
||||
}
|
@ -17,7 +17,7 @@
|
||||
{"item": "computercraft:turtle_advanced"}
|
||||
],
|
||||
"result": {
|
||||
"components": {"computercraft:overlay": "computercraft:block/turtle_trans_overlay"},
|
||||
"components": {"computercraft:overlay": "computercraft:trans_flag"},
|
||||
"count": 1,
|
||||
"id": "computercraft:turtle_advanced"
|
||||
}
|
@ -20,7 +20,7 @@
|
||||
{"item": "computercraft:turtle_normal"}
|
||||
],
|
||||
"result": {
|
||||
"components": {"computercraft:overlay": "computercraft:block/turtle_rainbow_overlay"},
|
||||
"components": {"computercraft:overlay": "computercraft:rainbow_flag"},
|
||||
"count": 1,
|
||||
"id": "computercraft:turtle_normal"
|
||||
}
|
@ -17,7 +17,7 @@
|
||||
{"item": "computercraft:turtle_normal"}
|
||||
],
|
||||
"result": {
|
||||
"components": {"computercraft:overlay": "computercraft:block/turtle_trans_overlay"},
|
||||
"components": {"computercraft:overlay": "computercraft:trans_flag"},
|
||||
"count": 1,
|
||||
"id": "computercraft:turtle_normal"
|
||||
}
|
@ -7,6 +7,7 @@ package dan200.computercraft.data;
|
||||
import com.mojang.serialization.Codec;
|
||||
import dan200.computercraft.api.pocket.IPocketUpgrade;
|
||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import dan200.computercraft.shared.turtle.TurtleOverlay;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.core.HolderLookup;
|
||||
import net.minecraft.core.RegistrySetBuilder;
|
||||
@ -38,6 +39,7 @@ public final class DataProviders {
|
||||
Util.make(new RegistrySetBuilder(), builder -> {
|
||||
builder.add(ITurtleUpgrade.REGISTRY, TurtleUpgradeProvider::addUpgrades);
|
||||
builder.add(IPocketUpgrade.REGISTRY, PocketUpgradeProvider::addUpgrades);
|
||||
builder.add(TurtleOverlay.REGISTRY, TurtleOverlays::register);
|
||||
}));
|
||||
var fullRegistries = fullRegistryPatch.thenApply(RegistrySetBuilder.PatchedRegistries::full);
|
||||
|
||||
@ -57,7 +59,8 @@ public final class DataProviders {
|
||||
// and invoke that.
|
||||
try {
|
||||
Class.forName("dan200.computercraft.data.client.ClientDataProviders")
|
||||
.getMethod("add", GeneratorSink.class).invoke(null, generator);
|
||||
.getMethod("add", GeneratorSink.class, CompletableFuture.class)
|
||||
.invoke(null, generator, fullRegistries);
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ import dan200.computercraft.shared.recipe.ImpostorShapelessRecipe;
|
||||
import dan200.computercraft.shared.recipe.TransformShapedRecipe;
|
||||
import dan200.computercraft.shared.recipe.TransformShapelessRecipe;
|
||||
import dan200.computercraft.shared.recipe.function.CopyComponents;
|
||||
import dan200.computercraft.shared.turtle.TurtleOverlay;
|
||||
import dan200.computercraft.shared.turtle.items.TurtleItem;
|
||||
import dan200.computercraft.shared.turtle.recipes.TurtleUpgradeRecipe;
|
||||
import dan200.computercraft.shared.util.ColourUtils;
|
||||
@ -45,6 +46,7 @@ import net.minecraft.data.recipes.RecipeCategory;
|
||||
import net.minecraft.data.recipes.RecipeOutput;
|
||||
import net.minecraft.data.recipes.ShapedRecipeBuilder;
|
||||
import net.minecraft.data.recipes.ShapelessRecipeBuilder;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.tags.ItemTags;
|
||||
import net.minecraft.tags.TagKey;
|
||||
@ -96,7 +98,7 @@ final class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
||||
diskColours(add);
|
||||
pocketUpgrades(add, registries);
|
||||
turtleUpgrades(add, registries);
|
||||
turtleOverlays(add);
|
||||
turtleOverlays(add, registries);
|
||||
|
||||
addSpecial(add, new DiskRecipe(CraftingBookCategory.MISC));
|
||||
addSpecial(add, new ColourableRecipe(CraftingBookCategory.MISC));
|
||||
@ -189,8 +191,8 @@ final class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
||||
}
|
||||
}
|
||||
|
||||
private void turtleOverlays(RecipeOutput add) {
|
||||
turtleOverlay(add, "turtle_trans_overlay", x -> x
|
||||
private void turtleOverlays(RecipeOutput add, HolderLookup.Provider registries) {
|
||||
turtleOverlay(add, registries, TurtleOverlays.TRANS_FLAG, x -> x
|
||||
.unlockedBy("has_dye", inventoryChange(itemPredicate(ingredients.dye())))
|
||||
.requires(ColourUtils.getDyeTag(DyeColor.LIGHT_BLUE))
|
||||
.requires(ColourUtils.getDyeTag(DyeColor.PINK))
|
||||
@ -198,7 +200,7 @@ final class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
||||
.requires(Items.STICK)
|
||||
);
|
||||
|
||||
turtleOverlay(add, "turtle_rainbow_overlay", x -> x
|
||||
turtleOverlay(add, registries, TurtleOverlays.RAINBOW_FLAG, x -> x
|
||||
.unlockedBy("has_dye", inventoryChange(itemPredicate(ingredients.dye())))
|
||||
.requires(ColourUtils.getDyeTag(DyeColor.RED))
|
||||
.requires(ColourUtils.getDyeTag(DyeColor.ORANGE))
|
||||
@ -210,14 +212,14 @@ final class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
||||
);
|
||||
}
|
||||
|
||||
private void turtleOverlay(RecipeOutput add, String overlay, Consumer<ShapelessSpecBuilder> build) {
|
||||
var overlayId = ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "block/" + overlay);
|
||||
private void turtleOverlay(RecipeOutput add, HolderLookup.Provider registries, ResourceKey<TurtleOverlay> overlay, Consumer<ShapelessSpecBuilder> build) {
|
||||
var holder = registries.lookupOrThrow(overlay.registryKey()).getOrThrow(overlay);
|
||||
|
||||
for (var turtleItem : turtleItems()) {
|
||||
var name = RegistryHelper.getKeyOrThrow(BuiltInRegistries.ITEM, turtleItem);
|
||||
|
||||
var builder = ShapelessSpecBuilder
|
||||
.shapeless(RecipeCategory.REDSTONE, DataComponentUtil.createStack(turtleItem, ModRegistry.DataComponents.OVERLAY.get(), overlayId))
|
||||
.shapeless(RecipeCategory.REDSTONE, DataComponentUtil.createStack(turtleItem, ModRegistry.DataComponents.OVERLAY.get(), holder))
|
||||
.group(name.withSuffix("_overlay").toString())
|
||||
.unlockedBy("has_turtle", inventoryChange(turtleItem));
|
||||
build.accept(builder);
|
||||
@ -226,7 +228,7 @@ final class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
||||
.build(s -> new TransformShapelessRecipe(s, List.of(
|
||||
CopyComponents.builder(turtleItem).exclude(ModRegistry.DataComponents.OVERLAY.get()).build()
|
||||
)))
|
||||
.save(add, name.withSuffix("_overlays/" + overlay));
|
||||
.save(add, name.withSuffix("_overlays/" + overlay.location().getPath()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,38 @@
|
||||
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.data;
|
||||
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.shared.turtle.TurtleOverlay;
|
||||
import net.minecraft.data.worldgen.BootstrapContext;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
/**
|
||||
* Built-in turtle overlays.
|
||||
*/
|
||||
final class TurtleOverlays {
|
||||
public static final ResourceKey<TurtleOverlay> RAINBOW_FLAG = create("rainbow_flag");
|
||||
public static final ResourceKey<TurtleOverlay> TRANS_FLAG = create("trans_flag");
|
||||
|
||||
private static ResourceKey<TurtleOverlay> create(String name) {
|
||||
return ResourceKey.create(TurtleOverlay.REGISTRY, ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, name));
|
||||
}
|
||||
|
||||
private TurtleOverlays() {
|
||||
}
|
||||
|
||||
public static void register(BootstrapContext<TurtleOverlay> registry) {
|
||||
registry.register(RAINBOW_FLAG, new TurtleOverlay(
|
||||
ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "block/turtle_rainbow_overlay"),
|
||||
true
|
||||
));
|
||||
|
||||
registry.register(TRANS_FLAG, new TurtleOverlay(
|
||||
ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "block/turtle_trans_overlay"),
|
||||
true
|
||||
));
|
||||
}
|
||||
}
|
@ -72,6 +72,7 @@ import dan200.computercraft.shared.recipe.*;
|
||||
import dan200.computercraft.shared.recipe.function.CopyComponents;
|
||||
import dan200.computercraft.shared.recipe.function.RecipeFunction;
|
||||
import dan200.computercraft.shared.turtle.FurnaceRefuelHandler;
|
||||
import dan200.computercraft.shared.turtle.TurtleOverlay;
|
||||
import dan200.computercraft.shared.turtle.blocks.TurtleBlock;
|
||||
import dan200.computercraft.shared.turtle.blocks.TurtleBlockEntity;
|
||||
import dan200.computercraft.shared.turtle.inventory.TurtleMenu;
|
||||
@ -95,7 +96,6 @@ import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.network.codec.ByteBufCodecs;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.flag.FeatureFlags;
|
||||
import net.minecraft.world.inventory.MenuType;
|
||||
import net.minecraft.world.item.BlockItem;
|
||||
@ -320,8 +320,8 @@ public final class ModRegistry {
|
||||
/**
|
||||
* The overlay on a turtle.
|
||||
*/
|
||||
public static final RegistryEntry<DataComponentType<ResourceLocation>> OVERLAY = register("overlay", b -> b
|
||||
.persistent(ResourceLocation.CODEC).networkSynchronized(ResourceLocation.STREAM_CODEC)
|
||||
public static final RegistryEntry<DataComponentType<Holder<TurtleOverlay>>> OVERLAY = register("overlay", b -> b
|
||||
.persistent(TurtleOverlay.CODEC).networkSynchronized(TurtleOverlay.STREAM_CODEC)
|
||||
);
|
||||
|
||||
/**
|
||||
|
@ -0,0 +1,78 @@
|
||||
// SPDX-FileCopyrightText: 2024 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.shared.turtle;
|
||||
|
||||
import com.mojang.serialization.Codec;
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder;
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
import dan200.computercraft.shared.util.Holiday;
|
||||
import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import net.minecraft.network.codec.ByteBufCodecs;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.resources.RegistryFileCodec;
|
||||
import net.minecraft.resources.ResourceKey;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* A cosmetic overlay on a turtle.
|
||||
*
|
||||
* @param model The path to the overlay's model.
|
||||
* @param showElfOverlay Whether this overlay is compatible with the {@linkplain #ELF_MODEL Christmas elf model}.
|
||||
* @see ModRegistry.DataComponents#OVERLAY
|
||||
*/
|
||||
public record TurtleOverlay(ResourceLocation model, boolean showElfOverlay) {
|
||||
/**
|
||||
* The registry turtle overlays are stored in.
|
||||
*/
|
||||
public static final ResourceKey<Registry<TurtleOverlay>> REGISTRY = ResourceKey.createRegistryKey(ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "turtle_overlay"));
|
||||
|
||||
/**
|
||||
* The codec used to read/write turtle overlay definitions from datapacks.
|
||||
*/
|
||||
public static final Codec<TurtleOverlay> DIRECT_CODEC = RecordCodecBuilder.create(instance -> instance.group(
|
||||
ResourceLocation.CODEC.fieldOf("model").forGetter(TurtleOverlay::model),
|
||||
Codec.BOOL.optionalFieldOf("show_elf_overlay", false).forGetter(TurtleOverlay::showElfOverlay)
|
||||
).apply(instance, TurtleOverlay::new));
|
||||
|
||||
/**
|
||||
* The codec used for {@link TurtleOverlay} instances.
|
||||
*
|
||||
* @see ModRegistry.DataComponents#OVERLAY
|
||||
*/
|
||||
public static final Codec<Holder<TurtleOverlay>> CODEC = RegistryFileCodec.create(REGISTRY, DIRECT_CODEC);
|
||||
|
||||
/**
|
||||
* The stream codec used for {@link TurtleOverlay} instances.
|
||||
*/
|
||||
public static final StreamCodec<RegistryFriendlyByteBuf, Holder<TurtleOverlay>> STREAM_CODEC = ByteBufCodecs.holder(REGISTRY, StreamCodec.composite(
|
||||
ResourceLocation.STREAM_CODEC, TurtleOverlay::model,
|
||||
ByteBufCodecs.BOOL, TurtleOverlay::showElfOverlay,
|
||||
TurtleOverlay::new
|
||||
));
|
||||
|
||||
/**
|
||||
* An additional overlay that is rendered on all turtles at {@linkplain Holiday#CHRISTMAS Christmas}.
|
||||
*
|
||||
* @see #showElfOverlay()
|
||||
* @see #showElfOverlay(TurtleOverlay, boolean)
|
||||
*/
|
||||
public static final ResourceLocation ELF_MODEL = ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "block/turtle_elf_overlay");
|
||||
|
||||
/**
|
||||
* Determine whether we should show the {@linkplain #ELF_MODEL elf overlay}.
|
||||
*
|
||||
* @param overlay The current {@link TurtleOverlay}.
|
||||
* @param christmas Whether it is Christmas.
|
||||
* @return Whether we should show the elf overlay.
|
||||
*/
|
||||
public static boolean showElfOverlay(@Nullable TurtleOverlay overlay, boolean christmas) {
|
||||
return christmas && (overlay == null || overlay.showElfOverlay());
|
||||
}
|
||||
}
|
@ -20,6 +20,7 @@ import dan200.computercraft.shared.computer.core.ServerComputer;
|
||||
import dan200.computercraft.shared.config.Config;
|
||||
import dan200.computercraft.shared.container.BasicContainer;
|
||||
import dan200.computercraft.shared.platform.PlatformHelper;
|
||||
import dan200.computercraft.shared.turtle.TurtleOverlay;
|
||||
import dan200.computercraft.shared.turtle.apis.TurtleAPI;
|
||||
import dan200.computercraft.shared.turtle.core.TurtleBrain;
|
||||
import dan200.computercraft.shared.turtle.inventory.TurtleMenu;
|
||||
@ -30,7 +31,6 @@ import net.minecraft.core.NonNullList;
|
||||
import net.minecraft.core.component.DataComponentMap;
|
||||
import net.minecraft.core.component.DataComponents;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.ContainerHelper;
|
||||
@ -222,8 +222,9 @@ public class TurtleBlockEntity extends AbstractComputerBlockEntity implements Ba
|
||||
return brain.getColour();
|
||||
}
|
||||
|
||||
public @Nullable ResourceLocation getOverlay() {
|
||||
return brain.getOverlay();
|
||||
public @Nullable TurtleOverlay getOverlay() {
|
||||
var overlay = brain.getOverlay();
|
||||
return overlay == null ? null : overlay.value();
|
||||
}
|
||||
|
||||
public ITurtleAccess getAccess() {
|
||||
|
@ -20,6 +20,7 @@ import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.computer.core.ServerComputer;
|
||||
import dan200.computercraft.shared.config.Config;
|
||||
import dan200.computercraft.shared.container.InventoryDelegate;
|
||||
import dan200.computercraft.shared.turtle.TurtleOverlay;
|
||||
import dan200.computercraft.shared.turtle.blocks.TurtleBlockEntity;
|
||||
import dan200.computercraft.shared.util.BlockEntityHelpers;
|
||||
import dan200.computercraft.shared.util.Holiday;
|
||||
@ -32,7 +33,6 @@ import net.minecraft.core.component.DataComponentPatch;
|
||||
import net.minecraft.core.particles.ParticleTypes;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.tags.FluidTags;
|
||||
import net.minecraft.world.Container;
|
||||
@ -74,7 +74,7 @@ public class TurtleBrain implements TurtleAccessInternal {
|
||||
private int selectedSlot = 0;
|
||||
private int fuelLevel = 0;
|
||||
private int colourHex = -1;
|
||||
private @Nullable ResourceLocation overlay = null;
|
||||
private @Nullable Holder<TurtleOverlay> overlay = null;
|
||||
|
||||
private TurtleAnimation animation = TurtleAnimation.NONE;
|
||||
private int animationProgress = 0;
|
||||
@ -134,7 +134,7 @@ public class TurtleBrain implements TurtleAccessInternal {
|
||||
// Read fields
|
||||
colourHex = nbt.contains(NBT_COLOUR) ? nbt.getInt(NBT_COLOUR) : -1;
|
||||
fuelLevel = nbt.contains(NBT_FUEL) ? nbt.getInt(NBT_FUEL) : 0;
|
||||
overlay = nbt.contains(NBT_OVERLAY) ? ResourceLocation.parse(nbt.getString(NBT_OVERLAY)) : null;
|
||||
overlay = nbt.contains(NBT_OVERLAY) ? NBTUtil.decodeFrom(TurtleOverlay.CODEC, registries, nbt, NBT_OVERLAY) : null;
|
||||
|
||||
// Read upgrades
|
||||
setUpgradeDirect(TurtleSide.LEFT, NBTUtil.decodeFrom(TurtleUpgrades.instance().upgradeDataCodec(), registries, nbt, NBT_LEFT_UPGRADE));
|
||||
@ -144,7 +144,7 @@ public class TurtleBrain implements TurtleAccessInternal {
|
||||
private void writeCommon(CompoundTag nbt, HolderLookup.Provider registries) {
|
||||
nbt.putInt(NBT_FUEL, fuelLevel);
|
||||
if (colourHex != -1) nbt.putInt(NBT_COLOUR, colourHex);
|
||||
if (overlay != null) nbt.putString(NBT_OVERLAY, overlay.toString());
|
||||
if (overlay != null) NBTUtil.encodeTo(TurtleOverlay.CODEC, registries, nbt, NBT_OVERLAY, overlay);
|
||||
|
||||
// Write upgrades
|
||||
NBTUtil.encodeTo(TurtleUpgrades.instance().upgradeDataCodec(), registries, nbt, NBT_LEFT_UPGRADE, getUpgradeWithData(TurtleSide.LEFT));
|
||||
@ -418,11 +418,11 @@ public class TurtleBrain implements TurtleAccessInternal {
|
||||
BlockEntityHelpers.updateBlock(owner);
|
||||
}
|
||||
|
||||
public @Nullable ResourceLocation getOverlay() {
|
||||
public @Nullable Holder<TurtleOverlay> getOverlay() {
|
||||
return overlay;
|
||||
}
|
||||
|
||||
public void setOverlay(@Nullable ResourceLocation overlay) {
|
||||
public void setOverlay(@Nullable Holder<TurtleOverlay> overlay) {
|
||||
if (!Objects.equals(this.overlay, overlay)) {
|
||||
this.overlay = overlay;
|
||||
BlockEntityHelpers.updateBlock(owner);
|
||||
|
@ -12,11 +12,11 @@ import dan200.computercraft.api.upgrades.UpgradeData;
|
||||
import dan200.computercraft.impl.TurtleUpgrades;
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
import dan200.computercraft.shared.computer.items.AbstractComputerItem;
|
||||
import dan200.computercraft.shared.turtle.TurtleOverlay;
|
||||
import dan200.computercraft.shared.turtle.blocks.TurtleBlock;
|
||||
import net.minecraft.core.cauldron.CauldronInteraction;
|
||||
import net.minecraft.core.component.DataComponents;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.ItemInteractionResult;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.block.LayeredCauldronBlock;
|
||||
@ -74,8 +74,9 @@ public class TurtleItem extends AbstractComputerItem {
|
||||
return stack.get(side == TurtleSide.LEFT ? ModRegistry.DataComponents.LEFT_TURTLE_UPGRADE.get() : ModRegistry.DataComponents.RIGHT_TURTLE_UPGRADE.get());
|
||||
}
|
||||
|
||||
public static @Nullable ResourceLocation getOverlay(ItemStack stack) {
|
||||
return stack.get(ModRegistry.DataComponents.OVERLAY.get());
|
||||
public static @Nullable TurtleOverlay getOverlay(ItemStack stack) {
|
||||
var overlay = stack.get(ModRegistry.DataComponents.OVERLAY.get());
|
||||
return overlay == null ? null : overlay.value();
|
||||
}
|
||||
|
||||
public static int getFuelLevel(ItemStack stack) {
|
||||
|
@ -82,7 +82,8 @@ public class ComponentizationFixers {
|
||||
|
||||
if (item.is(TURTLES)) {
|
||||
item.moveTagToComponent("Fuel", "computercraft:fuel");
|
||||
item.moveTagToComponent("Overlay", "computercraft:overlay");
|
||||
|
||||
item.removeTag("Overlay").result().ifPresent(x -> item.setComponent("computercraft:overlay", fixOverlay(x)));
|
||||
|
||||
moveUpgradeToComponent(item, ops, "LeftUpgrade", "LeftUpgradeNbt", "computercraft:left_turtle_upgrade");
|
||||
moveUpgradeToComponent(item, ops, "RightUpgrade", "RightUpgradeNbt", "computercraft:right_turtle_upgrade");
|
||||
@ -152,6 +153,7 @@ public class ComponentizationFixers {
|
||||
return typed -> typed.updateTyped(input, output, typed2 -> typed2.update(DSL.remainderFinder(), x -> {
|
||||
x = moveUpgradeData(x, "LeftUpgrade", "LeftUpgradeNbt");
|
||||
x = moveUpgradeData(x, "RightUpgrade", "RightUpgradeNbt");
|
||||
x = x.update("Overlay", ComponentizationFixers::fixOverlay);
|
||||
return x;
|
||||
}));
|
||||
}
|
||||
@ -164,6 +166,20 @@ public class ComponentizationFixers {
|
||||
return ops.set(key, createUpgradeData(ops, upgradeId, ops.get(dataKey))).remove(dataKey);
|
||||
}
|
||||
|
||||
private static Dynamic<?> fixOverlay(Dynamic<?> overlay) {
|
||||
// Rewrite known overlays to their new ids.
|
||||
var overlayId = overlay.asString(null);
|
||||
if (overlayId == null) return overlay;
|
||||
|
||||
return switch (overlayId) {
|
||||
// Map known overlays to their new id.
|
||||
case "computercraft:block/turtle_trans_overlay" -> overlay.createString("computercraft:trans_flag");
|
||||
case "computercraft:block/turtle_rainbow_overlay" -> overlay.createString("computercraft:rainbow_flag");
|
||||
// And unknown overlays to a direct entry.
|
||||
default -> overlay.emptyMap().set("model", overlay);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new upgrade data.
|
||||
*
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
package dan200.computercraft.gametest
|
||||
|
||||
import dan200.computercraft.api.ComputerCraftAPI
|
||||
import dan200.computercraft.api.detail.BasicItemDetailProvider
|
||||
import dan200.computercraft.api.detail.VanillaDetailRegistries
|
||||
import dan200.computercraft.api.lua.ObjectArguments
|
||||
@ -21,7 +22,9 @@ import dan200.computercraft.shared.peripheral.modem.wired.CableBlock
|
||||
import dan200.computercraft.shared.peripheral.modem.wired.CableModemVariant
|
||||
import dan200.computercraft.shared.peripheral.monitor.MonitorBlock
|
||||
import dan200.computercraft.shared.peripheral.monitor.MonitorEdgeState
|
||||
import dan200.computercraft.shared.turtle.TurtleOverlay
|
||||
import dan200.computercraft.shared.turtle.apis.TurtleAPI
|
||||
import dan200.computercraft.shared.turtle.items.TurtleItem
|
||||
import dan200.computercraft.shared.util.WaterloggableHelpers
|
||||
import dan200.computercraft.test.core.assertArrayEquals
|
||||
import dan200.computercraft.test.core.computer.LuaTaskContext
|
||||
@ -654,6 +657,27 @@ class Turtle_Test {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a structure created on an older version of the game, and checks that data fixers have been applied.
|
||||
*/
|
||||
@GameTest
|
||||
fun Data_fixers(helper: GameTestHelper) = helper.sequence {
|
||||
thenExecute {
|
||||
val overlay = helper.level.registryAccess().registryOrThrow(TurtleOverlay.REGISTRY)
|
||||
.get(ResourceLocation.fromNamespaceAndPath(ComputerCraftAPI.MOD_ID, "trans_flag"))!!
|
||||
val upgrade = helper.level.registryAccess().registryOrThrow(ITurtleUpgrade.REGISTRY)
|
||||
.get(ResourceLocation.withDefaultNamespace("diamond_pickaxe"))!!
|
||||
|
||||
val turtleBe = helper.getBlockEntity(BlockPos(1, 2, 1), ModRegistry.BlockEntities.TURTLE_NORMAL.get())
|
||||
assertEquals(overlay, turtleBe.overlay)
|
||||
assertEquals(upgrade, turtleBe.getUpgrade(TurtleSide.LEFT))
|
||||
|
||||
val turtleItem = turtleBe.getItem(0)
|
||||
assertEquals(overlay, TurtleItem.getOverlay(turtleItem))
|
||||
assertEquals(upgrade, TurtleItem.getUpgrade(turtleItem, TurtleSide.LEFT))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* `turtle.suck` only pulls for the current side.
|
||||
*/
|
||||
|
39
projects/common/src/testMod/resources/data/cctest/structures/turtle_test.data_fixers.snbt
generated
Normal file
39
projects/common/src/testMod/resources/data/cctest/structures/turtle_test.data_fixers.snbt
generated
Normal file
@ -0,0 +1,39 @@
|
||||
{
|
||||
DataVersion: 3465,
|
||||
size: [3, 3, 3],
|
||||
data: [
|
||||
{pos: [0, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [1, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 0], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 1], state: "minecraft:polished_andesite"},
|
||||
{pos: [2, 0, 2], state: "minecraft:polished_andesite"},
|
||||
{pos: [0, 1, 0], state: "minecraft:air"},
|
||||
{pos: [0, 1, 1], state: "minecraft:air"},
|
||||
{pos: [0, 1, 2], state: "minecraft:air"},
|
||||
{pos: [1, 1, 0], state: "minecraft:air"},
|
||||
{pos: [1, 1, 1], state: "computercraft:turtle_normal{facing:south,waterlogged:false}", nbt: {ComputerId: 123, Fuel: 0, Items: [{Count: 1b, Slot: 0b, id: "computercraft:turtle_normal", tag: {ComputerId: 123, LeftUpgrade: "minecraft:diamond_pickaxe", Overlay: "computercraft:block/turtle_trans_overlay"}}], LeftUpgrade: "minecraft:diamond_pickaxe", LeftUpgradeNbt: {}, On: 0b, Overlay: "computercraft:block/turtle_trans_overlay", Owner: {LowerId: -4770154215762454977L, Name: "Player436", UpperId: 114963728012426084L}, Slot: 0, id: "computercraft:turtle_normal"}},
|
||||
{pos: [1, 1, 2], state: "minecraft:air"},
|
||||
{pos: [2, 1, 0], state: "minecraft:air"},
|
||||
{pos: [2, 1, 1], state: "minecraft:air"},
|
||||
{pos: [2, 1, 2], state: "minecraft:air"},
|
||||
{pos: [0, 2, 0], state: "minecraft:air"},
|
||||
{pos: [0, 2, 1], state: "minecraft:air"},
|
||||
{pos: [0, 2, 2], state: "minecraft:air"},
|
||||
{pos: [1, 2, 0], state: "minecraft:air"},
|
||||
{pos: [1, 2, 1], state: "minecraft:air"},
|
||||
{pos: [1, 2, 2], state: "minecraft:air"},
|
||||
{pos: [2, 2, 0], state: "minecraft:air"},
|
||||
{pos: [2, 2, 1], state: "minecraft:air"},
|
||||
{pos: [2, 2, 2], state: "minecraft:air"}
|
||||
],
|
||||
entities: [],
|
||||
palette: [
|
||||
"minecraft:polished_andesite",
|
||||
"minecraft:air",
|
||||
"computercraft:turtle_normal{facing:south,waterlogged:false}"
|
||||
]
|
||||
}
|
@ -53,7 +53,7 @@ public class ComputerCraftClient {
|
||||
ClientRegistry.registerMainThread(ItemProperties::register);
|
||||
|
||||
PreparableModelLoadingPlugin.register(CustomModelLoader::prepare, (state, context) -> {
|
||||
ClientRegistry.registerExtraModels(context::addModels);
|
||||
ClientRegistry.registerExtraModels(context::addModels, state.getExtraModels());
|
||||
context.resolveModel().register(ctx -> state.loadModel(ctx.id()));
|
||||
context.modifyModelAfterBake().register((model, ctx) -> model == null ? null : state.wrapModel(ctx, model));
|
||||
});
|
||||
|
@ -25,6 +25,7 @@ import org.slf4j.LoggerFactory;
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
@ -46,13 +47,15 @@ public final class CustomModelLoader {
|
||||
|
||||
private final Map<ResourceLocation, UnbakedModel> models = new HashMap<>();
|
||||
private final Map<ResourceLocation, String> emissiveModels = new HashMap<>();
|
||||
private final Collection<ResourceLocation> extraModels;
|
||||
|
||||
private CustomModelLoader() {
|
||||
private CustomModelLoader(Collection<ResourceLocation> extraModels) {
|
||||
this.extraModels = extraModels;
|
||||
}
|
||||
|
||||
public static CompletableFuture<CustomModelLoader> prepare(ResourceManager resources, Executor executor) {
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
var loader = new CustomModelLoader();
|
||||
var loader = new CustomModelLoader(ExtraModels.loadAll(resources));
|
||||
for (var resource : resources.listResources("models", x -> x.getNamespace().equals(ComputerCraftAPI.MOD_ID) && x.getPath().endsWith(".json")).entrySet()) {
|
||||
loader.loadModel(resource.getKey(), resource.getValue());
|
||||
}
|
||||
@ -85,6 +88,10 @@ public final class CustomModelLoader {
|
||||
}
|
||||
}
|
||||
|
||||
public Collection<ResourceLocation> getExtraModels() {
|
||||
return extraModels;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load a custom model. This searches for CC models with a custom {@code loader} field.
|
||||
*
|
||||
|
@ -25,6 +25,7 @@ import dan200.computercraft.shared.peripheral.modem.wired.WiredModemFullBlockEnt
|
||||
import dan200.computercraft.shared.peripheral.modem.wireless.WirelessModemBlockEntity;
|
||||
import dan200.computercraft.shared.platform.FabricConfigFile;
|
||||
import dan200.computercraft.shared.recipe.function.RecipeFunction;
|
||||
import dan200.computercraft.shared.turtle.TurtleOverlay;
|
||||
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerChunkEvents;
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
|
||||
@ -70,6 +71,7 @@ public class ComputerCraft {
|
||||
|
||||
DynamicRegistries.registerSynced(ITurtleUpgrade.REGISTRY, TurtleUpgrades.instance().upgradeCodec());
|
||||
DynamicRegistries.registerSynced(IPocketUpgrade.REGISTRY, PocketUpgrades.instance().upgradeCodec());
|
||||
DynamicRegistries.registerSynced(TurtleOverlay.REGISTRY, TurtleOverlay.DIRECT_CODEC);
|
||||
|
||||
ModRegistry.register();
|
||||
ModRegistry.registerMainThread();
|
||||
|
@ -6,6 +6,7 @@ package dan200.computercraft.client;
|
||||
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.api.client.turtle.RegisterTurtleModellersEvent;
|
||||
import dan200.computercraft.client.model.ExtraModels;
|
||||
import dan200.computercraft.client.model.turtle.TurtleModelLoader;
|
||||
import dan200.computercraft.client.turtle.TurtleUpgradeModellers;
|
||||
import net.minecraft.client.Minecraft;
|
||||
@ -57,7 +58,8 @@ public final class ForgeClientRegistry {
|
||||
@SubscribeEvent
|
||||
public static void registerModels(ModelEvent.RegisterAdditional event) {
|
||||
gatherModellers();
|
||||
ClientRegistry.registerExtraModels(x -> event.register(ModelResourceLocation.standalone(x)));
|
||||
var extraModels = ExtraModels.loadAll(Minecraft.getInstance().getResourceManager());
|
||||
ClientRegistry.registerExtraModels(x -> event.register(ModelResourceLocation.standalone(x)), extraModels);
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
|
@ -33,6 +33,7 @@ import dan200.computercraft.shared.peripheral.modem.wired.WiredModemFullBlockEnt
|
||||
import dan200.computercraft.shared.peripheral.modem.wireless.WirelessModemBlockEntity;
|
||||
import dan200.computercraft.shared.platform.ForgeConfigFile;
|
||||
import dan200.computercraft.shared.recipe.function.RecipeFunction;
|
||||
import dan200.computercraft.shared.turtle.TurtleOverlay;
|
||||
import net.minecraft.network.RegistryFriendlyByteBuf;
|
||||
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
@ -92,6 +93,7 @@ public final class ComputerCraft {
|
||||
public static void registerDynamicRegistries(DataPackRegistryEvent.NewRegistry event) {
|
||||
event.dataPackRegistry(ITurtleUpgrade.REGISTRY, TurtleUpgrades.instance().upgradeCodec(), TurtleUpgrades.instance().upgradeCodec());
|
||||
event.dataPackRegistry(IPocketUpgrade.REGISTRY, PocketUpgrades.instance().upgradeCodec(), PocketUpgrades.instance().upgradeCodec());
|
||||
event.dataPackRegistry(TurtleOverlay.REGISTRY, TurtleOverlay.DIRECT_CODEC, TurtleOverlay.DIRECT_CODEC);
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
|
Loading…
Reference in New Issue
Block a user