Merge branch 'master' into scoreboard-criterion-mod

This commit is contained in:
replaceitem authored and GitHub committed 2020-12-16 20:40:29 +01:00
commit 6bb7eb2ae2
63 files changed
+2386 -797

No files matched your search

+16 -3
View File
@@ -5,7 +5,19 @@ import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import carpet.commands.*;
import carpet.commands.CounterCommand;
import carpet.commands.DistanceCommand;
import carpet.commands.DrawCommand;
import carpet.commands.InfoCommand;
import carpet.commands.LogCommand;
import carpet.commands.MobAICommand;
import carpet.commands.PerimeterInfoCommand;
import carpet.commands.PlayerCommand;
import carpet.commands.ProfileCommand;
import carpet.commands.ScriptCommand;
import carpet.commands.SpawnCommand;
import carpet.commands.TestCommand;
import carpet.commands.TickCommand;
import carpet.network.ServerNetworkHandler;
import carpet.helpers.HopperCounter;
import carpet.helpers.TickSpeed;
@@ -90,9 +102,9 @@ public class CarpetServer implements ClientModInitializer,DedicatedServerModInit
public static void onServerLoadedWorlds(MinecraftServer minecraftServer)
{
HopperCounter.resetAll(minecraftServer);
extensions.forEach(e -> e.onServerLoadedWorlds(minecraftServer));
scriptServer.initializeForWorld();
HopperCounter.resetAll(minecraftServer);
}
public static void tick(MinecraftServer server)
@@ -143,6 +155,7 @@ public class CarpetServer implements ClientModInitializer,DedicatedServerModInit
{
ServerNetworkHandler.onPlayerJoin(player);
LoggerRegistry.playerConnected(player);
scriptServer.onPlayerJoin(player);
extensions.forEach(e -> e.onPlayerLoggedIn(player));
}
@@ -156,9 +169,9 @@ public class CarpetServer implements ClientModInitializer,DedicatedServerModInit
public static void onServerClosed(MinecraftServer server)
{
if (scriptServer != null) scriptServer.onClose();
ServerNetworkHandler.close();
currentCommandDispatcher = null;
if (scriptServer != null) scriptServer.onClose();
LoggerRegistry.stopLoggers();
extensions.forEach(e -> e.onServerClosed(server));
+2 -2
View File
@@ -36,7 +36,7 @@ import static carpet.settings.RuleCategory.CLIENT;
@SuppressWarnings("CanBeFinal")
public class CarpetSettings
{
public static final String carpetVersion = "1.4.19+v201125";
public static final String carpetVersion = "1.4.21+v201216";
public static final Logger LOG = LogManager.getLogger("carpet");
public static boolean skipGenerationChecks = false;
public static boolean impendingFillSkipUpdates = false;
@@ -427,7 +427,7 @@ public class CarpetSettings
extra = "if /script is enabled",
category = SCARPET
)
public static boolean scriptsAutoload = false;
public static boolean scriptsAutoload = true;
@Rule(desc = "Enables /player command to control/spawn players", category = COMMAND)
public static String commandPlayer = "ops";
@@ -58,6 +58,9 @@ public class PlayerCommand
.then(makeActionCommand("dropStack", EntityPlayerActionPack.ActionType.DROP_STACK))
.then(makeDropCommand("dropStack", true))
.then(makeActionCommand("swapHands", EntityPlayerActionPack.ActionType.SWAP_HANDS))
.then(literal("hotbar")
.then(argument("slot", IntegerArgumentType.integer(1, 9))
.executes(c -> manipulate(c, ap -> ap.setSlot(IntegerArgumentType.getInteger(c, "slot"))))))
.then(literal("kill").executes(PlayerCommand::kill))
.then(literal("shadow"). executes(PlayerCommand::shadow))
.then(literal("mount").executes(manipulation(ap -> ap.mount(true)))
@@ -18,6 +18,7 @@ import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.suggestion.Suggestions;
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
import net.minecraft.block.Block;
@@ -84,7 +85,7 @@ public class ScriptCommand
private static CompletableFuture<Suggestions> suggestCode(
CommandContext<ServerCommandSource> context,
SuggestionsBuilder suggestionsBuilder
)
) throws CommandSyntaxException
{
CarpetScriptHost currentHost = getHost(context);
String previous = suggestionsBuilder.getRemaining().toLowerCase(Locale.ROOT);
@@ -291,7 +292,7 @@ public class ScriptCommand
)?1:0)))))).
then(literal("remove_from").
then(argument("event", StringArgumentType.word()).
suggests( (cc, bb) -> suggestMatching(CarpetEventServer.Event.publicEvents(CarpetServer.scriptServer).stream().map(ev -> ev.name).collect(Collectors.toList()) ,bb)).
suggests( (cc, bb) -> suggestMatching(CarpetEventServer.Event.publicEvents(CarpetServer.scriptServer).stream().filter(CarpetEventServer.Event::isNeeded).map(ev -> ev.name).collect(Collectors.toList()) ,bb)).
then(argument("call", StringArgumentType.greedyString()).
suggests( (cc, bb) -> suggestMatching(CarpetEventServer.Event.getEvent(StringArgumentType.getString(cc, "event"), CarpetServer.scriptServer).handler.callList.stream().map(CarpetEventServer.Callback::toString), bb)).
executes( (cc) -> CarpetServer.scriptServer.events.removeEventFromCommand(
@@ -311,14 +312,14 @@ public class ScriptCommand
suggests( (cc, bb) -> suggestMatching(CarpetServer.scriptServer.modules.keySet(), bb)).
then(b).then(u).then(o).then(l).then(s).then(c).then(h).then(i).then(e).then(t))));
}
private static CarpetScriptHost getHost(CommandContext<ServerCommandSource> context)
private static CarpetScriptHost getHost(CommandContext<ServerCommandSource> context) throws CommandSyntaxException
{
CarpetScriptHost host;
try
{
String name = StringArgumentType.getString(context, "app").toLowerCase(Locale.ROOT);
CarpetScriptHost parentHost = CarpetServer.scriptServer.modules.getOrDefault(name, CarpetServer.scriptServer.globalHost);
host = parentHost.retrieveForExecution(context.getSource());
host = parentHost.retrieveOwnForExecution(context.getSource());
}
catch (IllegalArgumentException ignored)
{
@@ -327,7 +328,7 @@ public class ScriptCommand
host.setChatErrorSnooper(context.getSource());
return host;
}
private static Collection<String> suggestFunctionCalls(CommandContext<ServerCommandSource> c)
private static Collection<String> suggestFunctionCalls(CommandContext<ServerCommandSource> c) throws CommandSyntaxException
{
CarpetScriptHost host = getHost(c);
return host.globaFunctionNames(host.main, s -> !s.startsWith("_")).sorted().collect(Collectors.toList());
@@ -350,7 +351,7 @@ public class ScriptCommand
}
return 1;
}
private static int listGlobals(CommandContext<ServerCommandSource> context, boolean all)
private static int listGlobals(CommandContext<ServerCommandSource> context, boolean all) throws CommandSyntaxException
{
CarpetScriptHost host = getHost(context);
ServerCommandSource source = context.getSource();
@@ -425,7 +426,7 @@ public class ScriptCommand
//host.resetErrorSnooper(); // lets say no need to reset the snooper in case something happens on the way
}
private static int invoke(CommandContext<ServerCommandSource> context, String call, BlockPos pos1, BlockPos pos2, String args)
private static int invoke(CommandContext<ServerCommandSource> context, String call, BlockPos pos1, BlockPos pos2, String args) throws CommandSyntaxException
{
ServerCommandSource source = context.getSource();
CarpetScriptHost host = getHost(context);
@@ -453,7 +454,7 @@ public class ScriptCommand
}
private static int compute(CommandContext<ServerCommandSource> context, String expr)
private static int compute(CommandContext<ServerCommandSource> context, String expr) throws CommandSyntaxException
{
ServerCommandSource source = context.getSource();
CarpetScriptHost host = getHost(context);
@@ -463,7 +464,7 @@ public class ScriptCommand
});
}
private static int scriptScan(CommandContext<ServerCommandSource> context, BlockPos origin, BlockPos a, BlockPos b, String expr)
private static int scriptScan(CommandContext<ServerCommandSource> context, BlockPos origin, BlockPos a, BlockPos b, String expr) throws CommandSyntaxException
{
ServerCommandSource source = context.getSource();
CarpetScriptHost host = getHost(context);
@@ -512,7 +513,7 @@ public class ScriptCommand
private static int scriptFill(CommandContext<ServerCommandSource> context, BlockPos origin, BlockPos a, BlockPos b, String expr,
BlockStateArgument block, Predicate<CachedBlockPosition> replacement, String mode)
BlockStateArgument block, Predicate<CachedBlockPosition> replacement, String mode) throws CommandSyntaxException
{
ServerCommandSource source = context.getSource();
CarpetScriptHost host = getHost(context);
@@ -3,4 +3,5 @@ package carpet.fakes;
public interface HungerManagerInterface {
float getExhaustionCM();
void setExhaustionCM(float exhaust);
void setSaturationCM(float saturn);
}
@@ -0,0 +1,7 @@
package carpet.fakes;
import net.minecraft.world.level.ServerWorldProperties;
public interface ServerWorldInterface {
ServerWorldProperties getWorldPropertiesCM();
}
@@ -10,6 +10,7 @@ import net.minecraft.entity.player.PlayerInventory;
import net.minecraft.entity.vehicle.BoatEntity;
import net.minecraft.entity.vehicle.MinecartEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.network.packet.s2c.play.HeldItemChangeS2CPacket;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket;
import net.minecraft.server.world.ServerWorld;
@@ -276,6 +277,12 @@ public class EntityPlayerActionPack
}
}
public void setSlot(int slot)
{
player.inventory.selectedSlot = slot-1;
player.networkHandler.sendPacket(new HeldItemChangeS2CPacket(slot-1));
}
public enum ActionType
{
USE(true)
@@ -132,7 +132,8 @@ public class OptimizedExplosion
if (d12 <= 1.0D) {
double d5 = entity.getX() - eAccess.getX();
double d7 = entity.getY() + (double) entity.getStandingEyeHeight() - eAccess.getY();
// Change in 1.16 snapshots to fix a bug with TNT jumping
double d7 = (entity instanceof TntEntity ? entity.getY() : entity.getEyeY()) - eAccess.getY();
double d9 = entity.getZ() - eAccess.getZ();
double d13 = (double) MathHelper.sqrt(d5 * d5 + d7 * d7 + d9 * d9);
@@ -8,13 +8,11 @@ import org.spongepowered.asm.mixin.Shadow;
@Mixin(HungerManager.class)
public class HungerManagerMixin_scarpetEventsMixin implements HungerManagerInterface {
@Shadow private float exhaustion;
public float getExhaustionCM(){
return exhaustion;
}
@Shadow private float foodSaturationLevel;
@Override
public void setExhaustionCM(float exhaust)
{
exhaustion = exhaust;
}
public float getExhaustionCM(){ return exhaustion; }
@Override
public void setExhaustionCM(float exhaust) { exhaustion = exhaust; }
@Override
public void setSaturationCM(float saturn) { foodSaturationLevel = saturn; }
}
@@ -69,7 +69,7 @@ public abstract class PlayerManager_fakePlayersMixin
ServerPlayerEntity serverPlayerEntity_3 = (ServerPlayerEntity) var5.next();
if(serverPlayerEntity_3 instanceof EntityPlayerMPFake)
{
serverPlayerEntity_3.kill();
((EntityPlayerMPFake)serverPlayerEntity_3).kill(new TranslatableText("multiplayer.disconnect.duplicate_login"));
continue;
}
serverPlayerEntity_3.networkHandler.disconnect(new TranslatableText("multiplayer.disconnect.duplicate_login"));
@@ -1,6 +1,7 @@
package carpet.mixins;
import carpet.CarpetSettings;
import carpet.fakes.ServerWorldInterface;
import carpet.script.CarpetEventServer;
import net.minecraft.entity.Entity;
import net.minecraft.entity.LightningEntity;
@@ -9,7 +10,9 @@ import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.ChunkPos;
import net.minecraft.util.profiler.Profiler;
import net.minecraft.world.chunk.WorldChunk;
import net.minecraft.world.level.ServerWorldProperties;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@@ -19,7 +22,7 @@ import static carpet.script.CarpetEventServer.Event.ENTITY_LOAD;
import static carpet.script.CarpetEventServer.Event.LIGHTNING;
@Mixin(ServerWorld.class)
public class ServerWorld_scarpetEventMixin
public class ServerWorld_scarpetMixin implements ServerWorldInterface
{
@Inject(method = "tickChunk", locals = LocalCapture.CAPTURE_FAILHARD, at = @At(
value = "INVOKE",
@@ -54,4 +57,9 @@ public class ServerWorld_scarpetEventMixin
}
}
@Shadow
private ServerWorldProperties worldProperties;
public ServerWorldProperties getWorldPropertiesCM(){
return worldProperties;
}
}
@@ -0,0 +1,34 @@
package carpet.mixins;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.screen.slot.TradeOutputSlot;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.village.Merchant;
import net.minecraft.village.MerchantInventory;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
import static carpet.script.CarpetEventServer.Event.PLAYER_TRADES;
@Mixin(value = TradeOutputSlot.class)
public abstract class TradeOutputSlot_scarpetEventMixin {
@Shadow @Final private Merchant merchant;
@Shadow @Final private MerchantInventory merchantInventory;
@Inject(method = "onTakeItem", at = @At(value = "INVOKE",
target = "Lnet/minecraft/village/Merchant;trade(Lnet/minecraft/village/TradeOffer;)V")
)
private void onTrade(PlayerEntity player, ItemStack stack, CallbackInfoReturnable<ItemStack> cir) {
if(PLAYER_TRADES.isNeeded() && !this.merchant.getMerchantWorld().isClient())
{
PLAYER_TRADES.onTrade((ServerPlayerEntity) player, merchant, merchantInventory.getTradeOffer());
}
}
}
@@ -217,6 +217,13 @@ public class ServerNetworkHandler
}
public static String getPlayerStatus(ServerPlayerEntity player)
{
if (remoteCarpetPlayers.containsKey(player)) return "carpet "+remoteCarpetPlayers.get(player);
if (validCarpetPlayers.contains(player)) return "carpet "+CarpetSettings.carpetVersion;
return "vanilla";
}
private static class DataBuilder
{
private CompoundTag tag;
@@ -9,6 +9,7 @@ import net.minecraft.network.packet.s2c.play.PlayerListS2CPacket;
import net.minecraft.entity.damage.DamageSource;
import net.minecraft.entity.player.HungerManager;
import net.minecraft.network.NetworkSide;
import net.minecraft.text.Text;
import net.minecraft.text.TranslatableText;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.ServerTask;
@@ -95,9 +96,14 @@ public class EntityPlayerMPFake extends ServerPlayerEntity
@Override
public void kill()
{
kill(Messenger.s("Killed"));
}
public void kill(Text reason)
{
this.server.send(new ServerTask(this.server.getTicks(), () -> {
this.networkHandler.onDisconnected(Messenger.s("Killed"));
this.networkHandler.onDisconnected(reason);
}));
}
@@ -120,6 +126,12 @@ public class EntityPlayerMPFake extends ServerPlayerEntity
super.onDeath(cause);
setHealth(20);
this.hungerManager = new HungerManager();
kill();
kill(this.getDamageTracker().getDeathMessage());
}
@Override
public String getIp()
{
return "127.0.0.1";
}
}
@@ -6,6 +6,7 @@ import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.ServerPlayNetworkHandler;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.Text;
import net.minecraft.text.TranslatableText;
public class NetHandlerPlayServerFake extends ServerPlayNetworkHandler
{
@@ -22,6 +23,10 @@ public class NetHandlerPlayServerFake extends ServerPlayNetworkHandler
@Override
public void disconnect(Text message)
{
if (player instanceof EntityPlayerMPFake && message instanceof TranslatableText && ((TranslatableText) message).getKey().equals("multiplayer.disconnect.idling"))
{
((EntityPlayerMPFake) player).kill(new TranslatableText(((TranslatableText) message).getKey()));
}
}
}
@@ -14,9 +14,11 @@ import carpet.script.value.Value;
import carpet.script.value.ValueConversions;
import carpet.settings.ParsedRule;
import carpet.utils.Messenger;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import net.minecraft.block.BlockState;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.damage.DamageSource;
import net.minecraft.entity.passive.MerchantEntity;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.entity.Entity;
@@ -34,6 +36,8 @@ import net.minecraft.util.math.Direction;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.registry.Registry;
import net.minecraft.util.registry.RegistryKey;
import net.minecraft.village.Merchant;
import net.minecraft.village.TradeOffer;
import net.minecraft.world.World;
import net.minecraft.world.chunk.Chunk;
import org.apache.commons.lang3.tuple.Pair;
@@ -47,8 +51,9 @@ import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.regex.Matcher;
@@ -65,59 +70,84 @@ public class CarpetEventServer
public static class Callback
{
public final String host;
public final String optionalTarget;
public final FunctionValue function;
public final List<Value> parametrizedArgs;
public Callback(String host, FunctionValue function, List<Value> parametrizedArgs)
public Callback(String host, String target, FunctionValue function, List<Value> parametrizedArgs)
{
this.host = host;
this.function = function;
this.optionalTarget = target;
this.parametrizedArgs = parametrizedArgs==null?NOARGS:parametrizedArgs;
}
/**
* Used also in entity events
* @param asSource - entity command source
* @param sender - entity command source
* @param runtimeArgs = options
*/
public boolean execute(ServerCommandSource asSource, List<Value> runtimeArgs)
public boolean execute(ServerCommandSource sender, List<Value> runtimeArgs)
{
if (this.parametrizedArgs.isEmpty())
return CarpetServer.scriptServer.runas(
asSource.withLevel(CarpetSettings.runPermissionLevel),
host, function, runtimeArgs);
else
if (!this.parametrizedArgs.isEmpty())
{
List<Value> combinedArgs = new ArrayList<>(runtimeArgs);
combinedArgs.addAll(this.parametrizedArgs);
return CarpetServer.scriptServer.runas(
asSource.withLevel(CarpetSettings.runPermissionLevel),
host, function, combinedArgs);
runtimeArgs = new ArrayList<>(runtimeArgs);
runtimeArgs.addAll(this.parametrizedArgs);
}
return CarpetServer.scriptServer.runEventCall(
sender.withLevel(CarpetSettings.runPermissionLevel),
host, optionalTarget, function, runtimeArgs
);
}
/**
* Used also in entity events
* @param recipient - optional target player argument
* @param sender - sender of the signal
* @param optionalRecipient - optional target player argument
* @param runtimeArgs = options
*/
public Boolean targetPlayer(ServerPlayerEntity recipient, List<Value> runtimeArgs)
public int signal(ServerCommandSource sender, ServerPlayerEntity optionalRecipient, List<Value> runtimeArgs)
{
// recipent of the call doesn't match the handlingHost
if (optionalRecipient != null && !optionalRecipient.getEntityName().equals(optionalTarget))
return 0;
List<Value> args = runtimeArgs;
if (!this.parametrizedArgs.isEmpty())
{
args = new ArrayList<>(runtimeArgs);
args.addAll(this.parametrizedArgs);
}
return CarpetServer.scriptServer.runForPlayer(recipient, host, function, args);
return CarpetServer.scriptServer.signal(sender, optionalRecipient, host, function, args, false);
}
@Override
public String toString()
{
return function.getString()+((host==null)?"":"(from "+host+")");
return function.getString()+((host==null)?"":"(from "+host+(optionalTarget == null?"":"/"+optionalTarget)+")");
}
public static class Signature
{
String function;
String host;
String target;
public Signature(String fun, String h, String t)
{
function = fun;
host = h;
target = t;
}
}
public static Signature fromString(String str)
{
Pattern find = Pattern.compile("(\\w+)(?:\\(from (\\w+)(?:/(\\w+))?\\))?");
Matcher matcher = find.matcher(str);
if(matcher.matches())
{
return new Signature(matcher.group(1), matcher.group(2), matcher.group(3));
}
return new Signature(str, null, null);
}
}
@@ -129,7 +159,8 @@ public class CarpetEventServer
public ScheduledCall(CarpetContext context, FunctionValue function, List<Value> args, long dueTime)
{
super(context.host.getName(), function, args);
// ignoring target as we will be always calling self
super(context.host.getName(), null, function, args);
this.ctx = context;
this.dueTime = dueTime;
}
@@ -139,7 +170,7 @@ public class CarpetEventServer
*/
public void execute()
{
CarpetServer.scriptServer.runas(ctx.origin, ctx.s, host, function, parametrizedArgs);
CarpetServer.scriptServer.runScheduledCall(ctx.origin, ctx.s, host, (CarpetScriptHost) ctx.host, function, parametrizedArgs);
}
@@ -173,7 +204,7 @@ public class CarpetEventServer
}
}
public int callCustom(ServerPlayerEntity receipient, List<Value> callArg)
public int signal(ServerCommandSource sender, ServerPlayerEntity optinoalReceipient, List<Value> callArg)
{
if (callList.isEmpty()) return 0;
//List<Callback> fails = new ArrayList<>();
@@ -181,15 +212,13 @@ public class CarpetEventServer
int successes = 0;
for (Callback call: callList)
{
Boolean result = call.targetPlayer(receipient, callArg);
//if (result == Boolean.FALSE) fails.add(call);
if (result == Boolean.TRUE) successes++;
successes += Math.max(0, call.signal(sender, optinoalReceipient, callArg));
}
//for (Callback call : fails) callList.remove(call);
return successes;
}
public boolean addEventCall(ServerCommandSource source, String hostName, String funName, Function<ScriptHost, Boolean> verifier)
public boolean addFromExternal(ServerCommandSource source, String hostName, String funName, Consumer<ScriptHost> hostOnEventHandler)
{
ScriptHost host = CarpetServer.scriptServer.getHostByName(hostName);
if (host == null)
@@ -198,11 +227,7 @@ public class CarpetEventServer
Messenger.m(source, "r Unknown app "+hostName);
return false;
}
if (!verifier.apply(host))
{
Messenger.m(source, "r Global event can only be added to apps with global scope");
return false;
}
hostOnEventHandler.accept(host);
FunctionValue udf = host.getFunction(funName);
if (udf == null || udf.getArguments().size() != reqArgs)
{
@@ -210,11 +235,24 @@ public class CarpetEventServer
Messenger.m(source, "r Callback doesn't expect required number of arguments: "+reqArgs);
return false;
}
String target = null;
if (host.isPerUser())
{
try
{
target = source.getPlayer().getEntityName();
}
catch (CommandSyntaxException e)
{
Messenger.m(source, "r Cannot add event to a player scoped app from a command without a player context");
return false;
}
}
//all clear
//remove duplicates
removeEventCall(hostName, udf.getString());
callList.add(new Callback(hostName, udf, null));
removeEventCall(hostName, target, udf.getString());
callList.add(new Callback(hostName, target, udf, null));
return true;
}
public boolean addEventCallInternal(ScriptHost host, FunctionValue function, List<Value> args)
@@ -225,20 +263,39 @@ public class CarpetEventServer
}
//all clear
//remove duplicates
removeEventCall(host.getName(), function.getString());
callList.add(new Callback(host.getName(), function, args));
removeEventCall(host.getName(), host.user, function.getString());
callList.add(new Callback(host.getName(), host.user, function, args));
return true;
}
public void removeEventCall(String hostName, String funName)
public void removeEventCall(String hostName, String target, String funName)
{
callList.removeIf((c)-> c.function.getString().equals(funName) && ( hostName == c.host || (c.host != null && c.host.equalsIgnoreCase(hostName) )) );
callList.removeIf((c)-> c.function.getString().equals(funName)
&& (Objects.equals(c.host, hostName))
&& (Objects.equals(c.optionalTarget, target))
);
}
public void removeAllCalls(CarpetScriptHost host)
{
callList.removeIf((c)-> c.host != null && c.host.equals(host.getName()));
callList.removeIf((c)-> (Objects.equals(c.host, host.getName()))
&& (Objects.equals(c.optionalTarget, host.user)));
}
public void createChildEvents(CarpetScriptHost host)
{
List<Callback> copyCalls = new ArrayList<>();
callList.forEach((c)->
{
if ((Objects.equals(c.host, host.getName()))
&& c.optionalTarget == null)
{
copyCalls.add(new Callback(c.host, host.user, c.function, c.parametrizedArgs));
}
});
callList.addAll(copyCalls);
}
public void clearEverything()
{
callList.clear();
@@ -255,6 +312,30 @@ public class CarpetEventServer
return events;
}
public static final Event START = new Event("server_starts", 0, true)
{
@Override
public void onTick()
{
handler.call(Collections::emptyList, () ->
CarpetServer.minecraft_server.getCommandSource().
withWorld(CarpetServer.minecraft_server.getWorld(World.OVERWORLD))
);
}
};
public static final Event SHUTDOWN = new Event("server_shuts_down", 0, true)
{
@Override
public void onTick()
{
handler.call(Collections::emptyList, () ->
CarpetServer.minecraft_server.getCommandSource().
withWorld(CarpetServer.minecraft_server.getWorld(World.OVERWORLD))
);
}
};
public static final Event TICK = new Event("tick", 0, true)
{
@Override
@@ -470,6 +551,20 @@ public class CarpetEventServer
), player::getCommandSource);
}
};
public static final Event PLAYER_TRADES = new Event("player_trades", 5, false)
{
@Override
public void onTrade(ServerPlayerEntity player, Merchant merchant, TradeOffer tradeOffer)
{
handler.call( () -> Arrays.asList(
new EntityValue(player),
merchant instanceof MerchantEntity ? new EntityValue((MerchantEntity) merchant) : Value.NULL,
ValueConversions.of(tradeOffer.getOriginalFirstBuyItem()),
ValueConversions.of(tradeOffer.getSecondBuyItem()),
ValueConversions.of(tradeOffer.getSellItem())
), player::getCommandSource);
}
};
public static final Event PLAYER_PICKS_UP_ITEM = new Event("player_picks_up_item", 2, false)
{
@Override
@@ -801,6 +896,12 @@ public class CarpetEventServer
host.getScriptServer().events.customEvents.values().forEach((e) -> e.handler.removeAllCalls(host));
}
public static void transferAllHostEventsToChild(CarpetScriptHost host)
{
byName.values().forEach((e) -> e.handler.createChildEvents(host));
host.getScriptServer().events.customEvents.values().forEach((e) -> e.handler.createChildEvents(host));
}
public static void clearAllBuiltinEvents()
{
byName.values().forEach(e -> e.handler.clearEverything());
@@ -842,6 +943,7 @@ public class CarpetEventServer
public void onDamage(Entity target, float amount, DamageSource source) { }
public void onRecipeSelected(ServerPlayerEntity player, Identifier recipe, boolean fullStack) {}
public void onSlotSwitch(ServerPlayerEntity player, int from, int to) {}
public void onTrade(ServerPlayerEntity player, Merchant merchant, TradeOffer tradeOffer) {}
public void onWorldEvent(ServerWorld world, BlockPos pos) { }
@@ -888,7 +990,7 @@ public class CarpetEventServer
{
return false;
}
boolean added = ev.handler.addEventCall(source, host, funName, h -> canAddEvent(ev, h));
boolean added = ev.handler.addFromExternal(source, host, funName, h -> onEventAddedToHost(ev, h));
if (added) Messenger.m(source, "gi Added " + funName + " to " + event);
return added;
}
@@ -897,8 +999,7 @@ public class CarpetEventServer
{
// this is globals only
Event ev = Event.byName.get(event);
if (!canAddEvent(ev, host))
throw new InternalExpressionException("Global event "+event+" can only be added to apps with global scope");
onEventAddedToHost(ev, host);
boolean success = ev.handler.addEventCallInternal(host, function, args==null?NOARGS:args);
if (!success) throw new InternalExpressionException("Global event "+event+" requires "+ev.handler.reqArgs+", not "+(function.getNumParams()-((args==null)?0:args.size())));
}
@@ -906,22 +1007,21 @@ public class CarpetEventServer
public boolean handleCustomEvent(String event, CarpetScriptHost host, FunctionValue function, List<Value> args)
{
Event ev = Event.getOrCreateCustom(event, host.getScriptServer());
if (!canAddEvent(ev, host))
throw new InternalExpressionException("Global built-in event "+event+" can only be handled in apps with global scope");
onEventAddedToHost(ev, host);
return ev.handler.addEventCallInternal(host, function, args==null?NOARGS:args);
}
public int signalEvent(String event, CarpetScriptHost host, ServerPlayerEntity player, List<Value> callArgs)
public int signalEvent(String event, CarpetContext cc, ServerPlayerEntity optionalTarget, List<Value> callArgs)
{
Event ev = Event.getEvent(event, host.getScriptServer());
Event ev = Event.getEvent(event, ((CarpetScriptHost)cc.host).getScriptServer());
if (ev == null) return -1;
return ev.handler.callCustom(player, callArgs);
return ev.handler.signal(cc.s, optionalTarget, callArgs);
}
private boolean canAddEvent(Event event, ScriptHost host)
private void onEventAddedToHost(Event event, ScriptHost host)
{
if (event.deprecated()) host.issueDeprecation(event.name+" event");
return !(event.globalOnly && (host.perUser || host.parent != null));
//return !(event.globalOnly && (host.perUser || host.parent != null));
}
public boolean removeEventFromCommand(ServerCommandSource source, String event, String funName)
@@ -932,8 +1032,8 @@ public class CarpetEventServer
Messenger.m(source, "r Unknown event: " + event);
return false;
}
Pair<String,String> call = decodeCallback(funName);
ev.handler.removeEventCall(call.getLeft(), call.getRight());
Callback.Signature call = Callback.fromString(funName);
ev.handler.removeEventCall(call.host, call.target, call.function);
// could verified if actually removed
Messenger.m(source, "gi Removed event: " + funName + " from "+event);
return true;
@@ -949,13 +1049,15 @@ public class CarpetEventServer
public void removeBuiltInEvent(String event, CarpetScriptHost host, String funName)
{
Event ev = Event.getEvent(event, host.getScriptServer());
if (ev != null) ev.handler.removeEventCall(host.getName(), funName);
if (ev != null) ev.handler.removeEventCall(host.getName(), host.user, funName);
}
public void removeAllHostEvents(CarpetScriptHost host)
{
// remove event handlers
Event.removeAllHostEvents(host);
if (host.isPerUser())
for (ScriptHost child: host.userHosts.values()) Event.removeAllHostEvents((CarpetScriptHost) child);
// remove scheduled calls
scheduledCalls.removeIf(sc -> sc.host != null && sc.host.equals(host.getName()));
}
+101 -20
View File
@@ -1,6 +1,7 @@
package carpet.script;
import carpet.CarpetServer;
import carpet.CarpetSettings;
import carpet.script.api.Auxiliary;
import carpet.script.argument.FunctionArgument;
import carpet.script.bundled.Module;
@@ -10,12 +11,12 @@ import carpet.script.exception.CarpetExpressionException;
import carpet.script.exception.ExpressionException;
import carpet.script.exception.InternalExpressionException;
import carpet.script.exception.InvalidCallbackException;
import carpet.script.value.EntityValue;
import carpet.script.value.FunctionValue;
import carpet.script.value.MapValue;
import carpet.script.value.NumericValue;
import carpet.script.value.StringValue;
import carpet.script.value.Value;
import carpet.CarpetSettings;
import carpet.utils.Messenger;
import com.mojang.brigadier.builder.ArgumentBuilder;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
@@ -38,9 +39,11 @@ import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static java.lang.Math.max;
import static net.minecraft.server.command.CommandManager.literal;
@@ -117,7 +120,7 @@ public class CarpetScriptHost extends ScriptHost
private static int execute(CommandContext<ServerCommandSource> ctx, String hostName, FunctionArgument<Value> funcSpec, List<String> paramNames) throws CommandSyntaxException
{
CarpetScriptHost cHost = CarpetServer.scriptServer.modules.get(hostName).retrieveForExecution(ctx.getSource());
CarpetScriptHost cHost = CarpetServer.scriptServer.modules.get(hostName).retrieveOwnForExecution(ctx.getSource());
List<String> argNames = funcSpec.function.getArguments();
if ((argNames.size()-funcSpec.args.size()) != paramNames.size())
throw new SimpleCommandExceptionType(new LiteralText("Target function "+funcSpec.function.getPrettyString()+" as wrong number of arguments, required "+paramNames.size()+", found "+argNames.size()+" with "+funcSpec.args.size()+" provided")).create();
@@ -162,8 +165,9 @@ public class CarpetScriptHost extends ScriptHost
) throws CommandSyntaxException
{
String hostName = main.getName();
Function<ServerCommandSource, Boolean> configValidator = getCommandConfigPermissions();
LiteralArgumentBuilder<ServerCommandSource> command = literal(hostName).
requires((player) -> CarpetServer.scriptServer.modules.containsKey(hostName) && useValidator.apply(player));
requires((player) -> CarpetServer.scriptServer.modules.containsKey(hostName) && useValidator.apply(player) && configValidator.apply(player));
for (Pair<List<CommandToken>,FunctionArgument<Value>> commandData : entries)
{
command = this.addPathToCommand(command, commandData.getKey(), commandData.getValue());
@@ -171,12 +175,63 @@ public class CarpetScriptHost extends ScriptHost
return command;
}
public Function<ServerCommandSource, Boolean> getCommandConfigPermissions() throws CommandSyntaxException
{
Value confValue = appConfig.get(StringValue.of("command_permission"));
if (confValue == null) return s -> true;
if (confValue instanceof NumericValue)
{
int level = ((NumericValue) confValue).getInt();
if (level < 1 || level > 4) throw CommandArgument.error("Numeric permission level for custom commands should be between 1 and 4");
return s -> s.hasPermissionLevel(level);
}
if (!(confValue instanceof FunctionValue))
{
String perm = confValue.getString().toLowerCase(Locale.ROOT);
switch (perm)
{
case "ops": return s -> s.hasPermissionLevel(2);
case "server": return s -> !(s.getEntity() instanceof ServerPlayerEntity);
case "players": return s -> s.getEntity() instanceof ServerPlayerEntity;
case "all": return s -> true;
default: throw CommandArgument.error("Unknown command permission: "+perm);
}
}
FunctionValue fun = (FunctionValue) confValue;
if (fun.getNumParams() != 1) throw CommandArgument.error("Custom command permission function should expect 1 argument");
String hostName = getName();
return s -> {
try
{
CarpetScriptHost cHost = null;
cHost = CarpetServer.scriptServer.modules.get(hostName).retrieveOwnForExecution(s);
Value response = cHost.handleCommand(s, fun, Collections.singletonList(
(s.getEntity() instanceof ServerPlayerEntity)?new EntityValue(s.getEntity()):Value.NULL)
);
return response.getBoolean();
}
catch (CommandSyntaxException e)
{
Messenger.m(s, "rb Unable to run app command: "+e.getMessage());
return false;
}
};
}
@Override
protected ScriptHost duplicate()
{
return new CarpetScriptHost(scriptServer, main, false, this, appConfig, appArgTypes, commandValidator, isRuleApp);
}
@Override
protected void transferToChild(ScriptHost host)
{
super.transferToChild(host);
// transfer Events
CarpetEventServer.Event.transferAllHostEventsToChild((CarpetScriptHost) host);
}
@Override
public void addUserDefinedFunction(Context ctx, Module module, String funName, FunctionValue function)
{
@@ -208,7 +263,7 @@ public class CarpetScriptHost extends ScriptHost
if (!(ret instanceof MapValue)) return false;
Map<Value, Value> config = ((MapValue) ret).getMap();
setPerPlayer(config.getOrDefault(new StringValue("scope"), new StringValue("player")).getString().equalsIgnoreCase("player"));
persistenceRequired = config.getOrDefault(new StringValue("stay_loaded"), Value.FALSE).getBoolean();
persistenceRequired = config.getOrDefault(new StringValue("stay_loaded"), Value.TRUE).getBoolean();
// read custom arguments
Value arguments = config.get(StringValue.of("arguments"));
if (arguments != null)
@@ -319,38 +374,58 @@ public class CarpetScriptHost extends ScriptHost
}
}
public CarpetScriptHost retrieveForExecution(ServerCommandSource source)
public List<CarpetScriptHost> retrieveForExecution(ServerCommandSource source, String optionalTarget)
{
CarpetScriptHost host = this;
List<CarpetScriptHost> targets = new ArrayList<>();
if (perUser)
{
try
if (optionalTarget == null)
{
ServerPlayerEntity player = source.getPlayer();
host = (CarpetScriptHost) retrieveForExecution(player.getName().getString());
for (ServerPlayerEntity player : source.getMinecraftServer().getPlayerManager().getPlayerList())
{
CarpetScriptHost host = (CarpetScriptHost) retrieveForExecution(player.getEntityName());
targets.add(host);
if (host.errorSnooper == null) host.setChatErrorSnooper(player.getCommandSource());
}
}
catch (CommandSyntaxException e)
else
{
host = (CarpetScriptHost) retrieveForExecution((String) null);
ServerPlayerEntity player = source.getMinecraftServer().getPlayerManager().getPlayer(optionalTarget);
if (player != null)
{
CarpetScriptHost host = (CarpetScriptHost) retrieveForExecution(player.getEntityName());
targets.add(host);
if (host.errorSnooper == null) host.setChatErrorSnooper(player.getCommandSource());
}
}
}
if (host.errorSnooper == null) host.setChatErrorSnooper(source);
return host;
else
{
targets.add(this);
if (this.errorSnooper == null) this.setChatErrorSnooper(source);
}
return targets;
}
public CarpetScriptHost retrieveForExecution(ServerPlayerEntity target)
public CarpetScriptHost retrieveOwnForExecution(ServerCommandSource source) throws CommandSyntaxException
{
// allowing only run
if (perUser == (target == null)) return null;
if (!perUser)
{
if (errorSnooper == null) setChatErrorSnooper(this.scriptServer.server.getCommandSource());
if (errorSnooper == null) setChatErrorSnooper(source);
return this;
}
// user based
CarpetScriptHost userHost = (CarpetScriptHost)retrieveForExecution(target.getName().getString());
if (userHost.errorSnooper == null) userHost.setChatErrorSnooper(target.getCommandSource());
ServerPlayerEntity player;
try
{
player = source.getPlayer();
}
catch (CommandSyntaxException ignored)
{
throw new SimpleCommandExceptionType(new LiteralText("Cannot run player based apps without the player context")).create();
}
CarpetScriptHost userHost = (CarpetScriptHost)retrieveForExecution(player.getEntityName());
if (userHost.errorSnooper == null) userHost.setChatErrorSnooper(source);
return userHost;
}
@@ -645,6 +720,12 @@ public class CarpetScriptHost extends ScriptHost
return Module.listFile(main, resource, "txt", isShared);
}
public Stream<String> listFolder(String resource, String ext, boolean isShared)
{
if (getName() == null && !isShared) return null; //
return Module.listFolder(main, resource, ext, isShared);
}
public void tick()
{
@@ -1,8 +1,8 @@
package carpet.script;
import carpet.CarpetSettings;
import carpet.script.argument.FunctionArgument;
import carpet.script.bundled.BundledModule;
import carpet.CarpetSettings;
import carpet.CarpetServer;
import carpet.script.bundled.FileModule;
import carpet.script.bundled.Module;
@@ -16,19 +16,25 @@ import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.tree.CommandNode;
import net.fabricmc.api.EnvType;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.util.WorldSavePath;
import net.minecraft.util.math.BlockPos;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
@@ -116,31 +122,42 @@ public class CarpetScriptServer
});
if (CarpetSettings.scriptsAutoload)
{
Messenger.m(server.getCommandSource(), "Auto-loading world scarpet apps");
for (String moduleName: listAvailableModules(false))
{
addScriptHost(server.getCommandSource(), moduleName, null, true, true, false);
}
}
CarpetEventServer.Event.START.onTick();
}
public Module getModule(String name, boolean allowLibraries)
{
File folder = server.getSavePath(WorldSavePath.ROOT).resolve("scripts").toFile();
File[] listOfFiles = folder.listFiles();
if (listOfFiles != null)
for (File script : listOfFiles)
try {
Path folder = server.getSavePath(WorldSavePath.ROOT).resolve("scripts");
Files.createDirectories(folder);
Optional<Path>
scriptPath = Files.list(folder)
.filter(script ->
script.getFileName().toString().equalsIgnoreCase(name+".sc") ||
(allowLibraries && script.getFileName().toString().equalsIgnoreCase(name+".scl"))
).findFirst();
if (scriptPath.isPresent())
return new FileModule(scriptPath.get());
if (FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT)
{
if (script.getName().equalsIgnoreCase(name+".sc"))
{
return new FileModule(script);
}
if (allowLibraries && script.getName().equalsIgnoreCase(name+".scl"))
{
return new FileModule(script);
}
Path globalFolder = FabricLoader.getInstance().getConfigDir().resolve("carpet/scripts");
Files.createDirectories(globalFolder);
scriptPath = Files.walk(globalFolder)
.filter(script -> script.getFileName().toString().equalsIgnoreCase(name + ".sc") ||
(allowLibraries && script.getFileName().toString().equalsIgnoreCase(name + ".scl")))
.findFirst();
if (scriptPath.isPresent())
return new FileModule(scriptPath.get());
}
} catch (IOException e) {
CarpetSettings.LOG.error("Exception while loading the app: ", e);
}
for (Module moduleData : bundledModuleData)
{
if (moduleData.getName().equalsIgnoreCase(name) && (allowLibraries || !moduleData.isLibrary()))
@@ -173,17 +190,23 @@ public class CarpetScriptServer
if (!mi.isLibrary() && !mi.getName().endsWith("_beta")) moduleNames.add(mi.getName());
}
}
File folder = server.getSavePath(WorldSavePath.ROOT).resolve("scripts").toFile();
File[] listOfFiles = folder.listFiles();
if (listOfFiles == null)
return moduleNames;
for (File script : listOfFiles)
{
if (script.getName().endsWith(".sc"))
try {
Path worldScripts = server.getSavePath(WorldSavePath.ROOT).resolve("scripts");
Files.createDirectories(worldScripts);
Files.list(worldScripts)
.filter(f -> f.toString().endsWith(".sc"))
.forEach(f -> moduleNames.add(f.getFileName().toString().replaceFirst("\\.sc$","").toLowerCase(Locale.ROOT)));
if (includeBuiltIns && (FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT))
{
String name = script.getName().replaceFirst("\\.sc","").toLowerCase(Locale.ROOT);
moduleNames.add(name);
Path globalScripts = FabricLoader.getInstance().getConfigDir().resolve("carpet/scripts");
Files.createDirectories(globalScripts);
Files.walk(globalScripts)
.filter(f -> f.toString().endsWith(".sc"))
.forEach(f -> moduleNames.add(f.getFileName().toString().replaceFirst("\\.sc$","").toLowerCase(Locale.ROOT)));
}
} catch (IOException e) {
CarpetSettings.LOG.error("Exception while searching for apps: ", e);
}
return moduleNames;
}
@@ -198,7 +221,6 @@ public class CarpetScriptServer
public boolean addScriptHost(ServerCommandSource source, String name, Function<ServerCommandSource, Boolean> commandValidator,
boolean perPlayer, boolean autoload, boolean isRuleApp)
{
//TODO add per player modules to support player actions better on a server
if (commandValidator == null) commandValidator = p -> true;
long start = System.nanoTime();
name = name.toLowerCase(Locale.ROOT);
@@ -272,6 +294,11 @@ public class CarpetScriptServer
Messenger.m(source, "r Failed to build command system for "+name+" thus failed to load the app");
return false;
}
if (newHost.isPerUser())
{
// that will provide player hosts right at the startup
newHost.retrieveForExecution(source, null);
}
long end = System.nanoTime();
CarpetSettings.LOG.info("App "+name+" loaded in "+(end-start)/1000000+" ms");
return true;
@@ -297,12 +324,23 @@ public class CarpetScriptServer
return true;
}
Function<ServerCommandSource, Boolean> configValidator;
try
{
configValidator = host.getCommandConfigPermissions();
}
catch (CommandSyntaxException e)
{
Messenger.m(source, "rb "+e.getMessage());
return false;
}
LiteralArgumentBuilder<ServerCommandSource> command = literal(hostName).
requires((player) -> modules.containsKey(hostName) && useValidator.apply(player)).
requires((player) -> modules.containsKey(hostName) && useValidator.apply(player) && configValidator.apply(player)).
executes( (c) ->
{
Value response = modules.get(hostName).retrieveForExecution(c.getSource()).
handleCommandLegacy(c.getSource(),"__command", null, "");
CarpetScriptHost targetHost = modules.get(hostName).retrieveOwnForExecution(c.getSource());
Value response = targetHost.handleCommandLegacy(c.getSource(),"__command", null, "");
if (!response.isNull()) Messenger.m(c.getSource(), "gi "+response.getString());
return (int)response.readInteger();
});
@@ -331,15 +369,15 @@ public class CarpetScriptServer
then(literal(function).
requires((player) -> modules.containsKey(hostName) && modules.get(hostName).getFunction(function) != null).
executes((c) -> {
Value response = modules.get(hostName).retrieveForExecution(c.getSource()).
handleCommandLegacy(c.getSource(), function, null, "");
CarpetScriptHost targetHost = modules.get(hostName).retrieveOwnForExecution(c.getSource());
Value response = targetHost.handleCommandLegacy(c.getSource(),function, null, "");
if (!response.isNull()) Messenger.m(c.getSource(), "gi " + response.getString());
return (int) response.readInteger();
}).
then(argument("args...", StringArgumentType.greedyString()).
executes( (c) -> {
Value response = modules.get(hostName).retrieveForExecution(c.getSource()).
handleCommandLegacy(c.getSource(), function,null, StringArgumentType.getString(c, "args..."));
CarpetScriptHost targetHost = modules.get(hostName).retrieveOwnForExecution(c.getSource());
Value response = targetHost.handleCommandLegacy(c.getSource(),function, null, StringArgumentType.getString(c, "args..."));
if (!response.isNull()) Messenger.m(c.getSource(), "gi "+response.getString());
return (int)response.readInteger();
})));
@@ -370,54 +408,84 @@ public class CarpetScriptServer
return true;
}
public boolean runas(ServerCommandSource source, String hostname, FunctionValue udf, List<Value> argv)
{
return runas(BlockPos.ORIGIN, source, hostname, udf, argv);
}
public boolean runas(BlockPos origin, ServerCommandSource source, String hostname, FunctionValue udf, List<Value> argv)
public boolean runEventCall(ServerCommandSource sender, String hostname, String optionalTarget, FunctionValue udf, List<Value> argv)
{
CarpetScriptHost host = globalHost;
try
if (hostname != null)
host = modules.get(hostname);
if (host == null) return false;
// dummy call for player apps that reside on the global copy - do not run them, but report as successes.
if (host.isPerUser() && optionalTarget==null) return true;
ServerPlayerEntity target = null;
if (optionalTarget != null)
{
if (hostname != null)
host = modules.get(hostname).retrieveForExecution(source);
host.callUDF(origin, source, udf, argv);
target = sender.getMinecraftServer().getPlayerManager().getPlayer(optionalTarget);
if (target == null) return false;
}
catch (NullPointerException | InvalidCallbackException npe)
{
return false;
}
return true;
int successes = signal(sender, target, hostname, udf, argv, true );
return successes >= 0;
}
public Boolean runForPlayer(ServerPlayerEntity recipient, String hostname, FunctionValue udf, List<Value> argv)
public void runScheduledCall(BlockPos origin, ServerCommandSource source, String hostname, CarpetScriptHost host, FunctionValue udf, List<Value> argv)
{
if (hostname != null && !modules.containsKey(hostname)) // well - scheduled call app got unloaded
return;
try
{
host.callUDF(origin, source, udf, argv);
}
catch (NullPointerException | InvalidCallbackException ignored)
{
}
}
/**
* returns number of successful calls in the host or -1 if failed exceptionally
* @param sender
* @param optionalRecipient
* @param hostname
* @param udf
* @param argv
* @param reportFails - returns -1 when any call failed
* @return
*/
public int signal(ServerCommandSource sender, ServerPlayerEntity optionalRecipient, String hostname, FunctionValue udf, List<Value> argv, boolean reportFails)
{
CarpetScriptHost host;
ServerCommandSource source;
if (hostname == null)
{
if (recipient != null) return null;
host = globalHost;
source = server.getCommandSource();
if (optionalRecipient != null) return 0;
try
{
globalHost.callUDF(BlockPos.ORIGIN, sender, udf, argv);
}
catch (NullPointerException | InvalidCallbackException npe)
{
return reportFails?-1:0;
}
return 1;
}
else
String hostRecipient = optionalRecipient == null?null:optionalRecipient.getEntityName();
ServerCommandSource source = (optionalRecipient == null)? sender : optionalRecipient.getCommandSource().withLevel(CarpetSettings.runPermissionLevel);
int successes = 0;
for (CarpetScriptHost host : modules.get(hostname).retrieveForExecution(sender, hostRecipient))
{
host = modules.get(hostname).retrieveForExecution(recipient);
if (host == null) // not applicable
return null;
source = (recipient == null)? server.getCommandSource() : recipient.getCommandSource();
// getPlayer will always return nonnull cause retrieve for execution only returns hosts for existing players.
ServerCommandSource executingSource = host.perUser
? source.getMinecraftServer().getPlayerManager().getPlayer(host.user).getCommandSource()
: source.getMinecraftServer().getCommandSource();
try
{
host.callUDF(BlockPos.ORIGIN, source.withLevel(CarpetSettings.runPermissionLevel), udf, argv);
}
catch (NullPointerException | InvalidCallbackException npe)
{
if (reportFails) return -1;
continue;
}
successes ++;
}
try
{
host.callUDF(BlockPos.ORIGIN, source.withLevel(CarpetSettings.runPermissionLevel), udf, argv);
}
catch (NullPointerException | InvalidCallbackException npe)
{
return false;
}
return true;
return successes;
}
public void tick()
@@ -431,12 +499,30 @@ public class CarpetScriptServer
public void onClose()
{
CarpetEventServer.Event.SHUTDOWN.onTick();
for (ScriptHost host : modules.values())
{
host.onClose();
}
}
public void onPlayerJoin(ServerPlayerEntity player)
{
modules.values().forEach(h ->
{
if (h.isPerUser())
{
try
{
h.retrieveOwnForExecution(player.getCommandSource());
}
catch (CommandSyntaxException ignored)
{
}
}
});
}
static class TransferData
{
boolean perUser;
@@ -9,6 +9,7 @@ import carpet.script.value.StringValue;
import carpet.script.value.Value;
import net.minecraft.entity.Entity;
import net.minecraft.entity.damage.DamageSource;
import org.apache.commons.lang3.tuple.Pair;
import java.util.Arrays;
import java.util.Collections;
@@ -19,7 +20,7 @@ import java.util.Map;
public class EntityEventsGroup
{
private final Map<Event, Map<String, CarpetEventServer.Callback>> actions;
private final Map<Event, Map<Pair<String,String>, CarpetEventServer.Callback>> actions;
private final Entity entity;
public EntityEventsGroup(Entity e)
{
@@ -30,37 +31,47 @@ public class EntityEventsGroup
public void onEvent(Event type, Object... args)
{
if (actions.isEmpty()) return; // most of the cases, trying to be nice
Map<String, CarpetEventServer.Callback> actionSet = actions.get(type);
Map<Pair<String,String>, CarpetEventServer.Callback> actionSet = actions.get(type);
if (actionSet == null) return;
for (Iterator<Map.Entry<String, CarpetEventServer.Callback>> iterator = actionSet.entrySet().iterator(); iterator.hasNext(); )
for (Iterator<Map.Entry<Pair<String,String>, CarpetEventServer.Callback>> iterator = actionSet.entrySet().iterator(); iterator.hasNext(); )
{
Map.Entry<String, CarpetEventServer.Callback> action = iterator.next();
ScriptHost host = CarpetServer.scriptServer.getHostByName(action.getKey());
Map.Entry<Pair<String,String>, CarpetEventServer.Callback> action = iterator.next();
Pair<String,String> key = action.getKey();
ScriptHost host = CarpetServer.scriptServer.getHostByName(key.getLeft());
if (host == null)
{
iterator.remove();
continue;
}
if (key.getRight() != null)
{
if (entity.getServer().getPlayerManager().getPlayer(key.getRight())==null)
{
iterator.remove();
continue;
}
}
if (!type.call(action.getValue(), entity, args))
iterator.remove();
}
if (actionSet.isEmpty()) actions.remove(type);
}
public void addEvent(Event type, String hostName, FunctionValue fun, List<Value> extraargs)
public void addEvent(Event type, ScriptHost host, FunctionValue fun, List<Value> extraargs)
{
Pair<String,String> key = Pair.of(host.getName(), host.user);
if (fun != null)
{
CarpetEventServer.Callback call = type.create(hostName, fun, extraargs);
CarpetEventServer.Callback call = type.create(key, fun, extraargs);
if (call == null)
throw new InternalExpressionException("wrong number of arguments for callback, required "+type.argcount);
actions.computeIfAbsent(type, k -> new HashMap<>()).put(hostName, call);
actions.computeIfAbsent(type, k -> new HashMap<>()).put(key, call);
}
else
{
actions.computeIfAbsent(type, k -> new HashMap<>()).remove(hostName);
actions.computeIfAbsent(type, k -> new HashMap<>()).remove(key);
if (actions.get(type).isEmpty())
actions.remove(type);
}
@@ -107,13 +118,13 @@ public class EntityEventsGroup
argcount = args+1; // entity is not extra
byName.put(identifier, this);
}
public CarpetEventServer.Callback create(String hostName, FunctionValue function, List<Value> extraArgs)
public CarpetEventServer.Callback create(Pair<String,String> key, FunctionValue function, List<Value> extraArgs)
{
if ((function.getArguments().size()-(extraArgs == null ? 0 : extraArgs.size())) != argcount)
{
return null;
}
return new CarpetEventServer.Callback(hostName, function, extraArgs);
return new CarpetEventServer.Callback(key.getLeft(), key.getRight(), function, extraArgs);
}
public boolean call(CarpetEventServer.Callback tickCall, Entity entity, Object ... args)
{
+17 -8
View File
@@ -18,13 +18,10 @@ import java.util.Locale;
import java.util.Map;
import java.util.Random;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public abstract class ScriptHost
@@ -189,7 +186,13 @@ public abstract class ScriptHost
public FunctionValue getAssertFunction(Module module, String name)
{
FunctionValue ret = getFunction(module, name);
if (ret == null) throw new InternalExpressionException("Function "+name+" is not defined yet");
if (ret == null)
{
if (module == main)
throw new InternalExpressionException("Function '"+name+"' is not defined yet");
else
throw new InternalExpressionException("Function '"+name+"' is not defined nor visible by its name in the imported module '"+module.getName()+"'");
}
return ret;
}
private FunctionValue getFunction(Module module, String name)
@@ -318,15 +321,21 @@ public abstract class ScriptHost
ScriptHost oldUserHost = userHosts.get(user);
if (oldUserHost != null) return oldUserHost;
ScriptHost userHost = this.duplicate();
userHost.modules.putAll(this.modules);
this.moduleData.forEach((key, value) -> userHost.moduleData.put(key, new ModuleData(key, value)));
// fixing imports
userHost.moduleData.forEach((module, data) -> data.setImportsBasedOn(userHost, this.moduleData.get(data.parent)));
userHost.user = user;
this.transferToChild(userHost);
userHosts.put(user, userHost);
return userHost;
}
protected void transferToChild(ScriptHost host)
{
// adding imports
host.modules.putAll(this.modules);
this.moduleData.forEach((key, value) -> host.moduleData.put(key, new ModuleData(key, value)));
// fixing imports
host.moduleData.forEach((module, data) -> data.setImportsBasedOn(host, this.moduleData.get(data.parent)));
}
public void handleExpressionException(String msg, ExpressionException exc)
{
System.out.println(msg+": "+exc);
+74 -69
View File
@@ -18,6 +18,7 @@ import carpet.script.argument.FunctionArgument;
import carpet.script.argument.Vector3Argument;
import carpet.script.exception.ExitStatement;
import carpet.script.exception.InternalExpressionException;
import carpet.script.utils.FixedCommandSource;
import carpet.script.utils.ShapeDispatcher;
import carpet.script.utils.WorldTools;
import carpet.script.value.EntityValue;
@@ -32,8 +33,8 @@ import carpet.script.value.StringValue;
import carpet.script.value.Value;
import carpet.script.value.ValueConversions;
import carpet.utils.Messenger;
import com.google.common.collect.ImmutableMap;
import net.minecraft.block.BlockState;
import net.minecraft.command.argument.EntityAnchorArgumentType;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.EquipmentSlot;
@@ -48,9 +49,7 @@ import net.minecraft.network.packet.s2c.play.TitleS2CPacket;
import net.minecraft.network.packet.s2c.play.TitleS2CPacket.Action;
import net.minecraft.particle.ParticleEffect;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.command.CommandOutput;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.network.ServerPlayNetworkHandler;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.sound.SoundCategory;
@@ -63,12 +62,12 @@ 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.Vec2f;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.registry.DynamicRegistryManager;
import net.minecraft.util.registry.Registry;
import net.minecraft.world.World;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.lang3.tuple.Triple;
import java.util.ArrayList;
import java.util.Arrays;
@@ -87,19 +86,39 @@ 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));
private static final Map<String, String> supportedTypes = ImmutableMap.of(
"raw", ".txt",
"text", ".txt",
"nbt", ".nbt",
"folder", "folder"
);
public static String recognizeResource(Value value)
public static String recognizeResource(Value value, boolean isFloder)
{
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())
if (file.isEmpty() && !isFloder)
{
throw new InternalExpressionException("Cannot use "+origfile+" as resource name - must have some letters and numbers");
}
return file;
}
public static Triple<String, String, Boolean> getFileDescriptor(List<LazyValue> lv, Context c, boolean isFloder)
{
if (lv.size() < 2) throw new InternalExpressionException("File functions require path and type as first two arguments");
String resource = recognizeResource(lv.get(0).evalValue(c), isFloder);
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 (!supportedTypes.containsKey(type))
throw new InternalExpressionException("Unsupported file type: "+origtype);
if (type.equals("folder") && !isFloder)
throw new InternalExpressionException("Folder types are no supported for this IO function");
return Triple.of(resource, type, shared);
}
public static void apply(Expression expression)
{
expression.addLazyFunction("sound", -1, (c, t, lv) -> {
@@ -310,7 +329,7 @@ public class Auxiliary {
{
Value nameValue = lv.get(0).evalValue(c);
name = nameValue instanceof NullValue ? "" : nameValue.getString();
pointLocator = Vector3Argument.findIn(cc, lv, 1, true);
pointLocator = Vector3Argument.findIn(cc, lv, 1, true, false);
if (lv.size()>pointLocator.offset)
{
BlockArgument blockLocator = BlockArgument.findIn(cc, lv, pointLocator.offset, true, true, false);
@@ -446,28 +465,29 @@ public class Auxiliary {
{
if (lv.size() == 0 || lv.size() > 2) throw new InternalExpressionException("'print' takes one or two arguments");
ServerCommandSource s = ((CarpetContext)c).s;
MinecraftServer server = s.getMinecraftServer();
Value res = lv.get(0).evalValue(c);
List<ServerPlayerEntity> targets = null;
if (lv.size() == 2)
{
ServerPlayerEntity player = EntityValue.getPlayerByValue(s.getMinecraftServer(), res);
if (player == null) return LazyValue.NULL;
s = player.getCommandSource();
List<Value> playerValues = (res instanceof ListValue)?((ListValue) res).getItems():Collections.singletonList(res);
List<ServerPlayerEntity> playerTargets = new ArrayList<>();
playerValues.forEach(pv -> {
ServerPlayerEntity player = EntityValue.getPlayerByValue(server, pv);
if (player == null) throw new InternalExpressionException("Cannot target player "+pv.getString()+" in print");
playerTargets.add(player);
});
targets = playerTargets;
res = lv.get(1).evalValue(c);
}
if (res instanceof FormattedTextValue)
Text message = (res instanceof FormattedTextValue)?((FormattedTextValue) res).getText():new LiteralText(res.getString());
if (targets == null)
{
s.sendFeedback(((FormattedTextValue) res).getText(), false);
s.sendFeedback(message, false);
}
else
{
if (s.getEntity() instanceof PlayerEntity)
{
Messenger.m((PlayerEntity) s.getEntity(), "w " + res.getString());
}
else
{
Messenger.m(s, "w " + res.getString());
}
targets.forEach(p -> p.getCommandSource().sendFeedback(message, false));
}
Value finalRes = res;
return (_c, _t) -> finalRes; // pass through for variables
@@ -510,7 +530,7 @@ public class Auxiliary {
if (pVal instanceof FormattedTextValue)
title = ((FormattedTextValue) pVal).getText();
else
title = Text.of(pVal.getString());
title = new LiteralText(pVal.getString());
}
TitleS2CPacket timesPacket;
if (lv.size() > 3)
@@ -544,29 +564,14 @@ public class Auxiliary {
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);
Vec3d posf = new Vec3d((double)target.getX()+0.5D, target.getY(),(double)target.getZ()+0.5D);
ServerCommandSource s = ((CarpetContext)c).s;
try
{
Value[] error = {Value.NULL};
List<Value> output = new ArrayList<>();
Value retval = new NumericValue(s.getMinecraftServer().getCommandManager().execute(
new ServerCommandSource(
CommandOutput.DUMMY, posf, Vec2f.ZERO, s.getWorld(), CarpetSettings.runPermissionLevel,
s.getName(), s.getDisplayName(), s.getMinecraftServer(), s.getEntity(), true,
(ctx, succ, res) -> { }, EntityAnchorArgumentType.EntityAnchor.FEET)
{
@Override
public void sendError(Text message)
{
error[0] = new FormattedTextValue(message);
}
@Override
public void sendFeedback(Text message, boolean broadcastToOps)
{
output.add(new FormattedTextValue(message));
}
},
new FixedCommandSource(s, posf, error, output),
lv.get(0).evalValue(c).getString())
);
Value ret = ListValue.of(retval, ListValue.wrap(output), error[0]);
@@ -785,23 +790,29 @@ public class Auxiliary {
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);
expression.addLazyFunction("list_files", 2, (c, t, lv) ->
{
Triple<String, String, Boolean> fdesc = getFileDescriptor(lv, c, true);
Stream<String> files = ((CarpetScriptHost) c.host).listFolder(fdesc.getLeft(), supportedTypes.get(fdesc.getMiddle()), fdesc.getRight());
if (files == null) return LazyValue.NULL;
Value ret = ListValue.wrap(files.map(StringValue::of).collect(Collectors.toList()));
return (cc, tt) -> ret;
});
expression.addLazyFunction("read_file", 2, (c, t, lv) ->
{
Triple<String, String, Boolean> fdesc = getFileDescriptor(lv, c, false);
Value retVal;
if (type.equals("nbt"))
if (fdesc.getMiddle().equals("nbt"))
{
Tag state = ((CarpetScriptHost)((CarpetContext)c).host).readFileTag(resource, shared);
Tag state = ((CarpetScriptHost) c.host).readFileTag(fdesc.getLeft(), fdesc.getRight());
if (state == null) return LazyValue.NULL;
retVal = new NBTSerializableValue(state);
}
else
{
List<String> content = ((CarpetScriptHost) ((CarpetContext) c).host).readTextResource(resource, shared);
List<String> content = ((CarpetScriptHost) c.host).readTextResource(fdesc.getLeft(), fdesc.getRight());
if (content == null) return LazyValue.NULL;
retVal = ListValue.wrap(content.stream().map(StringValue::new).collect(Collectors.toList()));
}
@@ -809,33 +820,26 @@ public class Auxiliary {
});
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;
Triple<String, String, Boolean> fdesc = getFileDescriptor(lv, c, false);
return ((CarpetScriptHost) c.host).removeResourceFile(fdesc.getLeft(), fdesc.getRight(), fdesc.getMiddle())
? 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);
Triple<String, String, Boolean> fdesc = getFileDescriptor(lv, c, false);
boolean success;
if (type.equals("nbt"))
if (fdesc.getMiddle().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);
success = ((CarpetScriptHost) c.host).writeTagFile(tag, fdesc.getLeft(), fdesc.getRight());
}
else
{
@@ -860,7 +864,7 @@ public class Auxiliary {
data.add(lv.get(i).evalValue(c).getString());
}
}
success = ((CarpetScriptHost) ((CarpetContext) c).host).appendLogFile(resource, shared, type, data);
success = ((CarpetScriptHost) c.host).appendLogFile(fdesc.getLeft(), fdesc.getRight(), fdesc.getMiddle(), data);
}
return success?LazyValue.TRUE:LazyValue.FALSE;
});
@@ -874,7 +878,7 @@ public class Auxiliary {
if (lv.size()>0)
{
c.host.issueDeprecation("load_app_data(...) with arguments");
file = recognizeResource(lv.get(0).evalValue(c));
file = recognizeResource(lv.get(0).evalValue(c), false);
if (lv.size() > 1)
{
shared = lv.get(1).evalValue(c).getBoolean();
@@ -897,7 +901,7 @@ public class Auxiliary {
if (lv.size()>1)
{
c.host.issueDeprecation("store_app_data(...) with more than one argument");
file = recognizeResource(lv.get(1).evalValue(c));
file = recognizeResource(lv.get(1).evalValue(c), false);
if (lv.size() > 2)
{
shared = lv.get(2).evalValue(c).getBoolean();
@@ -961,6 +965,7 @@ public class Auxiliary {
{
if (lv.size() == 0)
throw new InternalExpressionException("'signal' requires at least one argument");
CarpetContext cc = (CarpetContext)c;
CarpetScriptServer server = ((CarpetScriptHost)c.host).getScriptServer();
String eventName = lv.get(0).evalValue(c).getString();
// no such event yet
@@ -975,10 +980,10 @@ public class Auxiliary {
args = FunctionValue.resolveArgs(lv.subList(2, lv.size()), c, t);
}
}
int counts = ((CarpetScriptHost)c.host).getScriptServer().events.signalEvent(eventName, (CarpetScriptHost) c.host, player, args);
int counts = ((CarpetScriptHost)c.host).getScriptServer().events.signalEvent(eventName, cc, player, args);
if (counts < 0) return LazyValue.NULL;
Value ret = new NumericValue(counts);
return (cc, tt) -> ret;
return (c_, t_) -> ret;
});
}
+30 -13
View File
@@ -23,6 +23,7 @@ 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.command.ServerCommandSource;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.Identifier;
@@ -34,6 +35,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
@@ -183,8 +185,9 @@ public class Entities {
expression.addLazyFunction("entity_list", 1, (c, t, lv) ->
{
String who = lv.get(0).evalValue(c).getString();
EntityValue.EntityClassDescriptor eDesc = EntityValue.getEntityDescriptor(who);
List<Entity> entityList = ((CarpetContext)c).s.getWorld().getEntitiesByType(eDesc.directType, eDesc.filteringPredicate);
ServerCommandSource source = ((CarpetContext)c).s;
EntityValue.EntityClassDescriptor eDesc = EntityValue.getEntityDescriptor(who, source.getMinecraftServer());
List<Entity> entityList = source.getWorld().getEntitiesByType(eDesc.directType, eDesc.filteringPredicate);
Value retval = ListValue.wrap(entityList.stream().map(EntityValue::new).collect(Collectors.toList()));
return (_c, _t ) -> retval;
});
@@ -194,16 +197,26 @@ public class Entities {
if (lv.size()<3) throw new InternalExpressionException("'entity_area' requires entity type, center and range arguments");
String who = lv.get(0).evalValue(c).getString();
CarpetContext cc = (CarpetContext)c;
Vector3Argument centerLocator = Vector3Argument.findIn(cc, lv, 1);
Vec3d center = centerLocator.vec;
if (centerLocator.fromBlock) center.add(0.5, 0.5, 0.5);
Vector3Argument centerLocator = Vector3Argument.findIn(cc, lv, 1, false, true);
Box centerBox;
if (centerLocator.entity != null)
{
centerBox = centerLocator.entity.getBoundingBox();
}
else
{
Vec3d center = centerLocator.vec;
if (centerLocator.fromBlock) center.add(0.5, 0.5, 0.5);
centerBox = new Box(center, center);
}
Vector3Argument rangeLocator = Vector3Argument.findIn(cc, lv, centerLocator.offset);
if (rangeLocator.fromBlock)
throw new InternalExpressionException("Range of 'entity_area' cannot come from a block argument");
Vec3d range = rangeLocator.vec;
Box area = new Box(center, center).expand(range.x, range.y, range.z);
EntityValue.EntityClassDescriptor eDesc = EntityValue.getEntityDescriptor(who);
List<? extends Entity> entityList = ((CarpetContext)c).s.getWorld().getEntitiesByType(eDesc.directType, area,eDesc.filteringPredicate);
Box area = centerBox.expand(range.x, range.y, range.z);
EntityValue.EntityClassDescriptor eDesc = EntityValue.getEntityDescriptor(who, cc.s.getMinecraftServer());
List<? extends Entity> entityList = cc.s.getWorld().getEntitiesByType(eDesc.directType, area,eDesc.filteringPredicate);
Value retval = ListValue.wrap(entityList.stream().map(EntityValue::new).collect(Collectors.toList()));
return (_c, _t ) -> retval;
});
@@ -228,7 +241,9 @@ public class Entities {
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();
String what = lv.get(1).evalValue(c).getString().toLowerCase(Locale.ROOT);
if (what.equals("tags"))
c.host.issueDeprecation("'tags' for entity querying");
Value retval;
if (lv.size()==2)
retval = ((EntityValue) v).get(what, null);
@@ -259,11 +274,13 @@ public class Entities {
return (cc, tt) -> v;
});
expression.addFunction("entity_types", (lv) ->
expression.addLazyFunction("entity_types", -1, (c, t, lv) ->
{
if (lv.size() > 1) throw new InternalExpressionException("'entity_types' requires one or no arguments");
String desc = (lv.size() == 1)?lv.get(0).getString():"*";
return EntityValue.getEntityDescriptor(desc).listValue;
String desc = (lv.size() == 1)?lv.get(0).evalValue(c).getString():"*";
Value ret = EntityValue.getEntityDescriptor(desc, ((CarpetContext) c).s.getMinecraftServer()).listValue;
return (_c, _t) -> ret;
});
expression.addLazyFunction("entity_load_handler", -1, (c, t, lv) ->
@@ -275,7 +292,7 @@ public class Entities {
? ((ListValue) entityValue).getItems().stream().map(Value::getString).collect(Collectors.toList())
: Collections.singletonList(entityValue.getString());
Set<EntityType<? extends Entity>> types = new HashSet<>();
descriptors.forEach(s -> types.addAll(EntityValue.getEntityDescriptor(s).typeList));
descriptors.forEach(s -> types.addAll(EntityValue.getEntityDescriptor(s, ((CarpetContext) c).s.getMinecraftServer()).typeList));
FunctionArgument<LazyValue> funArg = FunctionArgument.findIn(c, expression.module, lv, 1, true, false);
CarpetEventServer events = ((CarpetScriptHost)c.host).getScriptServer().events;
if (funArg.function == null)
@@ -12,6 +12,7 @@ import carpet.script.value.NullValue;
import carpet.script.value.NumericValue;
import carpet.script.value.StringValue;
import carpet.script.value.Value;
import carpet.script.value.ValueConversions;
import com.google.common.collect.Sets;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import net.minecraft.command.argument.ItemStackArgument;
@@ -32,6 +33,7 @@ import net.minecraft.recipe.ShapedRecipe;
import net.minecraft.recipe.ShapelessRecipe;
import net.minecraft.recipe.SpecialCraftingRecipe;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.tag.TagManager;
import net.minecraft.util.Identifier;
import net.minecraft.util.InvalidIdentifierException;
import net.minecraft.util.math.Vec3d;
@@ -40,6 +42,7 @@ import net.minecraft.util.registry.Registry;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.stream.Collectors;
public class Inventories {
public static void apply(Expression expression)
@@ -60,6 +63,43 @@ public class Inventories {
return (_c, _t) -> res;
});
expression.addLazyFunction("item_list", -1, (c, t, lv) ->
{
if (lv.size() == 0)
{
Value res = ListValue.wrap(Registry.ITEM.getIds().stream().map(ValueConversions::of).collect(Collectors.toList()));
return (cc, tt) -> res;
}
CarpetContext cc = (CarpetContext)c;
TagManager tagManager = cc.s.getMinecraftServer().getTagManager();
String tag = lv.get(0).evalValue(c).getString();
net.minecraft.tag.Tag<Item> itemTag = tagManager.getItems().getTag(new Identifier(tag));
if (itemTag == null) return LazyValue.NULL;
Value ret = ListValue.wrap(itemTag.values().stream().map(b -> ValueConversions.of(Registry.ITEM.getId(b))).collect(Collectors.toList()));
return (_c, _t) -> ret;
});
expression.addLazyFunction("item_tags", -1, (c, t, lv) ->
{
CarpetContext cc = (CarpetContext)c;
TagManager tagManager = cc.s.getMinecraftServer().getTagManager();
if (lv.size() == 0)
{
Value ret = ListValue.wrap(tagManager.getItems().getTagIds().stream().map(ValueConversions::of).collect(Collectors.toList()));
return (_c, _t) -> ret;
}
Item item = NBTSerializableValue.parseItem(lv.get(0).evalValue(c).getString()).getItem();
if (lv.size() == 1)
{
Value ret = ListValue.wrap(tagManager.getItems().getTagsFor(item).stream().map(ValueConversions::of).collect(Collectors.toList()));
return (_c, _t) -> ret;
}
String tag = lv.get(1).evalValue(c).getString();
net.minecraft.tag.Tag<Item> itemTag = tagManager.getItems().getTag(new Identifier(tag));
if (itemTag == null) return LazyValue.NULL;
return item.isIn(itemTag)?LazyValue.TRUE:LazyValue.FALSE;
});
expression.addLazyFunction("recipe_data", -1, (c, t, lv) ->
{
CarpetContext cc = (CarpetContext)c;
@@ -15,6 +15,9 @@ import carpet.script.value.FormattedTextValue;
import carpet.script.value.Value;
import com.google.common.collect.Lists;
import net.minecraft.entity.Entity;
import net.minecraft.entity.boss.BossBar;
import net.minecraft.entity.boss.BossBarManager;
import net.minecraft.entity.boss.CommandBossBar;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.scoreboard.Scoreboard;
import net.minecraft.scoreboard.ServerScoreboard;
@@ -23,9 +26,12 @@ import net.minecraft.scoreboard.ScoreboardObjective;
import net.minecraft.scoreboard.ScoreboardPlayerScore;
import net.minecraft.scoreboard.Team;
import net.minecraft.scoreboard.AbstractTeam;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.text.LiteralText;
import net.minecraft.text.Text;
import net.minecraft.util.Formatting;
import net.minecraft.util.Identifier;
import net.minecraft.util.InvalidIdentifierException;
import java.util.stream.Collectors;
@@ -404,6 +410,131 @@ public class Scoreboards {
}
return LazyValue.TRUE;
});
expression.addLazyFunction("bossbar", -1, (c, t, lv) ->
{
BossBarManager bossBarManager = ((CarpetContext)c).s.getMinecraftServer().getBossBarManager();
if(lv.size() > 3) throw new InternalExpressionException("'bossbar' accepts max three arguments");
if(lv.size() == 0) {
Value ret = ListValue.wrap(bossBarManager.getAll().stream().map(CommandBossBar::getId).map(Identifier::toString).map(StringValue::of).collect(Collectors.toList()));
return (_c, _t) -> ret;
}
String id = lv.get(0).evalValue(c).getString();
Identifier identifier;
try {
identifier = new Identifier(id);
} catch (InvalidIdentifierException invalidIdentifierException) {
return LazyValue.NULL;
}
if(lv.size() == 1) {
if(bossBarManager.get(identifier) != null) return LazyValue.FALSE;
Value ret = StringValue.of(bossBarManager.add(identifier,new LiteralText(id)).getId().toString());
return (_c, _t) -> ret;
}
String property = lv.get(1).evalValue(c).getString();
CommandBossBar bossBar = bossBarManager.get(identifier);
if(bossBar == null) return LazyValue.NULL;
Value propertyValue = (lv.size() == 3)?lv.get(2).evalValue(c):null;
switch (property) {
case "color":
if(propertyValue == null) {
BossBar.Color color = (bossBar).getColor();
if(color == null) return LazyValue.NULL;
return (_c, _t) -> StringValue.of(color.getName());
}
BossBar.Color color = BossBar.Color.byName(propertyValue.getString());
if(color == null) return LazyValue.NULL;
bossBar.setColor(BossBar.Color.byName(propertyValue.getString()));
return LazyValue.TRUE;
case "max":
if(propertyValue == null) return (_c, _t) -> NumericValue.of(bossBar.getMaxValue());
if(!(propertyValue instanceof NumericValue)) throw new InternalExpressionException("'bossbar' requires a number as the value for the property " + property);
bossBar.setMaxValue(((NumericValue) propertyValue).getInt());
return LazyValue.TRUE;
case "name":
if(propertyValue == null) return (_c, _t) -> new FormattedTextValue(bossBar.getName());
if(propertyValue instanceof FormattedTextValue) {
bossBar.setName(((FormattedTextValue) propertyValue).getText());
} else {
bossBar.setName(new LiteralText(propertyValue.getString()));
}
return LazyValue.TRUE;
case "add_player":
if(propertyValue == null) throw new InternalExpressionException("Bossbar property " + property + " can't be queried, add a third parameter");
if(propertyValue instanceof ListValue) {
((ListValue) propertyValue).getItems().forEach((v)->{
ServerPlayerEntity player = EntityValue.getPlayerByValue(((CarpetContext)c).s.getMinecraftServer(),propertyValue);
if(player != null) bossBar.addPlayer(player);
});
return LazyValue.TRUE;
}
ServerPlayerEntity player = EntityValue.getPlayerByValue(((CarpetContext) c).s.getMinecraftServer(), propertyValue);
if(player != null) {
bossBar.addPlayer(player);
return LazyValue.TRUE;
}
return LazyValue.FALSE;
case "players":
if(propertyValue == null) {
return (_c, _t) -> ListValue.wrap(bossBar.getPlayers().stream().map(EntityValue::new).collect(Collectors.toList()));
}
if(propertyValue instanceof ListValue) {
bossBar.clearPlayers();
((ListValue) propertyValue).getItems().forEach((v) -> {
ServerPlayerEntity p = EntityValue.getPlayerByValue(((CarpetContext) c).s.getMinecraftServer(), v);
if (p != null) bossBar.addPlayer(p);
});
return LazyValue.TRUE;
}
ServerPlayerEntity p = EntityValue.getPlayerByValue(((CarpetContext) c).s.getMinecraftServer(), propertyValue);
bossBar.clearPlayers();
if (p != null) {
bossBar.addPlayer(p);
return LazyValue.TRUE;
}
return LazyValue.FALSE;
case "style":
if(propertyValue == null) {
return (_c, _t) -> StringValue.of(bossBar.getOverlay().getName());
}
BossBar.Style style = BossBar.Style.byName(propertyValue.getString());
if(style == null) throw new InternalExpressionException("'" + propertyValue.getString() + "' is not a valid value for property " + property);
bossBar.setOverlay(style);
return LazyValue.TRUE;
case "value":
if(propertyValue == null) return (_c, _t) -> NumericValue.of(bossBar.getValue());
if(!(propertyValue instanceof NumericValue)) throw new InternalExpressionException("'bossbar' requires a number as the value for the property " + property);
bossBar.setValue(((NumericValue) propertyValue).getInt());
return LazyValue.TRUE;
case "visible":
if(propertyValue == null) return (_c, _t) -> new NumericValue(bossBar.isVisible());
bossBar.setVisible(propertyValue.getBoolean());
return LazyValue.TRUE;
case "remove":
bossBarManager.remove(bossBar);
return LazyValue.TRUE;
default:
throw new InternalExpressionException("Unknown bossbar property " + property);
}
});
}
}
@@ -6,6 +6,7 @@ import carpet.fakes.BiomeArrayInterface;
import carpet.fakes.ChunkGeneratorInterface;
import carpet.fakes.ChunkTicketManagerInterface;
import carpet.fakes.ServerChunkManagerInterface;
import carpet.fakes.ServerWorldInterface;
import carpet.fakes.SpawnHelperInnerInterface;
import carpet.fakes.ThreadedAnvilChunkStorageInterface;
import carpet.helpers.FeatureGenerator;
@@ -58,6 +59,7 @@ import net.minecraft.item.SwordItem;
import net.minecraft.item.TridentItem;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ChunkTicket;
import net.minecraft.server.world.ChunkTicketType;
@@ -89,6 +91,7 @@ import net.minecraft.world.gen.ChunkRandom;
import net.minecraft.world.gen.chunk.ChunkGenerator;
import net.minecraft.world.gen.feature.ConfiguredStructureFeature;
import net.minecraft.world.gen.feature.StructureFeature;
import net.minecraft.world.level.ServerWorldProperties;
import net.minecraft.world.poi.PointOfInterest;
import net.minecraft.world.poi.PointOfInterestStorage;
import net.minecraft.world.poi.PointOfInterestType;
@@ -413,6 +416,67 @@ public class WorldAccess {
});
expression.addLazyFunction("weather",-1,(c, t, lv) -> {
ServerWorld world = ((CarpetContext) c).s.getWorld();
if(lv.size()==0)//cos it can thunder when raining or when clear.
{
Value ret = new StringValue(world.isThundering() ? "thunder" : (world.isRaining() ? "rain" : "clear"));
return (_c, _t) -> ret;
}
Value weather = lv.get(0).evalValue(c);
ServerWorldProperties worldProperties = ((ServerWorldInterface) world).getWorldPropertiesCM();
if(lv.size()==1)
{
int ticks;
switch (weather.getString().toLowerCase(Locale.ROOT)) {
case "clear":
ticks = worldProperties.getClearWeatherTime();
break;
case "rain":
ticks = world.isRaining()? worldProperties.getRainTime():0;//cos if not it gives 1 for some reason
break;
case "thunder":
ticks = world.isThundering()? worldProperties.getThunderTime():0;//same dealio here
break;
default:
throw new InternalExpressionException("Weather can only be 'clear', 'rain' or 'thunder'");
}
Value ret = new NumericValue(ticks);
return (_c,_t) -> ret;
}
if(lv.size()==2)
{
int ticks = NumericValue.asNumber(lv.get(1).evalValue(c), "tick_time in 'weather'").getInt();
switch (weather.getString().toLowerCase(Locale.ROOT))
{
case "clear":
world.setWeather(ticks,0,false,false);
break;
case "rain":
world.setWeather(0,ticks,true,false);
break;
case "thunder":
world.setWeather(
0,
ticks,//this is used to set thunder time, idk why...
true,
true
);
break;
default:
throw new InternalExpressionException("Weather can only be 'clear', 'rain' or 'thunder'");
}
Value ret = NumericValue.of(ticks);
return (_c, _t) -> ret;
}
throw new InternalExpressionException("'weather' requires 0, 1 or 2 arguments");
});
expression.addLazyFunction("pos", 1, (c, t, lv) ->
{
Value arg = lv.get(0).evalValue(c);
@@ -713,6 +777,21 @@ public class WorldAccess {
}
args = ((ListValue) args.get(0)).getItems();
}
else if (args.get(0) instanceof MapValue)
{
if (args.size() == 2)
{
Value dataValue = NBTSerializableValue.fromValue( args.get(1));
if (dataValue instanceof NBTSerializableValue)
{
data = ((NBTSerializableValue) dataValue).getCompoundTag();
}
}
Map<Value, Value> state = ((MapValue) args.get(0)).getMap();
List<Value> mapargs = new ArrayList<>();
state.forEach( (k, v) -> {mapargs.add(k); mapargs.add(v);});
args = mapargs;
}
else
{
if ((args.size() & 1) == 1)
@@ -748,7 +827,7 @@ public class WorldAccess {
{
Clearable.clear(world.getBlockEntity(targetPos));
boolean success = world.setBlockState(targetPos, finalSourceBlockState, 2);
if (success && finalData != null)
if (finalData != null)
{
BlockEntity be = world.getBlockEntity(targetPos);
if (be != null)
@@ -759,6 +838,7 @@ public class WorldAccess {
destTag.putInt("z", targetPos.getZ());
be.fromTag(finalSourceBlockState, destTag);
be.markDirty();
success = true;
}
}
result[0] = success;
@@ -4,9 +4,11 @@ import carpet.script.CarpetContext;
import carpet.script.LazyValue;
import carpet.script.exception.InternalExpressionException;
import carpet.script.value.BlockValue;
import carpet.script.value.EntityValue;
import carpet.script.value.ListValue;
import carpet.script.value.NumericValue;
import carpet.script.value.Value;
import net.minecraft.entity.Entity;
import net.minecraft.util.math.Vec3d;
import java.util.List;
@@ -16,7 +18,8 @@ public class Vector3Argument extends Argument
public Vec3d vec;
public final double yaw;
public final double pitch;
public boolean fromBlock;
public boolean fromBlock = false;
public Entity entity = null;
private Vector3Argument(Vec3d v, int o)
{
super(o);
@@ -38,13 +41,19 @@ public class Vector3Argument extends Argument
return this;
}
private Vector3Argument withEntity(Entity e)
{
entity = e;
return this;
}
public static Vector3Argument findIn(CarpetContext c, List<LazyValue> params, int offset)
{
return findIn(c,params, offset, false);
return findIn(c,params, offset, false, false);
}
public static Vector3Argument findIn(CarpetContext c, List<LazyValue> params, int offset, boolean optionalDirection)
public static Vector3Argument findIn(CarpetContext c, List<LazyValue> params, int offset, boolean optionalDirection, boolean optionalEntity)
{
try
{
@@ -54,6 +63,11 @@ public class Vector3Argument extends Argument
// pos + 0.5v
return (new Vector3Argument(Vec3d.ofCenter(((BlockValue) v1).getPos()), 1+offset)).fromBlock();
}
if (optionalEntity && v1 instanceof EntityValue)
{
Entity e = ((EntityValue) v1).getEntity();
return new Vector3Argument(e.getPos(), 1+offset).withEntity(e);
}
if (v1 instanceof ListValue)
{
List<Value> args = ((ListValue) v1).getItems();
@@ -1,5 +1,7 @@
package carpet.script.bundled;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtIo;
import net.minecraft.nbt.PositionTracker;
import net.minecraft.nbt.Tag;
import net.minecraft.nbt.TagReaders;
@@ -16,23 +18,25 @@ import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.stream.Stream;
public class FileModule extends Module
{
private String name;
private String code;
private boolean library;
public FileModule(File sourceFile)
public FileModule(Path sourcePath)
{
library = sourceFile.getName().endsWith(".scl");
library = sourcePath.getFileName().toString().endsWith(".scl");
try
{
name = sourceFile.getName().replaceFirst("\\.scl?","").toLowerCase(Locale.ROOT);
code = new String(Files.readAllBytes(sourceFile.toPath()), StandardCharsets.UTF_8);
name = sourcePath.getFileName().toString().replaceFirst("\\.scl?","").toLowerCase(Locale.ROOT);
code = new String(Files.readAllBytes(sourcePath), StandardCharsets.UTF_8);
}
catch ( IOException e)
{
@@ -59,25 +63,40 @@ public class FileModule extends Module
}
//copied private method from net.minecraft.nbt.NbtIo.read()
// to read non-compound tags - these won't be compressed
public static Tag read(File file)
{
try (DataInputStream dataInput_1 = new DataInputStream(new FileInputStream(file)))
try
{
byte byte_1 = dataInput_1.readByte();
if (byte_1 == 0)
return NbtIo.readCompressed(file);
}
catch (IOException e)
{
try
{
return NbtIo.read(file);
}
catch (IOException ioException)
{
try (DataInputStream dataInput_1 = new DataInputStream(new FileInputStream(file)))
{
byte byte_1 = dataInput_1.readByte();
if (byte_1 == 0)
{
return null;
}
else
{
dataInput_1.readUTF();
return TagReaders.of(byte_1).read(dataInput_1, 0, PositionTracker.DEFAULT);
}
}
catch (IOException ignored)
{
}
return null;
}
else
{
dataInput_1.readUTF();
return TagReaders.of(byte_1).read(dataInput_1, 0, PositionTracker.DEFAULT);
}
}
catch (IOException ignored)
{
}
return null;
}
//copied private method from net.minecraft.nbt.NbtIo.write() and client method safe_write
@@ -86,18 +105,32 @@ public class FileModule extends Module
File file_2 = new File(file.getAbsolutePath() + "_tmp");
if (file_2.exists()) file_2.delete();
try(DataOutputStream dataOutputStream_1 = new DataOutputStream(new FileOutputStream(file_2)))
if (tag_1 instanceof CompoundTag)
{
dataOutputStream_1.writeByte(tag_1.getType());
if (tag_1.getType() != 0)
try
{
dataOutputStream_1.writeUTF("");
tag_1.write(dataOutputStream_1);
NbtIo.writeCompressed((CompoundTag) tag_1, file_2);
}
catch (IOException e)
{
return false;
}
}
catch (IOException e)
else
{
return false;
try (DataOutputStream dataOutputStream_1 = new DataOutputStream(new FileOutputStream(file_2)))
{
dataOutputStream_1.writeByte(tag_1.getType());
if (tag_1.getType() != 0)
{
dataOutputStream_1.writeUTF("");
tag_1.write(dataOutputStream_1);
}
}
catch (IOException e)
{
return false;
}
}
if (file.exists()) file.delete();
if (!file.exists()) file_2.renameTo(file);
@@ -143,4 +176,18 @@ public class FileModule extends Module
return null;
}
}
public static Stream<Path> listFiles(Path dir, String ext)
{
boolean folderListing = ext.equals("folder");
try
{
return Files.list(dir).
filter(path -> folderListing?Files.isDirectory(path):path.toString().endsWith(ext));
}
catch (IOException ignored)
{
return null;
}
}
}
+38 -24
View File
@@ -3,10 +3,12 @@ package carpet.script.bundled;
import carpet.CarpetServer;
import net.minecraft.nbt.Tag;
import net.minecraft.util.WorldSavePath;
import org.apache.commons.io.FilenameUtils;
import java.io.File;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.stream.Stream;
public abstract class Module
{
@@ -19,61 +21,73 @@ public abstract class Module
{
if (isShared)
{
return "shared/"+file;
return file.isEmpty()?"shared":"shared/"+file;
}
else if (module != null && module.getName() != null) // appdata
if (module != null && module.getName() != null) // appdata
{
return module.getName()+".data"+(file==null?"":"/"+file);
}
else
{
throw new RuntimeException("Invalid file descriptor: "+file);
return module.getName()+".data"+(file==null || file.isEmpty()?"":"/"+file);
}
throw new RuntimeException("Invalid file descriptor: "+file);
}
public static Tag getData(Module module, String file, boolean isShared)
{
File dataFile = resolveResource(module, file, "nbt", isShared);
Path dataFile = resolveResource(module, file, "nbt", isShared);
if (dataFile == null) return null;
if (!Files.exists(dataFile.toPath()) || !(dataFile.isFile())) return null;
synchronized (writeIOSync) { return FileModule.read(dataFile); }
if (!Files.exists(dataFile) || !(dataFile.toFile().isFile())) return null;
synchronized (writeIOSync) { return FileModule.read(dataFile.toFile()); }
}
public static boolean saveData(Module module, String file, Tag globalState, boolean isShared)
{
File dataFile = resolveResource(module, file, "nbt", isShared);
Path dataFile = resolveResource(module, file, "nbt", isShared);
if (dataFile == null) return false;
if (!Files.exists(dataFile.toPath().getParent()) && !dataFile.getParentFile().mkdirs()) return false;
synchronized (writeIOSync) { return FileModule.write(globalState, dataFile); }
if (!Files.exists(dataFile.getParent()) && !dataFile.toFile().getParentFile().mkdirs()) return false;
synchronized (writeIOSync) { return FileModule.write(globalState, dataFile.toFile()); }
}
public static boolean appendToTextFile(Module module, String resourceName, String type, boolean isShared, List<String> message)
{
File dataFile = resolveResource(module, resourceName, "txt", isShared);
Path dataFile = resolveResource(module, resourceName, "txt", isShared);
if (dataFile == null) return false;
if (!Files.exists(dataFile.toPath().getParent()) && !dataFile.getParentFile().mkdirs()) return false;
synchronized (writeIOSync) { return FileModule.appendText(dataFile, type.equals("text"), message); }
if (!Files.exists(dataFile.getParent()) && !dataFile.toFile().getParentFile().mkdirs()) return false;
synchronized (writeIOSync) { return FileModule.appendText(dataFile.toFile(), type.equals("text"), message); }
}
public static boolean dropExistingFile(Module module, String resourceName, String ext, boolean isShared)
{
File dataFile = resolveResource(module, resourceName, ext, isShared);
Path dataFile = resolveResource(module, resourceName, ext, isShared);
if (dataFile == null) return false;
synchronized (writeIOSync) { return dataFile.delete(); }
synchronized (writeIOSync) { return dataFile.toFile().delete(); }
}
public static List<String> listFile(Module module, String resourceName, String ext, boolean isShared)
{
File dataFile = resolveResource(module, resourceName, ext, isShared);
Path dataFile = resolveResource(module, resourceName, ext, isShared);
if (dataFile == null) return null;
if (!dataFile.exists()) return null;
synchronized (writeIOSync) { return FileModule.listFileContent(dataFile); }
if (!dataFile.toFile().exists()) return null;
synchronized (writeIOSync) { return FileModule.listFileContent(dataFile.toFile()); }
}
private static File resolveResource(Module module, String resourceName, String ext, boolean isShared)
public static Stream<String> listFolder(Module module, String resourceName, String ext, boolean isShared)
{
Path dir = resolveResource(module, resourceName, null, isShared);
if (dir == null) return null;
if (!Files.exists(dir)) return null;
Stream<Path> result;
synchronized (writeIOSync) { result = FileModule.listFiles(dir, ext); }
if (result == null) return null;
Path rootPath = resolveResource(module, "", null, isShared);
Stream<String> strings = result.map(p -> rootPath.relativize(p).toString().replaceAll("[\\\\/]","/"));
if (!ext.equals("folder"))
return strings.map(FilenameUtils::removeExtension);
return strings;
}
private static Path resolveResource(Module module, String resourceName, String ext, boolean isShared)
{
if (!isShared && (module == null || module.getName() == null)) return null;
return CarpetServer.minecraft_server.getSavePath(WorldSavePath.ROOT).resolve("scripts/"+getDescriptor(module, resourceName, isShared)+"."+ext).toFile();
return CarpetServer.minecraft_server.getSavePath(WorldSavePath.ROOT).resolve("scripts/"+getDescriptor(module, resourceName, isShared)+(ext==null?"":"."+ext));
}
@Override
@@ -63,6 +63,7 @@ import net.minecraft.command.argument.RotationArgumentType;
import net.minecraft.command.argument.ScoreHolderArgumentType;
import net.minecraft.command.argument.ScoreboardSlotArgumentType;
import net.minecraft.command.argument.SwizzleArgumentType;
import net.minecraft.command.argument.TeamArgumentType;
import net.minecraft.command.argument.TimeArgumentType;
import net.minecraft.command.argument.UuidArgumentType;
import net.minecraft.command.argument.Vec2ArgumentType;
@@ -130,7 +131,6 @@ public abstract class CommandArgument
new VanillaUnconfigurableArgument( "blockpredicate", BlockPredicateArgumentType::blockPredicate,
(c, p) -> ValueConversions.ofBlockPredicate(c.getSource().getMinecraftServer().getTagManager(), BlockPredicateArgumentType.getBlockPredicate(c, p)), false
),
// block_predicate todo - not sure about the returned format. Needs to match block tags used in the API (future)
new VanillaUnconfigurableArgument("teamcolor", ColorArgumentType::color,
(c, p) -> {
Formatting format = ColorArgumentType.getColor(c, p);
@@ -165,7 +165,6 @@ public abstract class CommandArgument
(c, p) -> ValueConversions.of(Registry.ENCHANTMENT.getId(ItemEnchantmentArgumentType.getEnchantment(c, p))), false
),
// item_predicate ?? //same as item but accepts tags, not sure right now
// slot // item_slot
new SlotArgument(),
new VanillaUnconfigurableArgument("item", ItemStackArgumentType::itemStack,
(c, p) -> ValueConversions.of(ItemStackArgumentType.getItemStackArgument(c, p).createStack(1, false)), false
@@ -241,7 +240,9 @@ public abstract class CommandArgument
new VanillaUnconfigurableArgument("swizzle", SwizzleArgumentType::swizzle,
(c, p) -> StringValue.of(SwizzleArgumentType.getSwizzle(c, p).stream().map(Direction.Axis::asString).collect(Collectors.joining())), true
),
// team
new VanillaUnconfigurableArgument("team", TeamArgumentType::team,
(c, p) -> StringValue.of(TeamArgumentType.getTeam(c, p).getName()), false
),
new VanillaUnconfigurableArgument("time", TimeArgumentType::time,
(c, p) -> new NumericValue(IntegerArgumentType.getInteger(c, p)), false
),
@@ -437,7 +438,7 @@ public abstract class CommandArgument
if (config.containsKey("options"))
{
Value optionsValue = config.get("options");
if (!(optionsValue instanceof ListValue)) throw new InternalExpressionException("Custom sting type requires options passed as a list");
if (!(optionsValue instanceof ListValue)) throw new InternalExpressionException("Custom string type requires options passed as a list");
validOptions = ((ListValue) optionsValue).getItems().stream()
.map(v -> caseSensitive?v.getString():(v.getString().toLowerCase(Locale.ROOT)))
.collect(Collectors.toSet());
@@ -135,23 +135,25 @@ public class DataStructures {
expression.addFunction("range", (lv) ->
{
long from = 0;
long to = 0;
long step = 1;
NumericValue from = Value.ZERO;
NumericValue to;
NumericValue step = Value.ONE;
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();
to = NumericValue.asNumber(lv.get(0));
if (lv.size() > 1)
{
from = to;
to = NumericValue.asNumber(lv.get(1)).getLong();
to = NumericValue.asNumber(lv.get(1));
if (lv.size() > 2)
{
step = NumericValue.asNumber(lv.get(2)).getLong();
step = NumericValue.asNumber(lv.get(2));
}
}
return LazyListValue.range(from, to, step);
return (from.isInteger() && to.isInteger() && step.isInteger())
? LazyListValue.rangeLong(from.getLong(), to.getLong(), step.getLong())
: LazyListValue.rangeDouble(from.getDouble(), to.getDouble(), step.getDouble());
});
expression.addLazyFunction("m", -1, (c, t, llv) ->
@@ -0,0 +1,206 @@
package carpet.script.utils;
import carpet.CarpetSettings;
import carpet.script.value.FormattedTextValue;
import carpet.script.value.Value;
import com.mojang.brigadier.ResultConsumer;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import net.minecraft.command.argument.EntityAnchorArgumentType;
import net.minecraft.entity.Entity;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.command.CommandOutput;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.text.Text;
import net.minecraft.util.math.Vec2f;
import net.minecraft.util.math.Vec3d;
import java.util.List;
import java.util.function.BinaryOperator;
public class FixedCommandSource extends ServerCommandSource
{
private Vec3d position;
private ServerWorld world;
private final String simpleName;
private final Text name;
private final MinecraftServer server;
private Entity entity;
private ResultConsumer<ServerCommandSource> resultConsumer;
private EntityAnchorArgumentType.EntityAnchor entityAnchor;
private Vec2f rotation;
private final Value[] error;
private final List<Value> chatOutput;
public FixedCommandSource(ServerCommandSource original, Vec3d pos, Value[] error, List<Value> chatOutput)
{
super(CommandOutput.DUMMY, pos, Vec2f.ZERO, original.getWorld(), CarpetSettings.runPermissionLevel,
original.getName(), original.getDisplayName(), original.getMinecraftServer(), original.getEntity(), true,
(ctx, succ, res) -> { }, EntityAnchorArgumentType.EntityAnchor.FEET);
position = pos;
world = original.getWorld();
simpleName = original.getName();
name = original.getDisplayName();
server = original.getMinecraftServer();
entity = original.getEntity();
resultConsumer = (ctx, succ, res) -> { };
entityAnchor = original.getEntityAnchor();
rotation = Vec2f.ZERO;
this.error = error;
this.chatOutput = chatOutput;
}
@Override
public ServerCommandSource withEntity(Entity entity)
{
this.entity = entity;
return this;
}
@Override
public ServerCommandSource withPosition(Vec3d position)
{
this.position = position;
return this;
}
@Override
public ServerCommandSource withRotation(Vec2f rotation)
{
this.rotation = rotation;
return this;
}
@Override
public ServerCommandSource withConsumer(ResultConsumer<ServerCommandSource> consumer)
{
this.resultConsumer = consumer;
return this;
}
@Override
public ServerCommandSource mergeConsumers(ResultConsumer<ServerCommandSource> consumer, BinaryOperator<ResultConsumer<ServerCommandSource>> binaryOperator)
{
return this.withConsumer(binaryOperator.apply(this.resultConsumer, consumer));
}
@Override
public ServerCommandSource withSilent()
{
return this;
}
@Override
public ServerCommandSource withLevel(int level)
{
return this;
}
@Override
public ServerCommandSource withMaxLevel(int level)
{
return this;
}
@Override
public ServerCommandSource withEntityAnchor(EntityAnchorArgumentType.EntityAnchor anchor)
{
entityAnchor = anchor;
return this;
}
@Override
public ServerCommandSource withWorld(ServerWorld world)
{
this.world = world;
return this;
}
@Override
public Text getDisplayName()
{
return name;
}
@Override
public String getName()
{
return simpleName;
}
@Override
public Vec3d getPosition()
{
return position;
}
@Override
public ServerWorld getWorld()
{
return world;
}
@Override
public Entity getEntity()
{
return entity;
}
@Override
public Entity getEntityOrThrow() throws CommandSyntaxException
{
if (entity == null) {
throw REQUIRES_ENTITY_EXCEPTION.create();
} else {
return entity;
}
}
@Override
public ServerPlayerEntity getPlayer() throws CommandSyntaxException
{
if (!(this.entity instanceof ServerPlayerEntity)) {
throw REQUIRES_PLAYER_EXCEPTION.create();
} else {
return (ServerPlayerEntity)this.entity;
}
}
@Override
public Vec2f getRotation()
{
return rotation;
}
@Override
public MinecraftServer getMinecraftServer()
{
return server;
}
@Override
public EntityAnchorArgumentType.EntityAnchor getEntityAnchor()
{
return entityAnchor;
}
@Override
public void onCommandComplete(CommandContext<ServerCommandSource> context, boolean success, int result)
{
}
@Override
public void sendError(Text message)
{
error[0] = new FormattedTextValue(message);
}
@Override
public void sendFeedback(Text message, boolean broadcastToOps)
{
chatOutput.add(new FormattedTextValue(message));
}
}
@@ -1,6 +1,7 @@
package carpet.script.utils;
import carpet.CarpetServer;
import carpet.CarpetSettings;
import carpet.script.CarpetContext;
import carpet.script.CarpetScriptHost;
import carpet.script.value.ListValue;
@@ -11,7 +12,9 @@ import carpet.script.value.Value;
import carpet.settings.ParsedRule;
import carpet.settings.SettingsManager;
import com.sun.management.OperatingSystemMXBean;
import net.fabricmc.loader.api.FabricLoader;
import net.minecraft.util.WorldSavePath;
import net.minecraft.world.GameRules;
import java.lang.management.ManagementFactory;
import java.nio.file.Path;
@@ -80,6 +83,7 @@ public class SystemInfo {
}
return whitelist;
});
put("server_dev_environment", c-> new NumericValue(FabricLoader.getInstance().isDevelopmentEnvironment()));
put("java_max_memory", c -> new NumericValue(Runtime.getRuntime().maxMemory()));
put("java_allocated_memory", c -> new NumericValue(Runtime.getRuntime().totalMemory()));
@@ -115,7 +119,7 @@ public class SystemInfo {
CarpetServer.extensions.forEach(e -> {
SettingsManager manager = e.customSettingsManager();
if (manager == null) return;
Collection<ParsedRule<?>> extensionRules = manager.getRules();
extensionRules.forEach(rule -> {
carpetRules.put(new StringValue(manager.getIdentifier()+":"+rule.name), new StringValue(rule.getAsString()));
@@ -123,14 +127,18 @@ public class SystemInfo {
});
return carpetRules;
});
put("world_gamerules", c->{
Map<Value, Value> rules = new HashMap<>();
final GameRules gameRules = c.s.getWorld().getGameRules();
GameRules.accept(new GameRules.Visitor() {
@Override
public <T extends GameRules.Rule<T>> void visit(GameRules.Key<T> key, GameRules.Type<T> type) {
rules.put(StringValue.of(key.getName()), StringValue.of(gameRules.get(key).toString()));
}
});
return MapValue.wrap(rules);
});
put("scarpet_version", c -> StringValue.of(CarpetSettings.carpetVersion));
}};
public static Value get(String what, CarpetContext cc)
@@ -9,6 +9,7 @@ import carpet.fakes.MobEntityInterface;
import carpet.fakes.HungerManagerInterface;
import carpet.fakes.ServerPlayerInteractionManagerInterface;
import carpet.helpers.Tracer;
import carpet.network.ServerNetworkHandler;
import carpet.patches.EntityPlayerMPFake;
import carpet.script.CarpetContext;
import carpet.script.EntityEventsGroup;
@@ -29,6 +30,7 @@ import net.minecraft.nbt.StringTag;
import net.minecraft.nbt.Tag;
import net.minecraft.network.packet.s2c.play.EntityPassengersSetS2CPacket;
import net.minecraft.network.packet.s2c.play.EntityPositionS2CPacket;
import net.minecraft.network.packet.s2c.play.ExperienceBarUpdateS2CPacket;
import net.minecraft.network.packet.s2c.play.PlayerPositionLookS2CPacket;
import net.minecraft.command.EntitySelector;
import net.minecraft.command.EntitySelectorReader;
@@ -221,10 +223,28 @@ public class EntityValue extends Value
return entity.hashCode();
}
public static EntityClassDescriptor getEntityDescriptor(String who)
public static EntityClassDescriptor getEntityDescriptor(String who, MinecraftServer server)
{
EntityClassDescriptor eDesc = EntityClassDescriptor.byName.get(who);
if (eDesc == null) throw new InternalExpressionException(who+" is not a valid entity descriptor");
if (eDesc == null)
{
boolean positive = true;
if (who.startsWith("!"))
{
positive = false;
who = who.substring(1);
}
net.minecraft.tag.Tag<EntityType<?>> eTag = server.getTagManager().getEntityTypes().getTag(new Identifier(who));
if (eTag == null) throw new InternalExpressionException(who+" is not a valid entity descriptor");
if (positive)
{
return new EntityClassDescriptor(null, e -> eTag.contains(e.getType()) && e.isAlive(), eTag.values().stream());
}
else
{
return new EntityClassDescriptor(null, e -> !eTag.contains(e.getType()) && e.isAlive(), Registry.ENTITY_TYPE.stream().filter(et -> !eTag.contains(et)));
}
}
return eDesc;
//TODO add more here like search by tags, or type
//if (who.startsWith('tag:'))
@@ -399,8 +419,21 @@ public class EntityValue extends Value
put("passengers", (e, a) -> ListValue.wrap(e.getPassengerList().stream().map(EntityValue::new).collect(Collectors.toList())));
put("mount", (e, a) -> (e.getVehicle()!=null)?new EntityValue(e.getVehicle()):Value.NULL);
put("unmountable", (e, a) -> new NumericValue(((EntityInterface)e).isPermanentVehicle()));
// deprecated
put("tags", (e, a) -> ListValue.wrap(e.getScoreboardTags().stream().map(StringValue::new).collect(Collectors.toList())));
put("scoreboard_tags", (e, a) -> ListValue.wrap(e.getScoreboardTags().stream().map(StringValue::new).collect(Collectors.toList())));
put("entity_tags", (e, a) -> ListValue.wrap(e.getServer().getTagManager().getEntityTypes().getTagsFor(e.getType()).stream().map(ValueConversions::of).collect(Collectors.toList())));
// deprecated
put("has_tag", (e, a) -> new NumericValue(e.getScoreboardTags().contains(a.getString())));
put("has_scoreboard_tag", (e, a) -> new NumericValue(e.getScoreboardTags().contains(a.getString())));
put("has_entity_tag", (e, a) -> {
net.minecraft.tag.Tag<EntityType<?>> tag = e.getServer().getTagManager().getEntityTypes().getTag(new Identifier(a.getString()));
if (tag == null) return Value.NULL;
return new NumericValue(e.getType().isIn(tag));
});
put("yaw", (e, a)-> new NumericValue(e.yaw));
put("head_yaw", (e, a)-> {
if (e instanceof LivingEntity)
@@ -498,6 +531,31 @@ public class EntityValue extends Value
return Value.NULL;
});
put("absorption",(e, a)->{
if(e instanceof PlayerEntity) return new NumericValue(((PlayerEntity) e).getAbsorptionAmount());
return Value.NULL;
});
put("xp",(e, a)->{
if(e instanceof PlayerEntity) return new NumericValue(((PlayerEntity) e).totalExperience);
return Value.NULL;
});
put("xp_level", (e, a)->{
if(e instanceof PlayerEntity) return new NumericValue(((PlayerEntity) e).experienceLevel);
return Value.NULL;
});
put("xp_progress", (e, a)->{
if(e instanceof PlayerEntity) return new NumericValue(((PlayerEntity) e).experienceProgress);
return Value.NULL;
});
put("score", (e, a)->{
if(e instanceof PlayerEntity) return new NumericValue(((PlayerEntity) e).getScore());
return Value.NULL;
});
put("jumping", (e, a) -> {
if (e instanceof LivingEntity)
{
@@ -579,6 +637,14 @@ public class EntityValue extends Value
return Value.NULL;
});
put("client_brand", (e, a) -> {
if (e instanceof ServerPlayerEntity)
{
return StringValue.of(ServerNetworkHandler.getPlayerStatus((ServerPlayerEntity) e));
}
return Value.NULL;
});
put("team", (e, a) -> e.getScoreboardTeam()==null?Value.NULL:new StringValue(e.getScoreboardTeam().getName()));
put("ping", (e, a) -> {
@@ -1319,11 +1385,36 @@ public class EntityValue extends Value
if(e instanceof PlayerEntity) ((PlayerEntity) e).getHungerManager().addExhaustion((int) NumericValue.asNumber(v).getLong());
});
put("saturation", (e, v)-> {
if(e instanceof PlayerEntity) ((PlayerEntity) e).getHungerManager().setSaturationLevelClient((float)NumericValue.asNumber(v).getLong());
put("absorption", (e, v) -> {
if (e instanceof PlayerEntity) ((PlayerEntity) e).setAbsorptionAmount((float) NumericValue.asNumber(v, "absorbtion").getLong());
});
put("air", (e, v) -> e.setAir(NumericValue.asNumber(v).getInt()));
put("add_xp", (e, v) -> {
if (e instanceof PlayerEntity) ((PlayerEntity) e).addExperience(NumericValue.asNumber(v, "add_xp").getInt());
});
put("xp_level", (e, v) -> {
if (e instanceof PlayerEntity) ((PlayerEntity) e).addExperienceLevels(NumericValue.asNumber(v, "xp_level").getInt()-((PlayerEntity) e).experienceLevel);
});
put("xp_progress", (e, v) -> {
if (e instanceof ServerPlayerEntity)
{
ServerPlayerEntity p = (ServerPlayerEntity) e;
p.experienceProgress = NumericValue.asNumber(v, "xp_progress").getFloat();
p.networkHandler.sendPacket(new ExperienceBarUpdateS2CPacket(p.experienceProgress, p.totalExperience, p.experienceLevel));
}
});
put("xp_score", (e, v) -> {
if (e instanceof PlayerEntity) ((PlayerEntity) e).setScore(NumericValue.asNumber(v, "xp_score").getInt());
});
put("saturation", (e, v)-> {
if(e instanceof PlayerEntity) ((HungerManagerInterface) ((PlayerEntity) e).getHungerManager()).setSaturationCM(NumericValue.asNumber(v, "saturation").getFloat());
});
put("air", (e, v) -> e.setAir(NumericValue.asNumber(v, "air").getInt()));
put("breaking_progress", (e, a) -> {
if (e instanceof ServerPlayerEntity)
@@ -1372,7 +1463,7 @@ public class EntityValue extends Value
EntityEventsGroup.Event event = EntityEventsGroup.Event.byName.get(eventName);
if (event == null)
throw new InternalExpressionException("Unknown entity event: " + eventName);
((EntityInterface)entity).getEventContainer().addEvent(event, cc.host.getName(), fun, args);
((EntityInterface)entity).getEventContainer().addEvent(event, cc.host, fun, args);
}
@Override
@@ -3,6 +3,7 @@ package carpet.script.value;
import net.minecraft.nbt.StringTag;
import net.minecraft.nbt.Tag;
import net.minecraft.text.BaseText;
import net.minecraft.text.LiteralText;
import net.minecraft.text.Text;
public class FormattedTextValue extends StringValue
@@ -14,6 +15,33 @@ public class FormattedTextValue extends StringValue
this.text = text;
}
public static Value combine(Value left, Value right) {
BaseText text;
if (left instanceof FormattedTextValue)
{
text = (BaseText) ((FormattedTextValue) left).getText().shallowCopy();
}
else
{
if (left instanceof NullValue)
return right;
text = new LiteralText(left.getString());
}
if (right instanceof FormattedTextValue)
{
text.append(((FormattedTextValue) right).getText().shallowCopy());
return new FormattedTextValue(text);
}
else
{
if (right instanceof NullValue)
return left;
text.append(right.getString());
return new FormattedTextValue(text);
}
}
@Override
public String getString() {
return text.getString();
@@ -48,10 +76,16 @@ public class FormattedTextValue extends StringValue
return StringTag.of(Text.Serializer.toJson(text));
}
@Override
public Value add(Value o) {
return combine(this, o);
}
public String serialize()
{
return Text.Serializer.toJson(text);
}
public static FormattedTextValue deserialize(String serialized)
{
return new FormattedTextValue(Text.Serializer.fromJson(serialized));
@@ -11,7 +11,7 @@ import java.util.Locale;
public abstract class LazyListValue extends AbstractListValue implements Iterator<Value>
{
public static LazyListValue range(long from, long to, long step)
public static LazyListValue rangeDouble(double from, double to, double step)
{
return new LazyListValue()
{
@@ -23,10 +23,10 @@ public abstract class LazyListValue extends AbstractListValue implements Iterato
this.limit = to;
this.stepp = step;
}
private long start;
private long current;
private long limit;
private long stepp;
private final double start;
private double current;
private final double limit;
private final double stepp;
@Override
public Value next()
{
@@ -50,7 +50,50 @@ public abstract class LazyListValue extends AbstractListValue implements Iterato
@Override
public String getString()
{
return String.format(Locale.ROOT, "[%d, %d, ..., %d)",start, start+stepp, limit);
return String.format(Locale.ROOT, "[%s, %s, ..., %s)",NumericValue.of(start).getString(), NumericValue.of(start+stepp).getString(), NumericValue.of( limit).getString());
}
};
}
public static LazyListValue rangeLong(long from, long to, long step)
{
return new LazyListValue()
{
{
if (step == 0)
throw new InternalExpressionException("Range will never end with a zero step");
this.start = from;
this.current = this.start;
this.limit = to;
this.stepp = step;
}
private final long start;
private long current;
private final long limit;
private final long stepp;
@Override
public Value next()
{
Value val = new NumericValue(current);
current += stepp;
return val;
}
@Override
public void reset()
{
current = start;
}
@Override
public boolean hasNext()
{
return stepp > 0?(current < limit):(current > limit);
}
@Override
public String getString()
{
return String.format(Locale.ROOT, "[%s, %s, ..., %s)",NumericValue.of(start).getString(), NumericValue.of(start+stepp).getString(), NumericValue.of( limit).getString());
}
};
}
@@ -267,7 +267,7 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa
}
}
private static Map<String,ItemStackArgument> itemCache = new HashMap<>();
private static final Map<String,ItemStackArgument> itemCache = new HashMap<>();
public static ItemStackArgument parseItem(String itemString)
{
@@ -15,6 +15,7 @@ import java.math.RoundingMode;
import java.util.Locale;
import static java.lang.Math.abs;
import static java.lang.Math.signum;
public class NumericValue extends Value
{
@@ -57,6 +58,7 @@ public class NumericValue extends Value
{
if (value.isInfinite()) return "INFINITY";
if (value.isNaN()) return "NaN";
if (abs(value) < epsilon) return (signum(value) < 0)?"-0":"0"; //zero rounding fails with big decimals
// dobules have 16 point precision, 12 is plenty to display
return BigDecimal.valueOf(value).round(displayRounding).stripTrailingZeros().toPlainString();
}
@@ -306,4 +308,9 @@ public class NumericValue extends Value
if (longValue != null) return new NumericValue(-longValue);
return new NumericValue(-value);
}
public boolean isInteger()
{
return longValue!= null || getDouble() == (double)getLong();
}
}
+10 -4
View File
@@ -13,10 +13,12 @@ import java.util.regex.PatternSyntaxException;
public abstract class Value implements Comparable<Value>, Cloneable
{
public static Value FALSE = new NumericValue(0);
public static Value TRUE = new NumericValue(1);
public static Value ZERO = FALSE;
public static Value NULL = new NullValue();
public static NumericValue FALSE = new NumericValue(0);
public static NumericValue TRUE = new NumericValue(1);
public static NumericValue ZERO = FALSE;
public static NumericValue ONE = TRUE;
public static NullValue NULL = new NullValue();
public String boundVariable;
@@ -77,6 +79,10 @@ public abstract class Value implements Comparable<Value>, Cloneable
public Value add(Value o) {
String lstr = this.getString();
if (o instanceof FormattedTextValue)
{
return FormattedTextValue.combine(this, o);
}
if (lstr == null) // null
return new StringValue(o.getString());
String rstr = o.getString();