From a82ba31185e9142c8ce0b12d83d2a9d0ad573df6 Mon Sep 17 00:00:00 2001 From: dordsor21 Date: Sun, 2 Jun 2024 09:23:31 +0100 Subject: [PATCH] feat: allow NBT on wand items - closes #2383 --- .../core/util/gson/BaseItemAdapter.java | 61 +++++++++++++ .../com/sk89q/worldedit/LocalSession.java | 87 ++++++++++++++----- .../com/sk89q/worldedit/blocks/BaseItem.java | 1 + .../worldedit/command/SelectionCommands.java | 32 ++++--- .../sk89q/worldedit/command/ToolCommands.java | 4 +- .../util/PropertiesConfiguration.java | 4 +- .../worldedit/util/YAMLConfiguration.java | 4 +- .../sk89q/worldedit/util/gson/GsonUtil.java | 3 + 8 files changed, 156 insertions(+), 40 deletions(-) create mode 100644 worldedit-core/src/main/java/com/fastasyncworldedit/core/util/gson/BaseItemAdapter.java diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/gson/BaseItemAdapter.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/gson/BaseItemAdapter.java new file mode 100644 index 000000000..859ed9194 --- /dev/null +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/util/gson/BaseItemAdapter.java @@ -0,0 +1,61 @@ +package com.fastasyncworldedit.core.util.gson; + +import com.google.gson.JsonDeserializationContext; +import com.google.gson.JsonDeserializer; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonParseException; +import com.google.gson.JsonPrimitive; +import com.google.gson.JsonSerializationContext; +import com.google.gson.JsonSerializer; +import com.sk89q.worldedit.blocks.BaseItem; +import com.sk89q.worldedit.util.concurrency.LazyReference; +import com.sk89q.worldedit.util.nbt.TagStringIO; +import com.sk89q.worldedit.world.item.ItemType; +import com.sk89q.worldedit.world.item.ItemTypes; + +import java.io.IOException; +import java.lang.reflect.Type; + +public final class BaseItemAdapter implements JsonDeserializer, JsonSerializer { + + @Override + public BaseItem deserialize(JsonElement json, Type type, JsonDeserializationContext cont) throws JsonParseException { + JsonObject jsonObject = json.getAsJsonObject(); + JsonElement id = jsonObject.get("id"); + if (id != null) { + ItemType itemType = ItemTypes.get(id.getAsString()); + if (itemType == null) { + throw new JsonParseException("Could not parse item type `" + id + "`"); + } + return new BaseItem(itemType); + } + ItemType itemType = cont.deserialize(jsonObject.get("itemType").getAsJsonObject(), ItemType.class); + JsonElement nbt = jsonObject.get("nbt"); + if (nbt == null) { + return new BaseItem(itemType); + } + try { + return new BaseItem(itemType, LazyReference.computed(TagStringIO.get().asCompound(nbt.getAsString()))); + } catch (IOException e) { + throw new JsonParseException("Could not deserialize BaseItem", e); + } + } + + @Override + public JsonElement serialize( + final BaseItem baseItem, + final Type type, + final JsonSerializationContext jsonSerializationContext + ) { + JsonObject obj = new JsonObject(); + obj.add("itemType", jsonSerializationContext.serialize(baseItem.getType())); + try { + obj.add("nbt", baseItem.getNbt() == null ? null : new JsonPrimitive(TagStringIO.get().asString(baseItem.getNbt()))); + return obj; + } catch (IOException e) { + throw new JsonParseException("Could not deserialize BaseItem", e); + } + } + +} diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java b/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java index 5a5fa654e..8bf84ab8b 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/LocalSession.java @@ -52,6 +52,7 @@ import com.sk89q.worldedit.command.tool.SelectionWand; import com.sk89q.worldedit.command.tool.SinglePickaxe; import com.sk89q.worldedit.command.tool.Tool; import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.extension.input.ParserContext; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extension.platform.Locatable; import com.sk89q.worldedit.extent.NullExtent; @@ -80,7 +81,6 @@ import com.sk89q.worldedit.world.World; import com.sk89q.worldedit.world.block.BaseBlock; import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.item.ItemType; -import com.sk89q.worldedit.world.item.ItemTypes; import com.sk89q.worldedit.world.snapshot.experimental.Snapshot; import com.zaxxer.sparsebits.SparseBitSet; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; @@ -182,8 +182,10 @@ public class LocalSession implements TextureHolder { private String lastScript; private RegionSelectorType defaultSelector; private boolean useServerCUI = false; // Save this to not annoy players. - private ItemType wandItem; - private ItemType navWandItem; + //FAWE start - allow NBT + private BaseItem wandItem; + private BaseItem navWandItem; + //FAWE end /** * Construct the object. @@ -1199,7 +1201,7 @@ public class LocalSession implements TextureHolder { tool = tools.get(item.getInternalId()); } if (tool == SelectionWand.INSTANCE && !SelectionWand.INSTANCE.canUse(player)) { - tools.remove(wandItem.getInternalId()); + tools.remove(wandItem.getType().getInternalId()); loadDefaults(player, true); // Permissions have changed so redo the player's current tools. return null; } @@ -1253,18 +1255,20 @@ public class LocalSession implements TextureHolder { if (loadDefaults || force) { loadDefaults = false; LocalConfiguration config = WorldEdit.getInstance().getConfiguration(); + ParserContext context = new ParserContext(); + context.setActor(actor); if (wandItem == null) { - wandItem = ItemTypes.parse(config.wandItem); + wandItem = WorldEdit.getInstance().getItemFactory().parseFromInput(config.wandItem, context); } if (navWandItem == null) { - navWandItem = ItemTypes.parse(config.navigationWand); + navWandItem = WorldEdit.getInstance().getItemFactory().parseFromInput(config.navigationWand, context); } synchronized (this.tools) { - if (tools.get(navWandItem.getInternalId()) == null && NavigationWand.INSTANCE.canUse(actor)) { - tools.put(navWandItem.getInternalId(), NavigationWand.INSTANCE); + if (tools.get(navWandItem.getType().getInternalId()) == null && NavigationWand.INSTANCE.canUse(actor)) { + tools.put(navWandItem.getType().getInternalId(), NavigationWand.INSTANCE); } - if (tools.get(wandItem.getInternalId()) == null && SelectionWand.INSTANCE.canUse(actor)) { - tools.put(wandItem.getInternalId(), SelectionWand.INSTANCE); + if (tools.get(wandItem.getType().getInternalId()) == null && SelectionWand.INSTANCE.canUse(actor)) { + tools.put(wandItem.getType().getInternalId(), SelectionWand.INSTANCE); } } } @@ -1334,10 +1338,23 @@ public class LocalSession implements TextureHolder { * @param item the item type * @param tool the tool to set, which can be {@code null} * @throws InvalidToolBindException if the item can't be bound to that item + * @deprecated use {@link #setTool(BaseItem, Tool)} */ + @Deprecated public void setTool(ItemType item, @Nullable Tool tool) throws InvalidToolBindException { - if (item.hasBlockType()) { - throw new InvalidToolBindException(item, Caption.of("worldedit.error.blocks-cant-be-used")); + setTool(new BaseItem(item), tool); + } + + /** + * Set the tool. + * + * @param item the item type + * @param tool the tool to set, which can be {@code null} + * @throws InvalidToolBindException if the item can't be bound to that item + */ + public void setTool(BaseItem item, @Nullable Tool tool) throws InvalidToolBindException { + if (item.getType().hasBlockType()) { + throw new InvalidToolBindException(item.getType(), Caption.of("worldedit.error.blocks-cant-be-used")); } if (tool instanceof SelectionWand) { changeTool(this.wandItem, this.wandItem = item, tool); @@ -1348,7 +1365,7 @@ public class LocalSession implements TextureHolder { setDirty(); return; } - setTool(item.getDefaultState(), tool, null); + setTool(item, tool, null); } public void setTool(Player player, @Nullable Tool tool) throws InvalidToolBindException { @@ -1356,17 +1373,17 @@ public class LocalSession implements TextureHolder { setTool(item, tool, player); } - private void changeTool(ItemType oldType, ItemType newType, Tool newTool) { - if (oldType != null) { + private void changeTool(BaseItem oldItem, BaseItem newItem, Tool newTool) { + if (oldItem != null) { synchronized (this.tools) { - this.tools.remove(oldType.getInternalId()); + this.tools.remove(oldItem.getType().getInternalId()); } } synchronized (this.tools) { if (newTool == null) { - this.tools.remove(newType.getInternalId()); + this.tools.remove(newItem.getType().getInternalId()); } else { - this.tools.put(newType.getInternalId(), newTool); + this.tools.put(newItem.getType().getInternalId(), newTool); } } } @@ -1376,11 +1393,11 @@ public class LocalSession implements TextureHolder { if (type.hasBlockType() && type.getBlockType().getMaterial().isAir()) { throw new InvalidToolBindException(type, Caption.of("worldedit.error.blocks-cant-be-used")); } else if (tool instanceof SelectionWand) { - changeTool(this.wandItem, this.wandItem = item.getType(), tool); + changeTool(this.wandItem, this.wandItem = item, tool); setDirty(); return; } else if (tool instanceof NavigationWand) { - changeTool(this.navWandItem, this.navWandItem = item.getType(), tool); + changeTool(this.navWandItem, this.navWandItem = item, tool); setDirty(); return; } @@ -1877,9 +1894,32 @@ public class LocalSession implements TextureHolder { * Get the preferred wand item for this user, or {@code null} to use the default * * @return item id of wand item, or {@code null} + * @deprecated use {@link #getWandBaseItem()} */ + @Deprecated public String getWandItem() { - return wandItem.getId(); + return wandItem.getType().getId(); + } + + /** + * Get the preferred navigation wand item for this user, or {@code null} to use the default + * + * @return item id of nav wand item, or {@code null} + * @deprecated use {@link #getNavWandBaseItem()} + */ + @Deprecated + public String getNavWandItem() { + return navWandItem.getType().getId(); + } + + //FAWE start + /** + * Get the preferred wand item for this user, or {@code null} to use the default + * + * @return item id of wand item, or {@code null} + */ + public BaseItem getWandBaseItem() { + return wandItem == null ? null : new BaseItem(wandItem.getType(), wandItem.getNbtReference()); } /** @@ -1887,9 +1927,10 @@ public class LocalSession implements TextureHolder { * * @return item id of nav wand item, or {@code null} */ - public String getNavWandItem() { - return navWandItem.getId(); + public BaseItem getNavWandBaseItem() { + return navWandItem == null ? null : new BaseItem(navWandItem.getType(), navWandItem.getNbtReference()); } + //FAWE end /** * Get the last block distribution stored in this session. diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/blocks/BaseItem.java b/worldedit-core/src/main/java/com/sk89q/worldedit/blocks/BaseItem.java index 1dab627c7..ac8236297 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/blocks/BaseItem.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/blocks/BaseItem.java @@ -71,6 +71,7 @@ public class BaseItem implements NbtValued { * @param itemType The type to set */ public void setType(ItemType itemType) { + checkNotNull(itemType); this.itemType = itemType; } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/SelectionCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/SelectionCommands.java index d8e0943dc..ac13defc3 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/SelectionCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/SelectionCommands.java @@ -30,6 +30,7 @@ import com.sk89q.worldedit.EditSession; import com.sk89q.worldedit.LocalSession; import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.WorldEditException; +import com.sk89q.worldedit.blocks.BaseItem; import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.command.argument.SelectorChoice; import com.sk89q.worldedit.command.tool.NavigationWand; @@ -38,6 +39,8 @@ import com.sk89q.worldedit.command.util.CommandPermissions; import com.sk89q.worldedit.command.util.CommandPermissionsConditionGenerator; import com.sk89q.worldedit.command.util.Logging; import com.sk89q.worldedit.entity.Player; +import com.sk89q.worldedit.extension.input.InputParseException; +import com.sk89q.worldedit.extension.input.ParserContext; import com.sk89q.worldedit.extension.platform.Actor; import com.sk89q.worldedit.extension.platform.Locatable; import com.sk89q.worldedit.extension.platform.permission.ActorSelectorLimits; @@ -325,22 +328,29 @@ public class SelectionCommands { //FAWE start session.loadDefaults(player, true); //FAWE end - String wandId = navWand ? session.getNavWandItem() : session.getWandItem(); - if (wandId == null) { - wandId = navWand ? we.getConfiguration().navigationWand : we.getConfiguration().wandItem; + BaseItem wand = navWand ? session.getNavWandBaseItem() : session.getWandBaseItem(); + if (wand == null) { + String wandId = navWand ? we.getConfiguration().navigationWand : we.getConfiguration().wandItem; + //FAWE start - allow item NBT + ParserContext parserContext = new ParserContext(); + parserContext.setActor(player); + parserContext.setSession(session); + try { + wand = WorldEdit.getInstance().getItemFactory().parseFromInput(wandId, parserContext); + } catch (InputParseException e) { + player.print(Caption.of("worldedit.wand.invalid")); + return; + } } - ItemType itemType = ItemTypes.parse(wandId); - if (itemType == null) { - player.print(Caption.of("worldedit.wand.invalid")); - return; - } - player.giveItem(new BaseItemStack(itemType, 1)); + System.out.println("a "+ wand); + player.giveItem(new BaseItemStack(wand.getType(), wand.getNbtReference(), 1)); + //FAWE end //FAWE start - instance-iate session if (navWand) { - session.setTool(itemType, NavigationWand.INSTANCE); + session.setTool(wand, NavigationWand.INSTANCE); player.print(Caption.of("worldedit.wand.navwand.info")); } else { - session.setTool(itemType, SelectionWand.INSTANCE); + session.setTool(wand, SelectionWand.INSTANCE); player.print(Caption.of("worldedit.wand.selwand.info")); //FAWE end } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/command/ToolCommands.java b/worldedit-core/src/main/java/com/sk89q/worldedit/command/ToolCommands.java index 95499372c..f3be41469 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/command/ToolCommands.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/command/ToolCommands.java @@ -149,7 +149,7 @@ public class ToolCommands { throws InvalidToolBindException { //FAWE start isBrush = session.getTool(player) instanceof BrushTool; - session.setTool(player.getItemInHand(HandSide.MAIN_HAND).getType(), null); + session.setTool(player.getItemInHand(HandSide.MAIN_HAND), null); //FAWE end player.print(Caption.of(isBrush ? "worldedit.brush.none.equip" : "worldedit.tool.none.equip")); } @@ -163,7 +163,7 @@ public class ToolCommands { String translationKey ) throws InvalidToolBindException { BaseItemStack itemStack = player.getItemInHand(HandSide.MAIN_HAND); - session.setTool(itemStack.getType(), tool); + session.setTool(itemStack, tool); player.print(Caption.of(translationKey, itemStack.getRichName())); sendUnbindInstruction(player, UNBIND_COMMAND_COMPONENT); } diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/PropertiesConfiguration.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/PropertiesConfiguration.java index 3804be8d3..7bec7c2f5 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/PropertiesConfiguration.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/PropertiesConfiguration.java @@ -112,7 +112,7 @@ public class PropertiesConfiguration extends LocalConfiguration { logFile = getString("log-file", logFile); logFormat = getString("log-format", logFormat); registerHelp = getBool("register-help", registerHelp); - wandItem = getString("wand-item", wandItem).toLowerCase(Locale.ROOT); + wandItem = getString("wand-item", wandItem); try { wandItem = LegacyMapper.getInstance().getItemFromLegacy(Integer.parseInt(wandItem)).getId(); } catch (Throwable ignored) { @@ -122,7 +122,7 @@ public class PropertiesConfiguration extends LocalConfiguration { useInventory = getBool("use-inventory", useInventory); useInventoryOverride = getBool("use-inventory-override", useInventoryOverride); useInventoryCreativeOverride = getBool("use-inventory-creative-override", useInventoryCreativeOverride); - navigationWand = getString("nav-wand-item", navigationWand).toLowerCase(Locale.ROOT); + navigationWand = getString("nav-wand-item", navigationWand); try { navigationWand = LegacyMapper.getInstance().getItemFromLegacy(Integer.parseInt(navigationWand)).getId(); } catch (Throwable ignored) { diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/YAMLConfiguration.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/YAMLConfiguration.java index 7b0cbf4b8..4a5e45713 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/YAMLConfiguration.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/YAMLConfiguration.java @@ -58,7 +58,7 @@ public class YAMLConfiguration extends LocalConfiguration { profile = config.getBoolean("debug", profile); traceUnflushedSessions = config.getBoolean("debugging.trace-unflushed-sessions", traceUnflushedSessions); - wandItem = convertLegacyItem(config.getString("wand-item", wandItem)).toLowerCase(Locale.ROOT); + wandItem = convertLegacyItem(config.getString("wand-item", wandItem)); defaultChangeLimit = Math.max(-1, config.getInt( "limits.max-blocks-changed.default", defaultChangeLimit)); @@ -130,7 +130,7 @@ public class YAMLConfiguration extends LocalConfiguration { useInventoryCreativeOverride ); - navigationWand = convertLegacyItem(config.getString("navigation-wand.item", navigationWand)).toLowerCase(Locale.ROOT); + navigationWand = convertLegacyItem(config.getString("navigation-wand.item", navigationWand)); navigationWandMaxDistance = config.getInt("navigation-wand.max-distance", navigationWandMaxDistance); navigationUseGlass = config.getBoolean("navigation.use-glass", navigationUseGlass); diff --git a/worldedit-core/src/main/java/com/sk89q/worldedit/util/gson/GsonUtil.java b/worldedit-core/src/main/java/com/sk89q/worldedit/util/gson/GsonUtil.java index e5d05ee6d..416fbeaae 100644 --- a/worldedit-core/src/main/java/com/sk89q/worldedit/util/gson/GsonUtil.java +++ b/worldedit-core/src/main/java/com/sk89q/worldedit/util/gson/GsonUtil.java @@ -19,10 +19,12 @@ package com.sk89q.worldedit.util.gson; +import com.fastasyncworldedit.core.util.gson.BaseItemAdapter; import com.fastasyncworldedit.core.util.gson.ItemTypeAdapter; import com.fastasyncworldedit.core.util.gson.RegionSelectorAdapter; import com.google.gson.Gson; import com.google.gson.GsonBuilder; +import com.sk89q.worldedit.blocks.BaseItem; import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.regions.RegionSelector; @@ -48,6 +50,7 @@ public final class GsonUtil { //FAWE start gsonBuilder.registerTypeAdapter(RegionSelector.class, new RegionSelectorAdapter()); gsonBuilder.registerTypeAdapter(ItemType.class, new ItemTypeAdapter()); + gsonBuilder.registerTypeAdapter(BaseItem.class, new BaseItemAdapter()); //FAWE end return gsonBuilder; }