feat: allow NBT on wand items

- closes #2383
This commit is contained in:
dordsor21 2024-06-02 09:23:31 +01:00 committed by Jordan
parent 393ed00524
commit a82ba31185
8 changed files with 156 additions and 40 deletions

View File

@ -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<BaseItem>, JsonSerializer<BaseItem> {
@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);
}
}
}

View File

@ -52,6 +52,7 @@ import com.sk89q.worldedit.command.tool.SelectionWand;
import com.sk89q.worldedit.command.tool.SinglePickaxe; import com.sk89q.worldedit.command.tool.SinglePickaxe;
import com.sk89q.worldedit.command.tool.Tool; import com.sk89q.worldedit.command.tool.Tool;
import com.sk89q.worldedit.entity.Player; 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.Actor;
import com.sk89q.worldedit.extension.platform.Locatable; import com.sk89q.worldedit.extension.platform.Locatable;
import com.sk89q.worldedit.extent.NullExtent; 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.BaseBlock;
import com.sk89q.worldedit.world.block.BlockState; import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.item.ItemType; import com.sk89q.worldedit.world.item.ItemType;
import com.sk89q.worldedit.world.item.ItemTypes;
import com.sk89q.worldedit.world.snapshot.experimental.Snapshot; import com.sk89q.worldedit.world.snapshot.experimental.Snapshot;
import com.zaxxer.sparsebits.SparseBitSet; import com.zaxxer.sparsebits.SparseBitSet;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
@ -182,8 +182,10 @@ public class LocalSession implements TextureHolder {
private String lastScript; private String lastScript;
private RegionSelectorType defaultSelector; private RegionSelectorType defaultSelector;
private boolean useServerCUI = false; // Save this to not annoy players. private boolean useServerCUI = false; // Save this to not annoy players.
private ItemType wandItem; //FAWE start - allow NBT
private ItemType navWandItem; private BaseItem wandItem;
private BaseItem navWandItem;
//FAWE end
/** /**
* Construct the object. * Construct the object.
@ -1199,7 +1201,7 @@ public class LocalSession implements TextureHolder {
tool = tools.get(item.getInternalId()); tool = tools.get(item.getInternalId());
} }
if (tool == SelectionWand.INSTANCE && !SelectionWand.INSTANCE.canUse(player)) { 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. loadDefaults(player, true); // Permissions have changed so redo the player's current tools.
return null; return null;
} }
@ -1253,18 +1255,20 @@ public class LocalSession implements TextureHolder {
if (loadDefaults || force) { if (loadDefaults || force) {
loadDefaults = false; loadDefaults = false;
LocalConfiguration config = WorldEdit.getInstance().getConfiguration(); LocalConfiguration config = WorldEdit.getInstance().getConfiguration();
ParserContext context = new ParserContext();
context.setActor(actor);
if (wandItem == null) { if (wandItem == null) {
wandItem = ItemTypes.parse(config.wandItem); wandItem = WorldEdit.getInstance().getItemFactory().parseFromInput(config.wandItem, context);
} }
if (navWandItem == null) { if (navWandItem == null) {
navWandItem = ItemTypes.parse(config.navigationWand); navWandItem = WorldEdit.getInstance().getItemFactory().parseFromInput(config.navigationWand, context);
} }
synchronized (this.tools) { synchronized (this.tools) {
if (tools.get(navWandItem.getInternalId()) == null && NavigationWand.INSTANCE.canUse(actor)) { if (tools.get(navWandItem.getType().getInternalId()) == null && NavigationWand.INSTANCE.canUse(actor)) {
tools.put(navWandItem.getInternalId(), NavigationWand.INSTANCE); tools.put(navWandItem.getType().getInternalId(), NavigationWand.INSTANCE);
} }
if (tools.get(wandItem.getInternalId()) == null && SelectionWand.INSTANCE.canUse(actor)) { if (tools.get(wandItem.getType().getInternalId()) == null && SelectionWand.INSTANCE.canUse(actor)) {
tools.put(wandItem.getInternalId(), SelectionWand.INSTANCE); tools.put(wandItem.getType().getInternalId(), SelectionWand.INSTANCE);
} }
} }
} }
@ -1334,10 +1338,23 @@ public class LocalSession implements TextureHolder {
* @param item the item type * @param item the item type
* @param tool the tool to set, which can be {@code null} * @param tool the tool to set, which can be {@code null}
* @throws InvalidToolBindException if the item can't be bound to that item * @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 { public void setTool(ItemType item, @Nullable Tool tool) throws InvalidToolBindException {
if (item.hasBlockType()) { setTool(new BaseItem(item), tool);
throw new InvalidToolBindException(item, Caption.of("worldedit.error.blocks-cant-be-used")); }
/**
* 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) { if (tool instanceof SelectionWand) {
changeTool(this.wandItem, this.wandItem = item, tool); changeTool(this.wandItem, this.wandItem = item, tool);
@ -1348,7 +1365,7 @@ public class LocalSession implements TextureHolder {
setDirty(); setDirty();
return; return;
} }
setTool(item.getDefaultState(), tool, null); setTool(item, tool, null);
} }
public void setTool(Player player, @Nullable Tool tool) throws InvalidToolBindException { public void setTool(Player player, @Nullable Tool tool) throws InvalidToolBindException {
@ -1356,17 +1373,17 @@ public class LocalSession implements TextureHolder {
setTool(item, tool, player); setTool(item, tool, player);
} }
private void changeTool(ItemType oldType, ItemType newType, Tool newTool) { private void changeTool(BaseItem oldItem, BaseItem newItem, Tool newTool) {
if (oldType != null) { if (oldItem != null) {
synchronized (this.tools) { synchronized (this.tools) {
this.tools.remove(oldType.getInternalId()); this.tools.remove(oldItem.getType().getInternalId());
} }
} }
synchronized (this.tools) { synchronized (this.tools) {
if (newTool == null) { if (newTool == null) {
this.tools.remove(newType.getInternalId()); this.tools.remove(newItem.getType().getInternalId());
} else { } 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()) { if (type.hasBlockType() && type.getBlockType().getMaterial().isAir()) {
throw new InvalidToolBindException(type, Caption.of("worldedit.error.blocks-cant-be-used")); throw new InvalidToolBindException(type, Caption.of("worldedit.error.blocks-cant-be-used"));
} else if (tool instanceof SelectionWand) { } else if (tool instanceof SelectionWand) {
changeTool(this.wandItem, this.wandItem = item.getType(), tool); changeTool(this.wandItem, this.wandItem = item, tool);
setDirty(); setDirty();
return; return;
} else if (tool instanceof NavigationWand) { } else if (tool instanceof NavigationWand) {
changeTool(this.navWandItem, this.navWandItem = item.getType(), tool); changeTool(this.navWandItem, this.navWandItem = item, tool);
setDirty(); setDirty();
return; 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 * Get the preferred wand item for this user, or {@code null} to use the default
* *
* @return item id of wand item, or {@code null} * @return item id of wand item, or {@code null}
* @deprecated use {@link #getWandBaseItem()}
*/ */
@Deprecated
public String getWandItem() { 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} * @return item id of nav wand item, or {@code null}
*/ */
public String getNavWandItem() { public BaseItem getNavWandBaseItem() {
return navWandItem.getId(); return navWandItem == null ? null : new BaseItem(navWandItem.getType(), navWandItem.getNbtReference());
} }
//FAWE end
/** /**
* Get the last block distribution stored in this session. * Get the last block distribution stored in this session.

View File

@ -71,6 +71,7 @@ public class BaseItem implements NbtValued {
* @param itemType The type to set * @param itemType The type to set
*/ */
public void setType(ItemType itemType) { public void setType(ItemType itemType) {
checkNotNull(itemType);
this.itemType = itemType; this.itemType = itemType;
} }

View File

@ -30,6 +30,7 @@ import com.sk89q.worldedit.EditSession;
import com.sk89q.worldedit.LocalSession; import com.sk89q.worldedit.LocalSession;
import com.sk89q.worldedit.WorldEdit; import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.WorldEditException; import com.sk89q.worldedit.WorldEditException;
import com.sk89q.worldedit.blocks.BaseItem;
import com.sk89q.worldedit.blocks.BaseItemStack; import com.sk89q.worldedit.blocks.BaseItemStack;
import com.sk89q.worldedit.command.argument.SelectorChoice; import com.sk89q.worldedit.command.argument.SelectorChoice;
import com.sk89q.worldedit.command.tool.NavigationWand; 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.CommandPermissionsConditionGenerator;
import com.sk89q.worldedit.command.util.Logging; import com.sk89q.worldedit.command.util.Logging;
import com.sk89q.worldedit.entity.Player; 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.Actor;
import com.sk89q.worldedit.extension.platform.Locatable; import com.sk89q.worldedit.extension.platform.Locatable;
import com.sk89q.worldedit.extension.platform.permission.ActorSelectorLimits; import com.sk89q.worldedit.extension.platform.permission.ActorSelectorLimits;
@ -325,22 +328,29 @@ public class SelectionCommands {
//FAWE start //FAWE start
session.loadDefaults(player, true); session.loadDefaults(player, true);
//FAWE end //FAWE end
String wandId = navWand ? session.getNavWandItem() : session.getWandItem(); BaseItem wand = navWand ? session.getNavWandBaseItem() : session.getWandBaseItem();
if (wandId == null) { if (wand == null) {
wandId = navWand ? we.getConfiguration().navigationWand : we.getConfiguration().wandItem; String wandId = navWand ? we.getConfiguration().navigationWand : we.getConfiguration().wandItem;
} //FAWE start - allow item NBT
ItemType itemType = ItemTypes.parse(wandId); ParserContext parserContext = new ParserContext();
if (itemType == null) { parserContext.setActor(player);
parserContext.setSession(session);
try {
wand = WorldEdit.getInstance().getItemFactory().parseFromInput(wandId, parserContext);
} catch (InputParseException e) {
player.print(Caption.of("worldedit.wand.invalid")); player.print(Caption.of("worldedit.wand.invalid"));
return; 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 //FAWE start - instance-iate session
if (navWand) { if (navWand) {
session.setTool(itemType, NavigationWand.INSTANCE); session.setTool(wand, NavigationWand.INSTANCE);
player.print(Caption.of("worldedit.wand.navwand.info")); player.print(Caption.of("worldedit.wand.navwand.info"));
} else { } else {
session.setTool(itemType, SelectionWand.INSTANCE); session.setTool(wand, SelectionWand.INSTANCE);
player.print(Caption.of("worldedit.wand.selwand.info")); player.print(Caption.of("worldedit.wand.selwand.info"));
//FAWE end //FAWE end
} }

View File

@ -149,7 +149,7 @@ public class ToolCommands {
throws InvalidToolBindException { throws InvalidToolBindException {
//FAWE start //FAWE start
isBrush = session.getTool(player) instanceof BrushTool; 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 //FAWE end
player.print(Caption.of(isBrush ? "worldedit.brush.none.equip" : "worldedit.tool.none.equip")); player.print(Caption.of(isBrush ? "worldedit.brush.none.equip" : "worldedit.tool.none.equip"));
} }
@ -163,7 +163,7 @@ public class ToolCommands {
String translationKey String translationKey
) throws InvalidToolBindException { ) throws InvalidToolBindException {
BaseItemStack itemStack = player.getItemInHand(HandSide.MAIN_HAND); BaseItemStack itemStack = player.getItemInHand(HandSide.MAIN_HAND);
session.setTool(itemStack.getType(), tool); session.setTool(itemStack, tool);
player.print(Caption.of(translationKey, itemStack.getRichName())); player.print(Caption.of(translationKey, itemStack.getRichName()));
sendUnbindInstruction(player, UNBIND_COMMAND_COMPONENT); sendUnbindInstruction(player, UNBIND_COMMAND_COMPONENT);
} }

View File

@ -112,7 +112,7 @@ public class PropertiesConfiguration extends LocalConfiguration {
logFile = getString("log-file", logFile); logFile = getString("log-file", logFile);
logFormat = getString("log-format", logFormat); logFormat = getString("log-format", logFormat);
registerHelp = getBool("register-help", registerHelp); registerHelp = getBool("register-help", registerHelp);
wandItem = getString("wand-item", wandItem).toLowerCase(Locale.ROOT); wandItem = getString("wand-item", wandItem);
try { try {
wandItem = LegacyMapper.getInstance().getItemFromLegacy(Integer.parseInt(wandItem)).getId(); wandItem = LegacyMapper.getInstance().getItemFromLegacy(Integer.parseInt(wandItem)).getId();
} catch (Throwable ignored) { } catch (Throwable ignored) {
@ -122,7 +122,7 @@ public class PropertiesConfiguration extends LocalConfiguration {
useInventory = getBool("use-inventory", useInventory); useInventory = getBool("use-inventory", useInventory);
useInventoryOverride = getBool("use-inventory-override", useInventoryOverride); useInventoryOverride = getBool("use-inventory-override", useInventoryOverride);
useInventoryCreativeOverride = getBool("use-inventory-creative-override", useInventoryCreativeOverride); useInventoryCreativeOverride = getBool("use-inventory-creative-override", useInventoryCreativeOverride);
navigationWand = getString("nav-wand-item", navigationWand).toLowerCase(Locale.ROOT); navigationWand = getString("nav-wand-item", navigationWand);
try { try {
navigationWand = LegacyMapper.getInstance().getItemFromLegacy(Integer.parseInt(navigationWand)).getId(); navigationWand = LegacyMapper.getInstance().getItemFromLegacy(Integer.parseInt(navigationWand)).getId();
} catch (Throwable ignored) { } catch (Throwable ignored) {

View File

@ -58,7 +58,7 @@ public class YAMLConfiguration extends LocalConfiguration {
profile = config.getBoolean("debug", profile); profile = config.getBoolean("debug", profile);
traceUnflushedSessions = config.getBoolean("debugging.trace-unflushed-sessions", traceUnflushedSessions); 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( defaultChangeLimit = Math.max(-1, config.getInt(
"limits.max-blocks-changed.default", defaultChangeLimit)); "limits.max-blocks-changed.default", defaultChangeLimit));
@ -130,7 +130,7 @@ public class YAMLConfiguration extends LocalConfiguration {
useInventoryCreativeOverride 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); navigationWandMaxDistance = config.getInt("navigation-wand.max-distance", navigationWandMaxDistance);
navigationUseGlass = config.getBoolean("navigation.use-glass", navigationUseGlass); navigationUseGlass = config.getBoolean("navigation.use-glass", navigationUseGlass);

View File

@ -19,10 +19,12 @@
package com.sk89q.worldedit.util.gson; 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.ItemTypeAdapter;
import com.fastasyncworldedit.core.util.gson.RegionSelectorAdapter; import com.fastasyncworldedit.core.util.gson.RegionSelectorAdapter;
import com.google.gson.Gson; import com.google.gson.Gson;
import com.google.gson.GsonBuilder; import com.google.gson.GsonBuilder;
import com.sk89q.worldedit.blocks.BaseItem;
import com.sk89q.worldedit.math.BlockVector3; import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.math.Vector3; import com.sk89q.worldedit.math.Vector3;
import com.sk89q.worldedit.regions.RegionSelector; import com.sk89q.worldedit.regions.RegionSelector;
@ -48,6 +50,7 @@ public final class GsonUtil {
//FAWE start //FAWE start
gsonBuilder.registerTypeAdapter(RegionSelector.class, new RegionSelectorAdapter()); gsonBuilder.registerTypeAdapter(RegionSelector.class, new RegionSelectorAdapter());
gsonBuilder.registerTypeAdapter(ItemType.class, new ItemTypeAdapter()); gsonBuilder.registerTypeAdapter(ItemType.class, new ItemTypeAdapter());
gsonBuilder.registerTypeAdapter(BaseItem.class, new BaseItemAdapter());
//FAWE end //FAWE end
return gsonBuilder; return gsonBuilder;
} }