mirror of
				https://github.com/SquidDev-CC/CC-Tweaked
				synced 2025-10-30 21:23:00 +00:00 
			
		
		
		
	Allow upgrades to read/write upgrade data from ItemStacks (#1465)
This commit is contained in:
		| @@ -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"); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Edvin
					Edvin