diff --git a/docs/scarpet/Full.md b/docs/scarpet/Full.md
index 0c427544..4fa89e32 100644
--- a/docs/scarpet/Full.md
+++ b/docs/scarpet/Full.md
@@ -1655,11 +1655,19 @@ join('-', 'foo', 'bar') => foo-bar
Splits a string under `expr` by `delim` which can be a regular expression. If no delimiter is specified, it splits
by characters.
+If `expr` is a list, it will split the list into multiple sublists by the element (s) which equal `delim`, or which equal the empty string
+in case no delimiter is specified.
+
+Splitting a `null` value will return an empty list.
+
split('foo') => [f, o, o]
split('','foo') => [f, o, o]
split('.','foo.bar') => []
split('\\.','foo.bar') => [foo, bar]
+split(1,[2,5,1,2,3,1,5,6]) => [[2,5],[2,3],[5,6]]
+split(1,[1,2,3,1,4,5,1] => [[], [2,3], [4,5], []]
+split(null) => []
### `slice(expr, from, to?)`
@@ -3138,6 +3146,10 @@ Boolean, true if the entity is silent.
Boolean, true if the entity is affected by gravity, like most entities are.
+### `query(e, 'invulnerable')`
+
+Boolean, true if the entity is invulnerable.
+
### `query(e, 'immune_to_fire')`
Boolean, true if the entity is immune to fire.
@@ -3618,6 +3630,10 @@ Silences or unsilences the entity.
Toggles gravity for the entity.
+### `modify(e, 'invulnerable', boolean)`
+
+Toggles invulnerability for the entity.
+
### `modify(e, 'fire', ticks)`
Will set entity on fire for `ticks` ticks. Set to 0 to extinguish.
@@ -5006,6 +5022,12 @@ Available options in the scarpet app space:
Scarpet related properties
* `scarpet_version` - returns the version of the carpet your scarpet comes with.
+
+## NBT Storage
+
+### `nbt_storage()`, `nbt_storage(key)`, `nbt_storage(key, nbt)`
+Displays or modifies individual storage NBT tags. With no arguments, returns the list of current NBT storages. With specified `key`, returns the `nbt` associated with current `key`, or `null` if storage does not exist. With specified `key` and `nbt`, sets a new `nbt` value, returning previous value associated with the `key`.
+NOTE: This NBT storage is shared with all vanilla datapacks and scripts of the entire server and is persistent between restarts and reloads. You can also access this NBT storage with vanilla `/data storage ...` command.
# `/script run` command
Primary way to input commands. The command executes in the context, position, and dimension of the executing player,
diff --git a/docs/scarpet/api/Auxiliary.md b/docs/scarpet/api/Auxiliary.md
index 399d5fc1..cede1808 100644
--- a/docs/scarpet/api/Auxiliary.md
+++ b/docs/scarpet/api/Auxiliary.md
@@ -540,3 +540,9 @@ Available options in the scarpet app space:
Scarpet related properties
* `scarpet_version` - returns the version of the carpet your scarpet comes with.
+
+## NBT Storage
+
+### `nbt_storage()`, `nbt_storage(key)`, `nbt_storage(key, nbt)`
+Displays or modifies individual storage NBT tags. With no arguments, returns the list of current NBT storages. With specified `key`, returns the `nbt` associated with current `key`, or `null` if storage does not exist. With specified `key` and `nbt`, sets a new `nbt` value, returning previous value associated with the `key`.
+NOTE: This NBT storage is shared with all vanilla datapacks and scripts of the entire server and is persistent between restarts and reloads. You can also access this NBT storage with vanilla `/data storage ...` command.
diff --git a/docs/scarpet/language/Containers.md b/docs/scarpet/language/Containers.md
index f017adaf..07d6993a 100644
--- a/docs/scarpet/language/Containers.md
+++ b/docs/scarpet/language/Containers.md
@@ -160,11 +160,19 @@ join('-', 'foo', 'bar') => foo-bar
Splits a string under `expr` by `delim` which can be a regular expression. If no delimiter is specified, it splits
by characters.
+If `expr` is a list, it will split the list into multiple sublists by the element (s) which equal `delim`, or which equal the empty string
+in case no delimiter is specified.
+
+Splitting a `null` value will return an empty list.
+
split('foo') => [f, o, o]
split('','foo') => [f, o, o]
split('.','foo.bar') => []
split('\\.','foo.bar') => [foo, bar]
+split(1,[2,5,1,2,3,1,5,6]) => [[2,5],[2,3],[5,6]]
+split(1,[1,2,3,1,4,5,1] => [[], [2,3], [4,5], []]
+split(null) => []
### `slice(expr, from, to?)`
diff --git a/docs/scarpet/resources/editors/idea/2.txt b/docs/scarpet/resources/editors/idea/2.txt
index 25f4e954..4186fa25 100644
--- a/docs/scarpet/resources/editors/idea/2.txt
+++ b/docs/scarpet/resources/editors/idea/2.txt
@@ -67,6 +67,7 @@ map_colour
material
modify
nbt
+nbt_storage
neighbours
opacity
parse_nbt
diff --git a/docs/scarpet/resources/editors/npp/scarpet.xml b/docs/scarpet/resources/editors/npp/scarpet.xml
index 83cceb54..eeb7ca64 100644
--- a/docs/scarpet/resources/editors/npp/scarpet.xml
+++ b/docs/scarpet/resources/editors/npp/scarpet.xml
@@ -86,6 +86,8 @@
load_app_data store_app_data read_file write_file delete_file list_files
escape_nbt parse_nbt encode_nbt tag_matches statistic
+
+ nbt_storage
euler pi null true false
x y z p
diff --git a/src/main/java/carpet/CarpetSettings.java b/src/main/java/carpet/CarpetSettings.java
index 9deebff5..8b58fa79 100644
--- a/src/main/java/carpet/CarpetSettings.java
+++ b/src/main/java/carpet/CarpetSettings.java
@@ -19,6 +19,7 @@ import net.minecraft.world.World;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
+import java.util.Iterator;
import java.util.Locale;
import static carpet.settings.RuleCategory.BUGFIX;
@@ -642,6 +643,58 @@ public class CarpetSettings
)
public static int spawnChunksSize = 11;
+ public static class LightBatchValidator extends Validator {
+ public static void applyLightBatchSizes()
+ {
+ Iterator iterator = CarpetServer.minecraft_server.getWorlds().iterator();
+
+ while (iterator.hasNext())
+ {
+ ServerWorld serverWorld = iterator.next();
+ serverWorld.getChunkManager().getLightingProvider().setTaskBatchSize(lightEngineMaxBatchSize);
+ }
+ }
+ @Override public Integer validate(ServerCommandSource source, ParsedRule currentRule, Integer newValue, String string) {
+ if (source == null) return newValue;
+ if (newValue < 0)
+ {
+ Messenger.m(source, "r light batch size has to be at least 0");
+ return null;
+ }
+ if (currentRule.get().intValue() == newValue.intValue())
+ {
+ //must been some startup thing
+ return newValue;
+ }
+ if (CarpetServer.minecraft_server == null) return newValue;
+
+ // Set the field before we apply.
+ try
+ {
+ currentRule.field.set(null, newValue.intValue());
+ }
+ catch (IllegalAccessException e)
+ {
+ Messenger.m(source, "r Unable to access setting for "+currentRule.name);
+ return null;
+ }
+
+ applyLightBatchSizes(); // Apply new settings
+
+ return newValue;
+ }
+ }
+
+ @Rule(
+ desc = "Changes maximum light tasks batch size",
+ extra = {"Allows for a higher light suppression tolerance", "setting it to 5 - Default limit defined by the game"},
+ category = {EXPERIMENTAL, OPTIMIZATION},
+ strict = false,
+ options = {"5", "50", "100", "200"},
+ validate = LightBatchValidator.class
+ )
+ public static int lightEngineMaxBatchSize = 5;
+
@Rule(desc = "Coral structures will grow with bonemeal from coral plants", category = FEATURE)
public static boolean renewableCoral = false;
diff --git a/src/main/java/carpet/mixins/MinecraftServer_coreMixin.java b/src/main/java/carpet/mixins/MinecraftServer_coreMixin.java
index e638ec63..0638d9b9 100644
--- a/src/main/java/carpet/mixins/MinecraftServer_coreMixin.java
+++ b/src/main/java/carpet/mixins/MinecraftServer_coreMixin.java
@@ -52,5 +52,7 @@ public abstract class MinecraftServer_coreMixin
{
if (CarpetSettings.spawnChunksSize != 11)
CarpetSettings.ChangeSpawnChunksValidator.changeSpawnSize(CarpetSettings.spawnChunksSize);
+
+ CarpetSettings.LightBatchValidator.applyLightBatchSizes();
}
}
diff --git a/src/main/java/carpet/script/api/Auxiliary.java b/src/main/java/carpet/script/api/Auxiliary.java
index cd1c145d..4672a0ae 100644
--- a/src/main/java/carpet/script/api/Auxiliary.java
+++ b/src/main/java/carpet/script/api/Auxiliary.java
@@ -36,12 +36,14 @@ import carpet.script.value.Value;
import carpet.script.value.ValueConversions;
import carpet.utils.Messenger;
import net.minecraft.block.BlockState;
+import net.minecraft.command.DataCommandStorage;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.entity.decoration.ArmorStandEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
+import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtHelper;
import net.minecraft.nbt.StringTag;
import net.minecraft.nbt.Tag;
@@ -86,6 +88,7 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import java.util.stream.Stream;
+import static carpet.script.value.NBTSerializableValue.nameFromRegistryId;
import static java.lang.Math.max;
import static java.lang.Math.min;
@@ -1022,6 +1025,29 @@ public class Auxiliary {
Value ret = new NumericValue(counts);
return (c_, t_) -> ret;
});
+
+ // nbt_storage()
+ // nbt_storage(key)
+ // nbt_storage(key, nbt)
+ expression.addLazyFunction("nbt_storage", -1, (c, t, lv) -> {
+ if (lv.size() > 2) throw new InternalExpressionException("'nbt_storage' requires 0, 1 or 2 arguments.");
+ CarpetContext cc = (CarpetContext) c;
+ DataCommandStorage storage = cc.s.getMinecraftServer().getDataCommandStorage();
+ if (lv.size() == 0) {
+ Value ret = ListValue.wrap(storage.getIds().map(i -> new StringValue(nameFromRegistryId(i))).collect(Collectors.toList()));
+ return (_c, _t) -> ret;
+ }
+ String key = lv.get(0).evalValue(c).getString();
+ CompoundTag old_nbt = storage.get(new Identifier(key));
+ if (lv.size() == 2) {
+ Value nbt = lv.get(1).evalValue(c);
+ NBTSerializableValue new_nbt = (nbt instanceof NBTSerializableValue) ? (NBTSerializableValue) nbt
+ : NBTSerializableValue.parseString(nbt.getString(), true);
+ storage.set(new Identifier(key), new_nbt.getCompoundTag());
+ }
+ if (old_nbt == null) return LazyValue.NULL;
+ return (_c, _t) -> new NBTSerializableValue(old_nbt);
+ });
}
private static Stat getStat(StatType type, Identifier id)
diff --git a/src/main/java/carpet/script/language/DataStructures.java b/src/main/java/carpet/script/language/DataStructures.java
index 95b18749..90d02d99 100644
--- a/src/main/java/carpet/script/language/DataStructures.java
+++ b/src/main/java/carpet/script/language/DataStructures.java
@@ -14,10 +14,8 @@ import carpet.script.value.StringValue;
import carpet.script.value.Value;
import java.util.ArrayList;
-import java.util.Arrays;
import java.util.Collections;
import java.util.List;
-import java.util.regex.PatternSyntaxException;
import java.util.stream.Collectors;
public class DataStructures {
@@ -54,30 +52,23 @@ public class DataStructures {
expression.addFunction("split", (lv) ->
{
- String delimiter;
- String hwat;
+ Value delimiter;
+ Value hwat;
if (lv.size() == 1)
{
- hwat = lv.get(0).getString();
- delimiter = "";
+ hwat = lv.get(0);
+ delimiter = StringValue.EMPTY;
}
else if (lv.size() == 2)
{
- delimiter = lv.get(0).getString();
- hwat = lv.get(1).getString();
+ delimiter = lv.get(0);
+ hwat = lv.get(1);
}
else
{
throw new InternalExpressionException("'split' takes 1 or 2 arguments");
}
- try
- {
- return ListValue.wrap(Arrays.stream(hwat.split(delimiter)).map(StringValue::new).collect(Collectors.toList()));
- }
- catch (PatternSyntaxException pse)
- {
- throw new InternalExpressionException("Incorrect pattern for 'split': "+pse.getMessage());
- }
+ return hwat.split(delimiter);
});
expression.addFunction("slice", (lv) ->
diff --git a/src/main/java/carpet/script/value/ListValue.java b/src/main/java/carpet/script/value/ListValue.java
index a036af56..c3b23438 100644
--- a/src/main/java/carpet/script/value/ListValue.java
+++ b/src/main/java/carpet/script/value/ListValue.java
@@ -347,6 +347,24 @@ public class ListValue extends AbstractListValue implements ContainerValueInterf
return new ListValue((Collection extends Value>) getItems().subList(from, to));
}
+ @Override
+ public Value split(Value delimiter) {
+ ListValue result = new ListValue();
+ int startIndex = 0;
+ int index = 0;
+ for (Value val : this.items)
+ {
+ index++;
+ if (val.equals(delimiter))
+ {
+ result.items.add(ListValue.wrap(this.items.subList(startIndex, index-1)));
+ startIndex = index;
+ }
+ }
+ result.items.add(ListValue.wrap(this.items.subList(startIndex, length())));
+ return result;
+ }
+
@Override
public double readDoubleNumber()
{
diff --git a/src/main/java/carpet/script/value/MapValue.java b/src/main/java/carpet/script/value/MapValue.java
index abefe087..81375bdc 100644
--- a/src/main/java/carpet/script/value/MapValue.java
+++ b/src/main/java/carpet/script/value/MapValue.java
@@ -203,6 +203,11 @@ public class MapValue extends AbstractListValue implements ContainerValueInterfa
throw new InternalExpressionException("Cannot slice a map value");
}
+ @Override
+ public Value split(Value delimiter) {
+ throw new InternalExpressionException("Cannot split a map value");
+ }
+
@Override
public double readDoubleNumber()
{
diff --git a/src/main/java/carpet/script/value/NullValue.java b/src/main/java/carpet/script/value/NullValue.java
index 95df97c9..e2a1f3a7 100644
--- a/src/main/java/carpet/script/value/NullValue.java
+++ b/src/main/java/carpet/script/value/NullValue.java
@@ -1,5 +1,7 @@
package carpet.script.value;
+import java.util.ArrayList;
+
import com.google.gson.JsonElement;
import com.google.gson.JsonNull;
import net.minecraft.nbt.StringTag;
@@ -44,6 +46,11 @@ public class NullValue extends NumericValue // TODO check nonsingleton code
return o instanceof NullValue ? 0 : -1;
}
+ @Override
+ public Value in(Value value) {
+ return Value.NULL;
+ }
+
@Override
public String getTypeString()
{
@@ -63,6 +70,11 @@ public class NullValue extends NumericValue // TODO check nonsingleton code
return StringTag.of("null");
}
+ @Override
+ public Value split(Value delimiter) {
+ return ListValue.wrap(new ArrayList());
+ }
+
@Override
public JsonElement toJson()
{
diff --git a/src/main/java/carpet/script/value/Value.java b/src/main/java/carpet/script/value/Value.java
index 8f79f462..b9ac965c 100644
--- a/src/main/java/carpet/script/value/Value.java
+++ b/src/main/java/carpet/script/value/Value.java
@@ -6,10 +6,12 @@ import com.google.gson.JsonPrimitive;
import net.minecraft.nbt.Tag;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
+import java.util.stream.Collectors;
public abstract class Value implements Comparable, Cloneable
{
@@ -188,6 +190,19 @@ public abstract class Value implements Comparable, Cloneable
if (from > to) return StringValue.EMPTY;
return new StringValue(value.substring(from, to));
}
+
+ public Value split(Value delimiter)
+ {
+ try
+ {
+ return ListValue.wrap(Arrays.stream(getString().split(delimiter.getString())).map(StringValue::new).collect(Collectors.toList()));
+ }
+ catch (PatternSyntaxException pse)
+ {
+ throw new InternalExpressionException("Incorrect pattern for 'split': "+pse.getMessage());
+ }
+ }
+
public double readDoubleNumber()
{
String s = getString();