mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-01-26 00:46:54 +00:00
Allow upgrades to read/write upgrade data from ItemStacks (#1465)
This commit is contained in:
parent
94f5ede75a
commit
f54cb8a432
@ -11,6 +11,7 @@ import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import dan200.computercraft.api.turtle.TurtleSide;
|
||||
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
|
||||
import net.minecraft.client.resources.model.ModelResourceLocation;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
@ -28,12 +29,27 @@ public interface TurtleUpgradeModeller<T extends ITurtleUpgrade> {
|
||||
* When the current turtle is {@literal null}, this function should be constant for a given upgrade and side.
|
||||
*
|
||||
* @param upgrade The upgrade that you're getting the model for.
|
||||
* @param turtle Access to the turtle that the upgrade resides on. This will be null when getting item models!
|
||||
* @param turtle Access to the turtle that the upgrade resides on. This will be null when getting item models, unless
|
||||
* {@link #getModel(ITurtleUpgrade, CompoundTag, TurtleSide)} is overriden.
|
||||
* @param side Which side of the turtle (left or right) the upgrade resides on.
|
||||
* @return The model that you wish to be used to render your upgrade.
|
||||
*/
|
||||
TransformedModel getModel(T upgrade, @Nullable ITurtleAccess turtle, TurtleSide side);
|
||||
|
||||
/**
|
||||
* Obtain the model to be used when rendering a turtle peripheral.
|
||||
* <p>
|
||||
* This is used when rendering the turtle's item model, and so no {@link ITurtleAccess} is available.
|
||||
*
|
||||
* @param upgrade The upgrade that you're getting the model for.
|
||||
* @param data Upgrade data instance for current turtle side.
|
||||
* @param side Which side of the turtle (left or right) the upgrade resides on.
|
||||
* @return The model that you wish to be used to render your upgrade.
|
||||
*/
|
||||
default TransformedModel getModel(T upgrade, CompoundTag data, TurtleSide side) {
|
||||
return getModel(upgrade, (ITurtleAccess) null, side);
|
||||
}
|
||||
|
||||
/**
|
||||
* A basic {@link TurtleUpgradeModeller} which renders using the upgrade's {@linkplain ITurtleUpgrade#getCraftingItem()
|
||||
* crafting item}.
|
||||
|
@ -5,9 +5,11 @@
|
||||
package dan200.computercraft.api.pocket;
|
||||
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.api.upgrades.UpgradeBase;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Map;
|
||||
@ -69,6 +71,8 @@ public interface IPocketAccess {
|
||||
*
|
||||
* @return The upgrade's NBT.
|
||||
* @see #updateUpgradeNBTData()
|
||||
* @see UpgradeBase#getUpgradeItem(CompoundTag)
|
||||
* @see UpgradeBase#getUpgradeData(ItemStack)
|
||||
*/
|
||||
CompoundTag getUpgradeNBTData();
|
||||
|
||||
|
@ -8,10 +8,13 @@ import com.mojang.authlib.GameProfile;
|
||||
import dan200.computercraft.api.lua.ILuaCallback;
|
||||
import dan200.computercraft.api.lua.MethodResult;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.api.upgrades.UpgradeBase;
|
||||
import dan200.computercraft.api.upgrades.UpgradeData;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
@ -245,23 +248,51 @@ public interface ITurtleAccess {
|
||||
void playAnimation(TurtleAnimation animation);
|
||||
|
||||
/**
|
||||
* Returns the turtle on the specified side of the turtle, if there is one.
|
||||
* Returns the upgrade on the specified side of the turtle, if there is one.
|
||||
*
|
||||
* @param side The side to get the upgrade from.
|
||||
* @return The upgrade on the specified side of the turtle, if there is one.
|
||||
* @see #setUpgrade(TurtleSide, ITurtleUpgrade)
|
||||
* @see #getUpgradeWithData(TurtleSide)
|
||||
* @see #setUpgradeWithData(TurtleSide, UpgradeData)
|
||||
*/
|
||||
@Nullable
|
||||
ITurtleUpgrade getUpgrade(TurtleSide side);
|
||||
|
||||
/**
|
||||
* Returns the upgrade on the specified side of the turtle, along with its {@linkplain #getUpgradeNBTData(TurtleSide)
|
||||
* update data}.
|
||||
*
|
||||
* @param side The side to get the upgrade from.
|
||||
* @return The upgrade on the specified side of the turtle, along with its upgrade data, if there is one.
|
||||
* @see #getUpgradeWithData(TurtleSide)
|
||||
* @see #setUpgradeWithData(TurtleSide, UpgradeData)
|
||||
*/
|
||||
default @Nullable UpgradeData<ITurtleUpgrade> getUpgradeWithData(TurtleSide side) {
|
||||
var upgrade = getUpgrade(side);
|
||||
return upgrade == null ? null : UpgradeData.of(upgrade, getUpgradeNBTData(side));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the upgrade for a given side, resetting peripherals and clearing upgrade specific data.
|
||||
*
|
||||
* @param side The side to set the upgrade on.
|
||||
* @param upgrade The upgrade to set, may be {@code null} to clear.
|
||||
* @see #getUpgrade(TurtleSide)
|
||||
* @deprecated Use {@link #setUpgradeWithData(TurtleSide, UpgradeData)}
|
||||
*/
|
||||
void setUpgrade(TurtleSide side, @Nullable ITurtleUpgrade upgrade);
|
||||
@Deprecated
|
||||
default void setUpgrade(TurtleSide side, @Nullable ITurtleUpgrade upgrade) {
|
||||
setUpgradeWithData(side, upgrade == null ? null : UpgradeData.ofDefault(upgrade));
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the upgrade for a given side and its upgrade data.
|
||||
*
|
||||
* @param side The side to set the upgrade on.
|
||||
* @param upgrade The upgrade to set, may be {@code null} to clear.
|
||||
* @see #getUpgradeWithData(TurtleSide)
|
||||
*/
|
||||
void setUpgradeWithData(TurtleSide side, @Nullable UpgradeData<ITurtleUpgrade> upgrade);
|
||||
|
||||
/**
|
||||
* Returns the peripheral created by the upgrade on the specified side of the turtle, if there is one.
|
||||
@ -281,6 +312,8 @@ public interface ITurtleAccess {
|
||||
* @param side The side to get the upgrade data for.
|
||||
* @return The upgrade-specific data.
|
||||
* @see #updateUpgradeNBTData(TurtleSide)
|
||||
* @see UpgradeBase#getUpgradeItem(CompoundTag)
|
||||
* @see UpgradeBase#getUpgradeData(ItemStack)
|
||||
*/
|
||||
CompoundTag getUpgradeNBTData(TurtleSide side);
|
||||
|
||||
|
@ -7,6 +7,7 @@ package dan200.computercraft.api.turtle;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.api.upgrades.UpgradeBase;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@ -79,4 +80,17 @@ public interface ITurtleUpgrade extends UpgradeBase {
|
||||
*/
|
||||
default void update(ITurtleAccess turtle, TurtleSide side) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get upgrade data that should be persisted when the turtle was broken.
|
||||
* <p>
|
||||
* This method should be overridden when you don't need to store all upgrade data by default. For instance, if you
|
||||
* store peripheral state in the upgrade data, which should be lost when the turtle is broken.
|
||||
*
|
||||
* @param upgradeData Data that currently stored for this upgrade
|
||||
* @return Filtered version of this data.
|
||||
*/
|
||||
default CompoundTag getPersistedData(CompoundTag upgradeData) {
|
||||
return upgradeData;
|
||||
}
|
||||
}
|
||||
|
@ -4,10 +4,14 @@
|
||||
|
||||
package dan200.computercraft.api.upgrades;
|
||||
|
||||
import dan200.computercraft.api.pocket.IPocketAccess;
|
||||
import dan200.computercraft.api.pocket.IPocketUpgrade;
|
||||
import dan200.computercraft.api.turtle.ITurtleAccess;
|
||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import dan200.computercraft.api.turtle.TurtleSide;
|
||||
import dan200.computercraft.impl.PlatformHelper;
|
||||
import net.minecraft.Util;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
@ -50,6 +54,42 @@ public interface UpgradeBase {
|
||||
*/
|
||||
ItemStack getCraftingItem();
|
||||
|
||||
/**
|
||||
* Returns the item stack representing a currently equipped turtle upgrade.
|
||||
* <p>
|
||||
* While upgrades can store upgrade data ({@link ITurtleAccess#getUpgradeNBTData(TurtleSide)} and
|
||||
* {@link IPocketAccess#getUpgradeNBTData()}}, by default this data is discarded when an upgrade is unequipped,
|
||||
* and the original item stack is returned.
|
||||
* <p>
|
||||
* By overriding this method, you can create a new {@link ItemStack} which contains enough data to
|
||||
* {@linkplain #getUpgradeData(ItemStack) re-create the upgrade data} if the item is re-equipped.
|
||||
* <p>
|
||||
* When overriding this, you should override {@link #getUpgradeData(ItemStack)} and {@link #isItemSuitable(ItemStack)}
|
||||
* at the same time,
|
||||
*
|
||||
* @param upgradeData The current upgrade data. This should <strong>NOT</strong> be mutated.
|
||||
* @return The item stack returned when unequipping.
|
||||
*/
|
||||
default ItemStack getUpgradeItem(CompoundTag upgradeData) {
|
||||
return getCraftingItem();
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract upgrade data from an {@link ItemStack}.
|
||||
* <p>
|
||||
* This upgrade data will be available with {@link ITurtleAccess#getUpgradeNBTData(TurtleSide)} or
|
||||
* {@link IPocketAccess#getUpgradeNBTData()}.
|
||||
* <p>
|
||||
* This should be an inverse to {@link #getUpgradeItem(CompoundTag)}.
|
||||
*
|
||||
* @param stack The stack that was equipped by the turtle or pocket computer. This will have the same item as
|
||||
* {@link #getCraftingItem()}.
|
||||
* @return The upgrade data that should be set on the turtle or pocket computer.
|
||||
*/
|
||||
default CompoundTag getUpgradeData(ItemStack stack) {
|
||||
return new CompoundTag();
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine if an item is suitable for being used for this upgrade.
|
||||
* <p>
|
||||
|
@ -0,0 +1,82 @@
|
||||
// SPDX-FileCopyrightText: 2023 The CC: Tweaked Developers
|
||||
//
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package dan200.computercraft.api.upgrades;
|
||||
|
||||
import dan200.computercraft.api.pocket.IPocketUpgrade;
|
||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* An upgrade (i.e. a {@link ITurtleUpgrade}) and its current upgrade data.
|
||||
* <p>
|
||||
* <strong>IMPORTANT:</strong> The {@link #data()} in an upgrade data is often a reference to the original upgrade data.
|
||||
* Be careful to take a {@linkplain #copy() defensive copy} if you plan to use the data in this upgrade.
|
||||
*
|
||||
* @param upgrade The current upgrade.
|
||||
* @param data The upgrade's data.
|
||||
* @param <T> The type of upgrade, either {@link ITurtleUpgrade} or {@link IPocketUpgrade}.
|
||||
*/
|
||||
public record UpgradeData<T extends UpgradeBase>(T upgrade, CompoundTag data) {
|
||||
/**
|
||||
* A utility method to construct a new {@link UpgradeData} instance.
|
||||
*
|
||||
* @param upgrade An upgrade.
|
||||
* @param data The upgrade's data.
|
||||
* @param <T> The type of upgrade.
|
||||
* @return The new {@link UpgradeData} instance.
|
||||
*/
|
||||
public static <T extends UpgradeBase> UpgradeData<T> of(T upgrade, CompoundTag data) {
|
||||
return new UpgradeData<>(upgrade, data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an {@link UpgradeData} containing the default {@linkplain #data() data} for an upgrade.
|
||||
*
|
||||
* @param upgrade The upgrade instance.
|
||||
* @param <T> The type of upgrade.
|
||||
* @return The default upgrade data.
|
||||
*/
|
||||
public static <T extends UpgradeBase> UpgradeData<T> ofDefault(T upgrade) {
|
||||
return of(upgrade, upgrade.getUpgradeData(upgrade.getCraftingItem()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Take a copy of a (possibly {@code null}) {@link UpgradeData} instance.
|
||||
*
|
||||
* @param upgrade The copied upgrade data.
|
||||
* @param <T> The type of upgrade.
|
||||
* @return The newly created upgrade data.
|
||||
*/
|
||||
@Contract("!null -> !null; null -> null")
|
||||
public static <T extends UpgradeBase> @Nullable UpgradeData<T> copyOf(@Nullable UpgradeData<T> upgrade) {
|
||||
return upgrade == null ? null : upgrade.copy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@linkplain UpgradeBase#getUpgradeItem(CompoundTag) upgrade item} for this upgrade.
|
||||
* <p>
|
||||
* This returns a defensive copy of the item, to prevent accidental mutation of the upgrade data or original
|
||||
* {@linkplain UpgradeBase#getCraftingItem() upgrade stack}.
|
||||
*
|
||||
* @return This upgrade's item.
|
||||
*/
|
||||
public ItemStack getUpgradeItem() {
|
||||
return upgrade.getUpgradeItem(data).copy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Take a copy of this {@link UpgradeData}. This returns a new instance with the same upgrade and a fresh copy of
|
||||
* the upgrade data.
|
||||
*
|
||||
* @return A copy of the current instance.
|
||||
*/
|
||||
public UpgradeData<T> copy() {
|
||||
return new UpgradeData<>(upgrade(), data().copy());
|
||||
}
|
||||
}
|
@ -4,11 +4,13 @@
|
||||
|
||||
package dan200.computercraft.client.model.turtle;
|
||||
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.math.Transformation;
|
||||
import dan200.computercraft.api.client.TransformedModel;
|
||||
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;
|
||||
@ -21,9 +23,9 @@ import net.minecraft.world.item.ItemStack;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
@ -46,12 +48,19 @@ public final class TurtleModelParts<T> {
|
||||
|
||||
private record Combination(
|
||||
boolean colour,
|
||||
@Nullable ITurtleUpgrade leftUpgrade,
|
||||
@Nullable ITurtleUpgrade rightUpgrade,
|
||||
@Nullable UpgradeData<ITurtleUpgrade> leftUpgrade,
|
||||
@Nullable UpgradeData<ITurtleUpgrade> rightUpgrade,
|
||||
@Nullable ResourceLocation overlay,
|
||||
boolean christmas,
|
||||
boolean flip
|
||||
) {
|
||||
Combination copy() {
|
||||
if (leftUpgrade == null && rightUpgrade == null) return this;
|
||||
return new Combination(
|
||||
colour, UpgradeData.copyOf(leftUpgrade), UpgradeData.copyOf(rightUpgrade),
|
||||
overlay, christmas, flip
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private final BakedModel familyModel;
|
||||
@ -63,12 +72,20 @@ public final class TurtleModelParts<T> {
|
||||
* A cache of {@link TransformedModel} to the transformed {@link BakedModel}. This helps us pool the transformed
|
||||
* instances, reducing memory usage and hopefully ensuring their caches are hit more often!
|
||||
*/
|
||||
private final Map<TransformedModel, BakedModel> transformCache = new HashMap<>();
|
||||
private final Map<TransformedModel, BakedModel> transformCache = CacheBuilder.newBuilder()
|
||||
.concurrencyLevel(1)
|
||||
.expireAfterAccess(30, TimeUnit.SECONDS)
|
||||
.<TransformedModel, BakedModel>build()
|
||||
.asMap();
|
||||
|
||||
/**
|
||||
* A cache of {@link Combination}s to the combined model.
|
||||
*/
|
||||
private final Map<Combination, T> modelCache = new HashMap<>();
|
||||
private final Map<Combination, T> modelCache = CacheBuilder.newBuilder()
|
||||
.concurrencyLevel(1)
|
||||
.expireAfterAccess(30, TimeUnit.SECONDS)
|
||||
.<Combination, T>build()
|
||||
.asMap();
|
||||
|
||||
public TurtleModelParts(BakedModel familyModel, BakedModel colourModel, ModelTransformer transformer, Function<List<BakedModel>, T> combineModel) {
|
||||
this.familyModel = familyModel;
|
||||
@ -78,7 +95,15 @@ public final class TurtleModelParts<T> {
|
||||
}
|
||||
|
||||
public T getModel(ItemStack stack) {
|
||||
return modelCache.computeIfAbsent(getCombination(stack), buildModel);
|
||||
var combination = getCombination(stack);
|
||||
var existing = modelCache.get(combination);
|
||||
if (existing != null) return existing;
|
||||
|
||||
// Take a defensive copy of the upgrade data, and add it to the cache.
|
||||
var newCombination = combination.copy();
|
||||
var newModel = buildModel.apply(newCombination);
|
||||
modelCache.put(newCombination, newModel);
|
||||
return newModel;
|
||||
}
|
||||
|
||||
private Combination getCombination(ItemStack stack) {
|
||||
@ -89,8 +114,8 @@ public final class TurtleModelParts<T> {
|
||||
}
|
||||
|
||||
var colour = turtle.getColour(stack);
|
||||
var leftUpgrade = turtle.getUpgrade(stack, TurtleSide.LEFT);
|
||||
var rightUpgrade = turtle.getUpgrade(stack, TurtleSide.RIGHT);
|
||||
var leftUpgrade = turtle.getUpgradeWithData(stack, TurtleSide.LEFT);
|
||||
var rightUpgrade = turtle.getUpgradeWithData(stack, TurtleSide.RIGHT);
|
||||
var overlay = turtle.getOverlay(stack);
|
||||
var label = turtle.getLabel(stack);
|
||||
var flip = label != null && (label.equals("Dinnerbone") || label.equals("Grumm"));
|
||||
@ -110,18 +135,19 @@ public final class TurtleModelParts<T> {
|
||||
if (overlayModelLocation != null) {
|
||||
parts.add(transform(ClientPlatformHelper.get().getModel(modelManager, overlayModelLocation), transformation));
|
||||
}
|
||||
if (combo.leftUpgrade() != null) {
|
||||
var model = TurtleUpgradeModellers.getModel(combo.leftUpgrade(), null, TurtleSide.LEFT);
|
||||
parts.add(transform(model.getModel(), transformation.compose(model.getMatrix())));
|
||||
}
|
||||
if (combo.rightUpgrade() != null) {
|
||||
var model = TurtleUpgradeModellers.getModel(combo.rightUpgrade(), null, TurtleSide.RIGHT);
|
||||
parts.add(transform(model.getModel(), transformation.compose(model.getMatrix())));
|
||||
}
|
||||
|
||||
addUpgrade(parts, transformation, TurtleSide.LEFT, combo.leftUpgrade());
|
||||
addUpgrade(parts, transformation, TurtleSide.RIGHT, combo.rightUpgrade());
|
||||
|
||||
return parts;
|
||||
}
|
||||
|
||||
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);
|
||||
parts.add(transform(model.getModel(), transformation.compose(model.getMatrix())));
|
||||
}
|
||||
|
||||
private BakedModel transform(BakedModel model, Transformation transformation) {
|
||||
if (transformation.equals(Transformation.identity())) return model;
|
||||
return transformCache.computeIfAbsent(new TransformedModel(model, transformation), transformer);
|
||||
|
@ -14,8 +14,8 @@ import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
|
||||
import dan200.computercraft.impl.TurtleUpgrades;
|
||||
import dan200.computercraft.impl.UpgradeManager;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Map;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
@ -52,12 +52,18 @@ public final class TurtleUpgradeModellers {
|
||||
}
|
||||
}
|
||||
|
||||
public static TransformedModel getModel(ITurtleUpgrade upgrade, @Nullable ITurtleAccess access, TurtleSide side) {
|
||||
public static TransformedModel getModel(ITurtleUpgrade upgrade, ITurtleAccess access, TurtleSide side) {
|
||||
@SuppressWarnings("unchecked")
|
||||
var modeller = (TurtleUpgradeModeller<ITurtleUpgrade>) modelCache.computeIfAbsent(upgrade, TurtleUpgradeModellers::getModeller);
|
||||
return modeller.getModel(upgrade, access, side);
|
||||
}
|
||||
|
||||
public static TransformedModel getModel(ITurtleUpgrade upgrade, CompoundTag data, TurtleSide side) {
|
||||
@SuppressWarnings("unchecked")
|
||||
var modeller = (TurtleUpgradeModeller<ITurtleUpgrade>) modelCache.computeIfAbsent(upgrade, TurtleUpgradeModellers::getModeller);
|
||||
return modeller.getModel(upgrade, data, side);
|
||||
}
|
||||
|
||||
private static TurtleUpgradeModeller<?> getModeller(ITurtleUpgrade upgradeA) {
|
||||
var wrapper = TurtleUpgrades.instance().getWrapper(upgradeA);
|
||||
if (wrapper == null) return NULL_TURTLE_MODELLER;
|
||||
|
@ -8,6 +8,7 @@ import com.google.gson.JsonObject;
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.api.pocket.PocketUpgradeDataProvider;
|
||||
import dan200.computercraft.api.turtle.TurtleUpgradeDataProvider;
|
||||
import dan200.computercraft.api.upgrades.UpgradeData;
|
||||
import dan200.computercraft.core.util.Colour;
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
import dan200.computercraft.shared.common.IColouredItem;
|
||||
@ -110,7 +111,7 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
||||
var nameId = turtleItem.getFamily().name().toLowerCase(Locale.ROOT);
|
||||
|
||||
for (var upgrade : turtleUpgrades.getGeneratedUpgrades()) {
|
||||
var result = turtleItem.create(-1, null, -1, null, upgrade, -1, null);
|
||||
var result = turtleItem.create(-1, null, -1, null, UpgradeData.ofDefault(upgrade), -1, null);
|
||||
ShapedRecipeBuilder
|
||||
.shaped(RecipeCategory.REDSTONE, result.getItem())
|
||||
.group(String.format("%s:turtle_%s", ComputerCraftAPI.MOD_ID, nameId))
|
||||
@ -146,7 +147,7 @@ class RecipeProvider extends net.minecraft.data.recipes.RecipeProvider {
|
||||
var nameId = pocket.getFamily().name().toLowerCase(Locale.ROOT);
|
||||
|
||||
for (var upgrade : pocketUpgrades.getGeneratedUpgrades()) {
|
||||
var result = pocket.create(-1, null, -1, upgrade);
|
||||
var result = pocket.create(-1, null, -1, UpgradeData.ofDefault(upgrade));
|
||||
ShapedRecipeBuilder
|
||||
.shaped(RecipeCategory.REDSTONE, result.getItem())
|
||||
.group(String.format("%s:pocket_%s", ComputerCraftAPI.MOD_ID, nameId))
|
||||
|
@ -7,6 +7,7 @@ package dan200.computercraft.impl;
|
||||
import com.google.gson.*;
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.api.upgrades.UpgradeBase;
|
||||
import dan200.computercraft.api.upgrades.UpgradeData;
|
||||
import dan200.computercraft.api.upgrades.UpgradeSerialiser;
|
||||
import dan200.computercraft.shared.platform.PlatformHelper;
|
||||
import net.minecraft.core.Registry;
|
||||
@ -74,13 +75,13 @@ public class UpgradeManager<R extends UpgradeSerialiser<? extends T>, T extends
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public T get(ItemStack stack) {
|
||||
public UpgradeData<T> get(ItemStack stack) {
|
||||
if (stack.isEmpty()) return null;
|
||||
|
||||
for (var wrapper : current.values()) {
|
||||
var craftingStack = wrapper.upgrade().getCraftingItem();
|
||||
if (!craftingStack.isEmpty() && craftingStack.getItem() == stack.getItem() && wrapper.upgrade().isItemSuitable(stack)) {
|
||||
return wrapper.upgrade();
|
||||
return UpgradeData.of(wrapper.upgrade, wrapper.upgrade.getUpgradeData(stack));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,7 @@ 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.api.upgrades.UpgradeData;
|
||||
import dan200.computercraft.core.util.Colour;
|
||||
import dan200.computercraft.impl.PocketUpgrades;
|
||||
import dan200.computercraft.impl.TurtleUpgrades;
|
||||
@ -446,12 +447,12 @@ public final class ModRegistry {
|
||||
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))
|
||||
.map(x -> turtle.create(-1, null, -1, null, UpgradeData.ofDefault(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);
|
||||
PocketUpgrades.getVanillaUpgrades().map(x -> pocket.create(-1, null, -1, UpgradeData.ofDefault(x))).forEach(out::accept);
|
||||
}
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
package dan200.computercraft.shared.integration;
|
||||
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.api.upgrades.UpgradeData;
|
||||
import dan200.computercraft.impl.PocketUpgrades;
|
||||
import dan200.computercraft.impl.TurtleUpgrades;
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
@ -56,14 +57,14 @@ public final class RecipeModHelpers {
|
||||
for (var turtleSupplier : TURTLES) {
|
||||
var turtle = turtleSupplier.get();
|
||||
for (var upgrade : TurtleUpgrades.instance().getUpgrades()) {
|
||||
upgradeItems.add(turtle.create(-1, null, -1, null, upgrade, 0, null));
|
||||
upgradeItems.add(turtle.create(-1, null, -1, null, UpgradeData.ofDefault(upgrade), 0, null));
|
||||
}
|
||||
}
|
||||
|
||||
for (var pocketSupplier : POCKET_COMPUTERS) {
|
||||
var pocket = pocketSupplier.get();
|
||||
for (var upgrade : PocketUpgrades.instance().getUpgrades()) {
|
||||
upgradeItems.add(pocket.create(-1, null, -1, upgrade));
|
||||
upgradeItems.add(pocket.create(-1, null, -1, UpgradeData.ofDefault(upgrade)));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,7 @@ import dan200.computercraft.api.pocket.IPocketUpgrade;
|
||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import dan200.computercraft.api.turtle.TurtleSide;
|
||||
import dan200.computercraft.api.upgrades.UpgradeBase;
|
||||
import dan200.computercraft.api.upgrades.UpgradeData;
|
||||
import dan200.computercraft.impl.PocketUpgrades;
|
||||
import dan200.computercraft.impl.TurtleUpgrades;
|
||||
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
||||
@ -111,20 +112,22 @@ public class UpgradeRecipeGenerator<T> {
|
||||
|
||||
if (stack.getItem() instanceof TurtleItem item) {
|
||||
// Suggest possible upgrades which can be applied to this turtle
|
||||
var left = item.getUpgrade(stack, TurtleSide.LEFT);
|
||||
var right = item.getUpgrade(stack, TurtleSide.RIGHT);
|
||||
var left = item.getUpgradeWithData(stack, TurtleSide.LEFT);
|
||||
var right = item.getUpgradeWithData(stack, TurtleSide.RIGHT);
|
||||
if (left != null && right != null) return Collections.emptyList();
|
||||
|
||||
List<T> recipes = new ArrayList<>();
|
||||
var ingredient = Ingredient.of(stack);
|
||||
for (var upgrade : turtleUpgrades) {
|
||||
if (upgrade.turtle == null) throw new NullPointerException();
|
||||
|
||||
// The turtle is facing towards us, so upgrades on the left are actually crafted on the right.
|
||||
if (left == null) {
|
||||
recipes.add(turtle(ingredient, upgrade.ingredient, turtleWith(stack, upgrade.turtle, right)));
|
||||
recipes.add(turtle(ingredient, upgrade.ingredient, turtleWith(stack, UpgradeData.ofDefault(upgrade.turtle), right)));
|
||||
}
|
||||
|
||||
if (right == null) {
|
||||
recipes.add(turtle(upgrade.ingredient, ingredient, turtleWith(stack, left, upgrade.turtle)));
|
||||
recipes.add(turtle(upgrade.ingredient, ingredient, turtleWith(stack, left, UpgradeData.ofDefault(upgrade.turtle))));
|
||||
}
|
||||
}
|
||||
|
||||
@ -137,7 +140,8 @@ public class UpgradeRecipeGenerator<T> {
|
||||
List<T> recipes = new ArrayList<>();
|
||||
var ingredient = Ingredient.of(stack);
|
||||
for (var upgrade : pocketUpgrades) {
|
||||
recipes.add(pocket(upgrade.ingredient, ingredient, pocketWith(stack, upgrade.pocket)));
|
||||
if (upgrade.pocket == null) throw new NullPointerException();
|
||||
recipes.add(pocket(upgrade.ingredient, ingredient, pocketWith(stack, UpgradeData.ofDefault(upgrade.pocket))));
|
||||
}
|
||||
|
||||
return Collections.unmodifiableList(recipes);
|
||||
@ -180,21 +184,21 @@ public class UpgradeRecipeGenerator<T> {
|
||||
if (stack.getItem() instanceof TurtleItem item) {
|
||||
List<T> recipes = new ArrayList<>(0);
|
||||
|
||||
var left = item.getUpgrade(stack, TurtleSide.LEFT);
|
||||
var right = item.getUpgrade(stack, TurtleSide.RIGHT);
|
||||
var left = item.getUpgradeWithData(stack, TurtleSide.LEFT);
|
||||
var right = item.getUpgradeWithData(stack, TurtleSide.RIGHT);
|
||||
|
||||
// The turtle is facing towards us, so upgrades on the left are actually crafted on the right.
|
||||
if (left != null) {
|
||||
recipes.add(turtle(
|
||||
Ingredient.of(turtleWith(stack, null, right)),
|
||||
Ingredient.of(left.getCraftingItem()),
|
||||
Ingredient.of(left.getUpgradeItem()),
|
||||
stack
|
||||
));
|
||||
}
|
||||
|
||||
if (right != null) {
|
||||
recipes.add(turtle(
|
||||
Ingredient.of(right.getCraftingItem()),
|
||||
Ingredient.of(right.getUpgradeItem()),
|
||||
Ingredient.of(turtleWith(stack, left, null)),
|
||||
stack
|
||||
));
|
||||
@ -204,9 +208,9 @@ public class UpgradeRecipeGenerator<T> {
|
||||
} else if (stack.getItem() instanceof PocketComputerItem) {
|
||||
List<T> recipes = new ArrayList<>(0);
|
||||
|
||||
var back = PocketComputerItem.getUpgrade(stack);
|
||||
var back = PocketComputerItem.getUpgradeWithData(stack);
|
||||
if (back != null) {
|
||||
recipes.add(pocket(Ingredient.of(back.getCraftingItem()), Ingredient.of(pocketWith(stack, null)), stack));
|
||||
recipes.add(pocket(Ingredient.of(back.getUpgradeItem()), Ingredient.of(pocketWith(stack, null)), stack));
|
||||
}
|
||||
|
||||
return Collections.unmodifiableList(recipes);
|
||||
@ -215,7 +219,7 @@ public class UpgradeRecipeGenerator<T> {
|
||||
}
|
||||
}
|
||||
|
||||
private static ItemStack turtleWith(ItemStack stack, @Nullable ITurtleUpgrade left, @Nullable ITurtleUpgrade right) {
|
||||
private static ItemStack turtleWith(ItemStack stack, @Nullable UpgradeData<ITurtleUpgrade> left, @Nullable UpgradeData<ITurtleUpgrade> right) {
|
||||
var item = (TurtleItem) stack.getItem();
|
||||
return item.create(
|
||||
item.getComputerID(stack), item.getLabel(stack), item.getColour(stack),
|
||||
@ -223,7 +227,7 @@ public class UpgradeRecipeGenerator<T> {
|
||||
);
|
||||
}
|
||||
|
||||
private static ItemStack pocketWith(ItemStack stack, @Nullable IPocketUpgrade back) {
|
||||
private static ItemStack pocketWith(ItemStack stack, @Nullable UpgradeData<IPocketUpgrade> back) {
|
||||
var item = (PocketComputerItem) stack.getItem();
|
||||
return item.create(
|
||||
item.getComputerID(stack), item.getLabel(stack), item.getColour(stack), back
|
||||
@ -272,7 +276,7 @@ public class UpgradeRecipeGenerator<T> {
|
||||
recipes.add(turtle(
|
||||
ingredient, // Right upgrade, recipe on left
|
||||
Ingredient.of(turtleItem.create(-1, null, -1, null, null, 0, null)),
|
||||
turtleItem.create(-1, null, -1, null, turtle, 0, null)
|
||||
turtleItem.create(-1, null, -1, null, UpgradeData.ofDefault(turtle), 0, null)
|
||||
));
|
||||
}
|
||||
}
|
||||
@ -283,7 +287,7 @@ public class UpgradeRecipeGenerator<T> {
|
||||
recipes.add(pocket(
|
||||
ingredient,
|
||||
Ingredient.of(pocketItem.create(-1, null, -1, null)),
|
||||
pocketItem.create(-1, null, -1, pocket)
|
||||
pocketItem.create(-1, null, -1, UpgradeData.ofDefault(pocket))
|
||||
));
|
||||
}
|
||||
}
|
||||
|
@ -68,6 +68,13 @@ public interface PlatformHelper extends dan200.computercraft.impl.PlatformHelper
|
||||
return (PlatformHelper) dan200.computercraft.impl.PlatformHelper.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if we're running in a development environment.
|
||||
*
|
||||
* @return If we're running in a development environment.
|
||||
*/
|
||||
boolean isDevelopmentEnvironment();
|
||||
|
||||
/**
|
||||
* Create a new config builder.
|
||||
*
|
||||
|
@ -7,6 +7,7 @@ package dan200.computercraft.shared.pocket.apis;
|
||||
import dan200.computercraft.api.lua.ILuaAPI;
|
||||
import dan200.computercraft.api.lua.LuaFunction;
|
||||
import dan200.computercraft.api.pocket.IPocketUpgrade;
|
||||
import dan200.computercraft.api.upgrades.UpgradeData;
|
||||
import dan200.computercraft.impl.PocketUpgrades;
|
||||
import dan200.computercraft.shared.pocket.core.PocketServerComputer;
|
||||
import net.minecraft.core.NonNullList;
|
||||
@ -14,6 +15,7 @@ import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Control the current pocket computer, adding or removing upgrades.
|
||||
@ -68,7 +70,7 @@ public class PocketAPI implements ILuaAPI {
|
||||
if (newUpgrade == null) return new Object[]{ false, "Cannot find a valid upgrade" };
|
||||
|
||||
// Remove the current upgrade
|
||||
if (previousUpgrade != null) storeItem(player, previousUpgrade.getCraftingItem().copy());
|
||||
if (previousUpgrade != null) storeItem(player, previousUpgrade.getUpgradeItem());
|
||||
|
||||
// Set the new upgrade
|
||||
computer.setUpgrade(newUpgrade);
|
||||
@ -93,7 +95,7 @@ public class PocketAPI implements ILuaAPI {
|
||||
|
||||
computer.setUpgrade(null);
|
||||
|
||||
storeItem(player, previousUpgrade.getCraftingItem().copy());
|
||||
storeItem(player, previousUpgrade.getUpgradeItem());
|
||||
|
||||
return new Object[]{ true };
|
||||
}
|
||||
@ -105,13 +107,13 @@ public class PocketAPI implements ILuaAPI {
|
||||
}
|
||||
}
|
||||
|
||||
private static @Nullable IPocketUpgrade findUpgrade(NonNullList<ItemStack> inv, int start, @Nullable IPocketUpgrade previous) {
|
||||
private static @Nullable UpgradeData<IPocketUpgrade> findUpgrade(NonNullList<ItemStack> inv, int start, @Nullable UpgradeData<IPocketUpgrade> previous) {
|
||||
for (var i = 0; i < inv.size(); i++) {
|
||||
var invStack = inv.get((i + start) % inv.size());
|
||||
if (!invStack.isEmpty()) {
|
||||
var newUpgrade = PocketUpgrades.instance().get(invStack);
|
||||
|
||||
if (newUpgrade != null && newUpgrade != previous) {
|
||||
if (newUpgrade != null && !Objects.equals(newUpgrade, previous)) {
|
||||
// Consume an item from this stack and exit the loop
|
||||
invStack = invStack.copy();
|
||||
invStack.shrink(1);
|
||||
|
@ -7,6 +7,7 @@ package dan200.computercraft.shared.pocket.core;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.api.pocket.IPocketAccess;
|
||||
import dan200.computercraft.api.pocket.IPocketUpgrade;
|
||||
import dan200.computercraft.api.upgrades.UpgradeData;
|
||||
import dan200.computercraft.core.computer.ComputerSide;
|
||||
import dan200.computercraft.shared.common.IColouredItem;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
@ -109,8 +110,8 @@ public class PocketServerComputer extends ServerComputer implements IPocketAcces
|
||||
return upgrade == null ? Collections.emptyMap() : Collections.singletonMap(upgrade.getUpgradeID(), getPeripheral(ComputerSide.BACK));
|
||||
}
|
||||
|
||||
public @Nullable IPocketUpgrade getUpgrade() {
|
||||
return upgrade;
|
||||
public @Nullable UpgradeData<IPocketUpgrade> getUpgrade() {
|
||||
return upgrade == null ? null : UpgradeData.of(upgrade, getUpgradeNBTData());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -120,13 +121,11 @@ public class PocketServerComputer extends ServerComputer implements IPocketAcces
|
||||
*
|
||||
* @param upgrade The new upgrade to set it to, may be {@code null}.
|
||||
*/
|
||||
public void setUpgrade(@Nullable IPocketUpgrade upgrade) {
|
||||
if (this.upgrade == upgrade) return;
|
||||
|
||||
public void setUpgrade(@Nullable UpgradeData<IPocketUpgrade> upgrade) {
|
||||
synchronized (this) {
|
||||
PocketComputerItem.setUpgrade(stack, upgrade);
|
||||
updateUpgradeNBTData();
|
||||
this.upgrade = upgrade;
|
||||
this.upgrade = upgrade == null ? null : upgrade.upgrade();
|
||||
invalidatePeripheral();
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.api.filesystem.Mount;
|
||||
import dan200.computercraft.api.media.IMedia;
|
||||
import dan200.computercraft.api.pocket.IPocketUpgrade;
|
||||
import dan200.computercraft.api.upgrades.UpgradeData;
|
||||
import dan200.computercraft.core.computer.ComputerSide;
|
||||
import dan200.computercraft.impl.PocketUpgrades;
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
@ -23,6 +24,7 @@ import dan200.computercraft.shared.pocket.apis.PocketAPI;
|
||||
import dan200.computercraft.shared.pocket.core.PocketServerComputer;
|
||||
import dan200.computercraft.shared.pocket.inventory.PocketComputerMenuProvider;
|
||||
import dan200.computercraft.shared.util.IDAssigner;
|
||||
import dan200.computercraft.shared.util.NBTUtil;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.chat.Component;
|
||||
@ -58,7 +60,7 @@ public class PocketComputerItem extends Item implements IComputerItem, IMedia, I
|
||||
this.family = family;
|
||||
}
|
||||
|
||||
public static ItemStack create(int id, @Nullable String label, int colour, ComputerFamily family, @Nullable IPocketUpgrade upgrade) {
|
||||
public static ItemStack create(int id, @Nullable String label, int colour, ComputerFamily family, @Nullable UpgradeData<IPocketUpgrade> upgrade) {
|
||||
return switch (family) {
|
||||
case NORMAL -> ModRegistry.Items.POCKET_COMPUTER_NORMAL.get().create(id, label, colour, upgrade);
|
||||
case ADVANCED -> ModRegistry.Items.POCKET_COMPUTER_ADVANCED.get().create(id, label, colour, upgrade);
|
||||
@ -66,11 +68,14 @@ public class PocketComputerItem extends Item implements IComputerItem, IMedia, I
|
||||
};
|
||||
}
|
||||
|
||||
public ItemStack create(int id, @Nullable String label, int colour, @Nullable IPocketUpgrade upgrade) {
|
||||
public ItemStack create(int id, @Nullable String label, int colour, @Nullable UpgradeData<IPocketUpgrade> upgrade) {
|
||||
var result = new ItemStack(this);
|
||||
if (id >= 0) result.getOrCreateTag().putInt(NBT_ID, id);
|
||||
if (label != null) result.setHoverName(Component.literal(label));
|
||||
if (upgrade != null) result.getOrCreateTag().putString(NBT_UPGRADE, upgrade.getUpgradeID().toString());
|
||||
if (upgrade != null) {
|
||||
result.getOrCreateTag().putString(NBT_UPGRADE, upgrade.upgrade().getUpgradeID().toString());
|
||||
if (!upgrade.data().isEmpty()) result.getOrCreateTag().put(NBT_UPGRADE_INFO, upgrade.data().copy());
|
||||
}
|
||||
if (colour != -1) result.getOrCreateTag().putInt(NBT_COLOUR, colour);
|
||||
return result;
|
||||
}
|
||||
@ -207,7 +212,9 @@ public class PocketComputerItem extends Item implements IComputerItem, IMedia, I
|
||||
setInstanceID(stack, computer.register());
|
||||
setSessionID(stack, registry.getSessionID());
|
||||
|
||||
computer.updateValues(entity, stack, getUpgrade(stack));
|
||||
var upgrade = getUpgrade(stack);
|
||||
|
||||
computer.updateValues(entity, stack, upgrade);
|
||||
computer.addAPI(new PocketAPI(computer));
|
||||
|
||||
// Only turn on when initially creating the computer, rather than each tick.
|
||||
@ -244,7 +251,7 @@ public class PocketComputerItem extends Item implements IComputerItem, IMedia, I
|
||||
public ItemStack withFamily(ItemStack stack, ComputerFamily family) {
|
||||
return create(
|
||||
getComputerID(stack), getLabel(stack), getColour(stack),
|
||||
family, getUpgrade(stack)
|
||||
family, getUpgradeWithData(stack)
|
||||
);
|
||||
}
|
||||
|
||||
@ -294,20 +301,27 @@ public class PocketComputerItem extends Item implements IComputerItem, IMedia, I
|
||||
|
||||
public static @Nullable IPocketUpgrade getUpgrade(ItemStack stack) {
|
||||
var compound = stack.getTag();
|
||||
return compound != null && compound.contains(NBT_UPGRADE)
|
||||
? PocketUpgrades.instance().get(compound.getString(NBT_UPGRADE)) : null;
|
||||
if (compound == null || !compound.contains(NBT_UPGRADE)) return null;
|
||||
return PocketUpgrades.instance().get(compound.getString(NBT_UPGRADE));
|
||||
}
|
||||
|
||||
public static void setUpgrade(ItemStack stack, @Nullable IPocketUpgrade upgrade) {
|
||||
public static @Nullable UpgradeData<IPocketUpgrade> getUpgradeWithData(ItemStack stack) {
|
||||
var compound = stack.getTag();
|
||||
if (compound == null || !compound.contains(NBT_UPGRADE)) return null;
|
||||
var upgrade = PocketUpgrades.instance().get(compound.getString(NBT_UPGRADE));
|
||||
return upgrade == null ? null : UpgradeData.of(upgrade, NBTUtil.getCompoundOrEmpty(compound, NBT_UPGRADE_INFO));
|
||||
}
|
||||
|
||||
public static void setUpgrade(ItemStack stack, @Nullable UpgradeData<IPocketUpgrade> upgrade) {
|
||||
var compound = stack.getOrCreateTag();
|
||||
|
||||
if (upgrade == null) {
|
||||
compound.remove(NBT_UPGRADE);
|
||||
compound.remove(NBT_UPGRADE_INFO);
|
||||
} else {
|
||||
compound.putString(NBT_UPGRADE, upgrade.getUpgradeID().toString());
|
||||
compound.putString(NBT_UPGRADE, upgrade.upgrade().getUpgradeID().toString());
|
||||
compound.put(NBT_UPGRADE_INFO, upgrade.data().copy());
|
||||
}
|
||||
|
||||
compound.remove(NBT_UPGRADE_INFO);
|
||||
}
|
||||
|
||||
public static CompoundTag getUpgradeInfo(ItemStack stack) {
|
||||
|
@ -5,6 +5,7 @@
|
||||
package dan200.computercraft.shared.pocket.recipes;
|
||||
|
||||
import dan200.computercraft.api.pocket.IPocketUpgrade;
|
||||
import dan200.computercraft.api.upgrades.UpgradeData;
|
||||
import dan200.computercraft.impl.PocketUpgrades;
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
import dan200.computercraft.shared.pocket.items.PocketComputerItem;
|
||||
@ -62,7 +63,7 @@ public final class PocketComputerUpgradeRecipe extends CustomRecipe {
|
||||
if (PocketComputerItem.getUpgrade(computer) != null) return ItemStack.EMPTY;
|
||||
|
||||
// Check for upgrades around the item
|
||||
IPocketUpgrade upgrade = null;
|
||||
UpgradeData<IPocketUpgrade> upgrade = null;
|
||||
for (var y = 0; y < inventory.getHeight(); y++) {
|
||||
for (var x = 0; x < inventory.getWidth(); x++) {
|
||||
var item = inventory.getItem(x + y * inventory.getWidth());
|
||||
|
@ -5,7 +5,9 @@
|
||||
package dan200.computercraft.shared.turtle.blocks;
|
||||
|
||||
import dan200.computercraft.annotations.ForgeOverride;
|
||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import dan200.computercraft.api.turtle.TurtleSide;
|
||||
import dan200.computercraft.api.upgrades.UpgradeData;
|
||||
import dan200.computercraft.shared.computer.blocks.AbstractComputerBlock;
|
||||
import dan200.computercraft.shared.computer.blocks.AbstractComputerBlockEntity;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
@ -128,7 +130,7 @@ public class TurtleBlock extends AbstractComputerBlock<TurtleBlockEntity> implem
|
||||
if (stack.getItem() instanceof TurtleItem item) {
|
||||
// Set Upgrades
|
||||
for (var side : TurtleSide.values()) {
|
||||
turtle.getAccess().setUpgrade(side, item.getUpgrade(stack, side));
|
||||
turtle.getAccess().setUpgradeWithData(side, item.getUpgradeWithData(stack, side));
|
||||
}
|
||||
|
||||
turtle.getAccess().setFuelLevel(item.getFuelLevel(stack));
|
||||
@ -161,11 +163,16 @@ public class TurtleBlock extends AbstractComputerBlock<TurtleBlockEntity> implem
|
||||
var access = turtle.getAccess();
|
||||
return TurtleItem.create(
|
||||
turtle.getComputerID(), turtle.getLabel(), access.getColour(), turtle.getFamily(),
|
||||
access.getUpgrade(TurtleSide.LEFT), access.getUpgrade(TurtleSide.RIGHT),
|
||||
withPersistedData(access.getUpgradeWithData(TurtleSide.LEFT)),
|
||||
withPersistedData(access.getUpgradeWithData(TurtleSide.RIGHT)),
|
||||
access.getFuelLevel(), turtle.getOverlay()
|
||||
);
|
||||
}
|
||||
|
||||
private static @Nullable UpgradeData<ITurtleUpgrade> withPersistedData(@Nullable UpgradeData<ITurtleUpgrade> upgrade) {
|
||||
return upgrade == null ? null : UpgradeData.of(upgrade.upgrade(), upgrade.upgrade().getPersistedData(upgrade.data()));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public <U extends BlockEntity> BlockEntityTicker<U> getTicker(Level level, BlockState state, BlockEntityType<U> type) {
|
||||
|
@ -13,6 +13,7 @@ import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import dan200.computercraft.api.turtle.TurtleAnimation;
|
||||
import dan200.computercraft.api.turtle.TurtleCommand;
|
||||
import dan200.computercraft.api.turtle.TurtleSide;
|
||||
import dan200.computercraft.api.upgrades.UpgradeData;
|
||||
import dan200.computercraft.core.computer.ComputerSide;
|
||||
import dan200.computercraft.core.util.Colour;
|
||||
import dan200.computercraft.impl.TurtleUpgrades;
|
||||
@ -141,17 +142,16 @@ public class TurtleBrain implements TurtleAccessInternal {
|
||||
overlay = nbt.contains(NBT_OVERLAY) ? new ResourceLocation(nbt.getString(NBT_OVERLAY)) : null;
|
||||
|
||||
// Read upgrades
|
||||
setUpgradeDirect(TurtleSide.LEFT, nbt.contains(NBT_LEFT_UPGRADE) ? TurtleUpgrades.instance().get(nbt.getString(NBT_LEFT_UPGRADE)) : null);
|
||||
setUpgradeDirect(TurtleSide.RIGHT, nbt.contains(NBT_RIGHT_UPGRADE) ? TurtleUpgrades.instance().get(nbt.getString(NBT_RIGHT_UPGRADE)) : null);
|
||||
setUpgradeDirect(TurtleSide.LEFT, readUpgrade(nbt, NBT_LEFT_UPGRADE, NBT_LEFT_UPGRADE_DATA));
|
||||
setUpgradeDirect(TurtleSide.RIGHT, readUpgrade(nbt, NBT_RIGHT_UPGRADE, NBT_RIGHT_UPGRADE_DATA));
|
||||
}
|
||||
|
||||
// NBT
|
||||
upgradeNBTData.clear();
|
||||
if (nbt.contains(NBT_LEFT_UPGRADE_DATA)) {
|
||||
upgradeNBTData.put(TurtleSide.LEFT, nbt.getCompound(NBT_LEFT_UPGRADE_DATA).copy());
|
||||
}
|
||||
if (nbt.contains(NBT_RIGHT_UPGRADE_DATA)) {
|
||||
upgradeNBTData.put(TurtleSide.RIGHT, nbt.getCompound(NBT_RIGHT_UPGRADE_DATA).copy());
|
||||
}
|
||||
private @Nullable UpgradeData<ITurtleUpgrade> readUpgrade(CompoundTag tag, String upgradeKey, String dataKey) {
|
||||
if (!tag.contains(upgradeKey)) return null;
|
||||
var upgrade = TurtleUpgrades.instance().get(tag.getString(upgradeKey));
|
||||
if (upgrade == null) return null;
|
||||
|
||||
return UpgradeData.of(upgrade, tag.getCompound(dataKey));
|
||||
}
|
||||
|
||||
private void writeCommon(CompoundTag nbt) {
|
||||
@ -516,7 +516,7 @@ public class TurtleBrain implements TurtleAccessInternal {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUpgrade(TurtleSide side, @Nullable ITurtleUpgrade upgrade) {
|
||||
public void setUpgradeWithData(TurtleSide side, @Nullable UpgradeData<ITurtleUpgrade> upgrade) {
|
||||
if (!setUpgradeDirect(side, upgrade) || owner.getLevel() == null) return;
|
||||
|
||||
// This is a separate function to avoid updating the block when reading the NBT. We don't need to do this as
|
||||
@ -529,19 +529,18 @@ public class TurtleBrain implements TurtleAccessInternal {
|
||||
owner.updateInputsImmediately();
|
||||
}
|
||||
|
||||
private boolean setUpgradeDirect(TurtleSide side, @Nullable ITurtleUpgrade upgrade) {
|
||||
private boolean setUpgradeDirect(TurtleSide side, @Nullable UpgradeData<ITurtleUpgrade> upgrade) {
|
||||
// Remove old upgrade
|
||||
if (upgrades.containsKey(side)) {
|
||||
if (upgrades.get(side) == upgrade) return false;
|
||||
upgrades.remove(side);
|
||||
} else {
|
||||
if (upgrade == null) return false;
|
||||
}
|
||||
|
||||
upgradeNBTData.remove(side);
|
||||
var oldUpgrade = upgrades.remove(side);
|
||||
if (oldUpgrade == null && upgrade == null) return false;
|
||||
|
||||
// Set new upgrade
|
||||
if (upgrade != null) upgrades.put(side, upgrade);
|
||||
if (upgrade == null) {
|
||||
upgradeNBTData.remove(side);
|
||||
} else {
|
||||
upgrades.put(side, upgrade.upgrade());
|
||||
upgradeNBTData.put(side, upgrade.data().copy());
|
||||
}
|
||||
|
||||
// Notify clients and create peripherals
|
||||
if (owner.getLevel() != null && !owner.getLevel().isClientSide) {
|
||||
@ -595,7 +594,7 @@ public class TurtleBrain implements TurtleAccessInternal {
|
||||
|
||||
public float getToolRenderAngle(TurtleSide side, float f) {
|
||||
return (side == TurtleSide.LEFT && animation == TurtleAnimation.SWING_LEFT_TOOL) ||
|
||||
(side == TurtleSide.RIGHT && animation == TurtleAnimation.SWING_RIGHT_TOOL)
|
||||
(side == TurtleSide.RIGHT && animation == TurtleAnimation.SWING_RIGHT_TOOL)
|
||||
? 45.0f * (float) Math.sin(getAnimationFraction(f) * Math.PI)
|
||||
: 0.0f;
|
||||
}
|
||||
|
@ -5,6 +5,7 @@
|
||||
package dan200.computercraft.shared.turtle.core;
|
||||
|
||||
import dan200.computercraft.api.turtle.*;
|
||||
import dan200.computercraft.api.upgrades.UpgradeData;
|
||||
import dan200.computercraft.impl.TurtleUpgrades;
|
||||
import dan200.computercraft.shared.turtle.TurtleUtil;
|
||||
|
||||
@ -18,10 +19,10 @@ public class TurtleEquipCommand implements TurtleCommand {
|
||||
@Override
|
||||
public TurtleCommandResult execute(ITurtleAccess turtle) {
|
||||
// Determine the upgrade to replace
|
||||
var oldUpgrade = turtle.getUpgrade(side);
|
||||
var oldUpgrade = turtle.getUpgradeWithData(side);
|
||||
|
||||
// Determine the upgrade to equipLeft
|
||||
ITurtleUpgrade newUpgrade;
|
||||
UpgradeData<ITurtleUpgrade> newUpgrade;
|
||||
var selectedStack = turtle.getInventory().getItem(turtle.getSelectedSlot());
|
||||
if (!selectedStack.isEmpty()) {
|
||||
newUpgrade = TurtleUpgrades.instance().get(selectedStack);
|
||||
@ -32,8 +33,8 @@ public class TurtleEquipCommand implements TurtleCommand {
|
||||
|
||||
// Do the swapping:
|
||||
if (newUpgrade != null) turtle.getInventory().removeItem(turtle.getSelectedSlot(), 1);
|
||||
if (oldUpgrade != null) TurtleUtil.storeItemOrDrop(turtle, oldUpgrade.getCraftingItem().copy());
|
||||
turtle.setUpgrade(side, newUpgrade);
|
||||
if (oldUpgrade != null) TurtleUtil.storeItemOrDrop(turtle, oldUpgrade.getUpgradeItem());
|
||||
turtle.setUpgradeWithData(side, newUpgrade);
|
||||
|
||||
// Animate
|
||||
if (newUpgrade != null || oldUpgrade != null) {
|
||||
|
@ -7,6 +7,7 @@ package dan200.computercraft.shared.turtle.inventory;
|
||||
import dan200.computercraft.api.turtle.ITurtleAccess;
|
||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import dan200.computercraft.api.turtle.TurtleSide;
|
||||
import dan200.computercraft.api.upgrades.UpgradeData;
|
||||
import dan200.computercraft.impl.TurtleUpgrades;
|
||||
import net.minecraft.core.NonNullList;
|
||||
import net.minecraft.world.Container;
|
||||
@ -27,7 +28,7 @@ class UpgradeContainer implements Container {
|
||||
|
||||
private final ITurtleAccess turtle;
|
||||
|
||||
private final List<ITurtleUpgrade> lastUpgrade = Arrays.asList(null, null);
|
||||
private final List<UpgradeData<ITurtleUpgrade>> lastUpgrade = Arrays.asList(null, null);
|
||||
private final NonNullList<ItemStack> lastStack = NonNullList.withSize(2, ItemStack.EMPTY);
|
||||
|
||||
UpgradeContainer(ITurtleAccess turtle) {
|
||||
@ -44,22 +45,25 @@ class UpgradeContainer implements Container {
|
||||
|
||||
@Override
|
||||
public ItemStack getItem(int slot) {
|
||||
var upgrade = turtle.getUpgrade(getSide(slot));
|
||||
var side = getSide(slot);
|
||||
var upgrade = turtle.getUpgrade(side);
|
||||
if (upgrade == null) return ItemStack.EMPTY;
|
||||
|
||||
// We don't want to return getCraftingItem directly here, as consumers may mutate the stack (they shouldn't!,
|
||||
// but if they do it's a pain to track down). To avoid recreating the stack each tick, we maintain a simple
|
||||
// cache.
|
||||
if (upgrade == lastUpgrade.get(slot)) return lastStack.get(slot);
|
||||
// cache. We use an inlined getUpgradeData here to avoid the additional defensive copy.
|
||||
var upgradeData = UpgradeData.of(upgrade, turtle.getUpgradeNBTData(side));
|
||||
if (upgradeData.equals(lastUpgrade.get(slot))) return lastStack.get(slot);
|
||||
|
||||
var stack = upgrade == null ? ItemStack.EMPTY : upgrade.getCraftingItem().copy();
|
||||
lastUpgrade.set(slot, upgrade);
|
||||
var stack = upgradeData.getUpgradeItem();
|
||||
lastUpgrade.set(slot, upgradeData.copy());
|
||||
lastStack.set(slot, stack);
|
||||
return stack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem(int slot, ItemStack itemStack) {
|
||||
turtle.setUpgrade(getSide(slot), TurtleUpgrades.instance().get(itemStack));
|
||||
turtle.setUpgradeWithData(getSide(slot), TurtleUpgrades.instance().get(itemStack));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -8,12 +8,14 @@ import dan200.computercraft.annotations.ForgeOverride;
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import dan200.computercraft.api.turtle.TurtleSide;
|
||||
import dan200.computercraft.api.upgrades.UpgradeData;
|
||||
import dan200.computercraft.impl.TurtleUpgrades;
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
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 dan200.computercraft.shared.util.NBTUtil;
|
||||
import net.minecraft.core.cauldron.CauldronInteraction;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
@ -32,7 +34,7 @@ public class TurtleItem extends AbstractComputerItem implements IColouredItem {
|
||||
|
||||
public static ItemStack create(
|
||||
int id, @Nullable String label, int colour, ComputerFamily family,
|
||||
@Nullable ITurtleUpgrade leftUpgrade, @Nullable ITurtleUpgrade rightUpgrade,
|
||||
@Nullable UpgradeData<ITurtleUpgrade> leftUpgrade, @Nullable UpgradeData<ITurtleUpgrade> rightUpgrade,
|
||||
int fuelLevel, @Nullable ResourceLocation overlay
|
||||
) {
|
||||
return switch (family) {
|
||||
@ -46,7 +48,7 @@ public class TurtleItem extends AbstractComputerItem implements IColouredItem {
|
||||
|
||||
public ItemStack create(
|
||||
int id, @Nullable String label, int colour,
|
||||
@Nullable ITurtleUpgrade leftUpgrade, @Nullable ITurtleUpgrade rightUpgrade,
|
||||
@Nullable UpgradeData<ITurtleUpgrade> leftUpgrade, @Nullable UpgradeData<ITurtleUpgrade> rightUpgrade,
|
||||
int fuelLevel, @Nullable ResourceLocation overlay
|
||||
) {
|
||||
// Build the stack
|
||||
@ -58,11 +60,15 @@ public class TurtleItem extends AbstractComputerItem implements IColouredItem {
|
||||
if (overlay != null) stack.getOrCreateTag().putString(NBT_OVERLAY, overlay.toString());
|
||||
|
||||
if (leftUpgrade != null) {
|
||||
stack.getOrCreateTag().putString(NBT_LEFT_UPGRADE, leftUpgrade.getUpgradeID().toString());
|
||||
var tag = stack.getOrCreateTag();
|
||||
tag.putString(NBT_LEFT_UPGRADE, leftUpgrade.upgrade().getUpgradeID().toString());
|
||||
if (!leftUpgrade.data().isEmpty()) tag.put(NBT_LEFT_UPGRADE_DATA, leftUpgrade.data().copy());
|
||||
}
|
||||
|
||||
if (rightUpgrade != null) {
|
||||
stack.getOrCreateTag().putString(NBT_RIGHT_UPGRADE, rightUpgrade.getUpgradeID().toString());
|
||||
var tag = stack.getOrCreateTag();
|
||||
tag.putString(NBT_RIGHT_UPGRADE, rightUpgrade.upgrade().getUpgradeID().toString());
|
||||
if (!rightUpgrade.data().isEmpty()) tag.put(NBT_RIGHT_UPGRADE_DATA, rightUpgrade.data().copy());
|
||||
}
|
||||
|
||||
return stack;
|
||||
@ -117,7 +123,7 @@ public class TurtleItem extends AbstractComputerItem implements IColouredItem {
|
||||
return create(
|
||||
getComputerID(stack), getLabel(stack),
|
||||
getColour(stack), family,
|
||||
getUpgrade(stack, TurtleSide.LEFT), getUpgrade(stack, TurtleSide.RIGHT),
|
||||
getUpgradeWithData(stack, TurtleSide.LEFT), getUpgradeWithData(stack, TurtleSide.RIGHT),
|
||||
getFuelLevel(stack), getOverlay(stack)
|
||||
);
|
||||
}
|
||||
@ -127,7 +133,20 @@ public class TurtleItem extends AbstractComputerItem implements IColouredItem {
|
||||
if (tag == null) return null;
|
||||
|
||||
var key = side == TurtleSide.LEFT ? NBT_LEFT_UPGRADE : NBT_RIGHT_UPGRADE;
|
||||
return tag.contains(key) ? TurtleUpgrades.instance().get(tag.getString(key)) : null;
|
||||
if (!tag.contains(key)) return null;
|
||||
return TurtleUpgrades.instance().get(tag.getString(key));
|
||||
}
|
||||
|
||||
public @Nullable UpgradeData<ITurtleUpgrade> getUpgradeWithData(ItemStack stack, TurtleSide side) {
|
||||
var tag = stack.getTag();
|
||||
if (tag == null) return null;
|
||||
|
||||
var key = side == TurtleSide.LEFT ? NBT_LEFT_UPGRADE : NBT_RIGHT_UPGRADE;
|
||||
if (!tag.contains(key)) return null;
|
||||
var upgrade = TurtleUpgrades.instance().get(tag.getString(key));
|
||||
if (upgrade == null) return null;
|
||||
var dataKey = side == TurtleSide.LEFT ? NBT_LEFT_UPGRADE_DATA : NBT_RIGHT_UPGRADE_DATA;
|
||||
return UpgradeData.of(upgrade, NBTUtil.getCompoundOrEmpty(tag, dataKey));
|
||||
}
|
||||
|
||||
public @Nullable ResourceLocation getOverlay(ItemStack stack) {
|
||||
|
@ -38,8 +38,8 @@ public class TurtleOverlayRecipe extends ShapelessRecipe {
|
||||
turtle.getComputerID(stack),
|
||||
turtle.getLabel(stack),
|
||||
turtle.getColour(stack),
|
||||
turtle.getUpgrade(stack, TurtleSide.LEFT),
|
||||
turtle.getUpgrade(stack, TurtleSide.RIGHT),
|
||||
turtle.getUpgradeWithData(stack, TurtleSide.LEFT),
|
||||
turtle.getUpgradeWithData(stack, TurtleSide.RIGHT),
|
||||
turtle.getFuelLevel(stack),
|
||||
overlay
|
||||
);
|
||||
|
@ -6,6 +6,7 @@ package dan200.computercraft.shared.turtle.recipes;
|
||||
|
||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import dan200.computercraft.api.turtle.TurtleSide;
|
||||
import dan200.computercraft.api.upgrades.UpgradeData;
|
||||
import dan200.computercraft.impl.TurtleUpgrades;
|
||||
import dan200.computercraft.shared.ModRegistry;
|
||||
import dan200.computercraft.shared.turtle.items.TurtleItem;
|
||||
@ -104,9 +105,10 @@ public final class TurtleUpgradeRecipe extends CustomRecipe {
|
||||
// At this point we have a turtle + 1 or 2 items
|
||||
// Get the turtle we already have
|
||||
var itemTurtle = (TurtleItem) turtle.getItem();
|
||||
var upgrades = new ITurtleUpgrade[]{
|
||||
itemTurtle.getUpgrade(turtle, TurtleSide.LEFT),
|
||||
itemTurtle.getUpgrade(turtle, TurtleSide.RIGHT),
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
UpgradeData<ITurtleUpgrade>[] upgrades = new UpgradeData[]{
|
||||
itemTurtle.getUpgradeWithData(turtle, TurtleSide.LEFT),
|
||||
itemTurtle.getUpgradeWithData(turtle, TurtleSide.RIGHT),
|
||||
};
|
||||
|
||||
// Get the upgrades for the new items
|
||||
|
@ -9,6 +9,7 @@ import dan200.computercraft.api.turtle.*;
|
||||
import dan200.computercraft.shared.peripheral.modem.ModemState;
|
||||
import dan200.computercraft.shared.peripheral.modem.wireless.WirelessModemPeripheral;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.level.Level;
|
||||
@ -77,4 +78,9 @@ public class TurtleModem extends AbstractTurtleUpgrade {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public CompoundTag getPersistedData(CompoundTag upgradeData) {
|
||||
return new CompoundTag();
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ package dan200.computercraft.shared.util;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.io.BaseEncoding;
|
||||
import dan200.computercraft.core.util.Nullability;
|
||||
import dan200.computercraft.shared.platform.PlatformHelper;
|
||||
import net.minecraft.nbt.*;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
@ -19,6 +20,7 @@ import java.io.OutputStream;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
@ -27,9 +29,42 @@ public final class NBTUtil {
|
||||
@VisibleForTesting
|
||||
static final BaseEncoding ENCODING = BaseEncoding.base16().lowerCase();
|
||||
|
||||
private static final CompoundTag EMPTY_TAG;
|
||||
|
||||
static {
|
||||
// If in a development environment, create a magic immutable compound tag.
|
||||
// We avoid doing this in prod, as I fear it might mess up the JIT inlining things.
|
||||
if (PlatformHelper.get().isDevelopmentEnvironment()) {
|
||||
try {
|
||||
var ctor = CompoundTag.class.getDeclaredConstructor(Map.class);
|
||||
ctor.setAccessible(true);
|
||||
EMPTY_TAG = ctor.newInstance(Collections.emptyMap());
|
||||
} catch (ReflectiveOperationException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
} else {
|
||||
EMPTY_TAG = new CompoundTag();
|
||||
}
|
||||
}
|
||||
|
||||
private NBTUtil() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a singleton empty {@link CompoundTag}. This tag should never be modified.
|
||||
*
|
||||
* @return The empty compound tag.
|
||||
*/
|
||||
public static CompoundTag emptyTag() {
|
||||
if (EMPTY_TAG.size() != 0) LOG.error("The empty tag has been modified.");
|
||||
return EMPTY_TAG;
|
||||
}
|
||||
|
||||
public static CompoundTag getCompoundOrEmpty(CompoundTag tag, String key) {
|
||||
var childTag = tag.get(key);
|
||||
return childTag != null && childTag.getId() == Tag.TAG_COMPOUND ? (CompoundTag) childTag : emptyTag();
|
||||
}
|
||||
|
||||
private static @Nullable Tag toNBTTag(@Nullable Object object) {
|
||||
if (object == null) return null;
|
||||
if (object instanceof Boolean) return ByteTag.valueOf((byte) ((boolean) (Boolean) object ? 1 : 0));
|
||||
|
@ -58,6 +58,11 @@ import java.util.function.Predicate;
|
||||
|
||||
@AutoService({ PlatformHelper.class, dan200.computercraft.impl.PlatformHelper.class, ComputerCraftAPIService.class })
|
||||
public class TestPlatformHelper extends AbstractComputerCraftAPI implements PlatformHelper {
|
||||
@Override
|
||||
public boolean isDevelopmentEnvironment() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigFile.Builder createConfigBuilder() {
|
||||
throw new UnsupportedOperationException("Cannot create config file inside tests");
|
||||
|
@ -33,6 +33,7 @@ import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerType;
|
||||
import net.fabricmc.fabric.api.tag.convention.v1.ConventionalItemTags;
|
||||
import net.fabricmc.fabric.api.transfer.v1.item.InventoryStorage;
|
||||
import net.fabricmc.fabric.api.transfer.v1.item.ItemStorage;
|
||||
import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.minecraft.commands.synchronization.ArgumentTypeInfo;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
@ -81,6 +82,11 @@ import java.util.function.*;
|
||||
|
||||
@AutoService(dan200.computercraft.impl.PlatformHelper.class)
|
||||
public class PlatformHelperImpl implements PlatformHelper {
|
||||
@Override
|
||||
public boolean isDevelopmentEnvironment() {
|
||||
return FabricLoader.getInstance().isDevelopmentEnvironment();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigFile.Builder createConfigBuilder() {
|
||||
return new FabricConfigFile.Builder();
|
||||
|
@ -62,6 +62,7 @@ import net.minecraftforge.common.util.NonNullConsumer;
|
||||
import net.minecraftforge.event.ForgeEventFactory;
|
||||
import net.minecraftforge.eventbus.api.Event;
|
||||
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
|
||||
import net.minecraftforge.fml.loading.FMLLoader;
|
||||
import net.minecraftforge.items.wrapper.InvWrapper;
|
||||
import net.minecraftforge.items.wrapper.SidedInvWrapper;
|
||||
import net.minecraftforge.network.NetworkHooks;
|
||||
@ -76,6 +77,11 @@ import java.util.function.*;
|
||||
|
||||
@AutoService(dan200.computercraft.impl.PlatformHelper.class)
|
||||
public class PlatformHelperImpl implements PlatformHelper {
|
||||
@Override
|
||||
public boolean isDevelopmentEnvironment() {
|
||||
return !FMLLoader.isProduction();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConfigFile.Builder createConfigBuilder() {
|
||||
return new ForgeConfigFile.Builder();
|
||||
|
Loading…
Reference in New Issue
Block a user