refactor breaking up API and language defs

This commit is contained in:
gnembon committed 2020-09-09 15:37:03 -03:00
1 parent 34d548523d
commit 59575ec607
22 files changed
+5596 -5282

No files matched your search

File diff suppressed because it is too large. Load diff
@@ -1,6 +1,7 @@
package carpet.script;
import carpet.CarpetServer;
import carpet.script.api.Auxiliary;
import carpet.script.bundled.Module;
import carpet.script.exception.CarpetExpressionException;
import carpet.script.exception.ExpressionException;
@@ -26,7 +27,6 @@ import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import static java.lang.Math.max;
@@ -360,7 +360,7 @@ public class CarpetScriptHost extends ScriptHost
}
});
String markerName = CarpetExpression.MARKER_STRING+"_"+((getName()==null)?"":getName());
String markerName = Auxiliary.MARKER_STRING+"_"+((getName()==null)?"":getName());
for (ServerWorld world : scriptServer.server.getWorlds())
{
for (Entity e : world.getEntitiesByType(EntityType.ARMOR_STAND, (as) -> as.getScoreboardTags().contains(markerName)))
+3 -3
View File
@@ -24,12 +24,12 @@ public class Context
public final ScriptHost host;
Context(ScriptHost host)
public Context(ScriptHost host)
{
this.host = host;
}
LazyValue getVariable(String name)
public LazyValue getVariable(String name)
{
return variables.get(name);
}
@@ -39,7 +39,7 @@ public class Context
variables.put(name, lv);
}
void delVariable(String variable)
public void delVariable(String variable)
{
variables.remove(variable);
}
File diff suppressed because it is too large. Load diff
+4 -3
View File
@@ -3,6 +3,7 @@ package carpet.script;
import carpet.script.bundled.Module;
import carpet.script.exception.ExpressionException;
import carpet.script.exception.InternalExpressionException;
import carpet.script.language.Arithmetic;
import carpet.script.value.FunctionValue;
import carpet.script.value.Value;
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
@@ -20,7 +21,7 @@ import java.util.stream.Stream;
public abstract class ScriptHost
{
public static Map<Value, Value> systemGlobals = new ConcurrentHashMap<>();
private static Map<Long, Random> randomizers = new Long2ObjectOpenHashMap<>();
private static final Map<Long, Random> randomizers = new Long2ObjectOpenHashMap<>();
public Random getRandom(long aLong)
{
@@ -99,8 +100,8 @@ public abstract class ScriptHost
void initializeModuleGlobals(ModuleData md)
{
md.globalVariables.put("euler", (c, t) -> Expression.euler);
md.globalVariables.put("pi", (c, t) -> Expression.PI);
md.globalVariables.put("euler", (c, t) -> Arithmetic.euler);
md.globalVariables.put("pi", (c, t) -> Arithmetic.PI);
md.globalVariables.put("null", (c, t) -> Value.NULL);
md.globalVariables.put("true", (c, t) -> Value.TRUE);
md.globalVariables.put("false", (c, t) -> Value.FALSE);
+30
View File
@@ -0,0 +1,30 @@
package carpet.script.api;
import carpet.script.CarpetScriptHost;
import carpet.script.Expression;
import carpet.script.value.MapValue;
import carpet.script.value.NumericValue;
import carpet.script.value.StringValue;
import carpet.script.value.Value;
import java.util.stream.Collectors;
public class Apps {
public static void apply(Expression expression)
{
//todo doc and test all
expression.addLazyFunction("loaded_apps", 0, (c, t, lv) ->
{
// return a set of all loaded apps
Value ret = new MapValue(((CarpetScriptHost) c.host).getScriptServer().modules.keySet().stream().map(StringValue::new).collect(Collectors.toSet()));
return (cc, tt) -> ret;
});
expression.addLazyFunction("is_app_loaded", 1, (c, t, lv) ->
{
Value ret = new NumericValue(((CarpetScriptHost) c.host).getScriptServer().modules.containsKey(lv.get(0).evalValue(c).getString()));
return (cc, tt) -> ret;
});
}
}
@@ -0,0 +1,936 @@
package carpet.script.api;
import carpet.CarpetServer;
import carpet.CarpetSettings;
import carpet.fakes.MinecraftServerInterface;
import carpet.fakes.StatTypeInterface;
import carpet.fakes.ThreadedAnvilChunkStorageInterface;
import carpet.helpers.FeatureGenerator;
import carpet.script.CarpetContext;
import carpet.script.CarpetScriptHost;
import carpet.script.Context;
import carpet.script.Expression;
import carpet.script.LazyValue;
import carpet.script.argument.BlockArgument;
import carpet.script.argument.FunctionArgument;
import carpet.script.argument.Vector3Argument;
import carpet.script.exception.ExitStatement;
import carpet.script.exception.ExpressionException;
import carpet.script.exception.InternalExpressionException;
import carpet.script.utils.ShapeDispatcher;
import carpet.script.value.BlockValue;
import carpet.script.value.EntityValue;
import carpet.script.value.FormattedTextValue;
import carpet.script.value.ListValue;
import carpet.script.value.NBTSerializableValue;
import carpet.script.value.NullValue;
import carpet.script.value.NumericValue;
import carpet.script.value.StringValue;
import carpet.script.value.ThreadValue;
import carpet.script.value.Value;
import carpet.script.value.ValueConversions;
import carpet.utils.Messenger;
import net.minecraft.block.BlockState;
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.StringTag;
import net.minecraft.nbt.Tag;
import net.minecraft.network.packet.s2c.play.PlaySoundIdS2CPacket;
import net.minecraft.particle.ParticleEffect;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.sound.SoundCategory;
import net.minecraft.stat.Stat;
import net.minecraft.stat.StatType;
import net.minecraft.text.LiteralText;
import net.minecraft.util.Identifier;
import net.minecraft.util.InvalidIdentifierException;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.util.math.EulerAngle;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.registry.Registry;
import net.minecraft.util.registry.RegistryKey;
import net.minecraft.world.World;
import org.apache.commons.lang3.tuple.Pair;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.concurrent.CompletionException;
import java.util.stream.Collectors;
import static java.lang.Math.max;
import static java.lang.Math.min;
public class Auxiliary {
public static final String MARKER_STRING = "__scarpet_marker";
private static final Map<String, SoundCategory> mixerMap = Arrays.stream(SoundCategory.values()).collect(Collectors.toMap(SoundCategory::getName, k -> k));
public static String recognizeResource(Value value)
{
String origfile = value.getString();
String file = origfile.toLowerCase(Locale.ROOT).replaceAll("[^A-Za-z0-9\\-+_/]", "");
file = Arrays.stream(file.split("/+")).filter(s -> !s.isEmpty()).collect(Collectors.joining("/"));
if (file.isEmpty())
{
throw new InternalExpressionException("Cannot use "+origfile+" as resource name - must have some letters and numbers");
}
return file;
}
public static void apply(Expression expression)
{
expression.addLazyFunction("sound", -1, (c, t, lv) -> {
CarpetContext cc = (CarpetContext)c;
Identifier soundName = new Identifier(lv.get(0).evalValue(c).getString());
Vector3Argument locator = Vector3Argument.findIn(cc, lv, 1);
if (Registry.SOUND_EVENT.get(soundName) == null)
throw new InternalExpressionException("No such sound: "+soundName.getPath());
float volume = 1.0F;
float pitch = 1.0F;
SoundCategory mixer = SoundCategory.MASTER;
if (lv.size() > 0+locator.offset)
{
volume = (float) NumericValue.asNumber(lv.get(0+locator.offset).evalValue(c)).getDouble();
if (lv.size() > 1+locator.offset)
{
pitch = (float) NumericValue.asNumber(lv.get(1+locator.offset).evalValue(c)).getDouble();
if (lv.size() > 2+locator.offset)
{
String mixerName = lv.get(2+locator.offset).evalValue(c).getString();
mixer = mixerMap.get(mixerName.toLowerCase(Locale.ROOT));
if (mixer == null) throw new InternalExpressionException(mixerName +" is not a valid mixer name");
}
}
}
Vec3d vec = locator.vec;
double d0 = Math.pow(volume > 1.0F ? (double)(volume * 16.0F) : 16.0D, 2.0D);
int count = 0;
for (ServerPlayerEntity player : cc.s.getWorld().getPlayers( (p) -> p.squaredDistanceTo(vec) < d0))
{
count++;
player.networkHandler.sendPacket(new PlaySoundIdS2CPacket(soundName, mixer, vec, volume, pitch));
}
int totalPlayed = count;
return (_c, _t) -> new NumericValue(totalPlayed);
});
expression.addLazyFunction("particle", -1, (c, t, lv) ->
{
CarpetContext cc = (CarpetContext)c;
MinecraftServer ms = cc.s.getMinecraftServer();
ServerWorld world = cc.s.getWorld();
Vector3Argument locator = Vector3Argument.findIn(cc, lv, 1);
String particleName = lv.get(0).evalValue(c).getString();
int count = 10;
double speed = 0;
float spread = 0.5f;
ServerPlayerEntity player = null;
if (lv.size() > locator.offset)
{
count = (int) NumericValue.asNumber(lv.get(locator.offset).evalValue(c)).getLong();
if (lv.size() > 1+locator.offset)
{
spread = (float) NumericValue.asNumber(lv.get(1+locator.offset).evalValue(c)).getDouble();
if (lv.size() > 2+locator.offset)
{
speed = NumericValue.asNumber(lv.get(2 + locator.offset).evalValue(c)).getDouble();
if (lv.size() > 3 + locator.offset) // should accept entity as well as long as it is player
{
player = ms.getPlayerManager().getPlayer(lv.get(3 + locator.offset).evalValue(c).getString());
}
}
}
}
ParticleEffect particle = ShapeDispatcher.getParticleData(particleName);
Vec3d vec = locator.vec;
if (player == null)
{
for (PlayerEntity p : (world.getPlayers()))
{
world.spawnParticles((ServerPlayerEntity)p, particle, true, vec.x, vec.y, vec.z, count,
spread, spread, spread, speed);
}
}
else
{
world.spawnParticles(player,
particle, true, vec.x, vec.y, vec.z, count,
spread, spread, spread, speed);
}
return (c_, t_) -> Value.TRUE;
});
expression.addLazyFunction("particle_line", -1, (c, t, lv) ->
{
CarpetContext cc = (CarpetContext)c;
ServerWorld world = cc.s.getWorld();
String particleName = lv.get(0).evalValue(c).getString();
ParticleEffect particle = ShapeDispatcher.getParticleData(particleName);
Vector3Argument pos1 = Vector3Argument.findIn(cc, lv, 1);
Vector3Argument pos2 = Vector3Argument.findIn(cc, lv, pos1.offset);
double density = 1.0;
ServerPlayerEntity player = null;
if (lv.size() > pos2.offset+0 )
{
density = NumericValue.asNumber(lv.get(pos2.offset+0).evalValue(c)).getDouble();
if (density <= 0)
{
throw new InternalExpressionException("Particle density should be positive");
}
if (lv.size() > pos2.offset+1)
{
Value playerValue = lv.get(pos2.offset+1).evalValue(c);
if (playerValue instanceof EntityValue)
{
Entity e = ((EntityValue) playerValue).getEntity();
if (!(e instanceof ServerPlayerEntity)) throw new InternalExpressionException("'particle_line' player argument has to be a player");
player = (ServerPlayerEntity) e;
}
else
{
player = cc.s.getMinecraftServer().getPlayerManager().getPlayer(playerValue.getString());
}
}
}
Value retval = new NumericValue(ShapeDispatcher.drawParticleLine(
(player == null)?world.getPlayers(): Collections.singletonList(player),
particle, pos1.vec, pos2.vec, density));
return (c_, t_) -> retval;
});
expression.addLazyFunction("particle_box", -1, (c, t, lv) ->
{
CarpetContext cc = (CarpetContext)c;
ServerWorld world = cc.s.getWorld();
String particleName = lv.get(0).evalValue(c).getString();
ParticleEffect particle = ShapeDispatcher.getParticleData(particleName);
Vector3Argument pos1 = Vector3Argument.findIn(cc, lv, 1);
Vector3Argument pos2 = Vector3Argument.findIn(cc, lv, pos1.offset);
double density = 1.0;
ServerPlayerEntity player = null;
if (lv.size() > pos2.offset+0 )
{
density = NumericValue.asNumber(lv.get(pos2.offset+0).evalValue(c)).getDouble();
if (density <= 0)
{
throw new InternalExpressionException("Particle density should be positive");
}
if (lv.size() > pos2.offset+1)
{
Value playerValue = lv.get(pos2.offset+1).evalValue(c);
if (playerValue instanceof EntityValue)
{
Entity e = ((EntityValue) playerValue).getEntity();
if (!(e instanceof ServerPlayerEntity)) throw new InternalExpressionException("'particle_box' player argument has to be a player");
player = (ServerPlayerEntity) e;
}
else
{
player = cc.s.getMinecraftServer().getPlayerManager().getPlayer(playerValue.getString());
}
}
}
Vec3d a = pos1.vec;
Vec3d b = pos2.vec;
Vec3d from = new Vec3d(min(a.x, b.x), min(a.y, b.y), min(a.z, b.z));
Vec3d to = new Vec3d(max(a.x, b.x), max(a.y, b.y), max(a.z, b.z));
int particleCount = ShapeDispatcher.Box.particleMesh(
player==null?world.getPlayers():Collections.singletonList(player),
particle, density, from, to
);
return (c_, t_) -> new NumericValue(particleCount);
});
// deprecated
expression.alias("particle_rect", "particle_box");
expression.addLazyFunction("draw_shape", -1, (c, t, lv) ->
{
CarpetContext cc = (CarpetContext)c;
ServerPlayerEntity player[] = {null};
List<Pair<ShapeDispatcher.ExpiringShape, Map<String,Value>>> shapes = new ArrayList<>();
if (lv.size() == 1) // bulk
{
Value specLoad = lv.get(0).evalValue(c);
if (!(specLoad instanceof ListValue)) throw new InternalExpressionException("In bulk mode - shapes need to be provided as a list of shape specs");
for (Value list : ((ListValue) specLoad).getItems())
{
if (!(list instanceof ListValue)) throw new InternalExpressionException("In bulk mode - shapes need to be provided as a list of shape specs");
shapes.add( ShapeDispatcher.fromFunctionArgs(cc, ((ListValue) list).getItems(), player));
}
}
else
{
List<Value> params = new ArrayList<>();
for (LazyValue v : lv) params.add(v.evalValue(c));
shapes.add(ShapeDispatcher.fromFunctionArgs(cc, params, player));
}
ShapeDispatcher.sendShape(
(player[0]==null)?cc.s.getWorld().getPlayers():Collections.singletonList(player[0]),
shapes
);
return LazyValue.TRUE;
});
expression.addLazyFunction("create_marker", -1, (c, t, lv) ->{
CarpetContext cc = (CarpetContext)c;
BlockState targetBlock = null;
Vector3Argument pointLocator;
boolean interactable = true;
String name;
try
{
Value nameValue = lv.get(0).evalValue(c);
name = nameValue instanceof NullValue ? "" : nameValue.getString();
pointLocator = Vector3Argument.findIn(cc, lv, 1, true);
if (lv.size()>pointLocator.offset)
{
BlockArgument blockLocator = BlockArgument.findIn(cc, lv, pointLocator.offset, true, true);
if (blockLocator.block != null) targetBlock = blockLocator.block.getBlockState();
if (lv.size() > blockLocator.offset)
{
interactable = lv.get(blockLocator.offset).evalValue(c, Context.BOOLEAN).getBoolean();
}
}
}
catch (IndexOutOfBoundsException e)
{
throw new InternalExpressionException("'create_marker' requires a name and three coordinates, with optional direction, and optional block on its head");
}
ArmorStandEntity armorstand = new ArmorStandEntity(EntityType.ARMOR_STAND, cc.s.getWorld());
double yoffset;
if (targetBlock == null && name.isEmpty())
{
yoffset = 0.0;
}
else if (!interactable && targetBlock == null)
{
yoffset = -0.41;
}
else
{
if (targetBlock==null)
{
yoffset = -armorstand.getHeight()-0.41;
}
else
{
yoffset = -armorstand.getHeight()+0.3;
}
}
armorstand.refreshPositionAndAngles(
pointLocator.vec.x,
//pointLocator.vec.y - ((!interactable && targetBlock == null)?0.41f:((targetBlock==null)?(armorstand.getHeight()+0.41):(armorstand.getHeight()-0.3))),
pointLocator.vec.y + yoffset,
pointLocator.vec.z,
(float)pointLocator.yaw,
(float) pointLocator.pitch
);
armorstand.addScoreboardTag(MARKER_STRING+"_"+((cc.host.getName()==null)?"":cc.host.getName()));
armorstand.addScoreboardTag(MARKER_STRING);
if (targetBlock != null)
armorstand.equipStack(EquipmentSlot.HEAD, new ItemStack(targetBlock.getBlock().asItem()));
if (!name.isEmpty())
{
armorstand.setCustomName(new LiteralText(name));
armorstand.setCustomNameVisible(true);
}
armorstand.setHeadRotation(new EulerAngle((int)pointLocator.pitch,0,0));
armorstand.setNoGravity(true);
armorstand.setInvisible(true);
armorstand.setInvulnerable(true);
armorstand.getDataTracker().set(ArmorStandEntity.ARMOR_STAND_FLAGS, (byte)(interactable?8 : 16|8));
cc.s.getWorld().spawnEntity(armorstand);
EntityValue result = new EntityValue(armorstand);
return (_c, _t) -> result;
});
expression.addLazyFunction("remove_all_markers", 0, (c, t, lv) -> {
CarpetContext cc = (CarpetContext)c;
int total = 0;
String markerName = MARKER_STRING+"_"+((cc.host.getName()==null)?"":cc.host.getName());
for (Entity e : cc.s.getWorld().getEntitiesByType(EntityType.ARMOR_STAND, (as) -> as.getScoreboardTags().contains(markerName)))
{
total ++;
e.remove();
}
int finalTotal = total;
return (_cc, _tt) -> new NumericValue(finalTotal);
});
expression.addLazyFunction("nbt", 1, (c, t, lv) -> {
Value ret = NBTSerializableValue.fromValue(lv.get(0).evalValue(c));
return (cc, tt) -> ret;
});
expression.addLazyFunction("escape_nbt", 1, (c, t, lv) -> {
Value v = lv.get(0).evalValue(c);
String string = v.getString();
Value ret = new StringValue(StringTag.escape(string));
return (cc, tt) -> ret;
});
expression.addLazyFunction("parse_nbt", 1, (c, t, lv) -> {
Value v = lv.get(0).evalValue(c);
if (v instanceof NBTSerializableValue)
{
Value parsed = ((NBTSerializableValue) v).toValue();
return (cc, tt) -> parsed;
}
NBTSerializableValue ret = NBTSerializableValue.parseString(v.getString());
if (ret == null)
return LazyValue.NULL;
Value parsed = ret.toValue();
return (cc, tt) -> parsed;
});
expression.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
expression.addLazyFunction("print", -1, (c, t, lv) ->
{
if (lv.size() == 0 || lv.size() > 2) throw new InternalExpressionException("'print' takes one or two arguments");
ServerCommandSource s = ((CarpetContext)c).s;
Value res = lv.get(0).evalValue(c);
if (lv.size() == 2)
{
ServerPlayerEntity player = EntityValue.getPlayerByValue(s.getMinecraftServer(), res);
s = player.getCommandSource();
res = lv.get(1).evalValue(c);
}
if (res instanceof FormattedTextValue)
{
s.sendFeedback(((FormattedTextValue) res).getText(), false);
}
else
{
if (s.getEntity() instanceof PlayerEntity)
{
Messenger.m((PlayerEntity) s.getEntity(), "w " + res.getString());
}
else
{
Messenger.m(s, "w " + res.getString());
}
}
Value finalRes = res;
return (_c, _t) -> finalRes; // pass through for variables
});
expression.addLazyFunction("format", -1, (c, t, lv) -> {
if (lv.size() == 0 ) throw new InternalExpressionException("'format' requires at least one component");
List<Value> values = lv.stream().map(lazy -> lazy.evalValue(c)).collect(Collectors.toList());
if (values.get(0) instanceof ListValue && values.size()==1)
values = ((ListValue) values.get(0)).getItems();
Value ret = new FormattedTextValue(Messenger.c(values.stream().map(Value::getString).toArray()));
return (cc, tt) -> ret;
});
//"overidden" native call to cancel if on main thread
expression.addLazyFunction("task_join", 1, (c, t, lv) -> {
if (((CarpetContext)c).s.getMinecraftServer().isOnThread())
throw new InternalExpressionException("'task_join' cannot be called from main thread to avoid deadlocks");
Value v = lv.get(0).evalValue(c);
if (!(v instanceof ThreadValue))
throw new InternalExpressionException("'task_join' could only be used with a task value");
Value ret = ((ThreadValue) v).join();
return (_c, _t) -> ret;
});
expression.addLazyFunctionWithDelegation("task_dock", 1, (c, t, expr, tok, lv) -> {
CarpetContext cc = (CarpetContext)c;
MinecraftServer server = cc.s.getMinecraftServer();
if (server.isOnThread()) return lv.get(0); // pass through for on thread tasks
Value[] result = new Value[]{Value.NULL};
RuntimeException[] internal = new RuntimeException[]{null};
try
{
((CarpetContext) c).s.getMinecraftServer().submitAndJoin(() ->
{
try
{
result[0] = lv.get(0).evalValue(c, t);
}
catch (ExpressionException exc)
{
internal[0] = exc;
}
catch (InternalExpressionException exc)
{
internal[0] = new ExpressionException(c, expr, tok, exc.getMessage(), exc.stack);
}
catch (ArithmeticException exc)
{
internal[0] = new ExpressionException(c, expr, tok, "Your math is wrong, "+exc.getMessage());
}
});
}
catch (CompletionException exc)
{
throw new InternalExpressionException("Error while executing docked task section, internal stack trace is gone");
}
if (internal[0] != null)
{
throw internal[0];
}
Value ret = result[0]; // preventing from lazy evaluating of the result in case a future completes later
return (_c, _t) -> ret;
// pass through placeholder
// implmenetation should dock the task on the main thread.
});
expression.addLazyFunction("run", 1, (c, t, lv) -> {
BlockPos target = ((CarpetContext)c).origin;
Vec3d posf = new Vec3d((double)target.getX()+0.5D,(double)target.getY(),(double)target.getZ()+0.5D);
ServerCommandSource s = ((CarpetContext)c).s;
try
{
Value retval = new NumericValue(s.getMinecraftServer().getCommandManager().execute(
s.withPosition(posf).withSilent().withLevel(CarpetSettings.runPermissionLevel), lv.get(0).evalValue(c).getString()));
return (c_, t_) -> retval;
}
catch (Exception ignored) {}
return LazyValue.NULL;
});
expression.addLazyFunction("save", 0, (c, t, lv) -> {
ServerCommandSource s = ((CarpetContext)c).s;
s.getMinecraftServer().getPlayerManager().saveAllPlayerData();
s.getMinecraftServer().save(true,true,true);
s.getWorld().getChunkManager().tick(() -> true);
CarpetSettings.LOG.warn("Saved chunks");
return (cc, tt) -> Value.TRUE;
});
expression.addLazyFunction("tick_time", 0, (c, t, lv) ->
{
Value time = new NumericValue(((CarpetContext) c).s.getMinecraftServer().getTicks());
return (cc, tt) -> time;
});
expression.addLazyFunction("world_time", 0, (c, t, lv) ->
{
Value time = new NumericValue(((CarpetContext) c).s.getWorld().getTime());
return (cc, tt) -> time;
});
expression.addLazyFunction("day_time", -1, (c, t, lv) ->
{
Value time = new NumericValue(((CarpetContext) c).s.getWorld().getTimeOfDay());
if (lv.size() > 0)
{
long newTime = NumericValue.asNumber(lv.get(0).evalValue(c)).getLong();
if (newTime < 0) newTime = 0;
((CarpetContext) c).s.getWorld().setTimeOfDay(newTime);// setTimeOfDay(newTime);
}
return (cc, tt) -> time;
});
expression.addLazyFunction("last_tick_times", -1, (c, t, lv) ->
{
//assuming we are in the tick world section
// might be off one tick when run in the off tasks or asynchronously.
int currentReportedTick = ((CarpetContext) c).s.getMinecraftServer().getTicks()-1;
List<Value> ticks = new ArrayList<>(100);
final long[] tickArray = ((CarpetContext) c).s.getMinecraftServer().lastTickLengths;
for (int i=currentReportedTick+100; i > currentReportedTick; i--)
{
ticks.add(new NumericValue(((double)tickArray[i % 100])/1000000.0));
}
Value ret = ListValue.wrap(ticks);
return (cc, tt) -> ret;
});
expression.addLazyFunction("game_tick", -1, (c, t, lv) -> {
ServerCommandSource s = ((CarpetContext)c).s;
if (!s.getMinecraftServer().isOnThread()) throw new InternalExpressionException("Unable to run ticks from threads");
((MinecraftServerInterface)s.getMinecraftServer()).forceTick( () -> System.nanoTime()- CarpetServer.scriptServer.tickStart<50000000L);
if (lv.size()>0)
{
long ms_total = NumericValue.asNumber(lv.get(0).evalValue(c)).getLong();
long end_expected = CarpetServer.scriptServer.tickStart+ms_total*1000000L;
long wait = end_expected-System.nanoTime();
if (wait > 0L)
{
try
{
Thread.sleep(wait/1000000L);
}
catch (InterruptedException ignored)
{
}
}
}
CarpetServer.scriptServer.tickStart = System.nanoTime(); // for the next tick
Thread.yield();
if(CarpetServer.scriptServer.stopAll)
throw new ExitStatement(Value.NULL);
return (cc, tt) -> Value.TRUE;
});
expression.addLazyFunction("seed", -1, (c, t, lv) -> {
ServerCommandSource s = ((CarpetContext)c).s;
Value ret = new NumericValue(s.getWorld().getSeed());
return (cc, tt) -> ret;
});
expression.addLazyFunction("relight", -1, (c, t, lv) ->
{
CarpetContext cc = (CarpetContext) c;
BlockArgument locator = BlockArgument.findIn(cc, lv, 0);
BlockPos pos = locator.block.getPos();
ServerWorld world = cc.s.getWorld();
((ThreadedAnvilChunkStorageInterface) world.getChunkManager().threadedAnvilChunkStorage).relightChunk(new ChunkPos(pos));
WorldAccess.forceChunkUpdate(pos, world);
return LazyValue.TRUE;
});
expression.addLazyFunction("current_dimension", 0, (c, t, lv) -> {
ServerCommandSource s = ((CarpetContext)c).s;
Value retval = ValueConversions.dimName(s.getWorld());
return (cc, tt) -> retval;
});
expression.addLazyFunction("view_distance", 0, (c, t, lv) -> {
ServerCommandSource s = ((CarpetContext)c).s;
Value retval = new NumericValue(s.getMinecraftServer().getPlayerManager().getViewDistance());
return (cc, tt) -> retval;
});
expression.addLazyFunction("in_dimension", 2, (c, t, lv) -> {
ServerCommandSource outerSource = ((CarpetContext)c).s;
Value dimensionValue = lv.get(0).evalValue(c);
ServerCommandSource innerSource = outerSource;
if (dimensionValue instanceof EntityValue)
{
innerSource = outerSource.withWorld((ServerWorld) ((EntityValue)dimensionValue).getEntity().getEntityWorld());
}
else if (dimensionValue instanceof BlockValue)
{
BlockValue bv = (BlockValue)dimensionValue;
if (bv.getWorld() != null)
{
innerSource = outerSource.withWorld(bv.getWorld());
}
else
{
throw new InternalExpressionException("'in_dimension' accepts only world-localized block arguments");
}
}
else
{
String dimString = dimensionValue.getString().toLowerCase(Locale.ROOT);
switch (dimString)
{
case "nether":
case "the_nether":
innerSource = outerSource.withWorld(outerSource.getMinecraftServer().getWorld(World.NETHER)); //nether
break;
case "end":
case "the_end":
innerSource = outerSource.withWorld(outerSource.getMinecraftServer().getWorld(World.END)); //end
break;
case "overworld":
case "over_world":
innerSource = outerSource.withWorld(outerSource.getMinecraftServer().getWorld(World.OVERWORLD)); //ow
break;
default:
RegistryKey<World> dim = null;
Identifier id = new Identifier(dimString);
// not using RegistryKey.of since that one creates on check
for (RegistryKey<World> world : (outerSource.getMinecraftServer().getWorldRegistryKeys()))
{
if (id.equals(world.getValue()))
{
dim = world;
break;
}
}
if (dim == null)
throw new InternalExpressionException("Incorrect dimension string: "+dimString);
innerSource = outerSource.withWorld(outerSource.getMinecraftServer().getWorld(dim));
}
};
if (innerSource.getWorld() == outerSource.getWorld()) return lv.get(1);
Context newCtx = c.recreate();
((CarpetContext) newCtx).s = innerSource;
newCtx.variables = c.variables;
Value retval = lv.get(1).evalValue(newCtx);
return (cc, tt) -> retval;
});
expression.addLazyFunction("plop", 4, (c, t, lv) ->{
BlockArgument locator = BlockArgument.findIn((CarpetContext)c, lv, 0);
if (lv.size() <= locator.offset)
throw new InternalExpressionException("'plop' needs extra argument indicating what to plop");
String what = lv.get(locator.offset).evalValue(c).getString();
Value [] result = new Value[]{Value.NULL};
((CarpetContext)c).s.getMinecraftServer().submitAndJoin( () ->
{
Boolean res = FeatureGenerator.plop(what, ((CarpetContext) c).s.getWorld(), locator.block.getPos());
//Boolean res = FeatureGenerator.spawn(what, ((CarpetContext) c).s.getWorld(), locator.block.getPos());
if (res == null)
return;
if (what.equalsIgnoreCase("boulder")) // there might be more of those
WorldAccess.forceChunkUpdate(locator.block.getPos(), ((CarpetContext) c).s.getWorld());
result[0] = new NumericValue(res);
});
Value ret = result[0]; // preventing from lazy evaluating of the result in case a future completes later
return (_c, _t) -> ret;
});
expression.addLazyFunction("schedule", -1, (c, t, lv) -> {
if (lv.size()<2)
throw new InternalExpressionException("'schedule' should have at least 2 arguments, delay and call name");
long delay = NumericValue.asNumber(lv.get(0).evalValue(c)).getLong();
FunctionArgument functionArgument = FunctionArgument.findIn(c, expression.module, lv, 1, true, true);
CarpetServer.scriptServer.events.scheduleCall(
(CarpetContext) c,
functionArgument.function,
functionArgument.resolveArgs(c, Context.NONE),
delay
);
return (c_, t_) -> Value.TRUE;
});
expression.addLazyFunction("logger", -1, (c, t, lv) ->
{
//CarpetSettings.LOG.error("Standard Structures:");
//CarpetSettings.LOG.error(Registry.STRUCTURE_FEATURE.getIds().stream().map(i -> "`'"+NBTSerializableValue.nameFromRegistryId(i)+"'`").sorted(). collect(Collectors.joining(", ")));
//CarpetSettings.LOG.error("");
//CarpetSettings.LOG.error("Configured Structures:");
//CarpetSettings.LOG.error(BuiltinRegistries.CONFIGURED_STRUCTURE_FEATURE.getIds().stream().map(i -> "`'"+NBTSerializableValue.nameFromRegistryId(i)+"'`").sorted().collect(Collectors.joining(", ")));
//CarpetSettings.LOG.error("");
//CarpetSettings.LOG.error("Features:");
//CarpetSettings.LOG.error(Registry.FEATURE.getIds().stream().map(i -> "`'"+NBTSerializableValue.nameFromRegistryId(i)+"'`").sorted().collect(Collectors.joining(", ")));
//CarpetSettings.LOG.error("");
//CarpetSettings.LOG.error("Configured Features:");
//CarpetSettings.LOG.error(BuiltinRegistries.CONFIGURED_FEATURE.getIds().stream().map(i -> "`'"+NBTSerializableValue.nameFromRegistryId(i)+"'`").sorted().collect(Collectors.joining(", ")));
//CarpetSettings.LOG.error("");
//CarpetSettings.LOG.error("Custom:");
//CarpetSettings.LOG.error(FeatureGenerator.featureMap.keySet().stream().map(i -> "`'"+i+"'`").sorted().collect(Collectors.joining(", ")));
//CarpetSettings.LOG.error(Registry.ENTITY_TYPE.getIds().stream().sorted().map(ValueConversions::simplify).collect(Collectors.joining("`, `")));
Value res;
if(lv.size()==1)
{
res = lv.get(0).evalValue(c);
CarpetSettings.LOG.info(res.getString());
}
else if(lv.size()==2)
{
String level = lv.get(0).evalValue(c).getString().toLowerCase(Locale.ROOT);
res = lv.get(1).evalValue(c);
switch(level){
case "error": CarpetSettings.LOG.error(res.getString()); break;
case "warn": CarpetSettings.LOG.warn(res.getString()); break;
case "debug": CarpetSettings.LOG.debug(res.getString()); break;
case "fatal": CarpetSettings.LOG.fatal(res.getString()); break;
case "info": CarpetSettings.LOG.info(res.getString()); break;
default: throw new InternalExpressionException("Unknown log level for 'logger': "+level);
}
}
else throw new InternalExpressionException("logger takes 1 or 2 arguments");
return (_c, _t) -> res; // pass through for variables
});
expression.addLazyFunction("read_file", 2, (c, t, lv) -> {
String resource = recognizeResource(lv.get(0).evalValue(c));
String origtype = lv.get(1).evalValue(c).getString().toLowerCase(Locale.ROOT);
boolean shared = origtype.startsWith("shared_");
String type = shared ? origtype.substring(7) : origtype; //len(shared_)
if (!type.equals("raw") && !type.equals("text") && !type.equals("nbt"))
throw new InternalExpressionException("Unsupported file type: "+origtype);
Value retVal;
if (type.equals("nbt"))
{
Tag state = ((CarpetScriptHost)((CarpetContext)c).host).readFileTag(resource, shared);
if (state == null) return LazyValue.NULL;
retVal = new NBTSerializableValue(state);
}
else
{
List<String> content = ((CarpetScriptHost) ((CarpetContext) c).host).readTextResource(resource, shared);
if (content == null) return LazyValue.NULL;
retVal = ListValue.wrap(content.stream().map(StringValue::new).collect(Collectors.toList()));
}
return (cc, tt) -> retVal;
});
expression.addLazyFunction("delete_file", 2, (c, t, lv) -> {
String resource = recognizeResource(lv.get(0).evalValue(c));
String origtype = lv.get(1).evalValue(c).getString().toLowerCase(Locale.ROOT);
boolean shared = origtype.startsWith("shared_");
String type = shared ? origtype.substring(7) : origtype; //len(shared_)
if (!type.equals("raw") && !type.equals("text") && !type.equals("nbt"))
throw new InternalExpressionException("Unsupported file type: "+origtype);
boolean success = ((CarpetScriptHost)((CarpetContext)c).host).removeResourceFile(resource, shared, type);
return success?LazyValue.TRUE:LazyValue.FALSE;
});
expression.addLazyFunction("write_file", -1, (c, t, lv) -> {
if (lv.size() < 3) throw new InternalExpressionException("'write_file' requires three or more arguments");
String resource = recognizeResource(lv.get(0).evalValue(c));
String origtype = lv.get(1).evalValue(c).getString().toLowerCase(Locale.ROOT);
boolean shared = origtype.startsWith("shared_");
String type = shared ? origtype.substring(7) : origtype; //len(shared_)
if (!type.equals("raw") && !type.equals("text") && !type.equals("nbt"))
throw new InternalExpressionException("Unsupported file type: "+origtype);
boolean success;
if (type.equals("nbt"))
{
Value val = lv.get(2).evalValue(c);
NBTSerializableValue tagValue = (val instanceof NBTSerializableValue)
? (NBTSerializableValue) val
: new NBTSerializableValue(val.getString());
Tag tag = tagValue.getTag();
success = ((CarpetScriptHost)((CarpetContext)c).host).writeTagFile(tag, resource, shared);
}
else
{
List<String> data = new ArrayList<>();
if (lv.size()==3)
{
Value val = lv.get(2).evalValue(c);
if (val instanceof ListValue)
{
List<Value> lval = ((ListValue) val).getItems();
lval.forEach(v -> data.add(v.getString()));
}
else
{
data.add(val.getString());
}
}
else
{
for(int i = 2; i < lv.size(); i++)
{
data.add(lv.get(i).evalValue(c).getString());
}
}
success = ((CarpetScriptHost) ((CarpetContext) c).host).appendLogFile(resource, shared, type, data);
}
return success?LazyValue.TRUE:LazyValue.FALSE;
});
//write_file
expression.addLazyFunction("load_app_data", -1, (c, t, lv) ->
{
String file = null;
boolean shared = false;
if (lv.size()>0)
{
file = recognizeResource(lv.get(0).evalValue(c));
if (lv.size() > 1)
{
shared = lv.get(1).evalValue(c).getBoolean();
}
}
Tag state = ((CarpetScriptHost)((CarpetContext)c).host).readFileTag(file, shared);
if (state == null)
return (cc, tt) -> Value.NULL;
Value retVal = new NBTSerializableValue(state);
return (cc, tt) -> retVal;
});
expression.addLazyFunction("store_app_data", -1, (c, t, lv) ->
{
if (lv.size() == 0)
throw new InternalExpressionException("'store_app_data' needs NBT tag and an optional file");
Value val = lv.get(0).evalValue(c);
String file = null;
boolean shared = false;
if (lv.size()>1)
{
file = recognizeResource(lv.get(1).evalValue(c));
if (lv.size() > 2)
{
shared = lv.get(2).evalValue(c).getBoolean();
}
}
NBTSerializableValue tagValue = (val instanceof NBTSerializableValue)
? (NBTSerializableValue) val
: new NBTSerializableValue(val.getString());
Tag tag = tagValue.getTag();
boolean success = ((CarpetScriptHost)((CarpetContext)c).host).writeTagFile(tag, file, shared);
return success?LazyValue.TRUE:LazyValue.FALSE;
});
expression.addLazyFunction("statistic", 3, (c, t, lv) ->
{
Value playerValue = lv.get(0).evalValue(c);
CarpetContext cc = (CarpetContext)c;
ServerPlayerEntity player = EntityValue.getPlayerByValue(cc.s.getMinecraftServer(), playerValue);
if (player == null) return LazyValue.NULL;
Identifier category;
Identifier statName;
try
{
category = new Identifier(lv.get(1).evalValue(c).getString());
statName = new Identifier(lv.get(2).evalValue(c).getString());
}
catch (InvalidIdentifierException e)
{
return LazyValue.NULL;
}
StatType<?> type = Registry.STAT_TYPE.get(category);
if (type == null)
return LazyValue.NULL;
Stat<?> stat = getStat(type, statName);
if (stat == null)
return LazyValue.NULL;
return (_c, _t) -> new NumericValue(player.getStatHandler().getStat(stat));
});
}
private static <T> Stat<T> getStat(StatType<T> type, Identifier id)
{
T key = type.getRegistry().get(id);
if (key == null || !((StatTypeInterface)type).hasStatCreated(key))
return null;
return type.getOrCreateStat(key);
}
}
@@ -0,0 +1,513 @@
package carpet.script.api;
import carpet.script.CarpetContext;
import carpet.script.Context;
import carpet.script.Expression;
import carpet.script.LazyValue;
import carpet.script.argument.BlockArgument;
import carpet.script.argument.Vector3Argument;
import carpet.script.exception.BreakStatement;
import carpet.script.exception.ContinueStatement;
import carpet.script.exception.InternalExpressionException;
import carpet.script.value.BlockValue;
import carpet.script.value.LazyListValue;
import carpet.script.value.ListValue;
import carpet.script.value.NumericValue;
import carpet.script.value.Value;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3i;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import static java.lang.Math.abs;
import static java.lang.Math.max;
import static java.lang.Math.min;
public class BlockIterators {
public static void apply(Expression expression)
{
expression.addLazyFunction("scan", -1, (c, t, lv) ->
{
CarpetContext cc = (CarpetContext)c;
BlockArgument centerLocator = BlockArgument.findIn(cc, lv, 0);
Vector3Argument rangeLocator = Vector3Argument.findIn(cc, lv, centerLocator.offset);
BlockPos center = centerLocator.block.getPos();
Vec3i range;
if (rangeLocator.fromBlock)
{
range = new Vec3i(
abs(rangeLocator.vec.x - center.getX()),
abs(rangeLocator.vec.y - center.getY()),
abs(rangeLocator.vec.z - center.getZ())
);
//if (lv.size() > rangeLocator.offset+1) throw new InternalExpressionException("'scan' takes two block positions, and the expression")
}
else
{
range = new Vec3i(abs(rangeLocator.vec.x), abs(rangeLocator.vec.y), abs(rangeLocator.vec.z));
}
Vec3i upperRange = range;
if (lv.size() > rangeLocator.offset+1) // +1 cause we still need the expression
{
rangeLocator = Vector3Argument.findIn(cc, lv, rangeLocator.offset);
if (rangeLocator.fromBlock)
{
upperRange = new Vec3i(
abs(rangeLocator.vec.x - center.getX()),
abs(rangeLocator.vec.y - center.getY()),
abs(rangeLocator.vec.z - center.getZ())
);
//if (lv.size() > rangeLocator.offset+1) throw new InternalExpressionException("'scan' takes two block positions, and the expression")
}
else
{
upperRange = new Vec3i(abs(rangeLocator.vec.x), abs(rangeLocator.vec.y), abs(rangeLocator.vec.z));
}
}
if (lv.size() != rangeLocator.offset+1)
{
throw new InternalExpressionException("'scan' takes two, or three block positions, and an expression: "+lv.size()+" "+rangeLocator.offset);
}
LazyValue expr = lv.get(rangeLocator.offset);
int cx = center.getX();
int cy = center.getY();
int cz = center.getZ();
int xrange = range.getX();
int yrange = range.getY();
int zrange = range.getZ();
int xprange = upperRange.getX();
int yprange = upperRange.getY();
int zprange = upperRange.getZ();
//saving outer scope
LazyValue _x = c.getVariable("_x");
LazyValue _y = c.getVariable("_y");
LazyValue _z = c.getVariable("_z");
LazyValue __ = c.getVariable("_");
int sCount = 0;
outer:for (int y=cy-yrange; y <= cy+yprange; y++)
{
int yFinal = y;
c.setVariable("_y", (c_, t_) -> new NumericValue(yFinal).bindTo("_y"));
for (int x=cx-xrange; x <= cx+xprange; x++)
{
int xFinal = x;
c.setVariable("_x", (c_, t_) -> new NumericValue(xFinal).bindTo("_x"));
for (int z=cz-zrange; z <= cz+zprange; z++)
{
int zFinal = z;
c.setVariable("_z", (c_, t_) -> new NumericValue(zFinal).bindTo("_z"));
Value blockValue = BlockValue.fromCoords(((CarpetContext)c), xFinal,yFinal,zFinal).bindTo("_");
c.setVariable( "_", (cc_, t_c) -> blockValue);
Value result;
try
{
result = expr.evalValue(c, t);
}
catch (ContinueStatement notIgnored)
{
result = notIgnored.retval;
}
catch (BreakStatement notIgnored)
{
break outer;
}
if (t != Context.VOID && result.getBoolean())
{
sCount += 1;
}
}
}
}
//restoring outer scope
c.setVariable("_x", _x);
c.setVariable("_y", _y);
c.setVariable("_z", _z);
c.setVariable("_", __);
int finalSCount = sCount;
return (c_, t_) -> new NumericValue(finalSCount);
});
expression.addLazyFunction("volume", -1, (c, t, lv) ->
{
CarpetContext cc = (CarpetContext)c;
BlockArgument pos1Locator = BlockArgument.findIn(cc, lv, 0);
BlockArgument pos2Locator = BlockArgument.findIn(cc, lv, pos1Locator.offset);
BlockPos pos1 = pos1Locator.block.getPos();
BlockPos pos2 = pos2Locator.block.getPos();
int x1 = pos1.getX();
int y1 = pos1.getY();
int z1 = pos1.getZ();
int x2 = pos2.getX();
int y2 = pos2.getY();
int z2 = pos2.getZ();
int minx = min(x1, x2);
int miny = min(y1, y2);
int minz = min(z1, z2);
int maxx = max(x1, x2);
int maxy = max(y1, y2);
int maxz = max(z1, z2);
LazyValue expr = lv.get(pos2Locator.offset);
//saving outer scope
LazyValue _x = c.getVariable("_x");
LazyValue _y = c.getVariable("_y");
LazyValue _z = c.getVariable("_z");
LazyValue __ = c.getVariable("_");
int sCount = 0;
outer:for (int y=miny; y <= maxy; y++)
{
int yFinal = y;
c.setVariable("_y", (c_, t_) -> new NumericValue(yFinal).bindTo("_y"));
for (int x=minx; x <= maxx; x++)
{
int xFinal = x;
c.setVariable("_x", (c_, t_) -> new NumericValue(xFinal).bindTo("_x"));
for (int z=minz; z <= maxz; z++)
{
int zFinal = z;
c.setVariable("_z", (c_, t_) -> new NumericValue(zFinal).bindTo("_z"));
Value blockValue = BlockValue.fromCoords(((CarpetContext)c), xFinal,yFinal,zFinal).bindTo("_");
c.setVariable( "_", (cc_, t_c) -> blockValue);
Value result;
try
{
result = expr.evalValue(c, t);
}
catch (ContinueStatement notIgnored)
{
result = notIgnored.retval;
}
catch (BreakStatement notIgnored)
{
break outer;
}
if (t != Context.VOID && result.getBoolean())
{
sCount += 1;
}
}
}
}
//restoring outer scope
c.setVariable("_x", _x);
c.setVariable("_y", _y);
c.setVariable("_z", _z);
c.setVariable("_", __);
int finalSCount = sCount;
return (c_, t_) -> new NumericValue(finalSCount);
});
expression.addLazyFunction("neighbours", -1, (c, t, lv)->
{
BlockPos center = BlockArgument.findIn((CarpetContext) c, lv,0).block.getPos();
ServerWorld world = ((CarpetContext) c).s.getWorld();
List<Value> neighbours = new ArrayList<>();
neighbours.add(new BlockValue(null, world, center.up()));
neighbours.add(new BlockValue(null, world, center.down()));
neighbours.add(new BlockValue(null, world, center.north()));
neighbours.add(new BlockValue(null, world, center.south()));
neighbours.add(new BlockValue(null, world, center.east()));
neighbours.add(new BlockValue(null, world, center.west()));
Value retval = ListValue.wrap(neighbours);
return (c_, t_) -> retval;
});
expression.addLazyFunction("rect", -1, (c, t, lv)->
{
CarpetContext cc = (CarpetContext) c;
int cx, cy, cz;
int sminx, sminy, sminz;
int smaxx, smaxy, smaxz;
BlockArgument cposLocator = BlockArgument.findIn(cc, lv, 0);
BlockPos cpos = cposLocator.block.getPos();
cx = cpos.getX();
cy = cpos.getY();
cz = cpos.getZ();
if (lv.size() > cposLocator.offset)
{
Vector3Argument diffLocator = Vector3Argument.findIn(cc, lv, cposLocator.offset);
if (diffLocator.fromBlock)
{
sminx = MathHelper.floor(abs(diffLocator.vec.x - cx));
sminy = MathHelper.floor(abs(diffLocator.vec.y - cx));
sminz = MathHelper.floor(abs(diffLocator.vec.z - cx));
}
else
{
sminx = MathHelper.floor(abs(diffLocator.vec.x));
sminy = MathHelper.floor(abs(diffLocator.vec.y));
sminz = MathHelper.floor(abs(diffLocator.vec.z));
}
if (lv.size() > diffLocator.offset)
{
Vector3Argument posDiff = Vector3Argument.findIn(cc, lv, diffLocator.offset);
if (posDiff.fromBlock)
{
smaxx = MathHelper.floor(abs(posDiff.vec.x - cx));
smaxy = MathHelper.floor(abs(posDiff.vec.y - cx));
smaxz = MathHelper.floor(abs(posDiff.vec.z - cx));
}
else
{
smaxx = MathHelper.floor(abs(posDiff.vec.x));
smaxy = MathHelper.floor(abs(posDiff.vec.y));
smaxz = MathHelper.floor(abs(posDiff.vec.z));
}
}
else
{
smaxx = sminx;
smaxy = sminy;
smaxz = sminz;
}
}
else
{
sminx = 1;
sminy = 1;
sminz = 1;
smaxx = 1;
smaxy = 1;
smaxz = 1;
}
return (c_, t_) -> new LazyListValue()
{
final int minx = cx-sminx;
final int miny = cy-sminy;
final int minz = cz-sminz;
final int maxx = cx+smaxx;
final int maxy = cy+smaxy;
final int maxz = cz+smaxz;
int x;
int y;
int z;
{
reset();
}
@Override
public boolean hasNext()
{
return y <= maxy;
}
@Override
public Value next()
{
Value r = BlockValue.fromCoords(cc, x,y,z);
//possibly reroll context
x++;
if (x > maxx)
{
x = minx;
z++;
if (z > maxz)
{
z = minz;
y++;
// hasNext should fail if we went over
}
}
return r;
}
@Override
public void fatality()
{
// possibly return original x, y, z
super.fatality();
}
@Override
public void reset()
{
x = minx;
y = miny;
z = minz;
}
@Override
public String getString()
{
return String.format(Locale.ROOT, "rect[(%d,%d,%d),..,(%d,%d,%d)]",minx, miny, minz, maxx, maxy, maxz);
}
};
});
expression.addLazyFunction("diamond", -1, (c, t, lv)->
{
CarpetContext cc = (CarpetContext)c;
BlockArgument cposLocator=BlockArgument.findIn((CarpetContext)c,lv,0);
BlockPos cpos = cposLocator.block.getPos();
int cx;
int cy;
int cz;
int width;
int height;
try
{
cx = cpos.getX();
cy = cpos.getY();
cz = cpos.getZ();
if (lv.size()==cposLocator.offset)
{
Value retval = ListValue.of(
BlockValue.fromCoords(cc, cx, cy-1, cz),
BlockValue.fromCoords(cc, cx, cy, cz),
BlockValue.fromCoords(cc, cx-1, cy, cz),
BlockValue.fromCoords(cc, cx, cy, cz-1),
BlockValue.fromCoords(cc, cx+1, cy, cz),
BlockValue.fromCoords(cc, cx, cy, cz+1),
BlockValue.fromCoords(cc, cx, cy+1, cz)
);
return (_c, _t ) -> retval;
}
else if (lv.size()==1+cposLocator.offset)
{
width = (int) ((NumericValue) lv.get(cposLocator.offset).evalValue(c)).getLong();
height = 0;
}
else if(lv.size()==2+cposLocator.offset)
{
width = (int) ((NumericValue) lv.get(cposLocator.offset).evalValue(c)).getLong();
height = (int) ((NumericValue) lv.get(cposLocator.offset+1).evalValue(c)).getLong();
} else{
throw new InternalExpressionException("Incorrect number of arguments for 'diamond'");
}
}
catch (ClassCastException exc)
{
throw new InternalExpressionException("Attempted to pass a non-number to 'diamond'");
}
if (height == 0)
{
return (c_, t_) -> new LazyListValue()
{
int curradius;
int curpos;
{
reset();
}
@Override
public boolean hasNext()
{
return curradius <= width;
}
@Override
public Value next()
{
if (curradius == 0)
{
curradius = 1;
return BlockValue.fromCoords(cc, cx, cy, cz);
}
// x = 3-|i-6|
// z = |( (i-3)%12-6|-3
Value block = BlockValue.fromCoords(cc, cx+(curradius-abs(curpos-2*curradius)), cy, cz-curradius+abs( abs(curpos-curradius)%(4*curradius) -2*curradius ));
curpos++;
if (curpos>=curradius*4)
{
curradius++;
curpos = 0;
}
return block;
}
@Override
public void reset()
{
curradius = 0;
curpos = 0;
}
@Override
public String getString()
{
return String.format(Locale.ROOT, "diamond[(%d,%d,%d),%d,0]",cx, cy, cz, width);
}
};
}
else
{
return (c_, t_) -> new LazyListValue()
{
int curradius;
int curpos;
int curheight;
{
reset();
}
@Override
public boolean hasNext()
{
return curheight <= height;
}
@Override
public Value next()
{
if (curheight == -height || curheight == height)
{
return BlockValue.fromCoords(cc, cx, cy+curheight++, cz);
}
if (curradius == 0)
{
curradius++;
return BlockValue.fromCoords(cc, cx, cy+curheight, cz);
}
// x = 3-|i-6|
// z = |( (i-3)%12-6|-3
Value block = BlockValue.fromCoords(cc, cx+(curradius-abs(curpos-2*curradius)), cy+curheight, cz-curradius+abs( abs(curpos-curradius)%(4*curradius) -2*curradius ));
curpos++;
if (curpos>=curradius*4)
{
curradius++;
curpos = 0;
if (curradius>width -abs(width*curheight/height))
{
curheight++;
curradius = 0;
curpos = 0;
}
}
return block;
}
@Override
public void reset()
{
curradius = 0;
curpos = 0;
curheight = -height;
}
@Override
public String getString()
{
return String.format(Locale.ROOT, "diamond[(%d,%d,%d),%d,%d]",cx, cy, cz, width, height);
}
};
}
});
}
}
@@ -0,0 +1,293 @@
package carpet.script.api;
import carpet.script.CarpetContext;
import carpet.script.Expression;
import carpet.script.LazyValue;
import carpet.script.argument.Vector3Argument;
import carpet.script.exception.InternalExpressionException;
import carpet.script.value.EntityValue;
import carpet.script.value.FunctionValue;
import carpet.script.value.ListValue;
import carpet.script.value.NBTSerializableValue;
import carpet.script.value.NullValue;
import carpet.script.value.NumericValue;
import carpet.script.value.Value;
import com.mojang.brigadier.StringReader;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.SpawnReason;
import net.minecraft.entity.mob.MobEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.predicate.entity.EntityPredicates;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.Box;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.registry.Registry;
import org.apache.commons.lang3.tuple.Pair;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import java.util.function.Predicate;
import java.util.stream.Collectors;
public class Entities {
public static void apply(Expression expression)
{
expression.addLazyFunction("player", -1, (c, t, lv) -> {
if (lv.size() ==0)
{
Entity callingEntity = ((CarpetContext)c).s.getEntity();
if (callingEntity instanceof PlayerEntity)
{
Value retval = new EntityValue(callingEntity);
return (_c, _t) -> retval;
}
Vec3d pos = ((CarpetContext)c).s.getPosition();
PlayerEntity closestPlayer = ((CarpetContext)c).s.getWorld().getClosestPlayer(pos.x, pos.y, pos.z, -1.0, EntityPredicates.VALID_ENTITY);
if (closestPlayer != null)
{
Value retval = new EntityValue(closestPlayer);
return (_c, _t) -> retval;
}
return (_c, _t) -> Value.NULL;
}
String playerName = lv.get(0).evalValue(c).getString();
Value retval = Value.NULL;
if ("all".equalsIgnoreCase(playerName))
{
retval = ListValue.wrap(
((CarpetContext)c).s.getMinecraftServer().getPlayerManager().getPlayerList().
stream().map(EntityValue::new).collect(Collectors.toList()));
}
else if ("*".equalsIgnoreCase(playerName))
{
retval = ListValue.wrap(
((CarpetContext)c).s.getWorld().getPlayers().
stream().map(EntityValue::new).collect(Collectors.toList()));
}
else if ("survival".equalsIgnoreCase(playerName))
{
retval = ListValue.wrap(
((CarpetContext)c).s.getWorld().getPlayers((p) -> p.interactionManager.isSurvivalLike()).
stream().map(EntityValue::new).collect(Collectors.toList()));
}
else if ("creative".equalsIgnoreCase(playerName))
{
retval = ListValue.wrap(
((CarpetContext)c).s.getWorld().getPlayers(PlayerEntity::isCreative).
stream().map(EntityValue::new).collect(Collectors.toList()));
}
else if ("spectating".equalsIgnoreCase(playerName))
{
retval = ListValue.wrap(
((CarpetContext)c).s.getWorld().getPlayers(PlayerEntity::isSpectator).
stream().map(EntityValue::new).collect(Collectors.toList()));
}
else if ("!spectating".equalsIgnoreCase(playerName))
{
retval = ListValue.wrap(
((CarpetContext)c).s.getWorld().getPlayers((p) -> !p.isSpectator()).
stream().map(EntityValue::new).collect(Collectors.toList()));
}
else
{
ServerPlayerEntity player = ((CarpetContext) c).s.getMinecraftServer().getPlayerManager().getPlayer(playerName);
if (player != null)
retval = new EntityValue(player);
}
Value finalVar = retval;
return (cc, tt) -> finalVar;
});
expression.addLazyFunction("spawn", -1, (c, t, lv) -> {
CarpetContext cc = (CarpetContext)c;
if (lv.size() < 2)
throw new InternalExpressionException("'spawn' function takes mob name, and position to spawn");
String entityString = lv.get(0).evalValue(c).getString();
Identifier entityId;
try
{
entityId = Identifier.fromCommandInput(new StringReader(entityString));
EntityType type = Registry.ENTITY_TYPE.getOrEmpty(entityId).orElse(null);
if (type == null || !type.isSummonable())
return LazyValue.NULL;
}
catch (CommandSyntaxException exception)
{
return LazyValue.NULL;
}
Vector3Argument position = Vector3Argument.findIn(cc, lv, 1);
if (position.fromBlock)
position.vec = position.vec.subtract(0, 0.5, 0);
CompoundTag tag = new CompoundTag();
boolean hasTag = false;
if (lv.size() > position.offset)
{
Value nbt = lv.get(position.offset).evalValue(c);
NBTSerializableValue v = (nbt instanceof NBTSerializableValue) ? (NBTSerializableValue) nbt
: NBTSerializableValue.parseString(nbt.getString());
hasTag = true;
tag = v.getCompoundTag();
}
tag.putString("id", entityId.toString());
Vec3d vec3d = position.vec;
ServerWorld serverWorld = cc.s.getWorld();
Entity entity_1 = EntityType.loadEntityWithPassengers(tag, serverWorld, (entity_1x) -> {
entity_1x.refreshPositionAndAngles(vec3d.x, vec3d.y, vec3d.z, entity_1x.yaw, entity_1x.pitch);
return !serverWorld.tryLoadEntity(entity_1x) ? null : entity_1x;
});
if (entity_1 == null) {
return LazyValue.NULL;
} else {
if (!hasTag && entity_1 instanceof MobEntity) {
((MobEntity)entity_1).initialize(serverWorld, serverWorld.getLocalDifficulty(entity_1.getBlockPos()), SpawnReason.COMMAND, null, null);
}
Value res = new EntityValue(entity_1);
return (_c, _t) -> res;
}
});
expression.addLazyFunction("entity_id", 1, (c, t, lv) ->
{
Value who = lv.get(0).evalValue(c);
Entity e;
if (who instanceof NumericValue)
{
e = ((CarpetContext)c).s.getWorld().getEntityById((int)((NumericValue) who).getLong());
}
else
{
e = ((CarpetContext)c).s.getWorld().getEntity(UUID.fromString(who.getString()));
}
if (e==null)
{
return LazyValue.NULL;
}
return (cc, tt) -> new EntityValue(e);
});
expression.addLazyFunction("entity_list", 1, (c, t, lv) -> {
String who = lv.get(0).evalValue(c).getString();
Pair<EntityType<?>, Predicate<? super Entity>> pair = EntityValue.getPredicate(who);
if (pair == null)
{
throw new InternalExpressionException("Unknown entity selection criterion: "+who);
}
List<Entity> entityList = ((CarpetContext)c).s.getWorld().getEntitiesByType(pair.getKey(), pair.getValue());
Value retval = ListValue.wrap(entityList.stream().map(EntityValue::new).collect(Collectors.toList()));
return (_c, _t ) -> retval;
});
expression.addLazyFunction("entity_area", 7, (c, t, lv) -> {
Vec3d center = new Vec3d(
NumericValue.asNumber(lv.get(1).evalValue(c)).getDouble(),
NumericValue.asNumber(lv.get(2).evalValue(c)).getDouble(),
NumericValue.asNumber(lv.get(3).evalValue(c)).getDouble()
);
Box area = new Box(center, center).expand(
NumericValue.asNumber(lv.get(4).evalValue(c)).getDouble(),
NumericValue.asNumber(lv.get(5).evalValue(c)).getDouble(),
NumericValue.asNumber(lv.get(6).evalValue(c)).getDouble()
);
String who = lv.get(0).evalValue(c).getString();
Pair<EntityType<?>, Predicate<? super Entity>> pair = EntityValue.getPredicate(who);
if (pair == null)
{
throw new InternalExpressionException("Unknown entity selection criterion: "+who);
}
List<Entity> entityList = ((CarpetContext)c).s.getWorld().getEntitiesByType((EntityType<Entity>) pair.getKey(), area, pair.getValue());
Value retval = ListValue.wrap(entityList.stream().map(EntityValue::new).collect(Collectors.toList()));
return (_c, _t ) -> retval;
});
expression.addLazyFunction("entity_selector", -1, (c, t, lv) ->
{
String selector = lv.get(0).evalValue(c).getString();
List<Value> retlist = new ArrayList<>();
for (Entity e: EntityValue.getEntitiesFromSelector(((CarpetContext)c).s, selector))
{
retlist.add(new EntityValue(e));
}
return (c_, t_) -> ListValue.wrap(retlist);
});
expression.addLazyFunction("query", -1, (c, t, lv) -> {
if (lv.size()<2)
{
throw new InternalExpressionException("'query' takes entity as a first argument, and queried feature as a second");
}
Value v = lv.get(0).evalValue(c);
if (!(v instanceof EntityValue))
throw new InternalExpressionException("First argument to query should be an entity");
String what = lv.get(1).evalValue(c).getString();
Value retval;
if (lv.size()==2)
retval = ((EntityValue) v).get(what, null);
else if (lv.size()==3)
retval = ((EntityValue) v).get(what, lv.get(2).evalValue(c));
else
retval = ((EntityValue) v).get(what, ListValue.wrap(lv.subList(2, lv.size()).stream().map((vv) -> vv.evalValue(c)).collect(Collectors.toList())));
return (cc, tt) -> retval;
});
// or update
expression.addLazyFunction("modify", -1, (c, t, lv) -> {
if (lv.size()<2)
{
throw new InternalExpressionException("'modify' takes entity as a first argument, and queried feature as a second");
}
Value v = lv.get(0).evalValue(c);
if (!(v instanceof EntityValue))
throw new InternalExpressionException("First argument to modify should be an entity");
String what = lv.get(1).evalValue(c).getString();
if (lv.size()==2)
((EntityValue) v).set(what, null);
else if (lv.size()==3)
((EntityValue) v).set(what, lv.get(2).evalValue(c));
else
((EntityValue) v).set(what, ListValue.wrap(lv.subList(2, lv.size()).stream().map((vv) -> vv.evalValue(c)).collect(Collectors.toList())));
return (cc, tt) -> v;
});
// or update
expression.addLazyFunction("entity_event", -1, (c, t, lv) ->
{
if (lv.size()<3)
throw new InternalExpressionException("'entity_event' requires at least 3 arguments, entity, event to be handled, and function name, with optional arguments");
Value v = lv.get(0).evalValue(c);
if (!(v instanceof EntityValue))
throw new InternalExpressionException("First argument to entity_event should be an entity");
String what = lv.get(1).evalValue(c).getString();
Value functionValue = lv.get(2).evalValue(c);
if (functionValue instanceof NullValue)
functionValue = null;
else if (!(functionValue instanceof FunctionValue))
{
String name = functionValue.getString();
functionValue = c.host.getAssertFunction(expression.module, name);
}
FunctionValue function = (FunctionValue)functionValue;
List<Value> args = null;
if (lv.size()==4)
args = Collections.singletonList(lv.get(3).evalValue(c));
else if (lv.size()>4)
{
args = lv.subList(3, lv.size()).stream().map((vv) -> vv.evalValue(c)).collect(Collectors.toList());
}
((EntityValue) v).setEvent((CarpetContext)c, what, function, args);
return LazyValue.NULL;
});
}
}
@@ -0,0 +1,427 @@
package carpet.script.api;
import carpet.fakes.IngredientInterface;
import carpet.fakes.RecipeManagerInterface;
import carpet.script.CarpetContext;
import carpet.script.Expression;
import carpet.script.LazyValue;
import carpet.script.exception.InternalExpressionException;
import carpet.script.value.ListValue;
import carpet.script.value.NBTSerializableValue;
import carpet.script.value.NullValue;
import carpet.script.value.NumericValue;
import carpet.script.value.StringValue;
import carpet.script.value.Value;
import com.google.common.collect.Sets;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import net.minecraft.command.argument.ItemStackArgument;
import net.minecraft.entity.ItemEntity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.packet.s2c.play.ScreenHandlerSlotUpdateS2CPacket;
import net.minecraft.recipe.AbstractCookingRecipe;
import net.minecraft.recipe.CuttingRecipe;
import net.minecraft.recipe.Recipe;
import net.minecraft.recipe.RecipeType;
import net.minecraft.recipe.ShapedRecipe;
import net.minecraft.recipe.ShapelessRecipe;
import net.minecraft.recipe.SpecialCraftingRecipe;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.util.Identifier;
import net.minecraft.util.InvalidIdentifierException;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.registry.Registry;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
public class Inventories {
public static void apply(Expression expression)
{
expression.addLazyFunction("stack_limit", 1, (c, t, lv) ->
{
ItemStackArgument item = NBTSerializableValue.parseItem(lv.get(0).evalValue(c).getString());
Value res = new NumericValue(item.getItem().getMaxCount());
return (_c, _t) -> res;
});
expression.addLazyFunction("item_category", 1, (c, t, lv) ->
{
ItemStackArgument item = NBTSerializableValue.parseItem(lv.get(0).evalValue(c).getString());
Value res = new StringValue(item.getItem().getGroup().getName());
return (_c, _t) -> res;
});
expression.addLazyFunction("recipe_data", -1, (c, t, lv) ->
{
CarpetContext cc = (CarpetContext)c;
if (lv.size() < 1) throw new InternalExpressionException("'recipe_data' requires at least one argument");
String recipeName = lv.get(0).evalValue(c).getString();
RecipeType type = RecipeType.CRAFTING;
if (lv.size() > 1)
{
String recipeType = lv.get(1).evalValue(c).getString();
try
{
type = Registry.RECIPE_TYPE.get(new Identifier(recipeType));
}
catch (InvalidIdentifierException ignored)
{
throw new InternalExpressionException("Unknown crafting category: " + recipeType);
}
}
List<Recipe<?>> recipes;
try
{
recipes = ((RecipeManagerInterface) cc.s.getMinecraftServer().getRecipeManager()).getAllMatching(type, new Identifier(recipeName));
}
catch (InvalidIdentifierException ignored)
{
return LazyValue.NULL;
}
if (recipes.isEmpty())
return LazyValue.NULL;
List<Value> recipesOutput = new ArrayList<>();
for (Recipe<?> recipe: recipes)
{
ItemStack result = recipe.getOutput();
List<Value> ingredientValue = new ArrayList<>();
recipe.getPreviewInputs().forEach(
ingredient ->
{
// I am flattening ingredient lists per slot.
// consider recipe_data('wooden_sword','crafting') and ('iron_nugget', 'blasting') and notice difference
// in depths of lists.
List<Collection<ItemStack>> stacks = ((IngredientInterface) (Object) ingredient).getRecipeStacks();
if (stacks.isEmpty())
{
ingredientValue.add(Value.NULL);
}
else
{
List<Value> alternatives = new ArrayList<>();
stacks.forEach(col -> col.stream().map(ListValue::fromItemStack).forEach(alternatives::add));
ingredientValue.add(ListValue.wrap(alternatives));
}
}
);
Value recipeSpec;
if (recipe instanceof ShapedRecipe)
{
recipeSpec = ListValue.of(
new StringValue("shaped"),
new NumericValue(((ShapedRecipe) recipe).getWidth()),
new NumericValue(((ShapedRecipe) recipe).getHeight())
);
}
else if (recipe instanceof ShapelessRecipe)
{
recipeSpec = ListValue.of(new StringValue("shapeless"));
}
else if (recipe instanceof AbstractCookingRecipe)
{
recipeSpec = ListValue.of(
new StringValue("smelting"),
new NumericValue(((AbstractCookingRecipe) recipe).getCookTime()),
new NumericValue(((AbstractCookingRecipe) recipe).getExperience())
);
}
else if (recipe instanceof CuttingRecipe)
{
recipeSpec = ListValue.of(new StringValue("cutting"));
}
else if (recipe instanceof SpecialCraftingRecipe)
{
recipeSpec = ListValue.of(new StringValue("special"));
}
else
{
recipeSpec = ListValue.of(new StringValue("custom"));
}
recipesOutput.add(ListValue.of(ListValue.fromItemStack(result), ListValue.wrap(ingredientValue), recipeSpec));
}
Value ret = ListValue.wrap(recipesOutput);
return (_c, _t) -> ret;
});
expression.addLazyFunction("crafting_remaining_item", 1, (c, t, lv) ->
{
String itemStr = lv.get(0).evalValue(c).getString();
Item item;
try
{
Identifier id = new Identifier(itemStr);
item = Registry.ITEM.get(id);
if (item == Items.AIR && !id.getPath().equalsIgnoreCase("air"))
throw new InvalidIdentifierException("boo");
}
catch (InvalidIdentifierException ignored)
{
throw new InternalExpressionException("Incorrect item: "+itemStr);
}
if (!item.hasRecipeRemainder()) return LazyValue.NULL;
Value ret = new StringValue(NBTSerializableValue.nameFromRegistryId(Registry.ITEM.getId(item.getRecipeRemainder())));
return (_c, _t ) -> ret;
});
expression.addLazyFunction("inventory_size", -1, (c, t, lv) ->
{
CarpetContext cc = (CarpetContext) c;
NBTSerializableValue.InventoryLocator inventoryLocator = NBTSerializableValue.locateInventory(cc, lv, 0);
if (inventoryLocator == null)
return (_c, _t) -> Value.NULL;
Value res = new NumericValue(inventoryLocator.inventory.size());
return (_c, _t) -> res;
});
expression.addLazyFunction("inventory_has_items", -1, (c, t, lv) ->
{
CarpetContext cc = (CarpetContext) c;
NBTSerializableValue.InventoryLocator inventoryLocator = NBTSerializableValue.locateInventory(cc, lv, 0);
if (inventoryLocator == null)
return (_c, _t) -> Value.NULL;
Value res = new NumericValue(!inventoryLocator.inventory.isEmpty());
return (_c, _t) -> res;
});
//inventory_get(<b, e>, <n>) -> item_triple
expression.addLazyFunction("inventory_get", -1, (c, t, lv) ->
{
CarpetContext cc = (CarpetContext) c;
NBTSerializableValue.InventoryLocator inventoryLocator = NBTSerializableValue.locateInventory(cc, lv, 0);
if (inventoryLocator == null)
return (_c, _t) -> Value.NULL;
if (lv.size() == inventoryLocator.offset)
{
List<Value> fullInventory = new ArrayList<>();
for (int i = 0, maxi = inventoryLocator.inventory.size(); i < maxi; i++)
{
fullInventory.add(ListValue.fromItemStack(inventoryLocator.inventory.getStack(i)));
}
Value res = ListValue.wrap(fullInventory);
return (_c, _t) -> res;
}
int slot = (int)NumericValue.asNumber(lv.get(inventoryLocator.offset).evalValue(c)).getLong();
slot = NBTSerializableValue.validateSlot(slot, inventoryLocator.inventory);
if (slot == inventoryLocator.inventory.size())
return (_c, _t) -> Value.NULL;
Value res = ListValue.fromItemStack(inventoryLocator.inventory.getStack(slot));
return (_c, _t) -> res;
});
//inventory_set(<b,e>, <n>, <count>, <item>, <nbt>)
expression.addLazyFunction("inventory_set", -1, (c, t, lv) ->
{
CarpetContext cc = (CarpetContext) c;
NBTSerializableValue.InventoryLocator inventoryLocator = NBTSerializableValue.locateInventory(cc, lv, 0);
if (inventoryLocator == null)
return (_c, _t) -> Value.NULL;
if (lv.size() < inventoryLocator.offset+2)
throw new InternalExpressionException("'inventory_set' requires at least slot number and new stack size, and optional new item");
int slot = (int) NumericValue.asNumber(lv.get(inventoryLocator.offset+0).evalValue(c)).getLong();
slot = NBTSerializableValue.validateSlot(slot, inventoryLocator.inventory);
if (slot == inventoryLocator.inventory.size())
return (_c, _t) -> Value.NULL;
int count = (int) NumericValue.asNumber(lv.get(inventoryLocator.offset+1).evalValue(c)).getLong();
if (count == 0)
{
// clear slot
ItemStack removedStack = inventoryLocator.inventory.removeStack(slot);
syncPlayerInventory(inventoryLocator, slot);
//Value res = ListValue.fromItemStack(removedStack); // that tuple will be read only but cheaper if noone cares
return (_c, _t) -> ListValue.fromItemStack(removedStack);
}
if (lv.size() < inventoryLocator.offset+3)
{
ItemStack previousStack = inventoryLocator.inventory.getStack(slot);
ItemStack newStack = previousStack.copy();
newStack.setCount(count);
inventoryLocator.inventory.setStack(slot, newStack);
syncPlayerInventory(inventoryLocator, slot);
return (_c, _t) -> ListValue.fromItemStack(previousStack);
}
CompoundTag nbt = null; // skipping one argument
if (lv.size() > inventoryLocator.offset+3)
{
Value nbtValue = lv.get(inventoryLocator.offset+3).evalValue(c);
if (nbtValue instanceof NBTSerializableValue)
nbt = ((NBTSerializableValue)nbtValue).getCompoundTag();
else if (nbtValue instanceof NullValue)
nbt = null;
else
nbt = new NBTSerializableValue(nbtValue.getString()).getCompoundTag();
}
ItemStackArgument newitem = NBTSerializableValue.parseItem(
lv.get(inventoryLocator.offset+2).evalValue(c).getString(),
nbt
);
ItemStack previousStack = inventoryLocator.inventory.getStack(slot);
try
{
inventoryLocator.inventory.setStack(slot, newitem.createStack(count, false));
syncPlayerInventory(inventoryLocator, slot);
}
catch (CommandSyntaxException e)
{
throw new InternalExpressionException(e.getMessage());
}
return (_c, _t) -> ListValue.fromItemStack(previousStack);
});
//inventory_find(<b, e>, <item> or null (first empty slot), <start_from=0> ) -> <N> or null
expression.addLazyFunction("inventory_find", -1, (c, t, lv) ->
{
CarpetContext cc = (CarpetContext) c;
NBTSerializableValue.InventoryLocator inventoryLocator = NBTSerializableValue.locateInventory(cc, lv, 0);
if (inventoryLocator == null)
return (_c, _t) -> Value.NULL;
ItemStackArgument itemArg = null;
if (lv.size() > inventoryLocator.offset)
{
Value secondArg = lv.get(inventoryLocator.offset+0).evalValue(c);
if (!(secondArg instanceof NullValue))
itemArg = NBTSerializableValue.parseItem(secondArg.getString());
}
int startIndex = 0;
if (lv.size() > inventoryLocator.offset+1)
{
startIndex = (int) NumericValue.asNumber(lv.get(inventoryLocator.offset+1).evalValue(c)).getLong();
}
startIndex = NBTSerializableValue.validateSlot(startIndex, inventoryLocator.inventory);
for (int i = startIndex, maxi = inventoryLocator.inventory.size(); i < maxi; i++)
{
ItemStack stack = inventoryLocator.inventory.getStack(i);
if ( (itemArg == null && stack.isEmpty()) || (itemArg != null && itemArg.getItem().equals(stack.getItem())) )
{
Value res = new NumericValue(i);
return (_c, _t) -> res;
}
}
return (_c, _t) -> Value.NULL;
});
//inventory_remove(<b, e>, <item>, <amount=1>) -> bool
expression.addLazyFunction("inventory_remove", -1, (c, t, lv) -> {
CarpetContext cc = (CarpetContext) c;
NBTSerializableValue.InventoryLocator inventoryLocator = NBTSerializableValue.locateInventory(cc, lv, 0);
if (inventoryLocator == null)
return (_c, _t) -> Value.NULL;
if (lv.size() <= inventoryLocator.offset)
throw new InternalExpressionException("'inventory_remove' requires at least an item to be removed");
ItemStackArgument searchItem = NBTSerializableValue.parseItem(lv.get(inventoryLocator.offset).evalValue(c).getString());
int amount = 1;
if (lv.size() > inventoryLocator.offset+1)
{
amount = (int)NumericValue.asNumber(lv.get(inventoryLocator.offset+1).evalValue(c)).getLong();
}
// not enough
if (((amount == 1) && (!inventoryLocator.inventory.containsAny(Sets.newHashSet(searchItem.getItem()))))
|| (inventoryLocator.inventory.count(searchItem.getItem()) < amount))
{
return (_c, _t) -> Value.FALSE;
}
for (int i = 0, maxi = inventoryLocator.inventory.size(); i < maxi; i++)
{
ItemStack stack = inventoryLocator.inventory.getStack(i);
if (stack.isEmpty())
continue;
if (!stack.getItem().equals(searchItem.getItem()))
continue;
int left = stack.getCount()-amount;
if (left > 0)
{
stack.setCount(left);
inventoryLocator.inventory.setStack(i, stack);
syncPlayerInventory(inventoryLocator, i);
return (_c, _t) -> Value.TRUE;
}
else
{
inventoryLocator.inventory.removeStack(i);
syncPlayerInventory(inventoryLocator, i);
amount -= stack.getCount();
}
}
if (amount > 0)
throw new InternalExpressionException("Something bad happened - cannot pull all items from inventory");
return (_c, _t) -> Value.TRUE;
});
//inventory_drop(<b, e>, <n>, <amount=1, 0-whatever's there>) -> entity_item (and sets slot) or null if cannot
expression.addLazyFunction("drop_item", -1, (c, t, lv) ->
{
CarpetContext cc = (CarpetContext) c;
NBTSerializableValue.InventoryLocator inventoryLocator = NBTSerializableValue.locateInventory(cc, lv, 0);
if (inventoryLocator == null)
return (_c, _t) -> Value.NULL;
if (lv.size() == inventoryLocator.offset)
throw new InternalExpressionException("Slot number is required for inventory_drop");
int slot = (int)NumericValue.asNumber(lv.get(inventoryLocator.offset).evalValue(c)).getLong();
slot = NBTSerializableValue.validateSlot(slot, inventoryLocator.inventory);
if (slot == inventoryLocator.inventory.size())
return (_c, _t) -> Value.NULL;
int amount = 0;
if (lv.size() > inventoryLocator.offset+1)
amount = (int)NumericValue.asNumber(lv.get(inventoryLocator.offset+1).evalValue(c)).getLong();
if (amount < 0)
throw new InternalExpressionException("Cannot throw negative number of items");
ItemStack stack = inventoryLocator.inventory.getStack(slot);
if (stack == null || stack.isEmpty())
return (_c, _t) -> Value.ZERO;
if (amount == 0)
amount = stack.getCount();
ItemStack droppedStack = inventoryLocator.inventory.removeStack(slot, amount);
if (droppedStack.isEmpty())
{
return (_c, _t) -> Value.ZERO;
}
Object owner = inventoryLocator.owner;
ItemEntity item;
if (owner instanceof PlayerEntity)
{
item = ((PlayerEntity) owner).dropItem(droppedStack, false, true);
}
else if (owner instanceof LivingEntity)
{
LivingEntity villager = (LivingEntity)owner;
// stolen from LookTargetUtil.give((VillagerEntity)owner, droppedStack, (LivingEntity) owner);
double double_1 = villager.getY() - 0.30000001192092896D + (double)villager.getStandingEyeHeight();
item = new ItemEntity(villager.world, villager.getX(), double_1, villager.getZ(), droppedStack);
Vec3d vec3d_1 = villager.getRotationVec(1.0F).normalize().multiply(0.3);// new Vec3d(0, 0.3, 0);
item.setVelocity(vec3d_1);
item.setToDefaultPickupDelay();
cc.s.getWorld().spawnEntity(item);
}
else
{
Vec3d point = Vec3d.ofCenter(inventoryLocator.position); //pos+0.5v
item = new ItemEntity(cc.s.getWorld(), point.x, point.y, point.z, droppedStack);
item.setToDefaultPickupDelay();
cc.s.getWorld().spawnEntity(item);
}
Value res = new NumericValue(item.getStack().getCount());
return (_c, _t) -> res;
});
}
private static void syncPlayerInventory(NBTSerializableValue.InventoryLocator inventory, int int_1)
{
if (inventory.owner instanceof ServerPlayerEntity && !inventory.isEnder)
{
ServerPlayerEntity player = (ServerPlayerEntity) inventory.owner;
player.networkHandler.sendPacket(new ScreenHandlerSlotUpdateS2CPacket(
-2,
int_1,
inventory.inventory.getStack(int_1)
));
}
}
}
@@ -0,0 +1,63 @@
package carpet.script.api;
import carpet.script.CarpetContext;
import carpet.script.Expression;
import carpet.script.LazyValue;
import carpet.script.exception.InternalExpressionException;
import carpet.script.value.ListValue;
import carpet.script.value.MapValue;
import carpet.script.value.NumericValue;
import carpet.script.value.StringValue;
import carpet.script.value.Value;
import carpet.utils.SpawnReporter;
import it.unimi.dsi.fastutil.objects.Object2IntMap;
import net.minecraft.entity.SpawnGroup;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.world.SpawnHelper;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
public class Monitoring {
public static void apply(Expression expression)
{
// game processed snooper functions
expression.addLazyFunction("get_mob_counts", -1, (c, t, lv) ->
{
CarpetContext cc = (CarpetContext)c;
ServerWorld world = cc.s.getWorld();
SpawnHelper.Info info = world.getChunkManager().getSpawnInfo();
if (info == null) return LazyValue.NULL;
Object2IntMap<SpawnGroup> mobcounts = info.getGroupToCount();
int chunks = info.getSpawningChunkCount();
Value retVal;
if (lv.size() == 0)
{
Map<Value, Value> retDict = new HashMap<>();
for (SpawnGroup category: mobcounts.keySet())
{
int currentCap = (int)(category.getCapacity() * chunks / SpawnReporter.currentMagicNumber()); // MAGIC_NUMBER
retDict.put(
new StringValue(category.asString().toLowerCase(Locale.ROOT)),
ListValue.of(
new NumericValue(mobcounts.getInt(category)),
new NumericValue(currentCap))
);
}
retVal = MapValue.wrap(retDict);
}
else
{
String catString = lv.get(0).evalValue(c).getString();
SpawnGroup cat = SpawnGroup.byName(catString.toLowerCase(Locale.ROOT));
if (cat == null) throw new InternalExpressionException("Unreconized mob category: "+catString);
retVal = ListValue.of(
new NumericValue(mobcounts.getInt(cat)),
new NumericValue((int)(cat.getCapacity() * chunks / SpawnReporter.currentMagicNumber()))
);
}
return (_c, _t) -> retVal;
});
}
}
@@ -0,0 +1,149 @@
package carpet.script.api;
import carpet.script.CarpetContext;
import carpet.script.Expression;
import carpet.script.LazyValue;
import carpet.script.exception.InternalExpressionException;
import carpet.script.value.EntityValue;
import carpet.script.value.ListValue;
import carpet.script.value.NullValue;
import carpet.script.value.NumericValue;
import carpet.script.value.StringValue;
import carpet.script.value.Value;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.scoreboard.Scoreboard;
import net.minecraft.scoreboard.ScoreboardCriterion;
import net.minecraft.scoreboard.ScoreboardObjective;
import net.minecraft.scoreboard.ScoreboardPlayerScore;
import net.minecraft.text.LiteralText;
import java.util.stream.Collectors;
public class Scoreboards {
private static String getScoreboardKeyFromValue(Value keyValue)
{
if (keyValue instanceof EntityValue)
{
Entity e = ((EntityValue) keyValue).getEntity();
if (e instanceof PlayerEntity)
{
return e.getName().getString();
}
else
{
return e.getUuidAsString();
}
}
else
{
return keyValue.getString();
}
}
public static void apply(Expression expression)
{
// scoreboard(player,'objective')
// scoreboard(player, objective, newValue)
expression.addLazyFunction("scoreboard", -1, (c, t, lv) ->
{
CarpetContext cc = (CarpetContext)c;
Scoreboard scoreboard = cc.s.getMinecraftServer().getScoreboard();
if (lv.size()==0)
{
Value ret = ListValue.wrap(scoreboard.getObjectiveNames().stream().map(StringValue::new).collect(Collectors.toList()));
return (_c, _t) -> ret;
}
String objectiveName = lv.get(0).evalValue(c).getString();
ScoreboardObjective objective = scoreboard.getObjective(objectiveName);
if (objective == null) throw new InternalExpressionException("Unknown objective: "+objectiveName);
if (lv.size()==1)
{
Value ret = ListValue.wrap(scoreboard.getAllPlayerScores(objective).stream().map(s -> new StringValue(s.getPlayerName())).collect(Collectors.toList()));
return (_c, _t) -> ret;
}
String key = getScoreboardKeyFromValue(lv.get(1).evalValue(c));
if (!scoreboard.playerHasObjective(key, objective) && lv.size()==2)
return LazyValue.NULL;
ScoreboardPlayerScore scoreboardPlayerScore = scoreboard.getPlayerScore(key, objective);
Value retval = new NumericValue(scoreboardPlayerScore.getScore());
if (lv.size() > 2)
{
scoreboardPlayerScore.setScore(NumericValue.asNumber(lv.get(2).evalValue(c)).getInt());
}
return (_c, _t) -> retval;
});
expression.addLazyFunction("scoreboard_remove", -1, (c, t, lv)-> {
if (lv.size()==0) throw new InternalExpressionException("'scoreboard_remove' requires at least one parameter");
CarpetContext cc = (CarpetContext)c;
Scoreboard scoreboard = cc.s.getMinecraftServer().getScoreboard();
String objectiveName = lv.get(0).evalValue(c).getString();
ScoreboardObjective objective = scoreboard.getObjective(objectiveName);
if (objective == null)
return LazyValue.FALSE;
if (lv.size() == 1)
{
scoreboard.removeObjective(objective);
return LazyValue.TRUE;
}
String key = getScoreboardKeyFromValue(lv.get(1).evalValue(c));
if (!scoreboard.playerHasObjective(key, objective)) return LazyValue.NULL;
ScoreboardPlayerScore scoreboardPlayerScore = scoreboard.getPlayerScore(key, objective);
Value previous = new NumericValue(scoreboardPlayerScore.getScore());
scoreboard.resetPlayerScore(key, objective);
return (c_, t_) -> previous;
});
// objective_add('lvl','level')
// objective_add('counter')
expression.addLazyFunction("scoreboard_add", -1, (c, t, lv)-> {
CarpetContext cc = (CarpetContext)c;
Scoreboard scoreboard = cc.s.getMinecraftServer().getScoreboard();
if (lv.size() == 0 || lv.size()>2) throw new InternalExpressionException("'scoreboard_add' should have one or two parameters");
String objectiveName = lv.get(0).evalValue(c).getString();
ScoreboardCriterion criterion;
if (lv.size() == 1 )
{
criterion = ScoreboardCriterion.DUMMY;
}
else
{
String critetionName = lv.get(1).evalValue(c).getString();
criterion = ScoreboardCriterion.createStatCriterion(critetionName).orElse(null);
if (criterion==null)
{
throw new InternalExpressionException("Unknown scoreboard criterion: "+critetionName);
}
}
ScoreboardObjective objective = scoreboard.getObjective(objectiveName);
if (objective != null)
return LazyValue.FALSE;
scoreboard.addObjective(objectiveName, criterion, new LiteralText(objectiveName), criterion.getCriterionType());
return LazyValue.TRUE;
});
expression.addLazyFunction("scoreboard_display", 2, (c, t, lv) ->
{
CarpetContext cc = (CarpetContext)c;
Scoreboard scoreboard = cc.s.getMinecraftServer().getScoreboard();
String location = lv.get(0).evalValue(c).getString();
int slot = Scoreboard.getDisplaySlotId(location);
if (slot < 0) throw new InternalExpressionException("Invalid objective slot: "+location);
Value target = lv.get(1).evalValue(c);
if (target instanceof NullValue)
{
scoreboard.setObjectiveSlot(slot, null);
return (_c, _t) -> new NumericValue(slot);
}
String objectiveString = target.getString();
ScoreboardObjective objective = scoreboard.getObjective(objectiveString);
if (objective == null) throw new InternalExpressionException("Objective doesn't exist: "+objectiveString);
scoreboard.setObjectiveSlot(slot, objective);
return (_c, _t) -> new NumericValue(slot);
});
}
}
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,116 @@
package carpet.script.language;
import carpet.script.Context;
import carpet.script.Expression;
import carpet.script.exception.InternalExpressionException;
import carpet.script.value.ListValue;
import carpet.script.value.NumericValue;
import carpet.script.value.Value;
public class Arithmetic {
public static final Value PI = new NumericValue(
"3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679");
public static final Value euler = new NumericValue(
"2.71828182845904523536028747135266249775724709369995957496696762772407663");
public static void apply(Expression expression)
{
expression.addLazyFunction("not", 1, (c, t, lv) -> lv.get(0).evalValue(c, Context.BOOLEAN).getBoolean() ? ((cc, tt) -> Value.FALSE) : ((cc, tt) -> Value.TRUE));
expression.addUnaryFunction("fact", (v) ->
{
long number = NumericValue.asNumber(v).getLong();
long factorial = 1;
for (int i = 1; i <= number; i++)
{
factorial = factorial * i;
}
return new NumericValue(factorial);
});
expression.addMathematicalUnaryFunction("sin", (d) -> Math.sin(Math.toRadians(d)));
expression.addMathematicalUnaryFunction("cos", (d) -> Math.cos(Math.toRadians(d)));
expression.addMathematicalUnaryFunction("tan", (d) -> Math.tan(Math.toRadians(d)));
expression.addMathematicalUnaryFunction("asin", (d) -> Math.toDegrees(Math.asin(d)));
expression.addMathematicalUnaryFunction("acos", (d) -> Math.toDegrees(Math.acos(d)));
expression.addMathematicalUnaryFunction("atan", (d) -> Math.toDegrees(Math.atan(d)));
expression.addMathematicalBinaryFunction("atan2", (d, d2) -> Math.toDegrees(Math.atan2(d, d2)) );
expression.addMathematicalUnaryFunction("sinh", Math::sinh );
expression.addMathematicalUnaryFunction("cosh", Math::cosh );
expression.addMathematicalUnaryFunction("tanh", Math::tanh );
expression.addMathematicalUnaryFunction("sec", (d) -> 1.0 / Math.cos(Math.toRadians(d)) ); // Formula: sec(x) = 1 / cos(x)
expression.addMathematicalUnaryFunction("csc", (d) -> 1.0 / Math.sin(Math.toRadians(d)) ); // Formula: csc(x) = 1 / sin(x)
expression.addMathematicalUnaryFunction("sech", (d) -> 1.0 / Math.cosh(d) ); // Formula: sech(x) = 1 / cosh(x)
expression.addMathematicalUnaryFunction("csch", (d) -> 1.0 / Math.sinh(d) ); // Formula: csch(x) = 1 / sinh(x)
expression.addMathematicalUnaryFunction("cot", (d) -> 1.0 / Math.tan(Math.toRadians(d)) ); // Formula: cot(x) = cos(x) / sin(x) = 1 / tan(x)
expression.addMathematicalUnaryFunction("acot", (d) -> Math.toDegrees(Math.atan(1.0 / d)) );// Formula: acot(x) = atan(1/x)
expression.addMathematicalUnaryFunction("coth", (d) -> 1.0 / Math.tanh(d) ); // Formula: coth(x) = 1 / tanh(x)
expression.addMathematicalUnaryFunction("asinh", (d) -> Math.log(d + (Math.sqrt(Math.pow(d, 2) + 1)))); // Formula: asinh(x) = ln(x + sqrt(x^2 + 1))
expression.addMathematicalUnaryFunction("acosh", (d) -> Math.log(d + (Math.sqrt(Math.pow(d, 2) - 1)))); // Formula: acosh(x) = ln(x + sqrt(x^2 - 1))
expression.addMathematicalUnaryFunction("atanh", (d) -> // Formula: atanh(x) = 0.5*ln((1 + x)/(1 - x))
{
if (Math.abs(d) > 1 || Math.abs(d) == 1)
throw new InternalExpressionException("Number must be |x| < 1");
return 0.5 * Math.log((1 + d) / (1 - d));
});
expression.addMathematicalUnaryFunction("rad", Math::toRadians);
expression.addMathematicalUnaryFunction("deg", Math::toDegrees);
expression.addMathematicalUnaryFunction("ln", Math::log);
expression.addMathematicalUnaryFunction("ln1p", Math::log1p);
expression.addMathematicalUnaryFunction("log10", Math::log10);
expression.addMathematicalUnaryFunction("log", a -> Math.log(a)/Math.log(2));
expression.addMathematicalUnaryFunction("log1p", x -> Math.log1p(x)/Math.log(2));
expression.addMathematicalUnaryFunction("sqrt", Math::sqrt);
expression.addMathematicalUnaryFunction("abs", Math::abs);
expression.addMathematicalUnaryFunction("round", (d) -> (double)Math.round(d));
expression.addMathematicalUnaryFunction("floor", Math::floor);
expression.addMathematicalUnaryFunction("ceil", Math::ceil);
expression.addLazyFunction("mandelbrot", 3, (c, t, lv) -> {
double a0 = NumericValue.asNumber(lv.get(0).evalValue(c)).getDouble();
double b0 = NumericValue.asNumber(lv.get(1).evalValue(c)).getDouble();
long maxiter = NumericValue.asNumber(lv.get(2).evalValue(c)).getLong();
double a = 0.0D;
double b = 0.0D;
long iter = 0;
while(a*a+b*b<4 && iter < maxiter)
{
double temp = a*a-b*b+a0;
b = 2*a*b+b0;
a = temp;
iter++;
}
long iFinal = iter;
return (cc, tt) -> new NumericValue(iFinal);
});
expression.addFunction("max", (lv) ->
{
if (lv.size() == 0)
throw new InternalExpressionException("'max' requires at least one parameter");
Value max = null;
if (lv.size()==1 && lv.get(0) instanceof ListValue)
lv = ((ListValue) lv.get(0)).getItems();
for (Value parameter : lv)
{
if (max == null || parameter.compareTo(max) > 0) max = parameter;
}
return max;
});
expression.addFunction("min", (lv) ->
{
if (lv.size() == 0)
throw new InternalExpressionException("'min' requires at least one parameter");
Value min = null;
if (lv.size()==1 && lv.get(0) instanceof ListValue)
lv = ((ListValue) lv.get(0)).getItems();
for (Value parameter : lv)
{
if (min == null || parameter.compareTo(min) < 0) min = parameter;
}
return min;
});
expression.addUnaryFunction("relu", (v) -> v.compareTo(Value.ZERO) < 0 ? Value.ZERO : v);
}
}
@@ -0,0 +1,70 @@
package carpet.script.language;
import carpet.script.Context;
import carpet.script.Expression;
import carpet.script.LazyValue;
import carpet.script.exception.ExitStatement;
import carpet.script.exception.InternalExpressionException;
import carpet.script.exception.ThrowStatement;
import carpet.script.value.Value;
public class ControlFlow {
public static void apply(Expression expression) // public just to get the javadoc right
{
expression.addLazyBinaryOperator(";", Operators.precedence.get("nextop;"), true, (c, t, lv1, lv2) ->
{
lv1.evalValue(c, Context.VOID);
Value v2 = lv2.evalValue(c, t);
return (cc, tt) -> v2;
});
// if(cond1, expr1, cond2, expr2, ..., ?default) => value
expression.addLazyFunction("if", -1, (c, t, lv) ->
{
if ( lv.size() < 2 )
throw new InternalExpressionException("'if' statement needs to have at least one condition and one case");
for (int i=0; i<lv.size()-1; i+=2)
{
if (lv.get(i).evalValue(c, Context.BOOLEAN).getBoolean())
{
//int iFinal = i;
Value ret = lv.get(i+1).evalValue(c, t);
return (cc, tt) -> ret;
}
}
if (lv.size()%2 == 1)
{
Value ret = lv.get(lv.size() - 1).evalValue(c, t);
return (cc, tt) -> ret;
}
return (cc, tt) -> Value.NULL;
});
expression.addFunction("exit", (lv) -> { throw new ExitStatement(lv.size()==0?Value.NULL:lv.get(0)); });
expression.addFunction("throw", (lv)-> {throw new ThrowStatement(lv.size()==0?Value.NULL:lv.get(0)); });
expression.addLazyFunction("try", -1, (c, t, lv) ->
{
if (lv.size()==0)
throw new InternalExpressionException("'try' needs at least an expression block");
try
{
Value retval = lv.get(0).evalValue(c, t);
return (c_, t_) -> retval;
}
catch (ThrowStatement ret)
{
if (lv.size() == 1)
return (c_, t_) -> Value.NULL;
LazyValue __ = c.getVariable("_");
c.setVariable("_", (__c, __t) -> ret.retval.reboundedTo("_"));
Value val = lv.get(1).evalValue(c, t);
c.setVariable("_",__);
return (c_, t_) -> val;
}
});
}
}
@@ -0,0 +1,321 @@
package carpet.script.language;
import carpet.script.Context;
import carpet.script.Expression;
import carpet.script.LazyValue;
import carpet.script.exception.InternalExpressionException;
import carpet.script.value.ContainerValueInterface;
import carpet.script.value.LContainerValue;
import carpet.script.value.LazyListValue;
import carpet.script.value.ListValue;
import carpet.script.value.MapValue;
import carpet.script.value.NumericValue;
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.stream.Collectors;
public class DataStructures {
public static void apply(Expression expression)
{
expression.addFunction("l", lv ->
{
if (lv.size() == 1 && lv.get(0) instanceof LazyListValue)
return ListValue.wrap(((LazyListValue) lv.get(0)).unroll());
return new ListValue.ListConstructorValue(lv);
});
expression.addFunction("join", (lv) ->
{
if (lv.size() < 2)
throw new InternalExpressionException("'join' takes at least 2 arguments");
String delimiter = lv.get(0).getString();
List<Value> toJoin;
if (lv.size()==2 && lv.get(1) instanceof LazyListValue)
{
toJoin = ((LazyListValue) lv.get(1)).unroll();
}
else if (lv.size() == 2 && lv.get(1) instanceof ListValue)
{
toJoin = new ArrayList<>(((ListValue)lv.get(1)).getItems());
}
else
{
toJoin = lv.subList(1,lv.size());
}
return new StringValue(toJoin.stream().map(Value::getString).collect(Collectors.joining(delimiter)));
});
expression.addBinaryFunction("split", (d, v) -> {
String delimiter = d.getString();
String hwat = v.getString();
return ListValue.wrap(Arrays.stream(hwat.split(delimiter)).map(StringValue::new).collect(Collectors.toList()));
});
expression.addFunction("slice", (lv) -> {
if (lv.size() != 2 && lv.size() != 3)
throw new InternalExpressionException("'slice' takes 2 or 3 arguments");
Value hwat = lv.get(0);
long from = NumericValue.asNumber(lv.get(1)).getLong();
long to = -1;
if (lv.size()== 3)
to = NumericValue.asNumber(lv.get(2)).getLong();
return hwat.slice(from, to);
});
expression.addFunction("sort", (lv) ->
{
List<Value> toSort = lv;
if (lv.size()==1 && lv.get(0) instanceof ListValue)
{
toSort = new ArrayList<>(((ListValue)lv.get(0)).getItems());
}
Collections.sort(toSort);
return ListValue.wrap(toSort);
});
expression.addLazyFunction("sort_key", -1, (c, t, lv) -> //get working with iterators
{
if (lv.size() == 0)
throw new InternalExpressionException("First argument for 'sort_key' should be a List");
Value v = lv.get(0).evalValue(c);
if (!(v instanceof ListValue))
throw new InternalExpressionException("First argument for 'sort_key' should be a List");
List<Value> toSort = new ArrayList<>(((ListValue) v).getItems());
if (lv.size()==1)
{
Collections.shuffle(toSort);
Value ret = ListValue.wrap(toSort);
return (_c, _t) -> ret;
}
LazyValue sortKey = lv.get(1);
//scoping
LazyValue __ = c.getVariable("_");
Collections.sort(toSort,(v1, v2) -> {
c.setVariable("_",(cc, tt) -> v1);
Value ev1 = sortKey.evalValue(c);
c.setVariable("_",(cc, tt) -> v2);
Value ev2 = sortKey.evalValue(c);
return ev1.compareTo(ev2);
});
//revering scope
c.setVariable("_", __);
return (cc, tt) -> ListValue.wrap(toSort);
});
expression.addFunction("range", (lv) ->
{
long from = 0;
long to = 0;
long step = 1;
int argsize = lv.size();
if (argsize == 0 || argsize > 3)
throw new InternalExpressionException("'range' accepts from 1 to 3 arguments, not "+argsize);
to = NumericValue.asNumber(lv.get(0)).getLong();
if (lv.size() > 1)
{
from = to;
to = NumericValue.asNumber(lv.get(1)).getLong();
if (lv.size() > 2)
{
step = NumericValue.asNumber(lv.get(2)).getLong();
}
}
return LazyListValue.range(from, to, step);
});
expression.addLazyFunction("m", -1, (c, t, llv) ->
{
List<Value> lv = new ArrayList<>();
for (LazyValue lazyParam : llv) {
lv.add(lazyParam.evalValue(c, Context.MAPDEF)); // none type default by design
}
Value ret;
if (lv.size() == 1 && lv.get(0) instanceof LazyListValue)
ret = new MapValue(((LazyListValue) lv.get(0)).unroll());
else
ret = new MapValue(lv);
return (cc, tt) -> ret;
});
expression.addUnaryFunction("keys", v -> {
if (v instanceof MapValue)
return new ListValue(((MapValue) v).getMap().keySet());
return Value.NULL;
});
expression.addUnaryFunction("values", v -> {
if (v instanceof MapValue)
return new ListValue(((MapValue) v).getMap().values());
return Value.NULL;
});
expression.addUnaryFunction("pairs", v -> {
if (v instanceof MapValue)
return ListValue.wrap(((MapValue) v).getMap().entrySet().stream().map(
(p) -> ListValue.of(p.getKey(), p.getValue())
).collect(Collectors.toList()));
return Value.NULL;
});
expression.addLazyBinaryOperator(":", Operators.precedence.get("attribute~:"),true, (c, t, container_lv, key_lv) ->
{
Value container = container_lv.evalValue(c);
if (container instanceof LContainerValue)
{
ContainerValueInterface outerContainer = ((LContainerValue) container).getContainer();
if (outerContainer == null)
{
Value innerLValue = new LContainerValue(null, null);
return (cc, tt) -> innerLValue;
}
Value innerContainer = outerContainer.get(((LContainerValue) container).getAddress());
if (!(innerContainer instanceof ContainerValueInterface))
{
Value innerLValue = new LContainerValue(null, null);
return (cc, tt) -> innerLValue;
}
Value innerLValue = new LContainerValue((ContainerValueInterface) innerContainer, key_lv.evalValue(c));
return (cc, tt) -> innerLValue;
}
if (!(container instanceof ContainerValueInterface))
if (t == Context.LVALUE)
return (cc, tt) -> new LContainerValue(null, null);
else
return (cc, tt) -> Value.NULL;
Value address = key_lv.evalValue(c);
if (t != Context.LVALUE)
{
Value retVal = ((ContainerValueInterface) container).get(address);
return (cc, ct) -> retVal;
}
Value retVal = new LContainerValue((ContainerValueInterface) container, address);
return (cc, ct) -> retVal;
});
expression.addLazyFunction("get", -1, (c, t, lv) ->
{
if (lv.size() == 0)
throw new InternalExpressionException("'get' requires parameters");
if (lv.size() == 1)
{
Value v = lv.get(0).evalValue(c, Context.LVALUE);
if (!(v instanceof LContainerValue))
return (cc, tt) -> Value.NULL;
ContainerValueInterface container = ((LContainerValue) v).getContainer();
if (container == null)
return (cc, tt) -> Value.NULL;
Value ret = container.get(((LContainerValue) v).getAddress());
return (cc, tt) -> ret;
}
Value container = lv.get(0).evalValue(c);
for (int i = 1; i < lv.size(); i++)
{
if (!(container instanceof ContainerValueInterface)) return (cc, tt) -> Value.NULL;
container = ((ContainerValueInterface) container).get(lv.get(i).evalValue(c));
}
if (container == null)
return (cc, tt) -> Value.NULL;
Value finalContainer = container;
return (cc, tt) -> finalContainer;
});
expression.addLazyFunction("has", -1, (c, t, lv) ->
{
if (lv.size() == 0)
throw new InternalExpressionException("'has' requires parameters");
if (lv.size() == 1)
{
Value v = lv.get(0).evalValue(c, Context.LVALUE);
if (!(v instanceof LContainerValue))
return (cc, tt) -> Value.NULL;
ContainerValueInterface container = ((LContainerValue) v).getContainer();
if (container == null)
return (cc, tt) -> Value.NULL;
Value ret = new NumericValue(container.has(((LContainerValue) v).getAddress()));
return (cc, tt) -> ret;
}
Value container = lv.get(0).evalValue(c);
for (int i = 1; i < lv.size()-1; i++)
{
if (!(container instanceof ContainerValueInterface)) return (cc, tt) -> Value.NULL;
container = ((ContainerValueInterface) container).get(lv.get(i).evalValue(c));
}
if (!(container instanceof ContainerValueInterface))
return (cc, tt) -> Value.NULL;
Value ret = new NumericValue(((ContainerValueInterface) container).has(lv.get(lv.size()-1).evalValue(c)));
return (cc, tt) -> ret;
});
expression.addLazyFunction("put", -1, (c, t, lv) ->
{
if(lv.size()<2)
{
throw new InternalExpressionException("'put' takes at least three arguments, a container, address, and values to insert at that index");
}
Value container = lv.get(0).evalValue(c, Context.LVALUE);
if (container instanceof LContainerValue)
{
ContainerValueInterface internalContainer = ((LContainerValue) container).getContainer();
if (internalContainer == null)
{
return (cc, tt) -> Value.NULL;
}
Value address = ((LContainerValue) container).getAddress();
Value what = lv.get(1).evalValue(c);
Value retVal = new NumericValue( (lv.size() > 2)
? internalContainer.put(address, what, lv.get(2).evalValue(c))
: internalContainer.put(address, what));
return (cc, tt) -> retVal;
}
if(lv.size()<3)
{
throw new InternalExpressionException("'put' takes at least three arguments, a container, address, and values to insert at that index");
}
if (!(container instanceof ContainerValueInterface))
{
return (cc, tt) -> Value.NULL;
}
Value where = lv.get(1).evalValue(c);
Value what = lv.get(2).evalValue(c);
Value retVal = new NumericValue( (lv.size()>3)
? ((ContainerValueInterface) container).put(where, what, lv.get(3).evalValue(c))
: ((ContainerValueInterface) container).put(where, what));
return (cc, tt) -> retVal;
});
expression.addLazyFunction("delete", -1, (c, t, lv) ->
{
if (lv.size() == 0)
throw new InternalExpressionException("'delete' requires parameters");
if (lv.size() == 1)
{
Value v = lv.get(0).evalValue(c, Context.LVALUE);
if (!(v instanceof LContainerValue))
return (cc, tt) -> Value.NULL;
ContainerValueInterface container = ((LContainerValue) v).getContainer();
if (container == null)
return (cc, tt) -> Value.NULL;
Value ret = new NumericValue(container.delete(((LContainerValue) v).getAddress()));
return (cc, tt) -> ret;
}
Value container = lv.get(0).evalValue(c);
for (int i = 1; i < lv.size()-1; i++)
{
if (!(container instanceof ContainerValueInterface)) return (cc, tt) -> Value.NULL;
container = ((ContainerValueInterface) container).get(lv.get(i).evalValue(c));
}
if (!(container instanceof ContainerValueInterface))
return (cc, tt) -> Value.NULL;
Value ret = new NumericValue(((ContainerValueInterface) container).delete(lv.get(lv.size()-1).evalValue(c)));
return (cc, tt) -> ret;
});
}
}
@@ -0,0 +1,98 @@
package carpet.script.language;
import carpet.script.Context;
import carpet.script.Expression;
import carpet.script.LazyValue;
import carpet.script.argument.FunctionArgument;
import carpet.script.exception.InternalExpressionException;
import carpet.script.exception.ReturnStatement;
import carpet.script.value.FunctionSignatureValue;
import carpet.script.value.FunctionValue;
import carpet.script.value.GlobalValue;
import carpet.script.value.ListValue;
import carpet.script.value.StringValue;
import carpet.script.value.Value;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.stream.Collectors;
public class Functions {
public static void apply(Expression expression) // public just to get the javadoc right
{
// artificial construct to handle user defined functions and function definitions
expression.addLazyFunction("import", -1, (c, t, lv) ->
{
if (lv.size() < 1) throw new InternalExpressionException("'import' needs at least a module name to import, and list of values to import");
String moduleName = lv.get(0).evalValue(c).getString();
c.host.importModule(c, moduleName);
moduleName = moduleName.toLowerCase(Locale.ROOT);
if (lv.size() > 1)
c.host.importNames(c, expression.module, moduleName, lv.subList(1, lv.size()).stream().map((l) -> l.evalValue(c).getString()).collect(Collectors.toList()));
if (t == Context.VOID)
return LazyValue.NULL;
ListValue list = ListValue.wrap(c.host.availableImports(moduleName).map(StringValue::new).collect(Collectors.toList()));
return (cc, tt) -> list;
});
expression.addLazyFunctionWithDelegation("call",-1, (c, t, expr, tok, lv) -> { // adjust based on c
if (lv.size() == 0)
throw new InternalExpressionException("'call' expects at least function name to call");
//lv.remove(lv.size()-1); // aint gonna cut it // maybe it will because of the eager eval changes
if (t != Context.SIGNATURE) // just call the function
{
FunctionArgument functionArgument = FunctionArgument.findIn(c, expression.module, lv, 0, false, true);
FunctionValue fun = functionArgument.function;
Value retval = fun.callInContext(expr, c, t, fun.getExpression(), fun.getToken(), functionArgument.args).evalValue(c);
return (cc, tt) -> retval; ///!!!! dono might need to store expr and token in statics? (e? t?)
}
// gimme signature
String name = lv.get(0).evalValue(c).getString();
List<String> args = new ArrayList<>();
List<String> globals = new ArrayList<>();
for (int i = 1; i < lv.size(); i++)
{
Value v = lv.get(i).evalValue(c, Context.LOCALIZATION);
if (!v.isBound())
{
throw new InternalExpressionException("Only variables can be used in function signature, not " + v.getString());
}
if (v instanceof GlobalValue)
{
globals.add(v.boundVariable);
}
else
{
args.add(v.boundVariable);
}
}
Value retval = new FunctionSignatureValue(name, args, globals);
return (cc, tt) -> retval;
});
expression.addLazyFunction("outer", 1, (c, t, lv) -> {
if (t != Context.LOCALIZATION)
throw new InternalExpressionException("Outer scoping of variables is only possible in function signatures");
return (cc, tt) -> new GlobalValue(lv.get(0).evalValue(c));
});
//assigns const procedure to the lhs, returning its previous value
expression.addLazyBinaryOperatorWithDelegation("->", Operators.precedence.get("def->"), false, (c, type, e, t, lv1, lv2) ->
{
if (type == Context.MAPDEF)
{
Value result = ListValue.of(lv1.evalValue(c), lv2.evalValue(c));
return (cc, tt) -> result;
}
Value v1 = lv1.evalValue(c, Context.SIGNATURE);
if (!(v1 instanceof FunctionSignatureValue))
throw new InternalExpressionException("'->' operator requires a function signature on the LHS");
FunctionSignatureValue sign = (FunctionSignatureValue) v1;
Value result = expression.addContextFunction(c, sign.getName(), e, t, sign.getArgs(), sign.getGlobals(), lv2);
return (cc, tt) -> result;
});
expression.addFunction("return", (lv) -> { throw new ReturnStatement(lv.size()==0?Value.NULL:lv.get(0));} );
}
}
@@ -0,0 +1,410 @@
package carpet.script.language;
import carpet.script.Context;
import carpet.script.Expression;
import carpet.script.LazyValue;
import carpet.script.exception.BreakStatement;
import carpet.script.exception.ContinueStatement;
import carpet.script.exception.InternalExpressionException;
import carpet.script.value.AbstractListValue;
import carpet.script.value.ListValue;
import carpet.script.value.NumericValue;
import carpet.script.value.Value;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Loops {
public static void apply(Expression expression)
{
// condition and expression will get a bound '_i'
// returns last successful expression or false
// while(cond, limit, expr) => ??
expression.addFunction("break", lv -> {
if (lv.size()==0) throw new BreakStatement(null);
if (lv.size()==1) throw new BreakStatement(lv.get(0));
throw new InternalExpressionException("'break' can only be called with zero or one argument");
});
expression.addFunction("continue", lv -> {
if (lv.size()==0) throw new ContinueStatement(null);
if (lv.size()==1) throw new ContinueStatement(lv.get(0));
throw new InternalExpressionException("'continue' can only be called with zero or one argument");
});
expression.addLazyFunction("while", 3, (c, t, lv) ->
{
long limit = NumericValue.asNumber(lv.get(1).evalValue(c)).getLong();
LazyValue condition = lv.get(0);
LazyValue expr = lv.get(2);
long i = 0;
Value lastOne = Value.NULL;
//scoping
LazyValue _val = c.getVariable("_");
c.setVariable("_",(cc, tt) -> new NumericValue(0).bindTo("_"));
while (i<limit && condition.evalValue(c, Context.BOOLEAN).getBoolean() )
{
try
{
lastOne = expr.evalValue(c, t);
}
catch (BreakStatement | ContinueStatement stmt)
{
if (stmt.retval != null) lastOne = stmt.retval;
if (stmt instanceof BreakStatement) break;
}
i++;
long seriously = i;
c.setVariable("_", (cc, tt) -> new NumericValue(seriously).bindTo("_"));
}
//revering scope
c.setVariable("_", _val);
Value lastValueNoKidding = lastOne;
return (cc, tt) -> lastValueNoKidding;
});
// loop(Num, expr) => last_value
// expr receives bounded variable '_' indicating iteration
expression.addLazyFunction("loop", 2, (c, t, lv) ->
{
long limit = NumericValue.asNumber(lv.get(0).evalValue(c)).getLong();
Value lastOne = Value.NULL;
LazyValue expr = lv.get(1);
//scoping
LazyValue _val = c.getVariable("_");
for (long i=0; i < limit; i++)
{
long whyYouAsk = i;
c.setVariable("_", (cc, tt) -> new NumericValue(whyYouAsk).bindTo("_"));
try
{
lastOne = expr.evalValue(c, t);
}
catch (BreakStatement | ContinueStatement stmt)
{
if (stmt.retval != null) lastOne = stmt.retval;
if (stmt instanceof BreakStatement) break;
}
}
//revering scope
c.setVariable("_", _val);
Value trulyLastOne = lastOne;
return (cc, tt) -> trulyLastOne;
});
// map(list or Num, expr) => list_results
// receives bounded variable '_' with the expression
expression.addLazyFunction("map", 2, (c, t, lv) ->
{
Value rval= lv.get(0).evalValue(c);
if (!(rval instanceof AbstractListValue))
throw new InternalExpressionException("First argument of 'map' function should be a list or iterator");
Iterator<Value> iterator = ((AbstractListValue) rval).iterator();
LazyValue expr = lv.get(1);
//scoping
LazyValue _val = c.getVariable("_");
LazyValue _iter = c.getVariable("_i");
List<Value> result = new ArrayList<>();
for (int i=0; iterator.hasNext(); i++)
{
Value next = iterator.next();
String var = next.boundVariable;
next.bindTo("_");
int doYouReally = i;
c.setVariable("_", (cc, tt) -> next);
c.setVariable("_i", (cc, tt) -> new NumericValue(doYouReally).bindTo("_i"));
try
{
result.add(expr.evalValue(c));
}
catch (BreakStatement | ContinueStatement stmt)
{
if (stmt.retval != null) result.add(stmt.retval);
if (stmt instanceof BreakStatement)
{
next.boundVariable = var;
break;
}
}
next.boundVariable = var;
}
((AbstractListValue) rval).fatality();
Value ret = ListValue.wrap(result);
//revering scope
c.setVariable("_", _val);
c.setVariable("_i", _iter);
return (cc, tt) -> ret;
});
// grep(list or num, expr) => list
// receives bounded variable '_' with the expression, and "_i" with index
// produces list of values for which the expression is true
expression.addLazyFunction("filter", 2, (c, t, lv) ->
{
Value rval= lv.get(0).evalValue(c);
if (!(rval instanceof AbstractListValue))
throw new InternalExpressionException("First argument of 'filter' function should be a list or iterator");
Iterator<Value> iterator = ((AbstractListValue) rval).iterator();
LazyValue expr = lv.get(1);
//scoping
LazyValue _val = c.getVariable("_");
LazyValue _iter = c.getVariable("_i");
List<Value> result = new ArrayList<>();
for (int i=0; iterator.hasNext(); i++)
{
Value next = iterator.next();
String var = next.boundVariable;
next.bindTo("_");
int seriously = i;
c.setVariable("_", (cc, tt) -> next);
c.setVariable("_i", (cc, tt) -> new NumericValue(seriously).bindTo("_i"));
try
{
if(expr.evalValue(c, Context.BOOLEAN).getBoolean())
result.add(next);
}
catch (BreakStatement | ContinueStatement stmt)
{
if (stmt.retval != null && stmt.retval.getBoolean()) result.add(next);
if (stmt instanceof BreakStatement)
{
next.boundVariable = var;
break;
}
}
next.boundVariable = var;
}
((AbstractListValue) rval).fatality();
Value ret = ListValue.wrap(result);
//revering scope
c.setVariable("_", _val);
c.setVariable("_i", _iter);
return (cc, tt) -> ret;
});
// first(list, expr) => elem or null
// receives bounded variable '_' with the expression, and "_i" with index
// returns first element on the list for which the expr is true
expression.addLazyFunction("first", 2, (c, t, lv) ->
{
Value rval= lv.get(0).evalValue(c);
if (!(rval instanceof AbstractListValue))
throw new InternalExpressionException("First argument of 'first' function should be a list or iterator");
Iterator<Value> iterator = ((AbstractListValue) rval).iterator();
LazyValue expr = lv.get(1);
//scoping
LazyValue _val = c.getVariable("_");
LazyValue _iter = c.getVariable("_i");
Value result = Value.NULL;
for (int i=0; iterator.hasNext(); i++)
{
Value next = iterator.next();
String var = next.boundVariable;
next.bindTo("_");
int seriously = i;
c.setVariable("_", (cc, tt) -> next);
c.setVariable("_i", (cc, tt) -> new NumericValue(seriously).bindTo("_i"));
try
{
if(expr.evalValue(c, Context.BOOLEAN).getBoolean())
{
result = next;
next.boundVariable = var;
break;
}
}
catch (BreakStatement stmt)
{
result = stmt.retval == null? next : stmt.retval;
next.boundVariable = var;
break;
}
catch (ContinueStatement ignored)
{
throw new InternalExpressionException("'continue' inside 'first' function has no sense");
}
next.boundVariable = var;
}
//revering scope
((AbstractListValue) rval).fatality();
Value whyWontYouTrustMeJava = result;
c.setVariable("_", _val);
c.setVariable("_i", _iter);
return (cc, tt) -> whyWontYouTrustMeJava;
});
// all(list, expr) => boolean
// receives bounded variable '_' with the expression, and "_i" with index
// returns true if expr is true for all items
expression.addLazyFunction("all", 2, (c, t, lv) ->
{
Value rval= lv.get(0).evalValue(c);
if (!(rval instanceof AbstractListValue))
throw new InternalExpressionException("First argument of 'all' function should be a list or iterator");
Iterator<Value> iterator = ((AbstractListValue) rval).iterator();
LazyValue expr = lv.get(1);
//scoping
LazyValue _val = c.getVariable("_");
LazyValue _iter = c.getVariable("_i");
LazyValue result = LazyValue.TRUE;
for (int i=0; iterator.hasNext(); i++)
{
Value next = iterator.next();
String var = next.boundVariable;
next.bindTo("_");
int seriously = i;
c.setVariable("_", (cc, tt) -> next);
c.setVariable("_i", (cc, tt) -> new NumericValue(seriously).bindTo("_i"));
if(!expr.evalValue(c, Context.BOOLEAN).getBoolean())
{
result = LazyValue.FALSE;
next.boundVariable = var;
break;
}
next.boundVariable = var;
}
//revering scope
((AbstractListValue) rval).fatality();
c.setVariable("_", _val);
c.setVariable("_i", _iter);
return result;
});
// runs traditional for(init, condition, increment, body) tri-argument for loop with body in between
expression.addLazyFunction("c_for", 4, (c, t, lv) -> {
LazyValue initial = lv.get(0);
LazyValue condition = lv.get(1);
LazyValue increment = lv.get(2);
LazyValue body = lv.get(3);
int iterations = 0;
for (initial.evalValue(c, Context.VOID); condition.evalValue(c, Context.BOOLEAN).getBoolean(); increment.evalValue(c, Context.VOID))
{
try
{
body.evalValue(c, Context.VOID);
}
catch (BreakStatement stmt)
{
break;
}
catch (ContinueStatement ignored)
{
}
iterations++;
}
int finalIterations = iterations;
return (cc, tt) -> new NumericValue(finalIterations);
});
// similar to map, but returns total number of successes
// for(list, expr) => success_count
// can be substituted for first and all, but first is more efficient and all doesn't require knowing list size
expression.addLazyFunction("for", 2, (c, t, lv) ->
{
Value rval= lv.get(0).evalValue(c);
if (!(rval instanceof AbstractListValue))
throw new InternalExpressionException("First argument of 'for' function should be a list or iterator");
Iterator<Value> iterator = ((AbstractListValue) rval).iterator();
LazyValue expr = lv.get(1);
//scoping
LazyValue _val = c.getVariable("_");
LazyValue _ite = c.getVariable("_i");
int successCount = 0;
for (int i=0; iterator.hasNext(); i++)
{
Value next = iterator.next();
String var = next.boundVariable;
next.bindTo("_");
int seriously = i;
c.setVariable("_", (cc, tt) -> next);
c.setVariable("_i", (cc, tt) -> new NumericValue(seriously).bindTo("_i"));
Value result = Value.FALSE;
try
{
result = expr.evalValue(c, t);
}
catch (BreakStatement | ContinueStatement stmt)
{
if (stmt.retval != null) result = stmt.retval;
if (stmt instanceof BreakStatement)
{
next.boundVariable = var;
break;
}
}
if(t != Context.VOID && result.getBoolean())
successCount++;
next.boundVariable = var;
}
//revering scope
((AbstractListValue) rval).fatality();
c.setVariable("_", _val);
c.setVariable("_i", _ite);
long promiseWontChange = successCount;
return (cc, tt) -> new NumericValue(promiseWontChange);
});
// reduce(list, expr, ?acc) => value
// reduces values in the list with expression that gets accumulator
// each iteration expr receives acc - accumulator, and '_' - current list value
// returned value is substituted to the accumulator
expression.addLazyFunction("reduce", 3, (c, t, lv) ->
{
LazyValue expr = lv.get(1);
Value acc = lv.get(2).evalValue(c);
Value rval= lv.get(0).evalValue(c);
if (!(rval instanceof AbstractListValue))
throw new InternalExpressionException("First argument of 'reduce' should be a list or iterator");
Iterator<Value> iterator = ((AbstractListValue) rval).iterator();
if (!iterator.hasNext())
{
Value seriouslyWontChange = acc;
return (cc, tt) -> seriouslyWontChange;
}
//scoping
LazyValue _val = c.getVariable("_");
LazyValue _acc = c.getVariable("_a");
LazyValue _ite = c.getVariable("_i");
for (int i=0; iterator.hasNext(); i++)
{
Value next = iterator.next();
String var = next.boundVariable;
next.bindTo("_");
Value promiseWontChangeYou = acc;
int seriously = i;
c.setVariable("_a", (cc, tt) -> promiseWontChangeYou.bindTo("_a"));
c.setVariable("_", (cc, tt) -> next);
c.setVariable("_i", (cc, tt) -> new NumericValue(seriously).bindTo("_i"));
try
{
acc = expr.evalValue(c, t);
}
catch (BreakStatement | ContinueStatement stmt)
{
if (stmt.retval != null) acc = stmt.retval;
if (stmt instanceof BreakStatement)
{
next.boundVariable = var;
break;
}
}
next.boundVariable = var;
}
//reverting scope
((AbstractListValue) rval).fatality();
c.setVariable("_a", _acc);
c.setVariable("_", _val);
c.setVariable("_i", _ite);
Value hopeItsEnoughPromise = acc;
return (cc, tt) -> hopeItsEnoughPromise;
});
}
}
@@ -0,0 +1,219 @@
package carpet.script.language;
import carpet.script.Context;
import carpet.script.Expression;
import carpet.script.LazyValue;
import carpet.script.exception.InternalExpressionException;
import carpet.script.value.AbstractListValue;
import carpet.script.value.ContainerValueInterface;
import carpet.script.value.LContainerValue;
import carpet.script.value.ListValue;
import carpet.script.value.MapValue;
import carpet.script.value.NumericValue;
import carpet.script.value.Value;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
public class Operators {
public static final Map<String, Integer> precedence = new HashMap<String,Integer>() {{
put("attribute~:", 80);
put("unary+-!", 60);
put("exponent^", 40);
put("multiplication*/%", 30);
put("addition+-", 20);
put("compare>=><=<", 10);
put("equal==!=", 7);
put("and&&", 5);
put("or||", 4);
put("assign=<>", 3);
put("def->", 2);
put("nextop;", 1);
}};
public static void apply(Expression expression)
{
expression.addBinaryOperator("+", precedence.get("addition+-"), true, Value::add);
expression.addBinaryOperator("-", precedence.get("addition+-"), true, Value::subtract);
expression.addBinaryOperator("*", precedence.get("multiplication*/%"), true, Value::multiply);
expression.addBinaryOperator("/", precedence.get("multiplication*/%"), true, Value::divide);
expression.addBinaryOperator("%", precedence.get("multiplication*/%"), true, (v1, v2) ->
new NumericValue(NumericValue.asNumber(v1).getDouble() % NumericValue.asNumber(v2).getDouble()));
expression.addBinaryOperator("^", precedence.get("exponent^"), false, (v1, v2) ->
new NumericValue(java.lang.Math.pow(NumericValue.asNumber(v1).getDouble(), NumericValue.asNumber(v2).getDouble())));
expression.addLazyBinaryOperator("&&", precedence.get("and&&"), false, (c, t, lv1, lv2) ->
{
Value v1 = lv1.evalValue(c, Context.BOOLEAN);
if (!v1.getBoolean()) return (cc, tt) -> v1;
return lv2;
});
expression.addLazyBinaryOperator("||", precedence.get("or||"), false, (c, t, lv1, lv2) ->
{
Value v1 = lv1.evalValue(c, Context.BOOLEAN);
if (v1.getBoolean()) return (cc, tt) -> v1;
return lv2;
});
expression.addBinaryOperator("~", precedence.get("attribute~:"), true, Value::in);
expression.addBinaryOperator(">", precedence.get("compare>=><=<"), false, (v1, v2) ->
v1.compareTo(v2) > 0 ? Value.TRUE : Value.FALSE);
expression.addBinaryOperator(">=", precedence.get("compare>=><=<"), false, (v1, v2) ->
v1.compareTo(v2) >= 0 ? Value.TRUE : Value.FALSE);
expression.addBinaryOperator("<", precedence.get("compare>=><=<"), false, (v1, v2) ->
v1.compareTo(v2) < 0 ? Value.TRUE : Value.FALSE);
expression.addBinaryOperator("<=", precedence.get("compare>=><=<"), false, (v1, v2) ->
v1.compareTo(v2) <= 0 ? Value.TRUE : Value.FALSE);
expression.addBinaryOperator("==", precedence.get("equal==!="), false, (v1, v2) ->
v1.equals(v2) ? Value.TRUE : Value.FALSE);
expression.addBinaryOperator("!=", precedence.get("equal==!="), false, (v1, v2) ->
v1.equals(v2) ? Value.FALSE : Value.TRUE);
expression.addLazyBinaryOperator("=", precedence.get("assign=<>"), false, (c, t, lv1, lv2) ->
{
Value v1 = lv1.evalValue(c, Context.LVALUE);
Value v2 = lv2.evalValue(c);
if (v1 instanceof ListValue.ListConstructorValue && v2 instanceof ListValue)
{
List<Value> ll = ((ListValue)v1).getItems();
List<Value> rl = ((ListValue)v2).getItems();
if (ll.size() < rl.size()) throw new InternalExpressionException("Too many values to unpack");
if (ll.size() > rl.size()) throw new InternalExpressionException("Too few values to unpack");
for (Value v: ll) v.assertAssignable();
Iterator<Value> li = ll.iterator();
Iterator<Value> ri = rl.iterator();
while(li.hasNext())
{
String lname = li.next().getVariable();
Value vval = ri.next().reboundedTo(lname);
expression.setAnyVariable(c, lname, (cc, tt) -> vval);
}
return (cc, tt) -> Value.TRUE;
}
if (v1 instanceof LContainerValue)
{
ContainerValueInterface container = ((LContainerValue) v1).getContainer();
if (container == null)
return (cc, tt) -> Value.NULL;
Value address = ((LContainerValue) v1).getAddress();
if (!(container.put(address, v2))) return (cc, tt) -> Value.NULL;
return (cc, tt) -> v2;
}
v1.assertAssignable();
String varname = v1.getVariable();
Value copy = v2.reboundedTo(varname);
LazyValue boundedLHS = (cc, tt) -> copy;
expression.setAnyVariable(c, varname, boundedLHS);
return boundedLHS;
});
expression.addLazyBinaryOperator("+=", precedence.get("assign=<>"), false, (c, t, lv1, lv2) ->
{
Value v1 = lv1.evalValue(c, Context.LVALUE);
Value v2 = lv2.evalValue(c);
if (v1 instanceof ListValue.ListConstructorValue && v2 instanceof ListValue)
{
List<Value> ll = ((ListValue)v1).getItems();
List<Value> rl = ((ListValue)v2).getItems();
if (ll.size() < rl.size()) throw new InternalExpressionException("Too many values to unpack");
if (ll.size() > rl.size()) throw new InternalExpressionException("Too few values to unpack");
for (Value v: ll) v.assertAssignable();
Iterator<Value> li = ll.iterator();
Iterator<Value> ri = rl.iterator();
while(li.hasNext())
{
Value lval = li.next();
String lname = lval.getVariable();
Value result = lval.add(ri.next()).bindTo(lname);
expression.setAnyVariable(c, lname, (cc, tt) -> result);
}
return (cc, tt) -> Value.TRUE;
}
if (v1 instanceof LContainerValue)
{
ContainerValueInterface cvi = ((LContainerValue) v1).getContainer();
if (cvi == null)
{
throw new InternalExpressionException("Failed to resolve left hand side of the += operation");
}
Value key = ((LContainerValue) v1).getAddress();
Value value = cvi.get(key);
if (value instanceof ListValue || value instanceof MapValue)
{
((AbstractListValue) value).append(v2);
return (cc, tt) -> value;
}
else
{
Value res = value.add(v2);
cvi.put(key, res);
return (cc, tt) -> res;
}
}
v1.assertAssignable();
String varname = v1.getVariable();
LazyValue boundedLHS;
if (v1 instanceof ListValue || v1 instanceof MapValue)
{
((AbstractListValue) v1).append(v2);
boundedLHS = (cc, tt)-> v1;
}
else
{
Value result = v1.add(v2).bindTo(varname);
boundedLHS = (cc, tt) -> result;
}
expression.setAnyVariable(c, varname, boundedLHS);
return boundedLHS;
});
expression.addLazyBinaryOperator("<>", precedence.get("assign=<>"), false, (c, t, lv1, lv2) ->
{
Value v1 = lv1.evalValue(c);
Value v2 = lv2.evalValue(c);
if (v1 instanceof ListValue.ListConstructorValue && v2 instanceof ListValue.ListConstructorValue)
{
List<Value> ll = ((ListValue)v1).getItems();
List<Value> rl = ((ListValue)v2).getItems();
if (ll.size() < rl.size()) throw new InternalExpressionException("Too many values to unpack");
if (ll.size() > rl.size()) throw new InternalExpressionException("Too few values to unpack");
for (Value v: ll) v.assertAssignable();
for (Value v: rl) v.assertAssignable();
Iterator<Value> li = ll.iterator();
Iterator<Value> ri = rl.iterator();
while(li.hasNext())
{
Value lval = li.next();
Value rval = ri.next();
String lname = lval.getVariable();
String rname = rval.getVariable();
lval.reboundedTo(rname);
rval.reboundedTo(lname);
expression.setAnyVariable(c, lname, (cc, tt) -> rval);
expression.setAnyVariable(c, rname, (cc, tt) -> lval);
}
return (cc, tt) -> Value.TRUE;
}
v1.assertAssignable();
v2.assertAssignable();
String lvalvar = v1.getVariable();
String rvalvar = v2.getVariable();
Value lval = v2.reboundedTo(lvalvar);
Value rval = v1.reboundedTo(rvalvar);
expression.setAnyVariable(c, lvalvar, (cc, tt) -> lval);
expression.setAnyVariable(c, rvalvar, (cc, tt) -> rval);
return (cc, tt) -> lval;
});
expression.addUnaryOperator("-", false, (v) -> new NumericValue(-NumericValue.asNumber(v).getDouble()));
expression.addUnaryOperator("+", false, (v) -> new NumericValue(NumericValue.asNumber(v).getDouble()));
expression.addLazyUnaryOperator("!", precedence.get("unary+-!"), false, (c, t, lv)-> lv.evalValue(c, Context.BOOLEAN).getBoolean() ? (cc, tt)-> Value.FALSE : (cc, tt) -> Value.TRUE); // might need context boolean
}
}
@@ -0,0 +1,521 @@
package carpet.script.language;
import carpet.script.Context;
import carpet.script.Expression;
import carpet.script.LazyValue;
import carpet.script.ScriptHost;
import carpet.script.argument.FunctionArgument;
import carpet.script.exception.InternalExpressionException;
import carpet.script.utils.PerlinNoiseSampler;
import carpet.script.utils.SimplexNoiseSampler;
import carpet.script.value.FunctionValue;
import carpet.script.value.ListValue;
import carpet.script.value.NullValue;
import carpet.script.value.NumericValue;
import carpet.script.value.StringValue;
import carpet.script.value.ThreadValue;
import carpet.script.value.Value;
import org.apache.commons.lang3.text.WordUtils;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.IllegalFormatException;
import java.util.List;
import java.util.Locale;
import java.util.Random;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class Sys {
public static final Random randomizer = new Random();
// %[argument_index$][flags][width][.precision][t]conversion
private static final Pattern formatPattern = Pattern.compile("%(\\d+\\$)?([-#+ 0,(<]*)?(\\d+)?(\\.\\d+)?([tT])?([a-zA-Z%])");
public static void apply(Expression expression)
{
expression.addUnaryFunction("hash_code", v -> new NumericValue(v.hashCode()));
expression.addUnaryFunction("copy", Value::deepcopy);
expression.addLazyFunction("bool", 1, (c, t, lv) -> {
Value v = lv.get(0).evalValue(c, Context.BOOLEAN);
if (v instanceof StringValue)
{
String str = v.getString();
if ("false".equalsIgnoreCase(str) || "null".equalsIgnoreCase(str))
{
return (cc, tt) -> Value.FALSE;
}
}
Value retval = new NumericValue(v.getBoolean());
return (cc, tt) -> retval;
});
expression.addUnaryFunction("number", v -> {
if (v instanceof NumericValue)
{
return v;
}
try
{
return new NumericValue(v.getString());
}
catch (NumberFormatException format)
{
return Value.NULL;
}
});
expression.addFunction("str", lv ->
{
if (lv.size() == 0)
throw new InternalExpressionException("'str' requires at least one argument");
String format = lv.get(0).getString();
if (lv.size() == 1)
return new StringValue(format);
int argIndex = 1;
if (lv.get(1) instanceof ListValue && lv.size() == 2)
{
lv = ((ListValue) lv.get(1)).getItems();
argIndex = 0;
}
List<Object> args = new ArrayList<>();
Matcher m = formatPattern.matcher(format);
for (int i = 0, len = format.length(); i < len; ) {
if (m.find(i)) {
// Anything between the start of the string and the beginning
// of the format specifier is either fixed text or contains
// an invalid format string.
// [[scarpet]] but we skip it and let the String.format fail
char fmt = m.group(6).toLowerCase().charAt(0);
if (fmt == 's')
{
if (argIndex >= lv.size())
throw new InternalExpressionException("Not enough arguments for "+m.group(0));
args.add(lv.get(argIndex).getString());
argIndex++;
}
else if (fmt == 'd' || fmt == 'o' || fmt == 'x')
{
if (argIndex >= lv.size())
throw new InternalExpressionException("Not enough arguments for "+m.group(0));
args.add(lv.get(argIndex).readInteger());
argIndex++;
}
else if (fmt == 'a' || fmt == 'e' || fmt == 'f' || fmt == 'g')
{
if (argIndex >= lv.size())
throw new InternalExpressionException("Not enough arguments for "+m.group(0));
args.add(lv.get(argIndex).readDoubleNumber());
argIndex++;
}
else if (fmt == 'b')
{
if (argIndex >= lv.size())
throw new InternalExpressionException("Not enough arguments for "+m.group(0));
args.add(lv.get(argIndex).getBoolean());
argIndex++;
}
else if (fmt == '%')
{
//skip /%%
}
else
{
throw new InternalExpressionException("Format not supported: "+m.group(6));
}
i = m.end();
} else {
// No more valid format specifiers. Check for possible invalid
// format specifiers.
// [[scarpet]] but we skip it and let the String.format fail
break;
}
}
try
{
return new StringValue(String.format(Locale.ROOT, format, args.toArray()));
}
catch (IllegalFormatException ife)
{
throw new InternalExpressionException("Illegal string format: "+ife.getMessage());
}
});
expression.addUnaryFunction("lower", v -> new StringValue(v.getString().toLowerCase(Locale.ROOT)));
expression.addUnaryFunction("upper", v -> new StringValue(v.getString().toUpperCase(Locale.ROOT)));
expression.addUnaryFunction("title", v -> new StringValue(WordUtils.capitalizeFully(v.getString())));
expression.addFunction("replace", (lv) ->
{
if (lv.size() != 3 && lv.size() !=2)
throw new InternalExpressionException("'replace' expects string to read, pattern regex, and optional replacement string");
String data = lv.get(0).getString();
String regex = lv.get(1).getString();
String replacement = "";
if (lv.size() == 3)
replacement = lv.get(2).getString();
return new StringValue(data.replaceAll(regex, replacement));
});
expression.addFunction("replace_first", (lv) ->
{
if (lv.size() != 3 && lv.size() !=2)
throw new InternalExpressionException("'replace_first' expects string to read, pattern regex, and optional replacement string");
String data = lv.get(0).getString();
String regex = lv.get(1).getString();
String replacement = "";
if (lv.size() == 3)
replacement = lv.get(2).getString();
return new StringValue(data.replaceFirst(regex, replacement));
});
expression.addUnaryFunction("type", v -> new StringValue(v.getTypeString()));
expression.addUnaryFunction("length", v -> new NumericValue(v.length()));
expression.addLazyFunction("rand", -1, (c, t, lv) -> {
int argsize = lv.size();
Random randomizer = Sys.randomizer;
if (argsize != 1 && argsize != 2)
throw new InternalExpressionException("'rand' takes one (range) or two arguments (range and seed)");
if (argsize == 2) randomizer = c.host.getRandom(NumericValue.asNumber(lv.get(1).evalValue(c)).getLong());
Value argument = lv.get(0).evalValue(c);
if (argument instanceof ListValue)
{
List<Value> list = ((ListValue) argument).getItems();
Value retval = list.get(randomizer.nextInt(list.size()));
return (cc, tt) -> retval;
}
if (t == Context.BOOLEAN)
{
double rv = NumericValue.asNumber(argument).getDouble()*randomizer.nextFloat();
Value retval = rv<1.0D?Value.FALSE:Value.TRUE;
return (cc, tt) -> retval;
}
Value retval = new NumericValue(NumericValue.asNumber(argument).getDouble()*randomizer.nextDouble());
return (cc, tt) -> retval;
});
expression.addLazyFunction("perlin", -1, (c, t, lv) -> {
PerlinNoiseSampler sampler;
Value x, y, z;
if (lv.size() >= 4)
{
x = lv.get(0).evalValue(c);
y = lv.get(1).evalValue(c);
z = lv.get(2).evalValue(c);
sampler = PerlinNoiseSampler.getPerlin(NumericValue.asNumber(lv.get(3).evalValue(c)).getLong());
}
else
{
sampler = PerlinNoiseSampler.instance;
y = Value.NULL;
z = Value.NULL;
if (lv.size() == 0 )
throw new InternalExpressionException("'perlin' requires at least one dimension to sample from");
x = NumericValue.asNumber(lv.get(0).evalValue(c));
if (lv.size() > 1)
{
y = NumericValue.asNumber(lv.get(1).evalValue(c));
if (lv.size() > 2)
z = NumericValue.asNumber(lv.get(2).evalValue(c));
}
}
double result;
if (z instanceof NullValue)
if (y instanceof NullValue)
result = sampler.sample1d(NumericValue.asNumber(x).getDouble());
else
result = sampler.sample2d(NumericValue.asNumber(x).getDouble(), NumericValue.asNumber(y).getDouble());
else
result = sampler.sample3d(
NumericValue.asNumber(x).getDouble(),
NumericValue.asNumber(y).getDouble(),
NumericValue.asNumber(z).getDouble());
Value ret = new NumericValue(result);
return (cc, tt) -> ret;
});
expression.addLazyFunction("simplex", -1, (c, t, lv) -> {
SimplexNoiseSampler sampler;
Value x, y, z;
if (lv.size() >= 4)
{
x = lv.get(0).evalValue(c);
y = lv.get(1).evalValue(c);
z = lv.get(2).evalValue(c);
sampler = SimplexNoiseSampler.getSimplex(NumericValue.asNumber(lv.get(3).evalValue(c)).getLong());
}
else
{
sampler = SimplexNoiseSampler.instance;
z = Value.NULL;
if (lv.size() < 2 )
throw new InternalExpressionException("'simplex' requires at least two dimensions to sample from");
x = NumericValue.asNumber(lv.get(0).evalValue(c));
y = NumericValue.asNumber(lv.get(1).evalValue(c));
if (lv.size() > 2)
z = NumericValue.asNumber(lv.get(2).evalValue(c));
}
double result;
if (z instanceof NullValue)
result = sampler.sample2d(NumericValue.asNumber(x).getDouble(), NumericValue.asNumber(y).getDouble());
else
result = sampler.sample3d(
NumericValue.asNumber(x).getDouble(),
NumericValue.asNumber(y).getDouble(),
NumericValue.asNumber(z).getDouble());
Value ret = new NumericValue(result);
return (cc, tt) -> ret;
});
expression.addUnaryFunction("print", (v) ->
{
System.out.println(v.getString());
return v; // pass through for variables
});
expression.addUnaryFunction("sleep", (v) ->
{
long time = NumericValue.asNumber(v).getLong();
try
{
Thread.sleep(time);
Thread.yield();
}
catch (InterruptedException ignored) { }
return v; // pass through for variables
});
expression.addLazyFunction("time", 0, (c, t, lv) ->
{
Value time = new NumericValue((System.nanoTime() / 1000) / 1000.0);
return (cc, tt) -> time;
});
expression.addLazyFunction("unix_time", 0, (c, t, lv) ->
{
Value time = new NumericValue(System.currentTimeMillis());
return (cc, tt) -> time;
});
expression.addFunction("convert_date", lv ->
{
int argsize = lv.size();
if (lv.size() == 0) throw new InternalExpressionException("'convert_date' requires at least one parameter");
Value value = lv.get(0);
if (argsize == 1 && !(value instanceof ListValue))
{
Calendar cal = new GregorianCalendar(Locale.ROOT);
cal.setTimeInMillis(NumericValue.asNumber(value, "timestamp").getLong());
int weekday = cal.get(Calendar.DAY_OF_WEEK)-1;
if (weekday == 0) weekday = 7;
Value retVal = ListValue.ofNums(
cal.get(Calendar.YEAR),
cal.get(Calendar.MONTH)+1,
cal.get(Calendar.DAY_OF_MONTH),
cal.get(Calendar.HOUR_OF_DAY),
cal.get(Calendar.MINUTE),
cal.get(Calendar.SECOND),
weekday,
cal.get(Calendar.DAY_OF_YEAR),
cal.get(Calendar.WEEK_OF_YEAR)
);
return retVal;
}
else if(value instanceof ListValue)
{
lv = ((ListValue) value).getItems();
argsize = lv.size();
}
Calendar cal = new GregorianCalendar(0, 0, 0, 0, 0, 0);
if (argsize == 3)
{
cal.set(
NumericValue.asNumber(lv.get(0)).getInt(),
NumericValue.asNumber(lv.get(1)).getInt()-1,
NumericValue.asNumber(lv.get(2)).getInt()
);
}
else if (argsize == 6)
{
cal.set(
NumericValue.asNumber(lv.get(0)).getInt(),
NumericValue.asNumber(lv.get(1)).getInt()-1,
NumericValue.asNumber(lv.get(2)).getInt(),
NumericValue.asNumber(lv.get(3)).getInt(),
NumericValue.asNumber(lv.get(4)).getInt(),
NumericValue.asNumber(lv.get(5)).getInt()
);
}
else throw new InternalExpressionException("Date conversion requires 3 arguments for Dates or 6 arguments, for time");
return new NumericValue(cal.getTimeInMillis());
});
expression.addLazyFunction("profile_expr", 1, (c, t, lv) ->
{
LazyValue lazy = lv.get(0);
long end = System.nanoTime()+50000000L;
long it = 0;
while (System.nanoTime()<end)
{
lazy.evalValue(c);
it++;
}
Value res = new NumericValue(it);
return (cc, tt) -> res;
});
expression.addLazyFunction("var", 1, (c, t, lv) -> {
String varname = lv.get(0).evalValue(c).getString();
return expression.getOrSetAnyVariable(c, varname);
});
expression.addLazyFunction("undef", 1, (c, t, lv) ->
{
Value remove = lv.get(0).evalValue(c);
if (remove instanceof FunctionValue)
{
c.host.delFunction(expression.module, remove.getString());
return (cc, tt) -> Value.NULL;
}
String varname = remove.getString();
boolean isPrefix = varname.endsWith("*");
if (isPrefix)
varname = varname.replaceAll("\\*+$", "");
if (isPrefix)
{
c.host.delFunctionWithPrefix(expression.module, varname);
if (varname.startsWith("global_"))
{
c.host.delGlobalVariableWithPrefix(expression.module, varname);
}
else if (!varname.startsWith("_"))
{
c.removeVariablesMatching(varname);
}
}
else
{
c.host.delFunction(expression.module, varname);
if (varname.startsWith("global_"))
{
c.host.delGlobalVariable(expression.module, varname);
}
else if (!varname.startsWith("_"))
{
c.delVariable(varname);
}
}
return (cc, tt) -> Value.NULL;
});
//deprecate
expression.addLazyFunction("vars", 1, (c, t, lv) -> {
String prefix = lv.get(0).evalValue(c).getString();
List<Value> values = new ArrayList<>();
if (prefix.startsWith("global"))
{
c.host.globaVariableNames(expression.module, (s) -> s.startsWith(prefix)).forEach(s -> values.add(new StringValue(s)));
}
else
{
c.getAllVariableNames().stream().filter(s -> s.startsWith(prefix)).forEach(s -> values.add(new StringValue(s)));
}
Value retval = ListValue.wrap(values);
return (cc, tt) -> retval;
});
expression.addLazyFunctionWithDelegation("task", -1, (c, t, expr, tok, lv) -> {
if (lv.size() == 0)
throw new InternalExpressionException("'task' requires at least function to call as a parameter");
FunctionArgument functionArgument = FunctionArgument.findIn(c, expression.module, lv, 0, true, false);
Value queue = Value.NULL;
if (lv.size() > functionArgument.offset) queue = lv.get(functionArgument.offset).evalValue(c);
ThreadValue thread = new ThreadValue(queue, functionArgument.function, expr, tok, c, functionArgument.args);
Thread.yield();
return (cc, tt) -> thread;
});
expression.addFunction("task_count", (lv) ->
{
if (lv.size() > 0)
{
return new NumericValue(ThreadValue.taskCount(lv.get(0)));
}
return new NumericValue(ThreadValue.taskCount());
});
expression.addUnaryFunction("task_value", (v) -> {
if (!(v instanceof ThreadValue))
throw new InternalExpressionException("'task_value' could only be used with a task value");
return ((ThreadValue) v).getValue();
});
expression.addUnaryFunction("task_join", (v) -> {
if (!(v instanceof ThreadValue))
throw new InternalExpressionException("'task_join' could only be used with a task value");
return ((ThreadValue) v).join();
});
expression.addLazyFunction("task_dock", 1, (c, t, lv) -> {
// pass through placeholder
// implmenetation should dock the task on the main thread.
return lv.get(0);
});
expression.addUnaryFunction("task_completed", (v) -> {
if (!(v instanceof ThreadValue))
throw new InternalExpressionException("'task_completed' could only be used with a task value");
return new NumericValue(((ThreadValue) v).isFinished());
});
expression.addLazyFunction("synchronize", -1, (c, t, lv) ->
{
if (lv.size() == 0) throw new InternalExpressionException("'synchronize' require at least an expression to synchronize");
Value lockValue = Value.NULL;
int ind = 0;
if (lv.size() == 2)
{
lockValue = lv.get(0).evalValue(c);
ind = 1;
}
synchronized (ThreadValue.getLock(lockValue))
{
Value ret = lv.get(ind).evalValue(c, t);
return (_c, _t) -> ret;
}
});
expression.addLazyFunction("system_variable_get", -1, (c, t, lv) ->
{
if (lv.size() == 0) throw new InternalExpressionException("'system_variable_get' expects at least a key to be fetched");
Value key = lv.get(0).evalValue(c);
if (lv.size() > 1)
{
ScriptHost.systemGlobals.computeIfAbsent(key, k -> lv.get(1).evalValue(c));
}
Value res = ScriptHost.systemGlobals.get(key);
if (res!=null) return (cc, tt) -> res;
return (cc, tt) -> Value.NULL;
});
expression.addLazyFunction("system_variable_set", 2, (c, t, lv) ->
{
Value key = lv.get(0).evalValue(c);
Value value = lv.get(1).evalValue(c);
Value res = ScriptHost.systemGlobals.put(key, value);
if (res!=null) return (cc, tt) -> res;
return (cc, tt) -> Value.NULL;
});
}
}
@@ -3,8 +3,8 @@ package carpet.script.utils;
import carpet.CarpetSettings;
import carpet.network.ServerNetworkHandler;
import carpet.script.CarpetContext;
import carpet.script.Expression;
import carpet.script.exception.InternalExpressionException;
import carpet.script.language.Sys;
import carpet.script.value.BlockValue;
import carpet.script.value.EntityValue;
import carpet.script.value.FormattedTextValue;
@@ -1310,7 +1310,7 @@ public class ShapeDispatcher
for (Vec3d delta = new Vec3d(0.0,0.0,0.0);
delta.lengthSquared()<distance;
delta = delta.add(incvec.multiply(Expression.randomizer.nextFloat())))
delta = delta.add(incvec.multiply(Sys.randomizer.nextFloat())))
{
for (ServerPlayerEntity player : players)
{