diff --git a/LICENSE b/LICENSE
index 0a041280..467d914a 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,165 +1,21 @@
- GNU LESSER GENERAL PUBLIC LICENSE
- Version 3, 29 June 2007
+MIT License
- Copyright (C) 2007 Free Software Foundation, Inc.
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
+Copyright (c) 2020 gnembon
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
- This version of the GNU Lesser General Public License incorporates
-the terms and conditions of version 3 of the GNU General Public
-License, supplemented by the additional permissions listed below.
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
- 0. Additional Definitions.
-
- As used herein, "this License" refers to version 3 of the GNU Lesser
-General Public License, and the "GNU GPL" refers to version 3 of the GNU
-General Public License.
-
- "The Library" refers to a covered work governed by this License,
-other than an Application or a Combined Work as defined below.
-
- An "Application" is any work that makes use of an interface provided
-by the Library, but which is not otherwise based on the Library.
-Defining a subclass of a class defined by the Library is deemed a mode
-of using an interface provided by the Library.
-
- A "Combined Work" is a work produced by combining or linking an
-Application with the Library. The particular version of the Library
-with which the Combined Work was made is also called the "Linked
-Version".
-
- The "Minimal Corresponding Source" for a Combined Work means the
-Corresponding Source for the Combined Work, excluding any source code
-for portions of the Combined Work that, considered in isolation, are
-based on the Application, and not on the Linked Version.
-
- The "Corresponding Application Code" for a Combined Work means the
-object code and/or source code for the Application, including any data
-and utility programs needed for reproducing the Combined Work from the
-Application, but excluding the System Libraries of the Combined Work.
-
- 1. Exception to Section 3 of the GNU GPL.
-
- You may convey a covered work under sections 3 and 4 of this License
-without being bound by section 3 of the GNU GPL.
-
- 2. Conveying Modified Versions.
-
- If you modify a copy of the Library, and, in your modifications, a
-facility refers to a function or data to be supplied by an Application
-that uses the facility (other than as an argument passed when the
-facility is invoked), then you may convey a copy of the modified
-version:
-
- a) under this License, provided that you make a good faith effort to
- ensure that, in the event an Application does not supply the
- function or data, the facility still operates, and performs
- whatever part of its purpose remains meaningful, or
-
- b) under the GNU GPL, with none of the additional permissions of
- this License applicable to that copy.
-
- 3. Object Code Incorporating Material from Library Header Files.
-
- The object code form of an Application may incorporate material from
-a header file that is part of the Library. You may convey such object
-code under terms of your choice, provided that, if the incorporated
-material is not limited to numerical parameters, data structure
-layouts and accessors, or small macros, inline functions and templates
-(ten or fewer lines in length), you do both of the following:
-
- a) Give prominent notice with each copy of the object code that the
- Library is used in it and that the Library and its use are
- covered by this License.
-
- b) Accompany the object code with a copy of the GNU GPL and this license
- document.
-
- 4. Combined Works.
-
- You may convey a Combined Work under terms of your choice that,
-taken together, effectively do not restrict modification of the
-portions of the Library contained in the Combined Work and reverse
-engineering for debugging such modifications, if you also do each of
-the following:
-
- a) Give prominent notice with each copy of the Combined Work that
- the Library is used in it and that the Library and its use are
- covered by this License.
-
- b) Accompany the Combined Work with a copy of the GNU GPL and this license
- document.
-
- c) For a Combined Work that displays copyright notices during
- execution, include the copyright notice for the Library among
- these notices, as well as a reference directing the user to the
- copies of the GNU GPL and this license document.
-
- d) Do one of the following:
-
- 0) Convey the Minimal Corresponding Source under the terms of this
- License, and the Corresponding Application Code in a form
- suitable for, and under terms that permit, the user to
- recombine or relink the Application with a modified version of
- the Linked Version to produce a modified Combined Work, in the
- manner specified by section 6 of the GNU GPL for conveying
- Corresponding Source.
-
- 1) Use a suitable shared library mechanism for linking with the
- Library. A suitable mechanism is one that (a) uses at run time
- a copy of the Library already present on the user's computer
- system, and (b) will operate properly with a modified version
- of the Library that is interface-compatible with the Linked
- Version.
-
- e) Provide Installation Information, but only if you would otherwise
- be required to provide such information under section 6 of the
- GNU GPL, and only to the extent that such information is
- necessary to install and execute a modified version of the
- Combined Work produced by recombining or relinking the
- Application with a modified version of the Linked Version. (If
- you use option 4d0, the Installation Information must accompany
- the Minimal Corresponding Source and Corresponding Application
- Code. If you use option 4d1, you must provide the Installation
- Information in the manner specified by section 6 of the GNU GPL
- for conveying Corresponding Source.)
-
- 5. Combined Libraries.
-
- You may place library facilities that are a work based on the
-Library side by side in a single library together with other library
-facilities that are not Applications and are not covered by this
-License, and convey such a combined library under terms of your
-choice, if you do both of the following:
-
- a) Accompany the combined library with a copy of the same work based
- on the Library, uncombined with any other library facilities,
- conveyed under the terms of this License.
-
- b) Give prominent notice with the combined library that part of it
- is a work based on the Library, and explaining where to find the
- accompanying uncombined form of the same work.
-
- 6. Revised Versions of the GNU Lesser General Public License.
-
- The Free Software Foundation may publish revised and/or new versions
-of the GNU Lesser General Public License from time to time. Such new
-versions will be similar in spirit to the present version, but may
-differ in detail to address new problems or concerns.
-
- Each version is given a distinguishing version number. If the
-Library as you received it specifies that a certain numbered version
-of the GNU Lesser General Public License "or any later version"
-applies to it, you have the option of following the terms and
-conditions either of that published version or of any later version
-published by the Free Software Foundation. If the Library as you
-received it does not specify a version number of the GNU Lesser
-General Public License, you may choose any version of the GNU Lesser
-General Public License ever published by the Free Software Foundation.
-
- If the Library as you received it specifies that a proxy can decide
-whether future versions of the GNU Lesser General Public License shall
-apply, that proxy's public statement of acceptance of any version is
-permanent authorization for you to choose that version for the
-Library.
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/docs/scarpet/api/Auxiliary.md b/docs/scarpet/api/Auxiliary.md
index 0562a0fe..c5fddee4 100644
--- a/docs/scarpet/api/Auxiliary.md
+++ b/docs/scarpet/api/Auxiliary.md
@@ -130,6 +130,30 @@ Consult section about container operations in `Expression` to learn about possib
Excapes all the special characters in the string or nbt tag and returns a string that can be stored in nbt directly
as a string value.
+### parse_nbt(tag)
+
+Converts NBT tag to a scarpet value, which you can navigate through much better.
+
+Converts:
+ - Compound tags into maps with string keys
+ - List tags into list values
+ - Numbers (Ints, Floats, Doubles, Longs) into a number
+ - Rest is converted to strings.
+
+### encode_nbt(expr, force?)
+
+Encodes value of the expression as an NBT tag. By default (or when `force` is false), it will only allow
+to encode values that are guaranteed to return the same value when applied the resulting tag to `parse_nbt()`.
+Supported types that can reliably convert back and forth to and from NBT values are:
+ - Maps with string keywords
+ - Lists of items of the same type (scarpet will take care of unifying value types if possible)
+ - Numbers (encoded as Ints -> Longs -> Doubles, as needed)
+ - Strings
+
+Other value types will only be converted to tags (including NBT tags) if `force` is true. They would require
+extra treatment when loading them back from NBT, but using `force` true will always produce output / never
+produce an exception.
+
### `print(expr)`
Displays the result of the expression to the chat. Overrides default `scarpet` behaviour of sending everyting to stderr.
diff --git a/docs/scarpet/api/Entities.md b/docs/scarpet/api/Entities.md
index a0524460..cc9084d5 100644
--- a/docs/scarpet/api/Entities.md
+++ b/docs/scarpet/api/Entities.md
@@ -532,6 +532,15 @@ Modifies directly player raw hunger components. Has no effect on non-players
adds exhaustion value to the current player exhaustion level - that's the method you probably want to use
to manipulate how much 'food' some action cost.
+### `modify(e, 'nbt_merge', partial_tag)`
+
+Merges a partial tag into the entity data and reloads the entity from its updated tag. Cannot be applied to players
+
+### `modify(e, 'nbt', tag)`
+
+Reloads the entity from a supplied tag. Better get a valid entity tag, what can go wrong. Wonder what would happen if
+transplant rabbit's brain into a villager. Cannot be applied to players.
+
## Entity Events
There is a number of events that happen to entities that you can attach your own code to in the form of event handlers.
diff --git a/src/main/java/carpet/script/CarpetExpression.java b/src/main/java/carpet/script/CarpetExpression.java
index 63f91aac..c47809a0 100644
--- a/src/main/java/carpet/script/CarpetExpression.java
+++ b/src/main/java/carpet/script/CarpetExpression.java
@@ -2966,6 +2966,24 @@ public class CarpetExpression
return (cc, tt) -> parsed;
});
+ this.expr.addLazyFunction("encode_nbt", -1, (c, t, lv) -> {
+ int argSize = lv.size();
+ if (argSize==0 || argSize > 2) throw new InternalExpressionException("'encode_nbt' requires 1 or 2 parameters");
+ Value v = lv.get(0).evalValue(c);
+ boolean force = (argSize > 1) && lv.get(1).evalValue(c).getBoolean();
+ Tag tag;
+ try
+ {
+ tag = v.toTag(force);
+ }
+ catch (NBTSerializableValue.IncompatibleTypeException ignored)
+ {
+ throw new InternalExpressionException("cannot reliably encode to a tag the value of '"+ignored.val.getPrettyString()+"'");
+ }
+ Value tagValue = new NBTSerializableValue(tag);
+ return (cc, tt) -> tagValue;
+ });
+
//"overridden" native call that prints to stderr
this.expr.addLazyFunction("print", -1, (c, t, lv) ->
{
diff --git a/src/main/java/carpet/script/utils/ShapesRenderer.java b/src/main/java/carpet/script/utils/ShapesRenderer.java
index c1ab0edd..4bb6ca55 100644
--- a/src/main/java/carpet/script/utils/ShapesRenderer.java
+++ b/src/main/java/carpet/script/utils/ShapesRenderer.java
@@ -41,9 +41,6 @@ public class ShapesRenderer
{
this.client = minecraftClient;
shapes = new HashMap<>();
- shapes.put(World.OVERWORLD, new Long2ObjectOpenHashMap<>());
- shapes.put(World.NETHER, new Long2ObjectOpenHashMap<>());
- shapes.put(World.END, new Long2ObjectOpenHashMap<>());
}
public void render(Camera camera, float partialTick)
@@ -51,7 +48,7 @@ public class ShapesRenderer
//Camera camera = this.client.gameRenderer.getCamera();
ClientWorld iWorld = this.client.world;
RegistryKey dimensionType = iWorld.getRegistryKey();
- if (shapes.get(dimensionType).isEmpty()) return;
+ if (shapes.get(dimensionType) == null || shapes.get(dimensionType).isEmpty()) return;
long currentTime = client.world.getTime();
RenderSystem.disableTexture();
RenderSystem.enableDepthTest();
@@ -133,7 +130,7 @@ public class ShapesRenderer
long key = rshape.key();
synchronized (shapes)
{
- RenderedShape> existing = shapes.get(dim).get(key);
+ RenderedShape> existing = shapes.computeIfAbsent(dim, d -> new Long2ObjectOpenHashMap<>()).get(key);
if (existing != null)
{ // promoting previous shape
existing.expiryTick = rshape.expiryTick;
diff --git a/src/main/java/carpet/script/value/BlockValue.java b/src/main/java/carpet/script/value/BlockValue.java
index c090cb40..7579b267 100644
--- a/src/main/java/carpet/script/value/BlockValue.java
+++ b/src/main/java/carpet/script/value/BlockValue.java
@@ -12,7 +12,10 @@ import net.minecraft.block.Blocks;
import net.minecraft.item.ItemPlacementContext;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundTag;
+import net.minecraft.nbt.StringTag;
+import net.minecraft.nbt.Tag;
import net.minecraft.server.world.ServerWorld;
+import net.minecraft.state.property.Property;
import net.minecraft.util.dynamic.GlobalPos;
import net.minecraft.util.Hand;
import net.minecraft.util.hit.BlockHitResult;
@@ -24,8 +27,10 @@ import net.minecraft.world.World;
import net.minecraft.world.chunk.WorldChunk;
import java.util.Arrays;
+import java.util.Collection;
import java.util.HashMap;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.stream.Collectors;
@@ -180,6 +185,33 @@ public class BlockValue extends Value
public ServerWorld getWorld() { return world;}
+ @Override
+ public Tag toTag(boolean force)
+ {
+ if (!force) throw new NBTSerializableValue.IncompatibleTypeException(this);
+ // follows falling block convertion
+ CompoundTag tag = new CompoundTag();
+ CompoundTag state = new CompoundTag();
+ BlockState s = getBlockState();
+ state.put("Name", StringTag.of(Registry.BLOCK.getId(s.getBlock()).toString()));
+ Collection> properties = s.getProperties();
+ if (!properties.isEmpty())
+ {
+ CompoundTag props = new CompoundTag();
+ for (Property> p: properties)
+ {
+ props.put(p.getName(), StringTag.of(s.get(p).toString().toLowerCase(Locale.ROOT)));
+ }
+ state.put("Properties", props);
+ }
+ tag.put("BlockState", state);
+ CompoundTag dataTag = getData();
+ if (dataTag != null)
+ {
+ tag.put("TileEntityData", dataTag);
+ }
+ return tag;
+ }
public enum SpecificDirection {
UP("up",0.5, 0.0, 0.5, Direction.UP),
diff --git a/src/main/java/carpet/script/value/EntityValue.java b/src/main/java/carpet/script/value/EntityValue.java
index e1e24da0..5018e62c 100644
--- a/src/main/java/carpet/script/value/EntityValue.java
+++ b/src/main/java/carpet/script/value/EntityValue.java
@@ -1,6 +1,5 @@
package carpet.script.value;
-import carpet.CarpetSettings;
import carpet.fakes.EntityInterface;
import carpet.fakes.ItemEntityInterface;
import carpet.fakes.LivingEntityInterface;
@@ -12,10 +11,10 @@ import carpet.script.CarpetContext;
import carpet.script.EntityEventsGroup;
import carpet.script.argument.Vector3Argument;
import carpet.script.exception.InternalExpressionException;
-import carpet.utils.Messenger;
import com.mojang.brigadier.StringReader;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
-import net.minecraft.entity.player.HungerManager;
+import net.minecraft.nbt.StringTag;
+import net.minecraft.nbt.Tag;
import net.minecraft.network.packet.s2c.play.EntityPassengersSetS2CPacket;
import net.minecraft.network.packet.s2c.play.EntityPositionS2CPacket;
import net.minecraft.network.packet.s2c.play.PlayerPositionLookS2CPacket;
@@ -61,6 +60,7 @@ import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
+import java.util.UUID;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Predicate;
@@ -976,22 +976,37 @@ public class EntityValue extends Value
if(e instanceof PlayerEntity) ((PlayerEntity) e).getHungerManager().setSaturationLevelClient((float)NumericValue.asNumber(v).getLong());
});
- // gamemode [check]
- // spectate [check]
- // "fire" [check]
- // "extinguish" [set fire ticks to 0]
- // "silent" [check]
- // "gravity" [check]
- // "invulnerable" [check]
+ put("nbt", (e, v) -> {
+ if (!(e instanceof PlayerEntity))
+ {
+ UUID uUID = e.getUuid();
+ Value tagValue = NBTSerializableValue.fromValue(v);
+ if (tagValue instanceof NBTSerializableValue)
+ {
+ e.fromTag(((NBTSerializableValue) tagValue).getCompoundTag());
+ e.setUuid(uUID);
+ }
+ }
+ });
+ put("nbt_merge", (e, v) -> {
+ if (!(e instanceof PlayerEntity))
+ {
+ UUID uUID = e.getUuid();
+ Value tagValue = NBTSerializableValue.fromValue(v);
+ if (tagValue instanceof NBTSerializableValue)
+ {
+ CompoundTag nbttagcompound = e.toTag((new CompoundTag()));
+ nbttagcompound.copyFrom(((NBTSerializableValue) tagValue).getCompoundTag());
+ e.fromTag(nbttagcompound);
+ e.setUuid(uUID);
+ }
+ }
+ });
+
// "dimension" []
// "item" []
// "count", []
- // "age", [check]
// "effect_"name []
- // "hold" [inventory_set?]
- // "hold_offhand" [inventory_set?]
- // "jump" [check]
- // "nbt" <-big one, for now use run('data merge entity ...
}};
public void setEvent(CarpetContext cc, String eventName, FunctionValue fun, List args)
@@ -1001,4 +1016,14 @@ public class EntityValue extends Value
throw new InternalExpressionException("Unknown entity event: " + eventName);
((EntityInterface)entity).getEventContainer().addEvent(event, cc, fun, args);
}
+
+ @Override
+ public Tag toTag(boolean force)
+ {
+ if (!force) throw new NBTSerializableValue.IncompatibleTypeException(this);
+ CompoundTag tag = new CompoundTag();
+ tag.put("Data", getEntity().toTag( new CompoundTag()));
+ tag.put("Name", StringTag.of(Registry.ENTITY_TYPE.getId(entity.getType()).toString()));
+ return tag;
+ }
}
diff --git a/src/main/java/carpet/script/value/FormattedTextValue.java b/src/main/java/carpet/script/value/FormattedTextValue.java
index 501e3181..68983085 100644
--- a/src/main/java/carpet/script/value/FormattedTextValue.java
+++ b/src/main/java/carpet/script/value/FormattedTextValue.java
@@ -1,6 +1,9 @@
package carpet.script.value;
+import net.minecraft.nbt.StringTag;
+import net.minecraft.nbt.Tag;
import net.minecraft.text.BaseText;
+import net.minecraft.text.Text;
public class FormattedTextValue extends StringValue
{
@@ -37,4 +40,11 @@ public class FormattedTextValue extends StringValue
{
return text;
}
+
+ @Override
+ public Tag toTag(boolean force)
+ {
+ if (!force) throw new NBTSerializableValue.IncompatibleTypeException(this);
+ return StringTag.of(Text.Serializer.toJson(text));
+ }
}
diff --git a/src/main/java/carpet/script/value/FrameworkValue.java b/src/main/java/carpet/script/value/FrameworkValue.java
index 882c79ec..a432974e 100644
--- a/src/main/java/carpet/script/value/FrameworkValue.java
+++ b/src/main/java/carpet/script/value/FrameworkValue.java
@@ -1,5 +1,7 @@
package carpet.script.value;
+import net.minecraft.nbt.Tag;
+
public abstract class FrameworkValue extends Value
{
@Override
@@ -25,4 +27,10 @@ public abstract class FrameworkValue extends Value
{
throw new UnsupportedOperationException("Scarpet language component cannot be used as map key");
}
+
+ @Override
+ public Tag toTag(boolean force)
+ {
+ throw new UnsupportedOperationException("Scarpet language component cannot be serialized to the tag");
+ }
}
diff --git a/src/main/java/carpet/script/value/FunctionValue.java b/src/main/java/carpet/script/value/FunctionValue.java
index 3b872ccf..45791fad 100644
--- a/src/main/java/carpet/script/value/FunctionValue.java
+++ b/src/main/java/carpet/script/value/FunctionValue.java
@@ -13,6 +13,8 @@ import carpet.script.exception.ExpressionException;
import carpet.script.exception.InternalExpressionException;
import carpet.script.exception.ReturnStatement;
import carpet.script.exception.ThrowStatement;
+import net.minecraft.nbt.StringTag;
+import net.minecraft.nbt.Tag;
import java.util.ArrayList;
import java.util.Collections;
@@ -94,6 +96,8 @@ public class FunctionValue extends Value implements Fluff.ILazyFunction
return name.hashCode()+(int)variant;
}
+
+
@Override
public boolean equals(Object o)
{
@@ -231,4 +235,11 @@ public class FunctionValue extends Value implements Fluff.ILazyFunction
{
return args;
}
+
+ @Override
+ public Tag toTag(boolean force)
+ {
+ if (!force) throw new NBTSerializableValue.IncompatibleTypeException(this);
+ return StringTag.of(getString());
+ }
}
diff --git a/src/main/java/carpet/script/value/GlobalValue.java b/src/main/java/carpet/script/value/GlobalValue.java
index 697d630d..c38afb68 100644
--- a/src/main/java/carpet/script/value/GlobalValue.java
+++ b/src/main/java/carpet/script/value/GlobalValue.java
@@ -1,6 +1,7 @@
package carpet.script.value;
import carpet.script.exception.InternalExpressionException;
+import net.minecraft.nbt.Tag;
public class GlobalValue extends Value
{
@@ -28,4 +29,10 @@ public class GlobalValue extends Value
{
throw new UnsupportedOperationException("Global value cannot be used as a map key");
}
+
+ @Override
+ public Tag toTag(boolean force)
+ {
+ throw new UnsupportedOperationException("Global value cannot be serialized to the tag");
+ }
}
diff --git a/src/main/java/carpet/script/value/LazyListValue.java b/src/main/java/carpet/script/value/LazyListValue.java
index e57d7110..2edd637d 100644
--- a/src/main/java/carpet/script/value/LazyListValue.java
+++ b/src/main/java/carpet/script/value/LazyListValue.java
@@ -1,6 +1,8 @@
package carpet.script.value;
import carpet.script.exception.InternalExpressionException;
+import net.minecraft.nbt.StringTag;
+import net.minecraft.nbt.Tag;
import java.util.ArrayList;
import java.util.Iterator;
@@ -152,4 +154,11 @@ public abstract class LazyListValue extends AbstractListValue implements Iterato
{
return ("i"+getString()).hashCode();
}
+
+ @Override
+ public Tag toTag(boolean force)
+ {
+ if (!force) throw new NBTSerializableValue.IncompatibleTypeException(this);
+ return StringTag.of(getString());
+ }
}
diff --git a/src/main/java/carpet/script/value/ListValue.java b/src/main/java/carpet/script/value/ListValue.java
index e5307ae1..4aca8e41 100644
--- a/src/main/java/carpet/script/value/ListValue.java
+++ b/src/main/java/carpet/script/value/ListValue.java
@@ -3,13 +3,23 @@ package carpet.script.value;
import carpet.script.LazyValue;
import carpet.script.exception.InternalExpressionException;
import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.AbstractNumberTag;
+import net.minecraft.nbt.CompoundTag;
+import net.minecraft.nbt.DoubleTag;
+import net.minecraft.nbt.IntTag;
+import net.minecraft.nbt.ListTag;
+import net.minecraft.nbt.LongTag;
+import net.minecraft.nbt.StringTag;
+import net.minecraft.nbt.Tag;
import net.minecraft.util.registry.Registry;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.EnumSet;
import java.util.Iterator;
import java.util.List;
+import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
@@ -454,4 +464,65 @@ public class ListValue extends AbstractListValue implements ContainerValueInterf
{
return items.hashCode();
}
+
+ private enum TagTypeCompat
+ {
+ INT,
+ LONG,
+ DBL,
+ LIST,
+ MAP,
+ STRING;
+ private static TagTypeCompat getType(Tag tag)
+ {
+ if (tag instanceof IntTag) return INT;
+ if (tag instanceof LongTag) return LONG;
+ if (tag instanceof DoubleTag) return DBL;
+ if (tag instanceof ListTag) return LIST;
+ if (tag instanceof CompoundTag) return MAP;
+ return STRING;
+ }
+ }
+
+
+ @Override
+ public Tag toTag(boolean force)
+ {
+ int argSize = items.size();
+ if (argSize == 0) return new ListTag();
+ ListTag tag = new ListTag();
+ if (argSize ==1)
+ {
+ tag.add(items.get(0).toTag(force));
+ return tag;
+ }
+ // figuring out the types
+ List tags= new ArrayList<>();
+ items.forEach(v -> tags.add(v.toTag(force)));
+ Set cases = EnumSet.noneOf(TagTypeCompat.class);
+ tags.forEach(t -> cases.add(TagTypeCompat.getType(t)));
+ if (cases.size()==1) // well, one type of items
+ {
+ tag.addAll(tags);
+ return tag;
+ }
+ if (cases.contains(TagTypeCompat.LIST)
+ || cases.contains(TagTypeCompat.MAP)
+ || cases.contains(TagTypeCompat.STRING)) // incompatible types
+ {
+ if (!force) throw new NBTSerializableValue.IncompatibleTypeException(this);
+ tags.forEach(t -> tag.add(StringTag.of(t.asString())));
+ return tag;
+ }
+ // only numbers / mixed types
+ if (cases.contains(TagTypeCompat.DBL))
+ {
+ tags.forEach(t -> tag.add(DoubleTag.of(((AbstractNumberTag)t).getDouble())));
+ }
+ else
+ {
+ tags.forEach(t -> tag.add(LongTag.of(((AbstractNumberTag)t).getLong())));
+ }
+ return tag;
+ }
}
diff --git a/src/main/java/carpet/script/value/MapValue.java b/src/main/java/carpet/script/value/MapValue.java
index 61965e4e..f6332b4d 100644
--- a/src/main/java/carpet/script/value/MapValue.java
+++ b/src/main/java/carpet/script/value/MapValue.java
@@ -1,6 +1,8 @@
package carpet.script.value;
import carpet.script.exception.InternalExpressionException;
+import net.minecraft.nbt.CompoundTag;
+import net.minecraft.nbt.Tag;
import java.util.ArrayList;
import java.util.HashMap;
@@ -226,4 +228,16 @@ public class MapValue extends AbstractListValue implements ContainerValueInterfa
{
return map.hashCode();
}
+
+ @Override
+ public Tag toTag(boolean force)
+ {
+ CompoundTag tag = new CompoundTag() ;
+ map.forEach( (k, v) ->
+ {
+ if (!force && !(k instanceof StringValue)) throw new NBTSerializableValue.IncompatibleTypeException(k);
+ tag.put(k.getString(), v.toTag(force));
+ });
+ return tag;
+ }
}
diff --git a/src/main/java/carpet/script/value/NBTSerializableValue.java b/src/main/java/carpet/script/value/NBTSerializableValue.java
index fcf2009f..fe6b9473 100644
--- a/src/main/java/carpet/script/value/NBTSerializableValue.java
+++ b/src/main/java/carpet/script/value/NBTSerializableValue.java
@@ -638,4 +638,23 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa
{
return "nbt";
}
+
+
+ @Override
+ public Tag toTag(boolean force)
+ {
+ if (!force) throw new NBTSerializableValue.IncompatibleTypeException(this);
+ ensureOwnership();
+ return getTag();
+ }
+
+ public static class IncompatibleTypeException extends RuntimeException
+ {
+ private IncompatibleTypeException() {}
+ public Value val;
+ public IncompatibleTypeException(Value val)
+ {
+ this.val = val;
+ }
+ };
}
diff --git a/src/main/java/carpet/script/value/NullValue.java b/src/main/java/carpet/script/value/NullValue.java
index eeef2f0d..18ffcec1 100644
--- a/src/main/java/carpet/script/value/NullValue.java
+++ b/src/main/java/carpet/script/value/NullValue.java
@@ -1,5 +1,8 @@
package carpet.script.value;
+import net.minecraft.nbt.StringTag;
+import net.minecraft.nbt.Tag;
+
public class NullValue extends NumericValue // TODO check nonsingleton code
{
@Override
@@ -50,4 +53,11 @@ public class NullValue extends NumericValue // TODO check nonsingleton code
{
return 0;
}
+
+ @Override
+ public Tag toTag(boolean force)
+ {
+ if (!force) throw new NBTSerializableValue.IncompatibleTypeException(this);
+ return StringTag.of("null");
+ }
}
diff --git a/src/main/java/carpet/script/value/NumericValue.java b/src/main/java/carpet/script/value/NumericValue.java
index 2ae575fb..482d9fab 100644
--- a/src/main/java/carpet/script/value/NumericValue.java
+++ b/src/main/java/carpet/script/value/NumericValue.java
@@ -1,6 +1,10 @@
package carpet.script.value;
import carpet.script.exception.InternalExpressionException;
+import net.minecraft.nbt.DoubleTag;
+import net.minecraft.nbt.IntTag;
+import net.minecraft.nbt.LongTag;
+import net.minecraft.nbt.Tag;
import org.apache.commons.lang3.StringUtils;
import java.math.BigDecimal;
@@ -204,4 +208,20 @@ public class NumericValue extends Value
{
return (int)getLong();
}
+
+ @Override
+ public Tag toTag(boolean force)
+ {
+ long longValue = getLong();
+ if (value == (double)longValue)
+ {
+ if (abs(value) < Integer.MAX_VALUE-2)
+ return IntTag.of((int)longValue);
+ return LongTag.of(getLong());
+ }
+ else
+ {
+ return DoubleTag.of(value);
+ }
+ }
}
diff --git a/src/main/java/carpet/script/value/StringValue.java b/src/main/java/carpet/script/value/StringValue.java
index eba0cd16..c494865d 100644
--- a/src/main/java/carpet/script/value/StringValue.java
+++ b/src/main/java/carpet/script/value/StringValue.java
@@ -1,5 +1,8 @@
package carpet.script.value;
+import net.minecraft.nbt.StringTag;
+import net.minecraft.nbt.Tag;
+
public class StringValue extends Value
{
private String str;
@@ -30,4 +33,10 @@ public class StringValue extends Value
{
return "string";
}
+
+ @Override
+ public Tag toTag(boolean force)
+ {
+ return StringTag.of(str);
+ }
}
diff --git a/src/main/java/carpet/script/value/ThreadValue.java b/src/main/java/carpet/script/value/ThreadValue.java
index 98ac7623..e4ee5d50 100644
--- a/src/main/java/carpet/script/value/ThreadValue.java
+++ b/src/main/java/carpet/script/value/ThreadValue.java
@@ -6,6 +6,7 @@ import carpet.script.LazyValue;
import carpet.script.Tokenizer;
import carpet.script.exception.ExpressionException;
import carpet.script.exception.InternalExpressionException;
+import net.minecraft.nbt.Tag;
import java.util.HashMap;
import java.util.List;
@@ -144,4 +145,10 @@ public class ThreadValue extends Value
locks.clear();
}
+ @Override
+ public Tag toTag(boolean force)
+ {
+ if (!force) throw new NBTSerializableValue.IncompatibleTypeException(this);
+ return getValue().toTag(true);
+ }
}
diff --git a/src/main/java/carpet/script/value/Value.java b/src/main/java/carpet/script/value/Value.java
index f97fb8d5..82bdd664 100644
--- a/src/main/java/carpet/script/value/Value.java
+++ b/src/main/java/carpet/script/value/Value.java
@@ -1,6 +1,7 @@
package carpet.script.value;
import carpet.script.exception.InternalExpressionException;
+import net.minecraft.nbt.Tag;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@@ -203,4 +204,6 @@ public abstract class Value implements Comparable, Cloneable
throw new InternalExpressionException("Cannot make a copy of value: "+this);
}
}
+
+ public abstract Tag toTag(boolean force);
}