Merge branch 'master' into display-title-additions
This commit is contained in:
88 files changed
+3088
-1017
No files matched your search
@@ -5,6 +5,7 @@ import carpet.CarpetSettings;
|
||||
import carpet.helpers.TickSpeed;
|
||||
import carpet.script.exception.InternalExpressionException;
|
||||
import carpet.script.value.BlockValue;
|
||||
import carpet.script.value.BooleanValue;
|
||||
import carpet.script.value.EntityValue;
|
||||
import carpet.script.value.FunctionValue;
|
||||
import carpet.script.value.ListValue;
|
||||
@@ -14,12 +15,16 @@ import carpet.script.value.StringValue;
|
||||
import carpet.script.value.Value;
|
||||
import carpet.script.value.ValueConversions;
|
||||
import carpet.settings.ParsedRule;
|
||||
import carpet.utils.CarpetProfiler;
|
||||
import carpet.utils.Messenger;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.entity.EntityType;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.entity.TntEntity;
|
||||
import net.minecraft.entity.damage.DamageSource;
|
||||
import net.minecraft.entity.passive.MerchantEntity;
|
||||
import net.minecraft.entity.projectile.ProjectileEntity;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.command.ServerCommandSource;
|
||||
import net.minecraft.entity.Entity;
|
||||
@@ -41,6 +46,7 @@ import net.minecraft.village.Merchant;
|
||||
import net.minecraft.village.TradeOffer;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.chunk.Chunk;
|
||||
import net.minecraft.world.explosion.Explosion;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -51,6 +57,7 @@ import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
@@ -204,6 +211,7 @@ public class CarpetEventServer
|
||||
{
|
||||
if (callList.size() > 0)
|
||||
{
|
||||
CarpetProfiler.ProfilerToken currentSection = CarpetProfiler.start_section(null, "Scarpet events", CarpetProfiler.TYPE.GENERAL);
|
||||
List<Value> argv = argumentSupplier.get(); // empty for onTickDone
|
||||
ServerCommandSource source;
|
||||
try
|
||||
@@ -225,6 +233,7 @@ public class CarpetEventServer
|
||||
if (!call.execute(source, argv)) fails.add(call);
|
||||
}
|
||||
for (Callback call : fails) callList.remove(call);
|
||||
CarpetProfiler.end_current_section(currentSection);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -862,6 +871,59 @@ public class CarpetEventServer
|
||||
);
|
||||
}
|
||||
};
|
||||
//copy of Explosion.getCausingEntity() #TRACK#
|
||||
private static LivingEntity getExplosionCausingEntity(Entity entity)
|
||||
{
|
||||
if (entity == null) return null;
|
||||
else if (entity instanceof TntEntity) return ((TntEntity)entity).getCausingEntity();
|
||||
else if (entity instanceof LivingEntity) return (LivingEntity)entity;
|
||||
else if (entity instanceof ProjectileEntity) {
|
||||
Entity owner = ((ProjectileEntity)entity).getOwner();
|
||||
if (owner instanceof LivingEntity) return (LivingEntity)owner;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static final Event EXPLOSION_OUTCOME = new Event("explosion_outcome", 8, true)
|
||||
{
|
||||
@Override
|
||||
public void onExplosion(ServerWorld world, Entity e, Supplier<LivingEntity> attacker, double x, double y, double z, float power, boolean createFire, List<BlockPos> affectedBlocks, List<Entity> affectedEntities, Explosion.DestructionType type)
|
||||
{
|
||||
handler.call(
|
||||
() -> Arrays.asList(
|
||||
ListValue.fromTriple(x, y, z),
|
||||
NumericValue.of(power),
|
||||
EntityValue.of(e),
|
||||
EntityValue.of(attacker!= null?attacker.get():Event.getExplosionCausingEntity(e)),
|
||||
StringValue.of(type.name().toLowerCase(Locale.ROOT)),
|
||||
BooleanValue.of(createFire),
|
||||
new ListValue(affectedBlocks.stream().filter(b -> !world.isAir(b)). map( // da heck they send air blocks
|
||||
b -> new BlockValue(world.getBlockState(b),world,b)
|
||||
).collect(Collectors.toList())),
|
||||
new ListValue(affectedEntities.stream().map(EntityValue::of).collect(Collectors.toList()))
|
||||
), () -> CarpetServer.minecraft_server.getCommandSource().withWorld(world)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
public static final Event EXPLOSION = new Event("explosion", 6, true)
|
||||
{
|
||||
@Override
|
||||
public void onExplosion(ServerWorld world, Entity e, Supplier<LivingEntity> attacker, double x, double y, double z, float power, boolean createFire, List<BlockPos> affectedBlocks, List<Entity> affectedEntities, Explosion.DestructionType type)
|
||||
{
|
||||
handler.call(
|
||||
() -> Arrays.asList(
|
||||
ListValue.fromTriple(x, y, z),
|
||||
NumericValue.of(power),
|
||||
EntityValue.of(e),
|
||||
EntityValue.of(attacker!= null?attacker.get():Event.getExplosionCausingEntity(e)),
|
||||
StringValue.of(type.name().toLowerCase(Locale.ROOT)),
|
||||
BooleanValue.of(createFire)
|
||||
), () -> CarpetServer.minecraft_server.getCommandSource().withWorld(world)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
public static String getEntityLoadEventName(EntityType<? extends Entity> et)
|
||||
{
|
||||
@@ -982,7 +1044,7 @@ public class CarpetEventServer
|
||||
public void onSlotSwitch(ServerPlayerEntity player, int from, int to) {}
|
||||
public void onTrade(ServerPlayerEntity player, Merchant merchant, TradeOffer tradeOffer) {}
|
||||
|
||||
|
||||
public void onExplosion(ServerWorld world, Entity e, Supplier<LivingEntity> attacker, double x, double y, double z, float power, boolean createFire, List<BlockPos> affectedBlocks, List<Entity> affectedEntities, Explosion.DestructionType type) { }
|
||||
public void onWorldEvent(ServerWorld world, BlockPos pos) { }
|
||||
public void onWorldEventFlag(ServerWorld world, BlockPos pos, int flag) { }
|
||||
public void onCarpetRuleChanges(ParsedRule<?> rule, ServerCommandSource source) { }
|
||||
|
||||
@@ -3,6 +3,7 @@ package carpet.script;
|
||||
import carpet.CarpetServer;
|
||||
import carpet.CarpetSettings;
|
||||
import carpet.script.api.Auxiliary;
|
||||
import carpet.script.argument.FileArgument;
|
||||
import carpet.script.argument.FunctionArgument;
|
||||
import carpet.script.bundled.Module;
|
||||
import carpet.script.command.CommandArgument;
|
||||
@@ -17,9 +18,11 @@ import carpet.script.value.MapValue;
|
||||
import carpet.script.value.NumericValue;
|
||||
import carpet.script.value.StringValue;
|
||||
import carpet.script.value.Value;
|
||||
import carpet.utils.CarpetProfiler;
|
||||
import carpet.utils.Messenger;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
import com.mojang.brigadier.builder.ArgumentBuilder;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
@@ -32,6 +35,7 @@ import net.minecraft.server.command.ServerCommandSource;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.text.LiteralText;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
@@ -43,11 +47,13 @@ import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
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.argument;
|
||||
import static net.minecraft.server.command.CommandManager.literal;
|
||||
|
||||
public class CarpetScriptHost extends ScriptHost
|
||||
@@ -122,6 +128,7 @@ public class CarpetScriptHost extends ScriptHost
|
||||
|
||||
private static int execute(CommandContext<ServerCommandSource> ctx, String hostName, FunctionArgument<Value> funcSpec, List<String> paramNames) throws CommandSyntaxException
|
||||
{
|
||||
CarpetProfiler.ProfilerToken currentSection = CarpetProfiler.start_section(null, "Scarpet command", CarpetProfiler.TYPE.GENERAL);
|
||||
CarpetScriptHost cHost = CarpetServer.scriptServer.modules.get(hostName).retrieveOwnForExecution(ctx.getSource());
|
||||
List<String> argNames = funcSpec.function.getArguments();
|
||||
if ((argNames.size()-funcSpec.args.size()) != paramNames.size())
|
||||
@@ -135,7 +142,9 @@ public class CarpetScriptHost extends ScriptHost
|
||||
Value response = cHost.handleCommand(ctx.getSource(), funcSpec.function, args);
|
||||
// will skip prints for new
|
||||
//if (!response.isNull()) Messenger.m(ctx.getSource(), "gi " + response.getString());
|
||||
return (int) response.readInteger();
|
||||
int intres = (int) response.readInteger();
|
||||
CarpetProfiler.end_current_section(currentSection);
|
||||
return intres;
|
||||
}
|
||||
|
||||
public LiteralArgumentBuilder<ServerCommandSource> addPathToCommand(
|
||||
@@ -205,12 +214,15 @@ public class CarpetScriptHost extends ScriptHost
|
||||
return s -> {
|
||||
try
|
||||
{
|
||||
CarpetProfiler.ProfilerToken currentSection = CarpetProfiler.start_section(null, "Scarpet command", CarpetProfiler.TYPE.GENERAL);
|
||||
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();
|
||||
boolean res = response.getBoolean();
|
||||
CarpetProfiler.end_current_section(currentSection);
|
||||
return res;
|
||||
}
|
||||
catch (CommandSyntaxException e)
|
||||
{
|
||||
@@ -311,6 +323,107 @@ public class CarpetScriptHost extends ScriptHost
|
||||
}
|
||||
}
|
||||
|
||||
public Boolean addAppCommands(Consumer<Text> notifier)
|
||||
{
|
||||
if (appConfig.get(StringValue.of("commands")) != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
LiteralArgumentBuilder<ServerCommandSource> command = readCommands(commandValidator);
|
||||
if (command != null)
|
||||
{
|
||||
scriptServer.server.getCommandManager().getDispatcher().register(command);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
catch (CommandSyntaxException cse)
|
||||
{
|
||||
// failed
|
||||
notifier.accept(Messenger.c("r Failed to build command system for "+getName()+" thus failed to load the app ", cse.getRawMessage()));
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
return addLegacyCommand(notifier);
|
||||
}
|
||||
|
||||
private Boolean addLegacyCommand(Consumer<Text> notifier)
|
||||
{
|
||||
if (main == null) return false;
|
||||
if (getFunction("__command") == null) return false;
|
||||
|
||||
if (scriptServer.isInvalidCommandRoot(getName()))
|
||||
{
|
||||
notifier.accept(Messenger.c("gi Tried to mask vanilla command."));
|
||||
return null;
|
||||
}
|
||||
|
||||
Function<ServerCommandSource, Boolean> configValidator;
|
||||
try
|
||||
{
|
||||
configValidator = getCommandConfigPermissions();
|
||||
}
|
||||
catch (CommandSyntaxException e)
|
||||
{
|
||||
notifier.accept(Messenger.c("rb "+e.getMessage()));
|
||||
return null;
|
||||
}
|
||||
String hostName = getName();
|
||||
LiteralArgumentBuilder<ServerCommandSource> command = literal(hostName).
|
||||
requires((player) -> scriptServer.modules.containsKey(hostName) && commandValidator.apply(player) && configValidator.apply(player)).
|
||||
executes( (c) ->
|
||||
{
|
||||
CarpetScriptHost targetHost = scriptServer.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();
|
||||
});
|
||||
|
||||
for (String function : globaFunctionNames(main, s -> !s.startsWith("_")).sorted().collect(Collectors.toList()))
|
||||
{
|
||||
if (appConfig.getOrDefault(StringValue.of("legacy_command_type_support"), Value.FALSE).getBoolean())
|
||||
{
|
||||
try
|
||||
{
|
||||
FunctionValue functionValue = getFunction(function);
|
||||
command = addPathToCommand(
|
||||
command,
|
||||
CommandToken.parseSpec(CommandToken.specFromSignature(functionValue), this),
|
||||
FunctionArgument.fromCommandSpec(this, functionValue)
|
||||
);
|
||||
}
|
||||
catch (CommandSyntaxException e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
command = command.
|
||||
then(literal(function).
|
||||
requires((player) -> scriptServer.modules.containsKey(hostName) && scriptServer.modules.get(hostName).getFunction(function) != null).
|
||||
executes((c) -> {
|
||||
CarpetScriptHost targetHost = scriptServer.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) -> {
|
||||
CarpetScriptHost targetHost = scriptServer.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();
|
||||
})));
|
||||
}
|
||||
}
|
||||
scriptServer.server.getCommandManager().getDispatcher().register(command);
|
||||
return true;
|
||||
}
|
||||
|
||||
public LiteralArgumentBuilder<ServerCommandSource> readCommands(Function<ServerCommandSource, Boolean> useValidator) throws CommandSyntaxException
|
||||
{
|
||||
Value commands = appConfig.get(StringValue.of("commands"));
|
||||
@@ -440,7 +553,10 @@ public class CarpetScriptHost extends ScriptHost
|
||||
{
|
||||
try
|
||||
{
|
||||
return callLegacy(source, call, coords, arg);
|
||||
CarpetProfiler.ProfilerToken currentSection = CarpetProfiler.start_section(null, "Scarpet command", CarpetProfiler.TYPE.GENERAL);
|
||||
Value res = callLegacy(source, call, coords, arg);
|
||||
CarpetProfiler.end_current_section(currentSection);
|
||||
return res;
|
||||
}
|
||||
catch (CarpetExpressionException exc)
|
||||
{
|
||||
@@ -680,31 +796,31 @@ public class CarpetScriptHost extends ScriptHost
|
||||
|
||||
private void dumpState()
|
||||
{
|
||||
Module.saveData(main, null, globalState, false);
|
||||
Module.saveData(main, globalState);
|
||||
}
|
||||
|
||||
private Tag loadState()
|
||||
{
|
||||
return Module.getData(main, null, false);
|
||||
return Module.getData(main);
|
||||
}
|
||||
|
||||
public Tag readFileTag(String file, boolean isShared)
|
||||
public Tag readFileTag(FileArgument fdesc)
|
||||
{
|
||||
if (getName() == null && !isShared) return null;
|
||||
if (file != null)
|
||||
return Module.getData(main, file, isShared);
|
||||
if (getName() == null && !fdesc.isShared) return null;
|
||||
if (fdesc.resource != null)
|
||||
return fdesc.getNbtData(main);
|
||||
if (parent == null)
|
||||
return globalState;
|
||||
return ((CarpetScriptHost)parent).globalState;
|
||||
}
|
||||
|
||||
public boolean writeTagFile(Tag tag, String file, boolean isShared)
|
||||
public boolean writeTagFile(Tag tag, FileArgument fdesc)
|
||||
{
|
||||
if (getName() == null && !isShared) return false; // if belongs to an app, cannot be default host.
|
||||
if (getName() == null && !fdesc.isShared) return false; // if belongs to an app, cannot be default host.
|
||||
|
||||
if (file!= null)
|
||||
if (fdesc.resource != null)
|
||||
{
|
||||
return Module.saveData(main, file, tag, isShared);
|
||||
return fdesc.saveNbtData(main, tag);
|
||||
}
|
||||
|
||||
CarpetScriptHost responsibleHost = (parent != null)?(CarpetScriptHost) parent:this;
|
||||
@@ -717,34 +833,34 @@ public class CarpetScriptHost extends ScriptHost
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean removeResourceFile(String resource, boolean isShared, String type)
|
||||
public boolean removeResourceFile(FileArgument fdesc)
|
||||
{
|
||||
if (getName() == null && !isShared) return false; //
|
||||
return Module.dropExistingFile(main, resource, type, isShared);
|
||||
if (getName() == null && !fdesc.isShared) return false; //
|
||||
return fdesc.dropExistingFile(main);
|
||||
}
|
||||
|
||||
public boolean appendLogFile(String resource, boolean isShared, String type, List<String> data)
|
||||
public boolean appendLogFile(FileArgument fdesc, List<String> data)
|
||||
{
|
||||
if (getName() == null && !isShared) return false; // if belongs to an app, cannot be default host.
|
||||
return Module.appendToTextFile(main, resource, type, isShared, data);
|
||||
if (getName() == null && !fdesc.isShared) return false; // if belongs to an app, cannot be default host.
|
||||
return fdesc.appendToTextFile(main, data);
|
||||
}
|
||||
|
||||
public List<String> readTextResource(String resource, String type, boolean isShared)
|
||||
public List<String> readTextResource(FileArgument fdesc)
|
||||
{
|
||||
if (getName() == null && !isShared) return null;
|
||||
return Module.listFile(main, resource, type, isShared);
|
||||
if (getName() == null && !fdesc.isShared) return null;
|
||||
return fdesc.listFile(main);
|
||||
}
|
||||
|
||||
public JsonElement readJsonFile(String resource, String type, boolean isShared)
|
||||
public JsonElement readJsonFile(FileArgument fdesc)
|
||||
{
|
||||
if (getName() == null && !isShared) return null;
|
||||
return Module.readJsonFile(main, resource, type, isShared);
|
||||
if (getName() == null && !fdesc.isShared) return null;
|
||||
return fdesc.readJsonFile(main);
|
||||
}
|
||||
|
||||
public Stream<String> listFolder(String resource, String type, boolean isShared)
|
||||
public Stream<String> listFolder(FileArgument fdesc)
|
||||
{
|
||||
if (getName() == null && !isShared) return null; //
|
||||
return Module.listFolder(main, resource, type, isShared);
|
||||
if (getName() == null && !fdesc.isShared) return null; //
|
||||
return fdesc.listFolder(main);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,19 +1,15 @@
|
||||
package carpet.script;
|
||||
|
||||
import carpet.CarpetSettings;
|
||||
import carpet.script.argument.FunctionArgument;
|
||||
import carpet.script.bundled.BundledModule;
|
||||
import carpet.CarpetServer;
|
||||
import carpet.script.bundled.FileModule;
|
||||
import carpet.script.bundled.Module;
|
||||
import carpet.script.command.CommandToken;
|
||||
import carpet.script.exception.InvalidCallbackException;
|
||||
import carpet.script.value.FunctionValue;
|
||||
import carpet.script.value.StringValue;
|
||||
import carpet.script.value.Value;
|
||||
import carpet.utils.CarpetProfiler;
|
||||
import carpet.utils.Messenger;
|
||||
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.mojang.brigadier.tree.CommandNode;
|
||||
|
||||
@@ -26,6 +22,7 @@ import net.minecraft.util.WorldSavePath;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.FileVisitOption;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
@@ -93,6 +90,7 @@ public class CarpetScriptServer
|
||||
registerBuiltInScript(BundledModule.carpetNative("chunk_display", false));
|
||||
registerBuiltInScript(BundledModule.carpetNative("ai_tracker", false));
|
||||
registerBuiltInScript(BundledModule.carpetNative("draw_beta", false));
|
||||
registerBuiltInScript(BundledModule.carpetNative("shapes", true));
|
||||
registerBuiltInScript(BundledModule.carpetNative("distance_beta", false));
|
||||
}
|
||||
|
||||
@@ -136,7 +134,8 @@ public class CarpetScriptServer
|
||||
{
|
||||
try {
|
||||
Path folder = server.getSavePath(WorldSavePath.ROOT).resolve("scripts");
|
||||
Files.createDirectories(folder);
|
||||
if (!Files.exists(folder))
|
||||
Files.createDirectories(folder);
|
||||
Optional<Path>
|
||||
scriptPath = Files.list(folder)
|
||||
.filter(script ->
|
||||
@@ -149,7 +148,8 @@ public class CarpetScriptServer
|
||||
if (FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT)
|
||||
{
|
||||
Path globalFolder = FabricLoader.getInstance().getConfigDir().resolve("carpet/scripts");
|
||||
Files.createDirectories(globalFolder);
|
||||
if (!Files.exists(globalFolder))
|
||||
Files.createDirectories(globalFolder);
|
||||
scriptPath = Files.walk(globalFolder)
|
||||
.filter(script -> script.getFileName().toString().equalsIgnoreCase(name + ".sc") ||
|
||||
(allowLibraries && script.getFileName().toString().equalsIgnoreCase(name + ".scl")))
|
||||
@@ -194,7 +194,8 @@ public class CarpetScriptServer
|
||||
}
|
||||
try {
|
||||
Path worldScripts = server.getSavePath(WorldSavePath.ROOT).resolve("scripts");
|
||||
Files.createDirectories(worldScripts);
|
||||
if (!Files.exists(worldScripts))
|
||||
Files.createDirectories(worldScripts);
|
||||
Files.list(worldScripts)
|
||||
.filter(f -> f.toString().endsWith(".sc"))
|
||||
.forEach(f -> moduleNames.add(f.getFileName().toString().replaceFirst("\\.sc$","").toLowerCase(Locale.ROOT)));
|
||||
@@ -202,8 +203,9 @@ public class CarpetScriptServer
|
||||
if (includeBuiltIns && (FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT))
|
||||
{
|
||||
Path globalScripts = FabricLoader.getInstance().getConfigDir().resolve("carpet/scripts");
|
||||
Files.createDirectories(globalScripts);
|
||||
Files.walk(globalScripts)
|
||||
if (!Files.exists(globalScripts))
|
||||
Files.createDirectories(globalScripts);
|
||||
Files.walk(globalScripts, FileVisitOption.FOLLOW_LINKS)
|
||||
.filter(f -> f.toString().endsWith(".sc"))
|
||||
.forEach(f -> moduleNames.add(f.getFileName().toString().replaceFirst("\\.sc$","").toLowerCase(Locale.ROOT)));
|
||||
}
|
||||
@@ -223,6 +225,7 @@ public class CarpetScriptServer
|
||||
public boolean addScriptHost(ServerCommandSource source, String name, Function<ServerCommandSource, Boolean> commandValidator,
|
||||
boolean perPlayer, boolean autoload, boolean isRuleApp)
|
||||
{
|
||||
CarpetProfiler.ProfilerToken currentSection = CarpetProfiler.start_section(null, "Scarpet load", CarpetProfiler.TYPE.GENERAL);
|
||||
if (commandValidator == null) commandValidator = p -> true;
|
||||
long start = System.nanoTime();
|
||||
name = name.toLowerCase(Locale.ROOT);
|
||||
@@ -267,35 +270,23 @@ public class CarpetScriptServer
|
||||
}
|
||||
//addEvents(source, name);
|
||||
String action = reload?"reloaded":"loaded";
|
||||
if (newHost.appConfig.get(StringValue.of("commands")) != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
LiteralArgumentBuilder<ServerCommandSource> command = newHost.readCommands(commandValidator);
|
||||
if (command != null)
|
||||
{
|
||||
if (!isRuleApp) Messenger.m(source, "gi "+name+" app "+action+" with /"+name+" command");
|
||||
server.getCommandManager().getDispatcher().register(command);
|
||||
CarpetServer.settingsManager.notifyPlayersCommandsChanged();
|
||||
}
|
||||
else {
|
||||
if (!isRuleApp) Messenger.m(source, "gi "+name+" app "+action);
|
||||
}
|
||||
}
|
||||
catch (CommandSyntaxException cse)
|
||||
{
|
||||
removeScriptHost(source, name, false, false);
|
||||
Messenger.m(source, "r Failed to build command system for "+name+" thus failed to load the app ", cse.getRawMessage());
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
else if (!addLegacyCommand(source, name, reload, !isRuleApp, commandValidator)) // this needs to be moved to config reader, only supporting legacy command here
|
||||
Boolean isCommandAdded = newHost.addAppCommands(s -> {
|
||||
if (!isRuleApp) Messenger.m(source, s);
|
||||
});
|
||||
if (isCommandAdded == null) // error should be dispatched
|
||||
{
|
||||
removeScriptHost(source, name, false, false);
|
||||
Messenger.m(source, "r Failed to build command system for "+name+" thus failed to load the app");
|
||||
return false;
|
||||
}
|
||||
else if (isCommandAdded)
|
||||
{
|
||||
CarpetServer.settingsManager.notifyPlayersCommandsChanged();
|
||||
if (!isRuleApp) Messenger.m(source, "gi "+name+" app "+action+" with /"+name+" command");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!isRuleApp) Messenger.m(source, "gi "+name+" app "+action);
|
||||
}
|
||||
|
||||
if (newHost.isPerUser())
|
||||
{
|
||||
// that will provide player hosts right at the startup
|
||||
@@ -307,94 +298,15 @@ public class CarpetScriptServer
|
||||
FunctionValue onStart = newHost.getFunction("__on_start");
|
||||
if (onStart != null) newHost.callNow(onStart, Collections.emptyList());
|
||||
}
|
||||
CarpetProfiler.end_current_section(currentSection);
|
||||
long end = System.nanoTime();
|
||||
CarpetSettings.LOG.info("App "+name+" loaded in "+(end-start)/1000000+" ms");
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean addLegacyCommand(ServerCommandSource source, String hostName, boolean isReload, boolean notifySource, Function<ServerCommandSource, Boolean> useValidator)
|
||||
public boolean isInvalidCommandRoot(String appName)
|
||||
{
|
||||
CarpetScriptHost host = modules.get(hostName);
|
||||
String loaded = isReload?"reloaded":"loaded";
|
||||
if (host == null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (host.getFunction("__command") == null)
|
||||
{
|
||||
if (notifySource) Messenger.m(source, "gi "+hostName+" app "+loaded+".");
|
||||
return true;
|
||||
}
|
||||
if (holyMoly.contains(hostName))
|
||||
{
|
||||
Messenger.m(source, "gi "+hostName+" app "+loaded+" with no command.");
|
||||
Messenger.m(source, "gi Tried to mask vanilla command.");
|
||||
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) && configValidator.apply(player)).
|
||||
executes( (c) ->
|
||||
{
|
||||
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();
|
||||
});
|
||||
|
||||
for (String function : host.globaFunctionNames(host.main, s -> !s.startsWith("_")).sorted().collect(Collectors.toList()))
|
||||
{
|
||||
if (host.appConfig.getOrDefault(StringValue.of("legacy_command_type_support"), Value.FALSE).getBoolean())
|
||||
{
|
||||
try
|
||||
{
|
||||
FunctionValue functionValue = host.getFunction(function);
|
||||
command = host.addPathToCommand(
|
||||
command,
|
||||
CommandToken.parseSpec(CommandToken.specFromSignature(functionValue), host),
|
||||
FunctionArgument.fromCommandSpec(host, functionValue)
|
||||
);
|
||||
}
|
||||
catch (CommandSyntaxException e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
command = command.
|
||||
then(literal(function).
|
||||
requires((player) -> modules.containsKey(hostName) && modules.get(hostName).getFunction(function) != null).
|
||||
executes((c) -> {
|
||||
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) -> {
|
||||
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();
|
||||
})));
|
||||
}
|
||||
}
|
||||
if (notifySource) Messenger.m(source, "gi "+hostName+" app "+loaded+" with /"+hostName+" command");
|
||||
server.getCommandManager().getDispatcher().register(command);
|
||||
CarpetServer.settingsManager.notifyPlayersCommandsChanged();
|
||||
return true;
|
||||
return holyMoly.contains(appName);
|
||||
}
|
||||
|
||||
|
||||
@@ -498,11 +410,16 @@ public class CarpetScriptServer
|
||||
|
||||
public void tick()
|
||||
{
|
||||
CarpetProfiler.ProfilerToken token;
|
||||
token = CarpetProfiler.start_section(null, "Scarpet schedule", CarpetProfiler.TYPE.GENERAL);
|
||||
events.tick();
|
||||
CarpetProfiler.end_current_section(token);
|
||||
token = CarpetProfiler.start_section(null, "Scarpet app data", CarpetProfiler.TYPE.GENERAL);
|
||||
for (CarpetScriptHost host : modules.values())
|
||||
{
|
||||
host.tick();
|
||||
}
|
||||
CarpetProfiler.end_current_section(token);
|
||||
}
|
||||
|
||||
public void onClose()
|
||||
@@ -553,4 +470,9 @@ public class CarpetScriptServer
|
||||
init();
|
||||
apps.forEach((s, data) -> addScriptHost(server.getCommandSource(), s,data.commandValidator, data.perUser,false, data.isRuleApp));
|
||||
}
|
||||
|
||||
public void reAddCommands()
|
||||
{
|
||||
modules.values().forEach(host -> host.addAppCommands(s -> {}));
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,6 @@ import carpet.script.exception.ExpressionException;
|
||||
import carpet.script.exception.InternalExpressionException;
|
||||
import carpet.script.exception.ResolvedException;
|
||||
import carpet.script.exception.ReturnStatement;
|
||||
import carpet.script.exception.ThrowStatement;
|
||||
import carpet.script.language.Arithmetic;
|
||||
import carpet.script.language.ControlFlow;
|
||||
import carpet.script.language.DataStructures;
|
||||
@@ -232,7 +231,7 @@ public class Expression
|
||||
if (exc instanceof ExitStatement)
|
||||
return exc;
|
||||
if (exc instanceof InternalExpressionException)
|
||||
return new ExpressionException(c, e, token, exc.getMessage(), ((InternalExpressionException) exc).stack);
|
||||
return ((InternalExpressionException) exc).promote(c, e, token);
|
||||
if (exc instanceof ArithmeticException)
|
||||
return new ExpressionException(c, e, token, "Your math is wrong, "+exc.getMessage());
|
||||
if (exc instanceof ResolvedException)
|
||||
@@ -314,6 +313,11 @@ public class Expression
|
||||
addUnaryFunction(name, (v) -> new NumericValue(fun.apply(NumericValue.asNumber(v).getDouble())));
|
||||
}
|
||||
|
||||
public void addMathematicalUnaryIntFunction(String name, Function<Double, Long> fun)
|
||||
{
|
||||
addUnaryFunction(name, (v) -> new NumericValue(fun.apply(NumericValue.asNumber(v).getDouble())));
|
||||
}
|
||||
|
||||
public void addMathematicalBinaryFunction(String name, BiFunction<Double, Double, Double> fun)
|
||||
{
|
||||
addBinaryFunction(name, (w, v) ->
|
||||
@@ -624,9 +628,9 @@ public class Expression
|
||||
{
|
||||
return exprProvider.get().evalValue(c, expectedType);
|
||||
}
|
||||
catch (ContinueStatement | BreakStatement | ReturnStatement | ThrowStatement exc)
|
||||
catch (ContinueStatement | BreakStatement | ReturnStatement exc)
|
||||
{
|
||||
throw new ExpressionException(c, this, "Control flow functions, like continue, break, throw or return, should only be used in loops, try blocks, and functions respectively.");
|
||||
throw new ExpressionException(c, this, "Control flow functions, like continue, break or return, should only be used in loops, and functions respectively.");
|
||||
}
|
||||
catch (ExitStatement exit)
|
||||
{
|
||||
@@ -761,7 +765,7 @@ public class Expression
|
||||
{
|
||||
if (token.surface.equals(";"))
|
||||
{
|
||||
throw new ExpressionException(c, this, token, "Unnecessary semicolon");
|
||||
throw new ExpressionException(c, this, token, "Empty expression found for ';'");
|
||||
}
|
||||
throw new ExpressionException(c, this, token, "Missing parameter(s) for operator " + token);
|
||||
}
|
||||
|
||||
@@ -37,11 +37,15 @@ public abstract class ScriptHost
|
||||
|
||||
public Random getRandom(long aLong)
|
||||
{
|
||||
if (randomizers.size() > 1024)
|
||||
if (randomizers.size() > 65536)
|
||||
randomizers.clear();
|
||||
return randomizers.computeIfAbsent(aLong, Random::new);
|
||||
}
|
||||
|
||||
public boolean resetRandom(long aLong) {
|
||||
return randomizers.remove(aLong) != null;
|
||||
}
|
||||
|
||||
public static class ModuleData
|
||||
{
|
||||
Module parent;
|
||||
|
||||
@@ -406,6 +406,7 @@ public class Tokenizer implements Iterator<Tokenizer.Token>
|
||||
public int pos;
|
||||
public int linepos;
|
||||
public int lineno;
|
||||
public static final Token NONE = new Token();
|
||||
public Token morphedInto(TokenType newType, String newSurface)
|
||||
{
|
||||
Token created = new Token();
|
||||
|
||||
@@ -3,11 +3,13 @@ package carpet.script.api;
|
||||
import carpet.CarpetServer;
|
||||
import carpet.CarpetSettings;
|
||||
import carpet.fakes.MinecraftServerInterface;
|
||||
import carpet.fakes.ServerWorldInterface;
|
||||
import carpet.fakes.StatTypeInterface;
|
||||
import carpet.fakes.ThreadedAnvilChunkStorageInterface;
|
||||
import carpet.helpers.FeatureGenerator;
|
||||
import carpet.mixins.PlayerListHeaderS2CPacketMixin;
|
||||
import carpet.script.bundled.Module;
|
||||
import carpet.script.argument.FileArgument;
|
||||
import carpet.script.CarpetContext;
|
||||
import carpet.script.CarpetEventServer;
|
||||
import carpet.script.CarpetScriptHost;
|
||||
@@ -20,7 +22,10 @@ import carpet.script.argument.FunctionArgument;
|
||||
import carpet.script.argument.Vector3Argument;
|
||||
import carpet.script.exception.ExitStatement;
|
||||
import carpet.script.exception.InternalExpressionException;
|
||||
import carpet.script.exception.ThrowStatement;
|
||||
import carpet.script.exception.Throwables;
|
||||
import carpet.script.utils.FixedCommandSource;
|
||||
import carpet.script.utils.InputValidator;
|
||||
import carpet.script.utils.ScarpetJsonDeserializer;
|
||||
import carpet.script.utils.ShapeDispatcher;
|
||||
import carpet.script.utils.WorldTools;
|
||||
@@ -36,6 +41,10 @@ import carpet.script.value.StringValue;
|
||||
import carpet.script.value.Value;
|
||||
import carpet.script.value.ValueConversions;
|
||||
import carpet.utils.Messenger;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.Lists;
|
||||
import net.minecraft.SharedConstants;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.command.DataCommandStorage;
|
||||
import net.minecraft.entity.Entity;
|
||||
@@ -46,6 +55,7 @@ import net.minecraft.entity.player.PlayerEntity;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.NbtHelper;
|
||||
import net.minecraft.nbt.NbtOps;
|
||||
import net.minecraft.nbt.StringTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.network.packet.s2c.play.PlaySoundIdS2CPacket;
|
||||
@@ -53,6 +63,11 @@ import net.minecraft.network.packet.s2c.play.PlayerListHeaderS2CPacket;
|
||||
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.resource.DataPackSettings;
|
||||
import net.minecraft.resource.ReloadableResourceManagerImpl;
|
||||
import net.minecraft.resource.ResourcePackManager;
|
||||
import net.minecraft.resource.ResourcePackProfile;
|
||||
import net.minecraft.resource.ServerResourceManager;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.command.ServerCommandSource;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
@@ -64,21 +79,42 @@ import net.minecraft.text.LiteralText;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.InvalidIdentifierException;
|
||||
import net.minecraft.util.crash.CrashException;
|
||||
import net.minecraft.util.Util;
|
||||
import net.minecraft.util.WorldSavePath;
|
||||
import net.minecraft.util.dynamic.RegistryOps;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.ChunkPos;
|
||||
import net.minecraft.util.math.EulerAngle;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.util.registry.DynamicRegistryManager;
|
||||
import net.minecraft.util.registry.Registry;
|
||||
import net.minecraft.util.registry.RegistryKey;
|
||||
import net.minecraft.world.SaveProperties;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.biome.source.BiomeAccess;
|
||||
import net.minecraft.world.border.WorldBorderListener;
|
||||
import net.minecraft.world.dimension.DimensionOptions;
|
||||
import net.minecraft.world.dimension.DimensionType;
|
||||
import net.minecraft.world.gen.GeneratorOptions;
|
||||
import net.minecraft.world.gen.chunk.ChunkGenerator;
|
||||
import net.minecraft.world.level.UnmodifiableLevelProperties;
|
||||
import net.minecraft.world.level.storage.LevelStorage;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.apache.commons.lang3.tuple.Triple;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonParseException;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
@@ -97,8 +133,9 @@ import static java.lang.Math.min;
|
||||
public class Auxiliary {
|
||||
public static final String MARKER_STRING = "__scarpet_marker";
|
||||
private static final Map<String, SoundCategory> mixerMap = Arrays.stream(SoundCategory.values()).collect(Collectors.toMap(SoundCategory::getName, k -> k));
|
||||
public static final Gson gson = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().registerTypeAdapter(Value.class, new ScarpetJsonDeserializer()).create();
|
||||
public static final Gson GSON = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().registerTypeAdapter(Value.class, new ScarpetJsonDeserializer()).create();
|
||||
|
||||
@Deprecated
|
||||
public static String recognizeResource(Value value, boolean isFloder)
|
||||
{
|
||||
String origfile = value.getString();
|
||||
@@ -111,28 +148,20 @@ public class Auxiliary {
|
||||
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 (!Module.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) -> {
|
||||
CarpetContext cc = (CarpetContext)c;
|
||||
Identifier soundName = new Identifier(lv.get(0).evalValue(c).getString());
|
||||
if (lv.size() == 0)
|
||||
{
|
||||
Value ret = ListValue.wrap(Registry.SOUND_EVENT.getIds().stream().map(ValueConversions::of));
|
||||
return (_c, _t) -> ret;
|
||||
}
|
||||
String rawString = lv.get(0).evalValue(c).getString();
|
||||
Identifier soundName = new Identifier(rawString);
|
||||
Vector3Argument locator = Vector3Argument.findIn(cc, lv, 1);
|
||||
if (Registry.SOUND_EVENT.get(soundName) == null)
|
||||
throw new InternalExpressionException("No such sound: "+soundName.getPath());
|
||||
throw new ThrowStatement(rawString, Throwables.UNKNOWN_SOUND);
|
||||
float volume = 1.0F;
|
||||
float pitch = 1.0F;
|
||||
SoundCategory mixer = SoundCategory.MASTER;
|
||||
@@ -165,6 +194,12 @@ public class Auxiliary {
|
||||
expression.addLazyFunction("particle", -1, (c, t, lv) ->
|
||||
{
|
||||
CarpetContext cc = (CarpetContext)c;
|
||||
if (lv.size() == 0)
|
||||
{
|
||||
Value ret = ListValue.wrap(Registry.PARTICLE_TYPE.getIds().stream().map(ValueConversions::of));
|
||||
return (_c, _t) -> ret;
|
||||
}
|
||||
|
||||
MinecraftServer ms = cc.s.getMinecraftServer();
|
||||
ServerWorld world = cc.s.getWorld();
|
||||
Vector3Argument locator = Vector3Argument.findIn(cc, lv, 1);
|
||||
@@ -847,39 +882,28 @@ public class Auxiliary {
|
||||
|
||||
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(), fdesc.getMiddle(), fdesc.getRight());
|
||||
FileArgument fdesc = FileArgument.from(lv, c, true, FileArgument.Reason.READ);
|
||||
Stream<String> files = ((CarpetScriptHost) c.host).listFolder(fdesc);
|
||||
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);
|
||||
FileArgument fdesc = FileArgument.from(lv, c, false, FileArgument.Reason.READ);
|
||||
Value retVal;
|
||||
if (fdesc.getMiddle().equals("nbt"))
|
||||
if (fdesc.type == FileArgument.Type.NBT)
|
||||
{
|
||||
Tag state = ((CarpetScriptHost) c.host).readFileTag(fdesc.getLeft(), fdesc.getRight());
|
||||
Tag state = ((CarpetScriptHost) c.host).readFileTag(fdesc);
|
||||
if (state == null) return LazyValue.NULL;
|
||||
retVal = new NBTSerializableValue(state);
|
||||
}
|
||||
else if (fdesc.getMiddle().equals("json"))
|
||||
else if (fdesc.type == FileArgument.Type.JSON)
|
||||
{
|
||||
JsonElement json;
|
||||
try
|
||||
{
|
||||
json = ((CarpetScriptHost) c.host).readJsonFile(fdesc.getLeft(), fdesc.getMiddle(), fdesc.getRight());
|
||||
}
|
||||
catch (JsonParseException e)
|
||||
{
|
||||
Throwable exception = e;
|
||||
if(e.getCause() != null)
|
||||
exception = e.getCause();
|
||||
throw new InternalExpressionException("Failed to read JSON file: "+exception.getMessage());
|
||||
}
|
||||
Value parsedJson = gson.fromJson(json, Value.class);
|
||||
json = ((CarpetScriptHost) c.host).readJsonFile(fdesc);
|
||||
Value parsedJson = GSON.fromJson(json, Value.class);
|
||||
if (parsedJson == null)
|
||||
retVal = Value.NULL;
|
||||
else
|
||||
@@ -887,7 +911,7 @@ public class Auxiliary {
|
||||
}
|
||||
else
|
||||
{
|
||||
List<String> content = ((CarpetScriptHost) c.host).readTextResource(fdesc.getLeft(), fdesc.getMiddle(), fdesc.getRight());
|
||||
List<String> content = ((CarpetScriptHost) c.host).readTextResource(fdesc);
|
||||
if (content == null) return LazyValue.NULL;
|
||||
retVal = ListValue.wrap(content.stream().map(StringValue::new).collect(Collectors.toList()));
|
||||
}
|
||||
@@ -895,32 +919,29 @@ public class Auxiliary {
|
||||
});
|
||||
|
||||
expression.addLazyFunction("delete_file", 2, (c, t, lv) -> {
|
||||
Triple<String, String, Boolean> fdesc = getFileDescriptor(lv, c, false);
|
||||
return ((CarpetScriptHost) c.host).removeResourceFile(fdesc.getLeft(), fdesc.getRight(), fdesc.getMiddle())
|
||||
? LazyValue.TRUE
|
||||
: LazyValue.FALSE;
|
||||
FileArgument fdesc = FileArgument.from(lv, c, false, FileArgument.Reason.DELETE);
|
||||
return ((CarpetScriptHost) c.host).removeResourceFile(fdesc) ? 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");
|
||||
Triple<String, String, Boolean> fdesc = getFileDescriptor(lv, c, false);
|
||||
|
||||
FileArgument fdesc = FileArgument.from(lv, c, false, FileArgument.Reason.CREATE);
|
||||
|
||||
boolean success;
|
||||
if (fdesc.getMiddle().equals("nbt"))
|
||||
if (fdesc.type == FileArgument.Type.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) c.host).writeTagFile(tag, fdesc.getLeft(), fdesc.getRight());
|
||||
success = ((CarpetScriptHost) c.host).writeTagFile(tag, fdesc);
|
||||
}
|
||||
else if (fdesc.getMiddle().equals("json"))
|
||||
else if (fdesc.type == FileArgument.Type.JSON)
|
||||
{
|
||||
List<String> data = Collections.singletonList(gson.toJson(lv.get(2).evalValue(c).toJson()));
|
||||
((CarpetScriptHost) c.host).removeResourceFile(fdesc.getLeft(), fdesc.getRight(), fdesc.getMiddle());
|
||||
success = ((CarpetScriptHost) c.host).appendLogFile(fdesc.getLeft(), fdesc.getRight(), fdesc.getMiddle(), data);
|
||||
List<String> data = Collections.singletonList(GSON.toJson(lv.get(2).evalValue(c).toJson()));
|
||||
((CarpetScriptHost) c.host).removeResourceFile(fdesc);
|
||||
success = ((CarpetScriptHost) c.host).appendLogFile(fdesc, data);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -945,27 +966,22 @@ public class Auxiliary {
|
||||
data.add(lv.get(i).evalValue(c).getString());
|
||||
}
|
||||
}
|
||||
success = ((CarpetScriptHost) c.host).appendLogFile(fdesc.getLeft(), fdesc.getRight(), fdesc.getMiddle(), data);
|
||||
success = ((CarpetScriptHost) c.host).appendLogFile(fdesc, data);
|
||||
}
|
||||
return success?LazyValue.TRUE:LazyValue.FALSE;
|
||||
});
|
||||
|
||||
//write_file
|
||||
|
||||
expression.addLazyFunction("load_app_data", -1, (c, t, lv) ->
|
||||
{
|
||||
String file = null;
|
||||
boolean shared = false;
|
||||
FileArgument fdesc = new FileArgument(null, FileArgument.Type.NBT, null, false, false, FileArgument.Reason.READ);
|
||||
if (lv.size()>0)
|
||||
{
|
||||
c.host.issueDeprecation("load_app_data(...) with arguments");
|
||||
file = recognizeResource(lv.get(0).evalValue(c), false);
|
||||
if (lv.size() > 1)
|
||||
{
|
||||
shared = lv.get(1).evalValue(c).getBoolean();
|
||||
}
|
||||
String resource = recognizeResource(lv.get(0).evalValue(c), false);
|
||||
boolean shared = lv.size() > 1 && lv.get(1).evalValue(c).getBoolean();
|
||||
fdesc = new FileArgument(resource, FileArgument.Type.NBT, null, false, shared, FileArgument.Reason.READ);
|
||||
}
|
||||
Tag state = ((CarpetScriptHost)((CarpetContext)c).host).readFileTag(file, shared);
|
||||
Tag state = ((CarpetScriptHost) c.host).readFileTag(fdesc);
|
||||
if (state == null)
|
||||
return (cc, tt) -> Value.NULL;
|
||||
Value retVal = new NBTSerializableValue(state);
|
||||
@@ -977,22 +993,19 @@ public class Auxiliary {
|
||||
if (lv.size() == 0)
|
||||
throw new InternalExpressionException("'store_app_data' needs NBT tag and an optional file");
|
||||
Value val = lv.get(0).evalValue(c);
|
||||
String file = null;
|
||||
boolean shared = false;
|
||||
FileArgument fdesc = new FileArgument(null, FileArgument.Type.NBT, null, false, false, FileArgument.Reason.CREATE);
|
||||
if (lv.size()>1)
|
||||
{
|
||||
c.host.issueDeprecation("store_app_data(...) with more than one argument");
|
||||
file = recognizeResource(lv.get(1).evalValue(c), false);
|
||||
if (lv.size() > 2)
|
||||
{
|
||||
shared = lv.get(2).evalValue(c).getBoolean();
|
||||
}
|
||||
String resource = recognizeResource(lv.get(1).evalValue(c), false);
|
||||
boolean shared = lv.size() > 2 && lv.get(2).evalValue(c).getBoolean();
|
||||
fdesc = new FileArgument(resource, FileArgument.Type.NBT, null, false, shared, FileArgument.Reason.CREATE);
|
||||
}
|
||||
NBTSerializableValue tagValue = (val instanceof NBTSerializableValue)
|
||||
? (NBTSerializableValue) val
|
||||
: new NBTSerializableValue(val.getString());
|
||||
Tag tag = tagValue.getTag();
|
||||
boolean success = ((CarpetScriptHost)((CarpetContext)c).host).writeTagFile(tag, file, shared);
|
||||
boolean success = ((CarpetScriptHost) c.host).writeTagFile(tag, fdesc);
|
||||
return success?LazyValue.TRUE:LazyValue.FALSE;
|
||||
});
|
||||
|
||||
@@ -1089,8 +1102,179 @@ public class Auxiliary {
|
||||
if (old_nbt == null) return LazyValue.NULL;
|
||||
return (_c, _t) -> new NBTSerializableValue(old_nbt);
|
||||
});
|
||||
|
||||
// script run create_datapack('foo', {'foo' -> {'bar.json' -> {'c' -> true,'d' -> false,'e' -> {'foo' -> [1,2,3]},'a' -> 'foobar','b' -> 5}}})
|
||||
expression.addLazyFunction("create_datapack", 2, (c, t, lv) -> {
|
||||
CarpetContext cc = (CarpetContext)c;
|
||||
String origName = lv.get(0).evalValue(c).getString();
|
||||
String name = InputValidator.validateSimpleString(origName, true);
|
||||
MinecraftServer server = cc.s.getMinecraftServer();
|
||||
for (String dpName : server.getDataPackManager().getNames())
|
||||
{
|
||||
if (dpName.equalsIgnoreCase("file/"+name+".zip") ||
|
||||
dpName.equalsIgnoreCase("file/"+name))
|
||||
return LazyValue.NULL;
|
||||
|
||||
}
|
||||
Value dpdata = lv.get(1).evalValue(c);
|
||||
if (!(dpdata instanceof MapValue))
|
||||
throw new InternalExpressionException("datapack data needs to be a valid map type");
|
||||
ResourcePackManager packManager = server.getDataPackManager();
|
||||
Path dbFloder = server.getSavePath(WorldSavePath.DATAPACKS);
|
||||
Path packFloder = dbFloder.resolve(name+".zip");
|
||||
if (Files.exists(packFloder) || Files.exists(dbFloder.resolve(name))) return LazyValue.NULL;
|
||||
Boolean [] successful = new Boolean[]{true};
|
||||
server.submitAndJoin( () ->
|
||||
{
|
||||
try {
|
||||
//Files.createDirectory(packFloder);
|
||||
try (FileSystem zipfs = FileSystems.newFileSystem(URI.create("jar:" + packFloder.toUri().toString()), ImmutableMap.of("create", "true"))) {
|
||||
Path zipRoot = zipfs.getPath("/");
|
||||
zipValueToJson(zipRoot.resolve("pack.mcmeta"), MapValue.wrap(
|
||||
ImmutableMap.of(StringValue.of("pack"), MapValue.wrap(ImmutableMap.of(
|
||||
StringValue.of("pack_format"), new NumericValue(SharedConstants.getGameVersion().getPackVersion()),
|
||||
StringValue.of("description"), StringValue.of(name),
|
||||
StringValue.of("source"), StringValue.of("scarpet")
|
||||
)))
|
||||
));
|
||||
walkTheDPMap((MapValue) dpdata, zipRoot);
|
||||
}
|
||||
packManager.scanPacks();
|
||||
ResourcePackProfile resourcePackProfile = packManager.getProfile("file/" + name + ".zip");
|
||||
if (resourcePackProfile == null || packManager.getEnabledProfiles().contains(resourcePackProfile)) {
|
||||
throw new IOException();
|
||||
}
|
||||
List<ResourcePackProfile> list = Lists.newArrayList(packManager.getEnabledProfiles());
|
||||
resourcePackProfile.getInitialPosition().insert(list, resourcePackProfile, p -> p, false);
|
||||
|
||||
|
||||
server.reloadResources(list.stream().map(ResourcePackProfile::getName).collect(Collectors.toList())).
|
||||
exceptionally(exc -> {
|
||||
successful[0] = false;
|
||||
return null;
|
||||
}).join();
|
||||
if (!successful[0]) {
|
||||
throw new IOException();
|
||||
}
|
||||
} catch (IOException e)
|
||||
{
|
||||
successful[0] = false;
|
||||
try {
|
||||
FileUtils.forceDelete(packFloder.toFile());
|
||||
} catch (IOException ignored) {
|
||||
throw new InternalExpressionException("Failed to install a datapack and failed to clean up after it");
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
return successful[0]?LazyValue.TRUE:LazyValue.FALSE;
|
||||
});
|
||||
|
||||
expression.addLazyFunction("enable_hidden_dimensions", 0, (c, t, lv) -> {
|
||||
CarpetContext cc = (CarpetContext)c;
|
||||
// from minecraft.server.Main.main
|
||||
MinecraftServer server = cc.s.getMinecraftServer();
|
||||
LevelStorage.Session session = ((MinecraftServerInterface)server).getCMSession();
|
||||
DataPackSettings dataPackSettings = session.getDataPackSettings();
|
||||
ResourcePackManager resourcePackManager = server.getDataPackManager();
|
||||
DataPackSettings dataPackSettings2 = MinecraftServer.loadDataPacks(resourcePackManager, dataPackSettings == null ? DataPackSettings.SAFE_MODE : dataPackSettings, false);
|
||||
ServerResourceManager serverRM = ((MinecraftServerInterface)server).getResourceManager();
|
||||
ReloadableResourceManagerImpl resourceManager = (ReloadableResourceManagerImpl) serverRM.getResourceManager();
|
||||
|
||||
//believe the other one will fillup based on the datapacks only.
|
||||
resourceManager.close();
|
||||
resourcePackManager.createResourcePacks().forEach(resourceManager::addPack);
|
||||
|
||||
//not sure its needed, but doesn't seem to have a negative effect and might be used in some custom shtuff
|
||||
serverRM.loadRegistryTags();
|
||||
|
||||
RegistryOps<Tag> registryOps = RegistryOps.of(NbtOps.INSTANCE, serverRM.getResourceManager(), (DynamicRegistryManager.Impl) server.getRegistryManager());
|
||||
SaveProperties saveProperties = session.readLevelProperties(registryOps, dataPackSettings2);
|
||||
if (saveProperties == null) return LazyValue.NULL;
|
||||
//session.backupLevelDataFile(server.getRegistryManager(), saveProperties); // no need
|
||||
|
||||
// MinecraftServer.createWorlds
|
||||
// save properties should now contain dimension settings
|
||||
GeneratorOptions generatorOptions = saveProperties.getGeneratorOptions();
|
||||
boolean bl = generatorOptions.isDebugWorld();
|
||||
long l = generatorOptions.getSeed();
|
||||
long m = BiomeAccess.hashSeed(l);
|
||||
Map<RegistryKey<World>, ServerWorld> existing_worlds = ((MinecraftServerInterface)server).getCMWorlds();
|
||||
List<Value> addeds = new ArrayList<>();
|
||||
for (Map.Entry<RegistryKey<DimensionOptions>, DimensionOptions> entry : generatorOptions.getDimensions().getEntries()) {
|
||||
RegistryKey<DimensionOptions> registryKey = entry.getKey();
|
||||
if (!existing_worlds.containsKey(registryKey))
|
||||
{
|
||||
addeds.add(ValueConversions.of(registryKey.getValue()));
|
||||
RegistryKey<World> registryKey2 = RegistryKey.of(Registry.DIMENSION, registryKey.getValue());
|
||||
DimensionType dimensionType3 = entry.getValue().getDimensionType();
|
||||
ChunkGenerator chunkGenerator3 = entry.getValue().getChunkGenerator();
|
||||
UnmodifiableLevelProperties unmodifiableLevelProperties = new UnmodifiableLevelProperties(saveProperties, ((ServerWorldInterface) server.getOverworld()).getWorldPropertiesCM());
|
||||
ServerWorld serverWorld2 = new ServerWorld(server, Util.getMainWorkerExecutor(), session, unmodifiableLevelProperties, registryKey2, dimensionType3, WorldTools.NOOP_LISTENER, chunkGenerator3, bl, m, ImmutableList.of(), false);
|
||||
server.getOverworld().getWorldBorder().addListener(new WorldBorderListener.WorldBorderSyncer(serverWorld2.getWorldBorder()));
|
||||
existing_worlds.put(registryKey2, serverWorld2);
|
||||
}
|
||||
}
|
||||
Value ret = ListValue.wrap(addeds);
|
||||
return (_c, _t) -> ret;
|
||||
});
|
||||
}
|
||||
|
||||
private static void zipValueToJson(Path path, Value output) throws IOException
|
||||
{
|
||||
JsonElement element = output.toJson();
|
||||
if (element == null)
|
||||
throw new InternalExpressionException("Cannot interpret "+output.getPrettyString()+" as a json object");
|
||||
String string = GSON.toJson(element);
|
||||
Files.createDirectories(path.getParent());
|
||||
BufferedWriter bufferedWriter = Files.newBufferedWriter(path);
|
||||
Throwable incident = null;
|
||||
try
|
||||
{
|
||||
bufferedWriter.write(string);
|
||||
}
|
||||
catch (Throwable shitHappened)
|
||||
{
|
||||
incident = shitHappened;
|
||||
throw shitHappened;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (incident != null) {
|
||||
try {
|
||||
bufferedWriter.close();
|
||||
} catch (Throwable otherShitHappened) {
|
||||
incident.addSuppressed(otherShitHappened);
|
||||
}
|
||||
} else {
|
||||
bufferedWriter.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void walkTheDPMap(MapValue node, Path path) throws IOException
|
||||
{
|
||||
Map<Value,Value> items = node.getMap();
|
||||
for (Map.Entry<Value, Value> entry : items.entrySet())
|
||||
{
|
||||
Value val = entry.getValue();
|
||||
String strkey = entry.getKey().getString();
|
||||
Path child = path.resolve(strkey);
|
||||
if (strkey.endsWith(".json"))
|
||||
{
|
||||
zipValueToJson(child, val);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!(val instanceof MapValue)) throw new InternalExpressionException("Value of "+strkey+" should be a map");
|
||||
Files.createDirectory(child);
|
||||
walkTheDPMap((MapValue) val, child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static <T> Stat<T> getStat(StatType<T> type, Identifier id)
|
||||
{
|
||||
T key = type.getRegistry().get(id);
|
||||
|
||||
@@ -6,6 +6,8 @@ import carpet.script.CarpetContext;
|
||||
import carpet.script.Expression;
|
||||
import carpet.script.LazyValue;
|
||||
import carpet.script.exception.InternalExpressionException;
|
||||
import carpet.script.exception.ThrowStatement;
|
||||
import carpet.script.exception.Throwables;
|
||||
import carpet.script.value.ListValue;
|
||||
import carpet.script.value.NBTSerializableValue;
|
||||
import carpet.script.value.NullValue;
|
||||
@@ -91,7 +93,7 @@ public class Inventories {
|
||||
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()));
|
||||
Value ret = ListValue.wrap(tagManager.getItems().getTags().entrySet().stream().filter(e -> e.getValue().contains(item)).map(e -> ValueConversions.of(e.getKey())).collect(Collectors.toList()));
|
||||
return (_c, _t) -> ret;
|
||||
}
|
||||
String tag = lv.get(1).evalValue(c).getString();
|
||||
@@ -119,14 +121,7 @@ public class Inventories {
|
||||
}
|
||||
}
|
||||
List<Recipe<?>> recipes;
|
||||
try
|
||||
{
|
||||
recipes = ((RecipeManagerInterface) cc.s.getMinecraftServer().getRecipeManager()).getAllMatching(type, new Identifier(recipeName));
|
||||
}
|
||||
catch (InvalidIdentifierException ignored)
|
||||
{
|
||||
return LazyValue.NULL;
|
||||
}
|
||||
recipes = ((RecipeManagerInterface) cc.s.getMinecraftServer().getRecipeManager()).getAllMatching(type, new Identifier(recipeName));
|
||||
if (recipes.isEmpty())
|
||||
return LazyValue.NULL;
|
||||
List<Value> recipesOutput = new ArrayList<>();
|
||||
@@ -197,17 +192,9 @@ public class Inventories {
|
||||
{
|
||||
String itemStr = lv.get(0).evalValue(c).getString();
|
||||
Item item;
|
||||
try
|
||||
{
|
||||
Identifier id = new Identifier(itemStr);
|
||||
item = Registry.ITEM.get(id);
|
||||
if (item == Items.AIR && !id.getPath().equalsIgnoreCase("air"))
|
||||
throw new InvalidIdentifierException("boo");
|
||||
}
|
||||
catch (InvalidIdentifierException ignored)
|
||||
{
|
||||
throw new InternalExpressionException("Incorrect item: "+itemStr);
|
||||
}
|
||||
Identifier id = new Identifier(itemStr);
|
||||
item = Registry.ITEM.getOrEmpty(id).orElseThrow(() -> new ThrowStatement(itemStr, Throwables.UNKNOWN_ITEM));
|
||||
|
||||
if (!item.hasRecipeRemainder()) return LazyValue.NULL;
|
||||
Value ret = new StringValue(NBTSerializableValue.nameFromRegistryId(Registry.ITEM.getId(item.getRecipeRemainder())));
|
||||
return (_c, _t ) -> ret;
|
||||
|
||||
@@ -6,6 +6,8 @@ import carpet.script.CarpetContext;
|
||||
import carpet.script.Expression;
|
||||
import carpet.script.LazyValue;
|
||||
import carpet.script.exception.InternalExpressionException;
|
||||
import carpet.script.exception.ThrowStatement;
|
||||
import carpet.script.exception.Throwables;
|
||||
import carpet.script.value.EntityValue;
|
||||
import carpet.script.value.ListValue;
|
||||
import carpet.script.value.NullValue;
|
||||
@@ -33,6 +35,8 @@ import net.minecraft.util.Formatting;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.InvalidIdentifierException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class Scoreboards {
|
||||
@@ -77,16 +81,27 @@ public class Scoreboards {
|
||||
Value ret = ListValue.wrap(scoreboard.getAllPlayerScores(objective).stream().map(s -> new StringValue(s.getPlayerName())).collect(Collectors.toList()));
|
||||
return (_c, _t) -> ret;
|
||||
}
|
||||
|
||||
String key = getScoreboardKeyFromValue(lv.get(1).evalValue(c));
|
||||
if (!scoreboard.playerHasObjective(key, objective) && lv.size()==2)
|
||||
if(lv.size()==2) {
|
||||
if(scoreboard.playerHasObjective(key, objective)) {
|
||||
return (_c,_t) -> NumericValue.of(scoreboard.getPlayerScore(key,objective).getScore());
|
||||
}
|
||||
return LazyValue.NULL;
|
||||
ScoreboardPlayerScore scoreboardPlayerScore = scoreboard.getPlayerScore(key, objective);
|
||||
Value retval = new NumericValue(scoreboardPlayerScore.getScore());
|
||||
if (lv.size() > 2)
|
||||
{
|
||||
scoreboardPlayerScore.setScore(NumericValue.asNumber(lv.get(2).evalValue(c)).getInt());
|
||||
}
|
||||
return (_c, _t) -> retval;
|
||||
|
||||
Value value = lv.get(2).evalValue(c);
|
||||
if(value.isNull()) {
|
||||
ScoreboardPlayerScore score = scoreboard.getPlayerScore(key, objective);
|
||||
scoreboard.resetPlayerScore(key,objective);
|
||||
return (_c,_t) -> NumericValue.of(score.getScore());
|
||||
}
|
||||
if (value instanceof NumericValue) {
|
||||
ScoreboardPlayerScore score = scoreboard.getPlayerScore(key, objective);
|
||||
score.setScore(NumericValue.asNumber(value).getInt());
|
||||
return (_c,_t) -> NumericValue.of(score.getScore());
|
||||
}
|
||||
throw new InternalExpressionException("'scoreboard' requires a number or null as the third parameter");
|
||||
});
|
||||
|
||||
expression.addLazyFunction("scoreboard_remove", -1, (c, t, lv)->
|
||||
@@ -131,12 +146,13 @@ public class Scoreboards {
|
||||
criterion = ScoreboardCriterion.createStatCriterion(critetionName).orElse(null);
|
||||
if (criterion==null)
|
||||
{
|
||||
throw new InternalExpressionException("Unknown scoreboard criterion: "+critetionName);
|
||||
throw new ThrowStatement(critetionName, Throwables.UNKNOWN_CRITERION);
|
||||
}
|
||||
}
|
||||
|
||||
ScoreboardObjective objective = scoreboard.getObjective(objectiveName);
|
||||
if (objective != null) {
|
||||
c.host.issueDeprecation("reading or modifying an objective's criterion with scoreboard_add");
|
||||
if(lv.size() == 1) return (_c, _t) -> StringValue.of(objective.getCriterion().getName());
|
||||
if(objective.getCriterion().equals(criterion) || lv.size() == 1) return LazyValue.NULL;
|
||||
((Scoreboard_scarpetMixin)scoreboard).getObjectivesByCriterion().get(objective.getCriterion()).remove(objective);
|
||||
@@ -150,6 +166,72 @@ public class Scoreboards {
|
||||
return LazyValue.TRUE;
|
||||
});
|
||||
|
||||
expression.addLazyFunction("scoreboard_property", -1, (c, t, lv) ->
|
||||
{
|
||||
if(lv.size() < 2) throw new InternalExpressionException("'scoreboard_property' requires at least two parameters");
|
||||
CarpetContext cc = (CarpetContext)c;
|
||||
Scoreboard scoreboard = cc.s.getMinecraftServer().getScoreboard();
|
||||
ScoreboardObjective objective = scoreboard.getObjective(lv.get(0).evalValue(c).getString());
|
||||
if(objective == null) return LazyValue.NULL;
|
||||
|
||||
boolean modify = lv.size() > 2;
|
||||
Value setValue = null;
|
||||
if(modify) {
|
||||
setValue = lv.get(2).evalValue(c);
|
||||
}
|
||||
String property = lv.get(1).evalValue(c).getString();
|
||||
switch (property) {
|
||||
case "criterion":
|
||||
if(modify) {
|
||||
ScoreboardCriterion criterion = ScoreboardCriterion.createStatCriterion(setValue.getString()).orElse(null);
|
||||
if (criterion==null) throw new InternalExpressionException("Unknown scoreboard criterion: "+ setValue.getString());
|
||||
if(objective.getCriterion().equals(criterion) || lv.size() == 1) return LazyValue.FALSE;
|
||||
((Scoreboard_scarpetMixin)scoreboard).getObjectivesByCriterion().get(objective.getCriterion()).remove(objective);
|
||||
((ScoreboardObjective_scarpetMixin) objective).setCriterion(criterion);
|
||||
(((Scoreboard_scarpetMixin)scoreboard).getObjectivesByCriterion().computeIfAbsent(criterion, (criterion1) -> Lists.newArrayList())).add(objective);
|
||||
scoreboard.updateObjective(objective);
|
||||
return LazyValue.TRUE;
|
||||
}
|
||||
return (_c, _t) -> StringValue.of(objective.getCriterion().getName());
|
||||
case "display_name":
|
||||
if(modify) {
|
||||
Text text = (setValue instanceof FormattedTextValue)?((FormattedTextValue) setValue).getText():new LiteralText(setValue.getString());
|
||||
objective.setDisplayName(text);
|
||||
return LazyValue.TRUE;
|
||||
}
|
||||
return (_c, _t) -> new FormattedTextValue(objective.getDisplayName());
|
||||
case "display_slot":
|
||||
if(modify) {
|
||||
int slotId = Scoreboard.getDisplaySlotId(setValue.getString());
|
||||
if(slotId == -1) throw new InternalExpressionException("Unknown scoreboard display slot: " + setValue.getString());
|
||||
if(objective.equals(scoreboard.getObjectiveForSlot(slotId))) {
|
||||
return LazyValue.FALSE;
|
||||
}
|
||||
scoreboard.setObjectiveSlot(slotId,objective);
|
||||
return LazyValue.TRUE;
|
||||
}
|
||||
|
||||
List<Value> slots = new ArrayList<>();
|
||||
for(int i = 0; i < 19; i++) {
|
||||
if (scoreboard.getObjectiveForSlot(i) == objective) {
|
||||
String slotName = Scoreboard.getDisplaySlotName(i);
|
||||
slots.add(StringValue.of(slotName));
|
||||
}
|
||||
}
|
||||
return (_c, _t) -> ListValue.wrap(slots);
|
||||
case "render_type":
|
||||
if(modify) {
|
||||
ScoreboardCriterion.RenderType renderType = ScoreboardCriterion.RenderType.getType(setValue.getString().toLowerCase());
|
||||
if(objective.getRenderType().equals(renderType)) return LazyValue.FALSE;
|
||||
objective.setRenderType(renderType);
|
||||
return LazyValue.TRUE;
|
||||
}
|
||||
return (_c, _t) -> StringValue.of(objective.getRenderType().getName());
|
||||
default:
|
||||
throw new InternalExpressionException("scoreboard property '" + property + "' is not a valid property");
|
||||
}
|
||||
});
|
||||
|
||||
expression.addLazyFunction("scoreboard_display", 2, (c, t, lv) ->
|
||||
{
|
||||
CarpetContext cc = (CarpetContext)c;
|
||||
@@ -408,7 +490,7 @@ public class Scoreboards {
|
||||
team.setSuffix(suffix);
|
||||
break;
|
||||
default:
|
||||
throw new InternalExpressionException("team property " + propertyVal.getString() + " is not a valid property");
|
||||
throw new InternalExpressionException("team property '" + propertyVal.getString() + "' is not a valid property");
|
||||
}
|
||||
return LazyValue.TRUE;
|
||||
});
|
||||
|
||||
@@ -19,6 +19,8 @@ import carpet.script.LazyValue;
|
||||
import carpet.script.argument.BlockArgument;
|
||||
import carpet.script.argument.Vector3Argument;
|
||||
import carpet.script.exception.InternalExpressionException;
|
||||
import carpet.script.exception.ThrowStatement;
|
||||
import carpet.script.exception.Throwables;
|
||||
import carpet.script.utils.BiomeInfo;
|
||||
import carpet.script.utils.WorldTools;
|
||||
import carpet.script.value.BlockValue;
|
||||
@@ -48,6 +50,7 @@ import net.minecraft.enchantment.Enchantments;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.EntityType;
|
||||
import net.minecraft.entity.FallingBlockEntity;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.entity.ai.pathing.NavigationType;
|
||||
import net.minecraft.item.BlockItem;
|
||||
import net.minecraft.item.Item;
|
||||
@@ -59,7 +62,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.network.packet.s2c.play.ExplosionS2CPacket;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.server.world.ChunkTicket;
|
||||
import net.minecraft.server.world.ChunkTicketType;
|
||||
@@ -78,6 +81,7 @@ import net.minecraft.util.math.BlockBox;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.ChunkPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.util.registry.Registry;
|
||||
import net.minecraft.world.Heightmap;
|
||||
import net.minecraft.world.LightType;
|
||||
@@ -87,6 +91,7 @@ import net.minecraft.world.biome.Biome;
|
||||
import net.minecraft.world.chunk.Chunk;
|
||||
import net.minecraft.world.chunk.ChunkStatus;
|
||||
import net.minecraft.world.chunk.WorldChunk;
|
||||
import net.minecraft.world.explosion.Explosion;
|
||||
import net.minecraft.world.gen.ChunkRandom;
|
||||
import net.minecraft.world.gen.chunk.ChunkGenerator;
|
||||
import net.minecraft.world.gen.feature.ConfiguredStructureFeature;
|
||||
@@ -95,6 +100,7 @@ import net.minecraft.world.level.ServerWorldProperties;
|
||||
import net.minecraft.world.poi.PointOfInterest;
|
||||
import net.minecraft.world.poi.PointOfInterestStorage;
|
||||
import net.minecraft.world.poi.PointOfInterestType;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@@ -106,7 +112,6 @@ import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
@@ -121,8 +126,7 @@ public class WorldAccess {
|
||||
put("unknown", ChunkTicketType.UNKNOWN); // unknown
|
||||
}};
|
||||
// dummy entity for dummy requirements in the loot tables (see snowball)
|
||||
private static FallingBlockEntity DUMMY_ENTITY = new FallingBlockEntity(EntityType.FALLING_BLOCK, null);
|
||||
|
||||
private static FallingBlockEntity DUMMY_ENTITY = null;
|
||||
private static LazyValue booleanStateTest(
|
||||
Context c,
|
||||
String name,
|
||||
@@ -336,8 +340,8 @@ public class WorldAccess {
|
||||
String poiType = lv.get(locator.offset+1).evalValue(c).getString().toLowerCase(Locale.ROOT);
|
||||
if (!"any".equals(poiType))
|
||||
{
|
||||
PointOfInterestType type = Registry.POINT_OF_INTEREST_TYPE.get(new Identifier(poiType));
|
||||
if (type == PointOfInterestType.UNEMPLOYED && !"unemployed".equals(poiType)) return LazyValue.NULL;
|
||||
PointOfInterestType type = Registry.POINT_OF_INTEREST_TYPE.getOrEmpty(new Identifier(poiType))
|
||||
.orElseThrow(() -> new ThrowStatement(poiType, Throwables.UNKNOWN_POI));
|
||||
condition = (tt) -> tt == type;
|
||||
}
|
||||
if (locator.offset + 2 < lv.size())
|
||||
@@ -391,9 +395,8 @@ public class WorldAccess {
|
||||
return LazyValue.FALSE;
|
||||
}
|
||||
String poiTypeString = poi.getString().toLowerCase(Locale.ROOT);
|
||||
PointOfInterestType type = Registry.POINT_OF_INTEREST_TYPE.get(new Identifier(poiTypeString));
|
||||
// solving lack of null with defaulted registries
|
||||
if (type == PointOfInterestType.UNEMPLOYED && !"unemployed".equals(poiTypeString)) throw new InternalExpressionException("Unknown POI type: "+poiTypeString);
|
||||
PointOfInterestType type = Registry.POINT_OF_INTEREST_TYPE.getOrEmpty(new Identifier(poiTypeString))
|
||||
.orElseThrow(() -> new ThrowStatement(poiTypeString, Throwables.UNKNOWN_POI));
|
||||
int occupancy = 0;
|
||||
if (locator.offset + 1 < lv.size())
|
||||
{
|
||||
@@ -872,9 +875,8 @@ public class WorldAccess {
|
||||
{
|
||||
playerBreak = true;
|
||||
String itemString = val.getString();
|
||||
item = Registry.ITEM.get(new Identifier(itemString));
|
||||
if (item == Items.AIR && !itemString.equals("air"))
|
||||
throw new InternalExpressionException("Incorrect item: " + itemString);
|
||||
item = Registry.ITEM.getOrEmpty(new Identifier(itemString))
|
||||
.orElseThrow(() -> new ThrowStatement(itemString, Throwables.UNKNOWN_ITEM));
|
||||
}
|
||||
}
|
||||
CompoundTag tag = null;
|
||||
@@ -932,6 +934,7 @@ public class WorldAccess {
|
||||
{
|
||||
if (how > 0)
|
||||
tool.addEnchantment(Enchantments.FORTUNE, (int) how);
|
||||
if (DUMMY_ENTITY == null) DUMMY_ENTITY = new FallingBlockEntity(EntityType.FALLING_BLOCK, null);
|
||||
Block.dropStacks(state, world, where, be, DUMMY_ENTITY, tool);
|
||||
}
|
||||
}
|
||||
@@ -972,6 +975,88 @@ public class WorldAccess {
|
||||
return success ? LazyValue.TRUE : LazyValue.FALSE;
|
||||
});
|
||||
|
||||
expression.addLazyFunction("create_explosion", -1, (c, t, lv) ->
|
||||
{
|
||||
if (lv.isEmpty())
|
||||
throw new InternalExpressionException("'create_explosion' requires at least a position to explode");
|
||||
CarpetContext cc = (CarpetContext)c;
|
||||
float powah = 4.0f;
|
||||
Explosion.DestructionType mode = Explosion.DestructionType.BREAK;
|
||||
boolean createFire = false;
|
||||
Entity source = null;
|
||||
LivingEntity attacker = null;
|
||||
Vector3Argument location = Vector3Argument.findIn(cc, lv, 0, false, true);
|
||||
Vec3d pos = location.vec;
|
||||
if (lv.size() > location.offset)
|
||||
{
|
||||
powah = NumericValue.asNumber(lv.get(location.offset).evalValue(c), "explosion power").getFloat();
|
||||
if (powah < 0) throw new InternalExpressionException("Explosion power cannot be negative");
|
||||
if (lv.size() > location.offset+1)
|
||||
{
|
||||
String strval = lv.get(location.offset+1).evalValue(c).getString();
|
||||
try {
|
||||
mode = Explosion.DestructionType.valueOf(strval.toUpperCase(Locale.ROOT));
|
||||
}
|
||||
catch (IllegalArgumentException ile) { throw new InternalExpressionException("Illegal explosions block behaviour: "+strval); }
|
||||
if (lv.size() > location.offset+2)
|
||||
{
|
||||
createFire = lv.get(location.offset+2).evalValue(c, Context.BOOLEAN).getBoolean();
|
||||
if (lv.size() > location.offset+3)
|
||||
{
|
||||
Value enVal= lv.get(location.offset+3).evalValue(c);
|
||||
if (enVal.isNull()) {} // is null already
|
||||
else if (enVal instanceof EntityValue)
|
||||
{
|
||||
source = ((EntityValue) enVal).getEntity();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InternalExpressionException("Fourth parameter of the explosion has to be an entity, not "+enVal.getTypeString());
|
||||
}
|
||||
if (lv.size() > location.offset+4)
|
||||
{
|
||||
enVal = lv.get(location.offset+4).evalValue(c);
|
||||
if (enVal.isNull()) {} // is null already
|
||||
else if (enVal instanceof EntityValue)
|
||||
{
|
||||
Entity attackingEntity = ((EntityValue) enVal).getEntity();
|
||||
if (attackingEntity instanceof LivingEntity)
|
||||
{
|
||||
attacker = (LivingEntity) attackingEntity;
|
||||
}
|
||||
else throw new InternalExpressionException("Attacking entity needs to be a living thing, "+
|
||||
ValueConversions.of(Registry.ENTITY_TYPE.getId(attackingEntity.getType())).getString() +" ain't it.");
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InternalExpressionException("Fifth parameter of the explosion has to be a living entity, not "+enVal.getTypeString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
LivingEntity theAttacker = attacker;
|
||||
float thePowah = powah;
|
||||
|
||||
// copy of ServerWorld.createExplosion #TRACK#
|
||||
Explosion explosion = new Explosion(cc.s.getWorld(), source, null, null, pos.x, pos.y, pos.z, powah, createFire, mode){
|
||||
@Override
|
||||
public @Nullable LivingEntity getCausingEntity() {
|
||||
return theAttacker;
|
||||
}
|
||||
};
|
||||
explosion.collectBlocksAndDamageEntities();
|
||||
explosion.affectWorld(false);
|
||||
if (mode == Explosion.DestructionType.NONE) explosion.clearAffectedBlocks();
|
||||
cc.s.getWorld().getPlayers().forEach(spe -> {
|
||||
if (spe.squaredDistanceTo(pos) < 4096.0D)
|
||||
spe.networkHandler.sendPacket(new ExplosionS2CPacket(pos.x, pos.y, pos.z, thePowah, explosion.getAffectedBlocks(), explosion.getAffectedPlayers().get(spe)));
|
||||
});
|
||||
return LazyValue.TRUE;
|
||||
});
|
||||
|
||||
// TODO rename to use_item
|
||||
expression.addLazyFunction("place_item", -1, (c, t, lv) ->
|
||||
{
|
||||
@@ -1127,7 +1212,8 @@ public class WorldAccess {
|
||||
BlockArgument blockLocator = BlockArgument.findIn(cc, lv, 0, true);
|
||||
if (blockLocator.offset == lv.size())
|
||||
{
|
||||
Value ret = ListValue.wrap(tagManager.getBlocks().getTagsFor(blockLocator.block.getBlockState().getBlock()).stream().map(ValueConversions::of).collect(Collectors.toList()));
|
||||
Block target = blockLocator.block.getBlockState().getBlock();
|
||||
Value ret = ListValue.wrap(tagManager.getBlocks().getTags().entrySet().stream().filter(e -> e.getValue().contains(target)).map(e -> ValueConversions.of(e.getKey())).collect(Collectors.toList()));
|
||||
return (_c, _t) -> ret;
|
||||
}
|
||||
String tag = lv.get(blockLocator.offset).evalValue(c).getString();
|
||||
@@ -1151,7 +1237,7 @@ public class WorldAccess {
|
||||
if (locator.replacement != null)
|
||||
{
|
||||
biome = world.getRegistryManager().get(Registry.BIOME_KEY).get(new Identifier(locator.replacement));
|
||||
if (biome == null) return LazyValue.NULL;
|
||||
if (biome == null) throw new ThrowStatement(locator.replacement, Throwables.UNKNOWN_BIOME) ;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1180,9 +1266,8 @@ public class WorldAccess {
|
||||
throw new InternalExpressionException("'set_biome' needs a biome name as an argument");
|
||||
String biomeName = lv.get(locator.offset+0).evalValue(c).getString();
|
||||
// from locatebiome command code
|
||||
Biome biome = cc.s.getMinecraftServer().getRegistryManager().get(Registry.BIOME_KEY).get(new Identifier(biomeName));
|
||||
if (biome == null)
|
||||
throw new InternalExpressionException("Unknown biome: "+biomeName);
|
||||
Biome biome = cc.s.getMinecraftServer().getRegistryManager().get(Registry.BIOME_KEY).getOrEmpty(new Identifier(biomeName))
|
||||
.orElseThrow(() -> new ThrowStatement(biomeName, Throwables.UNKNOWN_BIOME));
|
||||
boolean doImmediateUpdate = true;
|
||||
if (lv.size() > locator.offset+1)
|
||||
{
|
||||
@@ -1251,8 +1336,8 @@ public class WorldAccess {
|
||||
if (!(requested instanceof NullValue))
|
||||
{
|
||||
String reqString = requested.getString();
|
||||
structure = Registry.STRUCTURE_FEATURE.get(new Identifier(reqString));
|
||||
if (structure == null) throw new InternalExpressionException("Unknown structure: " + reqString);
|
||||
structure = Registry.STRUCTURE_FEATURE.getOrEmpty(new Identifier(reqString))
|
||||
.orElseThrow(() -> new ThrowStatement(reqString, Throwables.UNKNOWN_STRUCTURE));
|
||||
}
|
||||
if (lv.size() > locator.offset+1)
|
||||
{
|
||||
@@ -1332,7 +1417,7 @@ public class WorldAccess {
|
||||
throw new InternalExpressionException("'set_structure requires at least position and a structure name");
|
||||
String structureName = lv.get(locator.offset).evalValue(c).getString().toLowerCase(Locale.ROOT);
|
||||
ConfiguredStructureFeature<?, ?> configuredStructure = FeatureGenerator.resolveConfiguredStructure(structureName, world, pos);
|
||||
if (configuredStructure == null) throw new InternalExpressionException("Unknown structure: "+structureName);
|
||||
if (configuredStructure == null) throw new ThrowStatement(structureName, Throwables.UNKNOWN_STRUCTURE);
|
||||
// good 'ol pointer
|
||||
Value[] result = new Value[]{Value.NULL};
|
||||
// technically a world modification. Even if we could let it slide, we will still park it
|
||||
@@ -1382,6 +1467,7 @@ public class WorldAccess {
|
||||
{
|
||||
if (lv.size() == 0) throw new InternalExpressionException("'custom_dimension' requires at least one argument");
|
||||
CarpetContext cc = (CarpetContext)c;
|
||||
cc.host.issueDeprecation("custom_dimension()");
|
||||
String worldKey = lv.get(0).evalValue(c).getString();
|
||||
|
||||
Long seed = null;
|
||||
|
||||
@@ -0,0 +1,502 @@
|
||||
package carpet.script.argument;
|
||||
|
||||
import carpet.CarpetServer;
|
||||
import carpet.script.Context;
|
||||
import carpet.script.LazyValue;
|
||||
import carpet.script.bundled.Module;
|
||||
import carpet.script.exception.InternalExpressionException;
|
||||
import carpet.script.exception.ThrowStatement;
|
||||
import carpet.script.exception.Throwables;
|
||||
import carpet.script.value.MapValue;
|
||||
import carpet.script.value.StringValue;
|
||||
import carpet.script.value.Value;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.JsonParser;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
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;
|
||||
import net.minecraft.util.WorldSavePath;
|
||||
import net.minecraft.util.crash.CrashException;
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import java.io.BufferedInputStream;
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.net.URI;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.FileSystem;
|
||||
import java.nio.file.FileSystemNotFoundException;
|
||||
import java.nio.file.FileSystems;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class FileArgument
|
||||
{
|
||||
public String resource;
|
||||
public Type type;
|
||||
public String zipContainer;
|
||||
public boolean isFolder;
|
||||
public boolean isShared;
|
||||
public Reason reason;
|
||||
private FileSystem zfs;
|
||||
private Path zipPath;
|
||||
|
||||
public final static Object writeIOSync = new Object();
|
||||
|
||||
public void close() {
|
||||
if (zfs != null && zfs.isOpen()) {
|
||||
try {
|
||||
zfs.close();
|
||||
} catch (IOException e) {
|
||||
throw new InternalExpressionException("Unable to close zip container: "+zipContainer);
|
||||
}
|
||||
zfs = null;
|
||||
}
|
||||
}
|
||||
|
||||
public enum Type
|
||||
{
|
||||
RAW("raw", ".txt"),
|
||||
TEXT("text", ".txt"),
|
||||
NBT("nbt", ".nbt"),
|
||||
JSON("json", ".json"),
|
||||
FOLDER("folder", "");
|
||||
|
||||
private final String id;
|
||||
private final String extension;
|
||||
|
||||
public static Map<String, Type> of = Arrays.stream(values()).collect(Collectors.toMap(t -> t.id, t -> t));
|
||||
|
||||
Type(String id, String extension)
|
||||
{
|
||||
this.id = id;
|
||||
this.extension = extension;
|
||||
}
|
||||
}
|
||||
|
||||
public enum Reason
|
||||
{
|
||||
READ, CREATE, DELETE
|
||||
}
|
||||
|
||||
public FileArgument(String resource, Type type, String zipContainer, boolean isFolder, boolean isShared, Reason reason)
|
||||
{
|
||||
this.resource = resource;
|
||||
this.type = type;
|
||||
this.zipContainer = zipContainer;
|
||||
this.isFolder = isFolder;
|
||||
this.isShared = isShared;
|
||||
this.reason = reason;
|
||||
this.zfs = null;
|
||||
this.zipPath = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "path: "+resource+" zip: "+zipContainer+" type: "+type.id+" folder: "+isFolder+" shared: "+isShared+" reason: "+reason.toString();
|
||||
}
|
||||
|
||||
public static FileArgument from(List<LazyValue> lv, Context c, boolean isFolder, Reason reason)
|
||||
{
|
||||
if (lv.size() < 2) throw new InternalExpressionException("File functions require path and type as first two arguments");
|
||||
Pair<String, String> resource = recognizeResource(lv.get(0).evalValue(c), isFolder);
|
||||
String origtype = lv.get(1).evalValue(c).getString().toLowerCase(Locale.ROOT);
|
||||
boolean shared = origtype.startsWith("shared_");
|
||||
String typeString = shared ? origtype.substring(7) : origtype; //len(shared_)
|
||||
Type type = Type.of.get(typeString);
|
||||
if (type==null)
|
||||
throw new InternalExpressionException("Unsupported file type: "+origtype);
|
||||
if (type==Type.FOLDER && !isFolder)
|
||||
throw new InternalExpressionException("Folder types are no supported for this IO function");
|
||||
return new FileArgument(resource.getLeft(), type,resource.getRight(), isFolder, shared, reason);
|
||||
|
||||
}
|
||||
|
||||
public static Pair<String,String> recognizeResource(Value value, boolean isFolder)
|
||||
{
|
||||
String origfile = value.getString();
|
||||
String[] pathElements = origfile.toLowerCase(Locale.ROOT).split("[/\\\\]+");
|
||||
List<String> path = new ArrayList<>();
|
||||
String zipPath = null;
|
||||
for (String token : pathElements)
|
||||
{
|
||||
boolean isZip = token.endsWith(".zip");
|
||||
if (zipPath != null && isZip) throw new InternalExpressionException(token+" indicates zip access in an already zipped location "+zipPath);
|
||||
if (isZip) token = token.substring(0, token.length()-4);
|
||||
token = token.replaceAll("[^A-Za-z0-9\\-+_]", "");
|
||||
if (token.isEmpty()) continue;
|
||||
if (isZip) token = token + ".zip";
|
||||
path.add(token);
|
||||
if (isZip)
|
||||
{
|
||||
zipPath = String.join("/", path);
|
||||
path.clear();
|
||||
}
|
||||
}
|
||||
if (path.isEmpty() && !isFolder)
|
||||
throw new InternalExpressionException(
|
||||
"Cannot use "+origfile+" as resource name: indicated path is empty"+((zipPath==null)?"":" in zip container "+zipPath)
|
||||
);
|
||||
return Pair.of(String.join("/", path), zipPath);
|
||||
}
|
||||
|
||||
private Path resolve(String suffix)
|
||||
{
|
||||
if (CarpetServer.minecraft_server == null)
|
||||
throw new InternalExpressionException("Accessing world files without server running");
|
||||
return CarpetServer.minecraft_server.getSavePath(WorldSavePath.ROOT).resolve("scripts/"+suffix);
|
||||
}
|
||||
|
||||
private Path toPath(Module module)
|
||||
{
|
||||
if (!isShared && (module == null || module.getName() == null)) return null;
|
||||
if (zipContainer == null)
|
||||
return resolve(getDescriptor(module, resource)+(isFolder?"":type.extension));
|
||||
else
|
||||
{
|
||||
if (zfs == null)
|
||||
{
|
||||
Map<String, String> env = new HashMap<>();
|
||||
if (reason == Reason.CREATE) env.put("create", "true");
|
||||
zipPath = resolve(getDescriptor(module, zipContainer));
|
||||
try {
|
||||
zfs = FileSystems.newFileSystem(URI.create("jar:"+ zipPath.toUri().toString()), env);
|
||||
}
|
||||
catch (FileSystemNotFoundException fsnfe)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw new ThrowStatement("Unable to open zip file: "+zipContainer, Throwables.IO_EXCEPTION);
|
||||
}
|
||||
}
|
||||
return zfs.getPath(resource+(isFolder?"/":type.extension));
|
||||
}
|
||||
}
|
||||
|
||||
private Path moduleRootPath(Module module)
|
||||
{
|
||||
if (!isShared && (module == null || module.getName() == null)) return null;
|
||||
return resolve(isShared?"shared":module.getName()+".data");
|
||||
}
|
||||
|
||||
public String getDisplayPath()
|
||||
{
|
||||
return (isShared?"shared/":"")+(zipContainer!=null?zipContainer+"/":"")+resource+type.extension;
|
||||
}
|
||||
|
||||
private String getDescriptor(Module module, String res)
|
||||
{
|
||||
if (isShared)
|
||||
{
|
||||
return res.isEmpty()?"shared":"shared/"+res;
|
||||
}
|
||||
if (module != null && module.getName() != null) // appdata
|
||||
{
|
||||
return module.getName()+".data"+(res==null || res.isEmpty()?"":"/"+res);
|
||||
}
|
||||
throw new InternalExpressionException("Invalid file descriptor: "+res);
|
||||
}
|
||||
|
||||
public Stream<Path> listFiles(Module module)
|
||||
{
|
||||
Path dir = toPath(module);
|
||||
if (dir == null || !Files.exists(dir)) return null;
|
||||
String ext = type.extension;
|
||||
try
|
||||
{
|
||||
return Files.list(dir).filter(path -> (type==Type.FOLDER)?path.toString().endsWith("/"):path.toString().endsWith(ext));
|
||||
}
|
||||
catch (IOException ignored)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public Stream<String> listFolder(Module module)
|
||||
{
|
||||
Stream<String> strings;
|
||||
try { synchronized (writeIOSync)
|
||||
{
|
||||
Stream<Path> result;
|
||||
result = listFiles(module);
|
||||
if (result == null) return null;
|
||||
Path rootPath = moduleRootPath(module);
|
||||
if (rootPath == null) return null;
|
||||
String zipComponent = (zipContainer != null) ? rootPath.relativize(zipPath).toString() : null;
|
||||
strings = result.map(p -> {
|
||||
if (zipContainer == null)
|
||||
return rootPath.relativize(p).toString().replaceAll("[\\\\/]+", "/");
|
||||
return (zipComponent + '/'+ p.toString()).replaceAll("[\\\\/]+", "/");
|
||||
});
|
||||
} }
|
||||
finally
|
||||
{
|
||||
close();
|
||||
}
|
||||
if (type == Type.FOLDER)
|
||||
return strings.map(s -> s.endsWith("/")?s.substring(0, s.length()-1):s);
|
||||
return strings.map(FilenameUtils::removeExtension);
|
||||
}
|
||||
|
||||
private void createPaths(Path file)
|
||||
{
|
||||
try {
|
||||
if ((zipContainer == null || file.getParent() != null) &&
|
||||
!Files.exists(file.getParent()) &&
|
||||
Files.createDirectories(file.getParent()) == null
|
||||
) throw new IOException();
|
||||
} catch (IOException e) {
|
||||
throw new ThrowStatement("Unable to create paths for "+file.toString(), Throwables.IO_EXCEPTION);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public boolean appendToTextFile(Module module, List<String> message)
|
||||
{
|
||||
try { synchronized (writeIOSync)
|
||||
{
|
||||
Path dataFile = toPath(module);//, resourceName, supportedTypes.get(type), isShared);
|
||||
if (dataFile == null) return false;
|
||||
createPaths(dataFile);
|
||||
OutputStream out = Files.newOutputStream(dataFile, StandardOpenOption.APPEND, StandardOpenOption.CREATE);
|
||||
try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out, StandardCharsets.UTF_8)))
|
||||
{
|
||||
for (String line: message)
|
||||
{
|
||||
writer.append(line);
|
||||
if (type == Type.TEXT) writer.newLine();
|
||||
}
|
||||
}
|
||||
} }
|
||||
catch (IOException e)
|
||||
{
|
||||
throw new ThrowStatement("Error when writing to the file: "+e, Throwables.IO_EXCEPTION);
|
||||
}
|
||||
finally
|
||||
{
|
||||
close();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public Tag getNbtData(Module module) // aka getData
|
||||
{
|
||||
try { synchronized (writeIOSync) {
|
||||
Path dataFile = toPath(module);
|
||||
if (dataFile == null || !Files.exists(dataFile)) return null;
|
||||
return readTag(dataFile);
|
||||
} }
|
||||
finally {
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
//copied private method from net.minecraft.nbt.NbtIo.read()
|
||||
// to read non-compound tags - these won't be compressed
|
||||
public static Tag readTag(Path path)
|
||||
{
|
||||
try
|
||||
{
|
||||
return NbtIo.readCompressed(Files.newInputStream(path));
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
// Copy of NbtIo.read(File) because that's now client-side only
|
||||
if (!Files.exists(path))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
try (DataInputStream in = new DataInputStream(new BufferedInputStream(Files.newInputStream(path))))
|
||||
{
|
||||
return NbtIo.read(in);
|
||||
}
|
||||
catch (IOException ioException)
|
||||
{
|
||||
// not compressed compound tag neither uncompressed compound tag - trying any type of a tag
|
||||
try (DataInputStream dataInput_1 = new DataInputStream(new BufferedInputStream(Files.newInputStream(path))))
|
||||
{
|
||||
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)
|
||||
{
|
||||
throw new ThrowStatement("Not a valid NBT tag in "+path.toString(), Throwables.NBT_ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (CrashException e)
|
||||
{
|
||||
throw new ThrowStatement("Error when reading NBT file "+path.toString(), Throwables.NBT_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean saveNbtData(Module module, Tag tag) // aka saveData
|
||||
{
|
||||
try { synchronized (writeIOSync) {
|
||||
Path dataFile = toPath(module);
|
||||
if (dataFile == null) return false;
|
||||
createPaths(dataFile);
|
||||
return writeTagDisk(tag, dataFile, zipContainer != null);
|
||||
} }
|
||||
finally
|
||||
{
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
//copied private method from net.minecraft.nbt.NbtIo.write() and client method safe_write
|
||||
public static boolean writeTagDisk(Tag tag_1, Path path, boolean zipped)
|
||||
{
|
||||
Path original = path;
|
||||
try
|
||||
{
|
||||
if (!zipped)
|
||||
{
|
||||
path = new File(path.toFile().getAbsolutePath() + "_tmp").toPath();
|
||||
Files.deleteIfExists(path);
|
||||
}
|
||||
|
||||
if (tag_1 instanceof CompoundTag)
|
||||
{
|
||||
NbtIo.writeCompressed((CompoundTag) tag_1, Files.newOutputStream(path));
|
||||
}
|
||||
else
|
||||
{
|
||||
try (DataOutputStream dataOutputStream_1 = new DataOutputStream(Files.newOutputStream(path)))
|
||||
{
|
||||
dataOutputStream_1.writeByte(tag_1.getType());
|
||||
if (tag_1.getType() != 0)
|
||||
{
|
||||
dataOutputStream_1.writeUTF("");
|
||||
tag_1.write(dataOutputStream_1);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!zipped)
|
||||
{
|
||||
Files.deleteIfExists(original);
|
||||
Files.move(path, original);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw new ThrowStatement("Unable to write tag to "+original.toString(), Throwables.IO_EXCEPTION);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean dropExistingFile(Module module)
|
||||
{
|
||||
|
||||
try { synchronized (writeIOSync)
|
||||
{
|
||||
Path dataFile = toPath(module);
|
||||
if (dataFile == null) return false;
|
||||
return Files.deleteIfExists(dataFile);
|
||||
} }
|
||||
catch (IOException e)
|
||||
{
|
||||
throw new ThrowStatement("Error while removing file: "+getDisplayPath(), Throwables.IO_EXCEPTION);
|
||||
}
|
||||
finally
|
||||
{
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
public List<String> listFile(Module module)
|
||||
{
|
||||
try { synchronized (writeIOSync) {
|
||||
Path dataFile = toPath(module);
|
||||
if (dataFile == null) return null;
|
||||
if (!Files.exists(dataFile)) return null;
|
||||
return listFileContent(dataFile);
|
||||
} }
|
||||
finally {
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
public static List<String> listFileContent(Path filePath)
|
||||
{
|
||||
try (BufferedReader reader = Files.newBufferedReader(filePath, StandardCharsets.UTF_8)) {
|
||||
List<String> result = new ArrayList<>();
|
||||
for (;;) {
|
||||
String line = reader.readLine();
|
||||
if (line == null)
|
||||
break;
|
||||
result.add(line.replaceAll("[\n\r]+",""));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw new ThrowStatement("Failed to read text file "+filePath.toString(), Throwables.IO_EXCEPTION);
|
||||
}
|
||||
}
|
||||
|
||||
public JsonElement readJsonFile(Module module) {
|
||||
try { synchronized (writeIOSync) {
|
||||
Path dataFile = toPath(module);
|
||||
if (dataFile == null || !Files.exists(dataFile)) return null;
|
||||
return readJsonContent(dataFile);
|
||||
} }
|
||||
finally {
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
public static JsonElement readJsonContent(Path filePath)
|
||||
{
|
||||
try (BufferedReader reader = Files.newBufferedReader(filePath, StandardCharsets.UTF_8))
|
||||
{
|
||||
return new JsonParser().parse(new JsonReader(reader));
|
||||
}
|
||||
catch (JsonParseException e)
|
||||
{
|
||||
Throwable exc = e;
|
||||
if(e.getCause() != null)
|
||||
exc = e.getCause();
|
||||
throw new ThrowStatement(MapValue.wrap(ImmutableMap.of(
|
||||
StringValue.of("error"), StringValue.of(exc.getMessage()),
|
||||
StringValue.of("path"), StringValue.of(filePath.toString())
|
||||
)), Throwables.JSON_ERROR);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
throw new ThrowStatement("Failed to read json file content "+filePath.toString(), Throwables.IO_EXCEPTION);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,33 +1,10 @@
|
||||
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;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
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;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonParser;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
|
||||
public class FileModule extends Module
|
||||
{
|
||||
@@ -66,149 +43,4 @@ public class FileModule extends Module
|
||||
return library;
|
||||
}
|
||||
|
||||
//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
|
||||
{
|
||||
return NbtIo.readCompressed(file);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
// Copy of NbtIo.read(File) because that's now client-side only
|
||||
if (!file.exists())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
try (DataInputStream in = new DataInputStream(new FileInputStream(file)))
|
||||
{
|
||||
return NbtIo.read(in);
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//copied private method from net.minecraft.nbt.NbtIo.write() and client method safe_write
|
||||
public static boolean write(Tag tag_1, File file)
|
||||
{
|
||||
File file_2 = new File(file.getAbsolutePath() + "_tmp");
|
||||
if (file_2.exists()) file_2.delete();
|
||||
|
||||
if (tag_1 instanceof CompoundTag)
|
||||
{
|
||||
try
|
||||
{
|
||||
NbtIo.writeCompressed((CompoundTag) tag_1, file_2);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
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);
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean appendText(Path filePath, boolean addNewLines, List<String> data)
|
||||
{
|
||||
try
|
||||
{
|
||||
OutputStream out = Files.newOutputStream(filePath, StandardOpenOption.APPEND, StandardOpenOption.CREATE);
|
||||
try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out, StandardCharsets.UTF_8)))
|
||||
{
|
||||
for (String line: data)
|
||||
{
|
||||
writer.append(line);
|
||||
if (addNewLines) writer.newLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public static List<String> listFileContent(Path filePath)
|
||||
{
|
||||
try (BufferedReader reader = Files.newBufferedReader(filePath, StandardCharsets.UTF_8)) {
|
||||
List<String> result = new ArrayList<>();
|
||||
for (;;) {
|
||||
String line = reader.readLine();
|
||||
if (line == null)
|
||||
break;
|
||||
result.add(line.replaceAll("[\n\r]+",""));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static JsonElement readJsonContent(Path filePath)
|
||||
{
|
||||
try (BufferedReader reader = Files.newBufferedReader(filePath, StandardCharsets.UTF_8))
|
||||
{
|
||||
return new JsonParser().parse(new JsonReader(reader));
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,112 +1,39 @@
|
||||
package carpet.script.bundled;
|
||||
|
||||
import carpet.CarpetServer;
|
||||
import carpet.script.argument.FileArgument;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.util.WorldSavePath;
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.gson.JsonElement;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public abstract class Module
|
||||
{
|
||||
public abstract String getName();
|
||||
public abstract String getCode();
|
||||
public abstract boolean isLibrary();
|
||||
public final static Object writeIOSync = new Object();
|
||||
public final static Map<String, String> supportedTypes = ImmutableMap.of(
|
||||
"raw", ".txt",
|
||||
"text", ".txt",
|
||||
"nbt", ".nbt",
|
||||
"json", ".json",
|
||||
"folder", "folder"
|
||||
);
|
||||
|
||||
private static String getDescriptor(Module module, String file, boolean isShared)
|
||||
public static Tag getData(Module module)
|
||||
{
|
||||
if (isShared)
|
||||
{
|
||||
return file.isEmpty()?"shared":"shared/"+file;
|
||||
}
|
||||
if (module != null && module.getName() != null) // appdata
|
||||
{
|
||||
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)
|
||||
{
|
||||
Path dataFile = resolveResource(module, file, ".nbt", isShared);
|
||||
Path dataFile = resolveResource(module);
|
||||
if (dataFile == null) return null;
|
||||
if (!Files.exists(dataFile) || !(dataFile.toFile().isFile())) return null;
|
||||
synchronized (writeIOSync) { return FileModule.read(dataFile.toFile()); }
|
||||
synchronized (FileArgument.writeIOSync) { return FileArgument.readTag(dataFile); }
|
||||
}
|
||||
|
||||
public static boolean saveData(Module module, String file, Tag globalState, boolean isShared)
|
||||
public static void saveData(Module module, Tag globalState)
|
||||
{
|
||||
Path dataFile = resolveResource(module, file, ".nbt", isShared);
|
||||
if (dataFile == null) return false;
|
||||
if (!Files.exists(dataFile.getParent()) && !dataFile.toFile().getParentFile().mkdirs()) return false;
|
||||
synchronized (writeIOSync) { return FileModule.write(globalState, dataFile.toFile()); }
|
||||
Path dataFile = resolveResource(module);
|
||||
if (dataFile == null) return;
|
||||
if (!Files.exists(dataFile.getParent()) && !dataFile.toFile().getParentFile().mkdirs()) return;
|
||||
synchronized (FileArgument.writeIOSync) { FileArgument.writeTagDisk(globalState, dataFile, false); }
|
||||
}
|
||||
|
||||
public static boolean appendToTextFile(Module module, String resourceName, String type, boolean isShared, List<String> message)
|
||||
private static Path resolveResource(Module module)
|
||||
{
|
||||
Path dataFile = resolveResource(module, resourceName, supportedTypes.get(type), isShared);
|
||||
if (dataFile == null) return false;
|
||||
if (!Files.exists(dataFile.getParent()) && !dataFile.toFile().getParentFile().mkdirs()) return false;
|
||||
synchronized (writeIOSync) { return FileModule.appendText(dataFile, type.equals("text"), message); }
|
||||
}
|
||||
|
||||
public static boolean dropExistingFile(Module module, String resourceName, String type, boolean isShared)
|
||||
{
|
||||
Path dataFile = resolveResource(module, resourceName, supportedTypes.get(type), isShared);
|
||||
if (dataFile == null) return false;
|
||||
synchronized (writeIOSync) { return dataFile.toFile().delete(); }
|
||||
}
|
||||
|
||||
public static List<String> listFile(Module module, String resourceName, String type, boolean isShared)
|
||||
{
|
||||
Path dataFile = resolveResource(module, resourceName, supportedTypes.get(type), isShared);
|
||||
if (dataFile == null) return null;
|
||||
if (!dataFile.toFile().exists()) return null;
|
||||
synchronized (writeIOSync) { return FileModule.listFileContent(dataFile); }
|
||||
}
|
||||
|
||||
public static JsonElement readJsonFile(Module module, String resourceName, String type, boolean isShared) {
|
||||
Path dataFile = resolveResource(module, resourceName, supportedTypes.get(type), isShared);
|
||||
if (dataFile == null) return null;
|
||||
if (!dataFile.toFile().exists()) return null;
|
||||
synchronized (writeIOSync) { return FileModule.readJsonContent(dataFile); }
|
||||
}
|
||||
|
||||
public static Stream<String> listFolder(Module module, String resourceName, String type, boolean isShared)
|
||||
{
|
||||
Path dir = resolveResource(module, resourceName, null, isShared);
|
||||
String ext = supportedTypes.get(type);
|
||||
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==null?"":ext));
|
||||
if (module == null || module.getName() == null) return null; // commandline app
|
||||
return CarpetServer.minecraft_server.getSavePath(WorldSavePath.ROOT).resolve("scripts/"+module.getName()+".data.nbt");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package carpet.script.command;
|
||||
|
||||
import carpet.CarpetServer;
|
||||
import carpet.fakes.BlockStateArgumentInterface;
|
||||
import carpet.script.CarpetScriptHost;
|
||||
import carpet.script.argument.FunctionArgument;
|
||||
@@ -14,6 +15,7 @@ import carpet.script.value.NumericValue;
|
||||
import carpet.script.value.StringValue;
|
||||
import carpet.script.value.Value;
|
||||
import carpet.script.value.ValueConversions;
|
||||
import carpet.utils.CarpetProfiler;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
import com.mojang.brigadier.arguments.ArgumentType;
|
||||
@@ -276,7 +278,12 @@ public abstract class CommandArgument
|
||||
{
|
||||
CommandArgument arg = getTypeForArgument(param, host);
|
||||
if (arg.suggestionProvider != null) return argument(param, arg.getArgumentType()).suggests(arg.suggestionProvider);
|
||||
return arg.needsMatching? argument(param, arg.getArgumentType()).suggests((c, b) -> arg.suggest(c, b, host)) : argument(param, arg.getArgumentType());
|
||||
if (!arg.needsMatching) return argument(param, arg.getArgumentType());
|
||||
String hostName = host.getName();
|
||||
return argument(param, arg.getArgumentType()).suggests((ctx, b) -> {
|
||||
CarpetScriptHost cHost = CarpetServer.scriptServer.modules.get(hostName).retrieveOwnForExecution(ctx.getSource());
|
||||
return arg.suggest(ctx, b, cHost);
|
||||
});
|
||||
}
|
||||
|
||||
protected String suffix;
|
||||
@@ -365,6 +372,7 @@ public abstract class CommandArgument
|
||||
{
|
||||
if (customSuggester != null)
|
||||
{
|
||||
CarpetProfiler.ProfilerToken currentSection = CarpetProfiler.start_section(null, "Scarpet command", CarpetProfiler.TYPE.GENERAL);
|
||||
Map<Value, Value> params = new HashMap<>();
|
||||
for(ParsedCommandNode<ServerCommandSource> pnode : context.getNodes())
|
||||
{
|
||||
@@ -379,7 +387,9 @@ public abstract class CommandArgument
|
||||
args.addAll(customSuggester.args);
|
||||
Value response = host.handleCommand(context.getSource(), customSuggester.function, args);
|
||||
if (!(response instanceof ListValue)) throw error("Custom suggester should return a list of options"+" for custom type "+suffix);
|
||||
return ((ListValue) response).getItems().stream().map(Value::getString).collect(Collectors.toList());
|
||||
Collection<String> res = ((ListValue) response).getItems().stream().map(Value::getString).collect(Collectors.toList());
|
||||
CarpetProfiler.end_current_section(currentSection);
|
||||
return res;
|
||||
}
|
||||
if (needsMatching) return examples;
|
||||
//return Lists.newArrayList("");
|
||||
|
||||
@@ -7,29 +7,43 @@ import carpet.script.Tokenizer;
|
||||
import carpet.script.value.FunctionValue;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/* The expression evaluators exception class. */
|
||||
public class ExpressionException extends RuntimeException implements ResolvedException
|
||||
{
|
||||
public final Context context;
|
||||
public final Tokenizer.Token token;
|
||||
public final List<FunctionValue> stack = new ArrayList<>();
|
||||
private final Supplier<String> lazyMessage;
|
||||
private String cachedMessage = null;
|
||||
|
||||
public ExpressionException(Context c, Expression e, String message)
|
||||
{
|
||||
super(makeMessage(c, e, null, message));
|
||||
context = c;
|
||||
this(c, e, Tokenizer.Token.NONE, message);
|
||||
}
|
||||
|
||||
public ExpressionException(Context c, Expression e, Tokenizer.Token t, String message)
|
||||
{
|
||||
super(makeMessage(c, e, t, message));
|
||||
context = c;
|
||||
this(c, e, t, message, Collections.emptyList());
|
||||
}
|
||||
public ExpressionException(Context c, Expression e, Tokenizer.Token t, String message, List<FunctionValue> stack)
|
||||
{
|
||||
super(makeMessage(c, e, t, message));
|
||||
super("Error");
|
||||
this.stack.addAll(stack);
|
||||
lazyMessage = () -> makeMessage(c, e, t, message);
|
||||
token = t;
|
||||
context = c;
|
||||
}
|
||||
|
||||
public ExpressionException(Context c, Expression e, Tokenizer.Token t, Supplier<String> messageSupplier, List<FunctionValue> stack)
|
||||
{
|
||||
super("Error");
|
||||
this.stack.addAll(stack);
|
||||
lazyMessage = () -> makeMessage(c, e, t, messageSupplier.get());
|
||||
token = t;
|
||||
context = c;
|
||||
}
|
||||
|
||||
@@ -68,4 +82,13 @@ public class ExpressionException extends RuntimeException implements ResolvedExc
|
||||
}
|
||||
return String.join("\n", errorMaker.apply(e, t, message));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
if (cachedMessage == null)
|
||||
{
|
||||
cachedMessage = lazyMessage.get();
|
||||
}
|
||||
return cachedMessage;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
package carpet.script.exception;
|
||||
|
||||
import carpet.script.Context;
|
||||
import carpet.script.Expression;
|
||||
import carpet.script.Tokenizer;
|
||||
import carpet.script.value.FunctionValue;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -13,4 +16,20 @@ public class InternalExpressionException extends RuntimeException
|
||||
{
|
||||
super(message);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Promotes this simple exception into one with context and extra information.
|
||||
*
|
||||
* <p>Provides a cleaner way of handling similar exceptions, in this case
|
||||
* {@link InternalExpressionException} and {@link ThrowStatement}
|
||||
* @param c Context
|
||||
* @param e Expression
|
||||
* @param token Token
|
||||
* @return The new {@link ExpressionException} (or {@link ProcessedThrowStatement}),
|
||||
* depending on the implementation.
|
||||
*/
|
||||
public ExpressionException promote(Context c, Expression e, Tokenizer.Token token)
|
||||
{
|
||||
return new ExpressionException(c, e, token, getMessage(), stack);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package carpet.script.exception;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import carpet.script.Context;
|
||||
import carpet.script.Expression;
|
||||
import carpet.script.Tokenizer.Token;
|
||||
import carpet.script.value.FunctionValue;
|
||||
import carpet.script.value.Value;
|
||||
|
||||
public class ProcessedThrowStatement extends ExpressionException {
|
||||
public final Throwables thrownExceptionType;
|
||||
public final Value data;
|
||||
|
||||
public ProcessedThrowStatement(Context c, Expression e, Token token, List<FunctionValue> stack, Throwables thrownExceptionType, Value data) {
|
||||
super(c, e, token, () -> "Unhandled "+thrownExceptionType.getId()+" exception: "+data.getString(), stack);
|
||||
this.thrownExceptionType = thrownExceptionType;
|
||||
this.data = data;
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,54 @@
|
||||
package carpet.script.exception;
|
||||
|
||||
import carpet.script.Context;
|
||||
import carpet.script.Expression;
|
||||
import carpet.script.Tokenizer.Token;
|
||||
import carpet.script.value.StringValue;
|
||||
import carpet.script.value.Value;
|
||||
|
||||
public class ThrowStatement extends ExitStatement
|
||||
public class ThrowStatement extends InternalExpressionException
|
||||
{
|
||||
public ThrowStatement(Value value)
|
||||
private final Throwables type;
|
||||
private final Value data;
|
||||
|
||||
/**
|
||||
* Creates a throw exception from a value, and assigns it a specified message.
|
||||
* <p>To be used when throwing from Scarpet's {@code throw} function
|
||||
* @param data The value to pass
|
||||
* @param type Exception type
|
||||
*/
|
||||
public ThrowStatement(Value data, Throwables type)
|
||||
{
|
||||
super(value);
|
||||
super(type.getId());
|
||||
this.data = data;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public ThrowStatement(Value data, Throwables parent, String subtype)
|
||||
{
|
||||
super(subtype);
|
||||
this.data = data;
|
||||
this.type = new Throwables(subtype, parent);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a throw exception.<br>
|
||||
* Conveniently creates a value from the {@code value} String
|
||||
* to be used easily in Java code
|
||||
* @param message The message to display when not handled
|
||||
* @param type An {@link Throwables} containing the inheritance data
|
||||
* for this exception. When throwing from Java,
|
||||
* those exceptions should be pre-registered.
|
||||
*/
|
||||
public ThrowStatement(String message, Throwables type)
|
||||
{
|
||||
super(type.getId());
|
||||
this.data = StringValue.of(message);
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionException promote(Context c, Expression e, Token token) {
|
||||
return new ProcessedThrowStatement(c, e, token, stack, type, data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package carpet.script.exception;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* This class contains default Scarpet catchable types, as well as their inheritance and
|
||||
* methods to check whether filters are compatible with those.
|
||||
*/
|
||||
public class Throwables {
|
||||
private final String id;
|
||||
private final Throwables parent;
|
||||
|
||||
private static final Map<String, Throwables> byId = new HashMap<>();
|
||||
|
||||
public static final Throwables THROWN_EXCEPTION_TYPE = register("exception", null);
|
||||
public static final Throwables VALUE_EXCEPTION = register("value_exception", THROWN_EXCEPTION_TYPE);
|
||||
public static final Throwables UNKNOWN_ITEM = register("unknown_item", VALUE_EXCEPTION);
|
||||
public static final Throwables UNKNOWN_BLOCK = register("unknown_block", VALUE_EXCEPTION);
|
||||
public static final Throwables UNKNOWN_BIOME = register("unknown_biome", VALUE_EXCEPTION);
|
||||
public static final Throwables UNKNOWN_SOUND = register("unknown_sound", VALUE_EXCEPTION);
|
||||
public static final Throwables UNKNOWN_PARTICLE = register("unknown_particle", VALUE_EXCEPTION);
|
||||
public static final Throwables UNKNOWN_POI = register("unknown_poi", VALUE_EXCEPTION);
|
||||
public static final Throwables UNKNOWN_DIMENSION = register("unknown_dimension", VALUE_EXCEPTION);
|
||||
public static final Throwables UNKNOWN_STRUCTURE = register("unknown_structure", VALUE_EXCEPTION);
|
||||
public static final Throwables UNKNOWN_CRITERION = register("unknown_criterion", VALUE_EXCEPTION);
|
||||
public static final Throwables IO_EXCEPTION = register("io_exception", THROWN_EXCEPTION_TYPE);
|
||||
public static final Throwables NBT_ERROR = register("nbt_error", IO_EXCEPTION);
|
||||
public static final Throwables JSON_ERROR = register("json_error", IO_EXCEPTION);
|
||||
public static final Throwables USER_DEFINED = register("user_exception", THROWN_EXCEPTION_TYPE);
|
||||
|
||||
/**
|
||||
* Creates an exception and registers it to be used as parent for
|
||||
* user defined exceptions in Scarpet's throw function.
|
||||
* <p>Scarpet exceptions should have a top-level parent being {@link Throwables#THROWN_EXCEPTION_TYPE}
|
||||
* @param id The value for the exception as a {@link String}.
|
||||
* @param parent The parent of the exception being created, or <code>null</code> if top-level
|
||||
* @return The created exception
|
||||
*/
|
||||
public static Throwables register(String id, Throwables parent)
|
||||
{
|
||||
Throwables exc = new Throwables(id, parent);
|
||||
byId.put(id, exc);
|
||||
return exc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new exception.
|
||||
* <p>Not suitable for creating exceptions that can't be caught.
|
||||
* Use an {@link InternalExpressionException} for that
|
||||
* @param id The exception's value as a {@link String}
|
||||
*/
|
||||
public Throwables(String id, Throwables parent)
|
||||
{
|
||||
this.id = id;
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public static Throwables getTypeForException(String type)
|
||||
{
|
||||
Throwables properType = byId.get(type);
|
||||
if (properType == null) throw new InternalExpressionException("Unknown exception type: "+type);
|
||||
return properType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether the given filter matches an instance of this exception, by checking equality
|
||||
* with itself and possible parents.
|
||||
* @param filter The type to check against
|
||||
* @return Whether or not the given value matches this exception's hierarchy
|
||||
*/
|
||||
public boolean isRelevantFor(String filter)
|
||||
{
|
||||
return (id.equals(filter) || (parent != null && parent.isRelevantFor(filter)));
|
||||
}
|
||||
|
||||
public boolean isUserException()
|
||||
{
|
||||
return this == USER_DEFINED || parent == USER_DEFINED;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the throwable type
|
||||
* @return The type of this exception
|
||||
*/
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
@@ -60,9 +60,9 @@ public class Arithmetic {
|
||||
expression.addMathematicalUnaryFunction("log1p", x -> Math.log1p(x)/Math.log(2));
|
||||
expression.addMathematicalUnaryFunction("sqrt", Math::sqrt);
|
||||
expression.addMathematicalUnaryFunction("abs", Math::abs);
|
||||
expression.addMathematicalUnaryFunction("round", (d) -> (double)Math.round(d));
|
||||
expression.addMathematicalUnaryFunction("floor", Math::floor);
|
||||
expression.addMathematicalUnaryFunction("ceil", Math::ceil);
|
||||
expression.addMathematicalUnaryIntFunction("round", Math::round);
|
||||
expression.addMathematicalUnaryIntFunction("floor", n -> (long)Math.floor(n));
|
||||
expression.addMathematicalUnaryIntFunction("ceil", n -> (long)Math.ceil(n));
|
||||
|
||||
expression.addLazyFunction("mandelbrot", 3, (c, t, lv) -> {
|
||||
double a0 = NumericValue.asNumber(lv.get(0).evalValue(c)).getDouble();
|
||||
|
||||
@@ -5,8 +5,17 @@ import carpet.script.Expression;
|
||||
import carpet.script.LazyValue;
|
||||
import carpet.script.exception.ExitStatement;
|
||||
import carpet.script.exception.InternalExpressionException;
|
||||
import carpet.script.exception.ProcessedThrowStatement;
|
||||
import carpet.script.exception.ThrowStatement;
|
||||
import carpet.script.exception.Throwables;
|
||||
import carpet.script.value.ListValue;
|
||||
import carpet.script.value.MapValue;
|
||||
import carpet.script.value.NumericValue;
|
||||
import carpet.script.value.StringValue;
|
||||
import carpet.script.value.Value;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class ControlFlow {
|
||||
public static void apply(Expression expression) // public just to get the javadoc right
|
||||
@@ -42,26 +51,98 @@ public class ControlFlow {
|
||||
|
||||
expression.addFunction("exit", (lv) -> { throw new ExitStatement(lv.size()==0?Value.NULL:lv.get(0)); });
|
||||
|
||||
expression.addFunction("throw", (lv)-> {throw new ThrowStatement(lv.size()==0?Value.NULL:lv.get(0)); });
|
||||
expression.addLazyFunction("throw", -1, (c, t, lv)->
|
||||
{
|
||||
switch (lv.size())
|
||||
{
|
||||
case 0:
|
||||
throw new ThrowStatement(Value.NULL, Throwables.USER_DEFINED);
|
||||
case 1:
|
||||
throw new ThrowStatement(lv.get(0).evalValue(c), Throwables.USER_DEFINED );
|
||||
case 2:
|
||||
throw new ThrowStatement(lv.get(1).evalValue(c), Throwables.getTypeForException(lv.get(0).evalValue(c).getString()));
|
||||
case 3:
|
||||
throw new ThrowStatement(lv.get(2).evalValue(c), Throwables.getTypeForException(lv.get(1).evalValue(c).getString()), lv.get(0).evalValue(c).getString());
|
||||
default:
|
||||
throw new InternalExpressionException("throw() can't accept more than 3 parameters");
|
||||
}
|
||||
});
|
||||
|
||||
expression.addLazyFunction("try", -1, (c, t, lv) ->
|
||||
{
|
||||
if (lv.size()==0)
|
||||
throw new InternalExpressionException("'try' needs at least an expression block");
|
||||
throw new InternalExpressionException("'try' needs at least an expression block, and either a catch_epr, or a number of pairs of filters and catch_expr");
|
||||
try
|
||||
{
|
||||
Value retval = lv.get(0).evalValue(c, t);
|
||||
return (c_, t_) -> retval;
|
||||
}
|
||||
catch (ThrowStatement ret)
|
||||
catch (ProcessedThrowStatement ret)
|
||||
{
|
||||
if (lv.size() == 1)
|
||||
{
|
||||
if (!ret.thrownExceptionType.isUserException())
|
||||
throw ret;
|
||||
return (c_, t_) -> Value.NULL;
|
||||
}
|
||||
if (lv.size() > 3 && lv.size() % 2 == 0)
|
||||
{
|
||||
throw new InternalExpressionException("Try-catch block needs the code to run, and either a catch expression for user thrown exceptions, or a number of pairs of filters and catch expressions");
|
||||
}
|
||||
|
||||
Value val = null; // This is always assigned at some point, just the compiler doesn't know
|
||||
|
||||
LazyValue __ = c.getVariable("_");
|
||||
c.setVariable("_", (__c, __t) -> ret.retval.reboundedTo("_"));
|
||||
Value val = lv.get(1).evalValue(c, t);
|
||||
c.setVariable("_",__);
|
||||
return (c_, t_) -> val;
|
||||
c.setVariable("_", (__c, __t) -> ret.data.reboundedTo("_"));
|
||||
LazyValue _trace = c.getVariable("_trace");
|
||||
c.setVariable("_trace", (__c, __t) -> MapValue.wrap(ImmutableMap.of(
|
||||
StringValue.of("stack"), ListValue.wrap(ret.stack.stream().map(f -> ListValue.of(
|
||||
StringValue.of(f.getModule().getName()),
|
||||
StringValue.of(f.getString()),
|
||||
NumericValue.of(f.getToken().lineno+1),
|
||||
NumericValue.of(f.getToken().linepos+1)
|
||||
)).collect(Collectors.toList())),
|
||||
|
||||
StringValue.of("locals"), MapValue.wrap(ret.context.variables.entrySet().stream().filter(e -> !e.getKey().equals("_trace")).collect(Collectors.toMap(
|
||||
e -> StringValue.of(e.getKey()),
|
||||
e -> e.getValue().evalValue(ret.context)
|
||||
))),
|
||||
StringValue.of("token"), ListValue.of(
|
||||
StringValue.of(ret.token.surface),
|
||||
NumericValue.of(ret.token.lineno+1),
|
||||
NumericValue.of(ret.token.linepos+1)
|
||||
)
|
||||
)));
|
||||
|
||||
if (lv.size() == 2)
|
||||
{
|
||||
if (ret.thrownExceptionType.isUserException())
|
||||
val = lv.get(1).evalValue(c, t);
|
||||
}
|
||||
else
|
||||
{
|
||||
int pointer = 1;
|
||||
while (pointer < lv.size() -1)
|
||||
{
|
||||
if (ret.thrownExceptionType.isRelevantFor(lv.get(pointer).evalValue(c).getString()))
|
||||
{
|
||||
val = lv.get(pointer + 1).evalValue(c, t);
|
||||
break;
|
||||
}
|
||||
pointer += 2;
|
||||
}
|
||||
}
|
||||
c.setVariable("_", __);
|
||||
if (_trace != null)
|
||||
c.setVariable("_trace", _trace);
|
||||
else
|
||||
c.delVariable("_trace");
|
||||
if (val == null) // not handled
|
||||
{
|
||||
throw ret;
|
||||
}
|
||||
Value retval = val;
|
||||
return (c_, t_) -> retval;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -119,7 +119,7 @@ public class Loops {
|
||||
c.setVariable("_i", (cc, tt) -> new NumericValue(doYouReally).bindTo("_i"));
|
||||
try
|
||||
{
|
||||
result.add(expr.evalValue(c));
|
||||
result.add(expr.evalValue(c, t));
|
||||
}
|
||||
catch (BreakStatement | ContinueStatement stmt)
|
||||
{
|
||||
|
||||
@@ -48,7 +48,7 @@ public class Sys {
|
||||
return (cc, tt) -> Value.FALSE;
|
||||
}
|
||||
}
|
||||
Value retval = new NumericValue(v.getBoolean());
|
||||
Value retval = v.getBoolean()?Value.TRUE:Value.FALSE;
|
||||
return (cc, tt) -> retval;
|
||||
});
|
||||
|
||||
@@ -56,7 +56,9 @@ public class Sys {
|
||||
{
|
||||
if (v instanceof NumericValue)
|
||||
{
|
||||
return v;
|
||||
NumericValue num = (NumericValue)v;
|
||||
if (num.isInteger()) return new NumericValue(num.getLong());
|
||||
return new NumericValue(num.getDouble());
|
||||
}
|
||||
try
|
||||
{
|
||||
@@ -208,6 +210,10 @@ public class Sys {
|
||||
Value retval = new NumericValue(NumericValue.asNumber(argument).getDouble()*randomizer.nextDouble());
|
||||
return (cc, tt) -> retval;
|
||||
});
|
||||
expression.addLazyFunction("reset_seed", 1, (c, t, lv) -> {
|
||||
boolean gotIt = c.host.resetRandom(NumericValue.asNumber(lv.get(0).evalValue(c)).getLong());
|
||||
return gotIt?LazyValue.TRUE:LazyValue.FALSE;
|
||||
});
|
||||
|
||||
expression.addLazyFunction("perlin", -1, (c, t, lv) ->
|
||||
{
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package carpet.script.utils;
|
||||
|
||||
import carpet.script.exception.InternalExpressionException;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
public class InputValidator {
|
||||
public static String validateSimpleString(String input, boolean strict)
|
||||
{
|
||||
String simplified = input.toLowerCase(Locale.ROOT).replaceAll("[^A-Za-z0-9+_]", "");
|
||||
if (simplified.isEmpty() || (strict && !simplified.equals(input)))
|
||||
throw new InternalExpressionException("simple name can only contain numbers, letter and _");
|
||||
return simplified;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,6 +4,8 @@ import carpet.CarpetSettings;
|
||||
import carpet.network.ServerNetworkHandler;
|
||||
import carpet.script.CarpetContext;
|
||||
import carpet.script.exception.InternalExpressionException;
|
||||
import carpet.script.exception.ThrowStatement;
|
||||
import carpet.script.exception.Throwables;
|
||||
import carpet.script.language.Sys;
|
||||
import carpet.script.value.BlockValue;
|
||||
import carpet.script.value.EntityValue;
|
||||
@@ -156,7 +158,7 @@ public class ShapeDispatcher
|
||||
}
|
||||
catch (CommandSyntaxException e)
|
||||
{
|
||||
throw new InternalExpressionException("No such particle: "+name);
|
||||
throw new ThrowStatement(name, Throwables.UNKNOWN_PARTICLE);
|
||||
}
|
||||
particleCache.put(name, particle);
|
||||
return particle;
|
||||
@@ -416,6 +418,8 @@ public class ShapeDispatcher
|
||||
put("facing", new StringValue("player")).
|
||||
put("raise", new NumericValue(0)).
|
||||
put("tilt", new NumericValue(0)).
|
||||
put("lean", new NumericValue(0)).
|
||||
put("turn", new NumericValue(0)).
|
||||
put("indent", new NumericValue(0)).
|
||||
put("height", new NumericValue(0)).
|
||||
put("align", new StringValue("center")).
|
||||
@@ -437,6 +441,8 @@ public class ShapeDispatcher
|
||||
Direction facing;
|
||||
float raise;
|
||||
float tilt;
|
||||
float lean;
|
||||
float turn;
|
||||
float size;
|
||||
float indent;
|
||||
int align;
|
||||
@@ -485,6 +491,8 @@ public class ShapeDispatcher
|
||||
|
||||
raise = NumericValue.asNumber(options.getOrDefault("raise", optional.get("raise"))).getFloat();
|
||||
tilt = NumericValue.asNumber(options.getOrDefault("tilt", optional.get("tilt"))).getFloat();
|
||||
lean = NumericValue.asNumber(options.getOrDefault("lean", optional.get("lean"))).getFloat();
|
||||
turn = NumericValue.asNumber(options.getOrDefault("turn", optional.get("turn"))).getFloat();
|
||||
indent = NumericValue.asNumber(options.getOrDefault("indent", optional.get("indent"))).getFloat();
|
||||
height = NumericValue.asNumber(options.getOrDefault("height", optional.get("height"))).getFloat();
|
||||
|
||||
@@ -517,6 +525,8 @@ public class ShapeDispatcher
|
||||
if (facing!= null) hash ^= facing.hashCode(); hash *= 1099511628211L;
|
||||
hash ^= Float.hashCode(raise); hash *= 1099511628211L;
|
||||
hash ^= Float.hashCode(tilt); hash *= 1099511628211L;
|
||||
hash ^= Float.hashCode(lean); hash *= 1099511628211L;
|
||||
hash ^= Float.hashCode(turn); hash *= 1099511628211L;
|
||||
hash ^= Float.hashCode(indent); hash *= 1099511628211L;
|
||||
hash ^= Float.hashCode(height); hash *= 1099511628211L;
|
||||
hash ^= Float.hashCode(size); hash *= 1099511628211L;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package carpet.script.utils;
|
||||
|
||||
import carpet.CarpetSettings;
|
||||
import carpet.utils.CarpetProfiler;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
import it.unimi.dsi.fastutil.longs.Long2ObjectOpenHashMap;
|
||||
import net.minecraft.client.MinecraftClient;
|
||||
@@ -49,6 +50,7 @@ public class ShapesRenderer
|
||||
|
||||
public void render(Camera camera, float partialTick)
|
||||
{
|
||||
CarpetProfiler.ProfilerToken token = CarpetProfiler.start_section(null, "Scarpet client", CarpetProfiler.TYPE.GENERAL);
|
||||
//Camera camera = this.client.gameRenderer.getCamera();
|
||||
ClientWorld iWorld = this.client.world;
|
||||
RegistryKey<World> dimensionType = iWorld.getRegistryKey();
|
||||
@@ -120,14 +122,17 @@ public class ShapesRenderer
|
||||
RenderSystem.defaultBlendFunc();
|
||||
RenderSystem.enableTexture();
|
||||
RenderSystem.shadeModel(7424);
|
||||
CarpetProfiler.end_current_section(token);
|
||||
}
|
||||
|
||||
public void addShapes(ListTag tag)
|
||||
{
|
||||
CarpetProfiler.ProfilerToken token = CarpetProfiler.start_section(null, "Scarpet client", CarpetProfiler.TYPE.GENERAL);
|
||||
for (int i=0, count = tag.size(); i < count; i++)
|
||||
{
|
||||
addShape(tag.getCompound(i));
|
||||
}
|
||||
CarpetProfiler.end_current_section(token);
|
||||
}
|
||||
|
||||
public void addShape(CompoundTag tag)
|
||||
@@ -169,12 +174,14 @@ public class ShapesRenderer
|
||||
|
||||
public void renewShapes()
|
||||
{
|
||||
CarpetProfiler.ProfilerToken token = CarpetProfiler.start_section(null, "Scarpet client", CarpetProfiler.TYPE.GENERAL);
|
||||
synchronized (shapes)
|
||||
{
|
||||
shapes.values().forEach(el -> el.values().forEach(shape -> {
|
||||
shape.expiryTick++;
|
||||
}));
|
||||
}
|
||||
CarpetProfiler.end_current_section(token);
|
||||
}
|
||||
|
||||
public abstract static class RenderedShape<T extends ShapeDispatcher.ExpiringShape>
|
||||
@@ -278,6 +285,14 @@ public class ShapesRenderer
|
||||
{
|
||||
RenderSystem.rotatef(shape.tilt, 0.0f, 0.0f, 1.0f);
|
||||
}
|
||||
if (shape.lean!=0.0f)
|
||||
{
|
||||
RenderSystem.rotatef(shape.lean, 1.0f, 0.0f, 0.0f);
|
||||
}
|
||||
if (shape.turn!=0.0f)
|
||||
{
|
||||
RenderSystem.rotatef(shape.turn, 0.0f, 1.0f, 0.0f);
|
||||
}
|
||||
RenderSystem.translatef(-10*shape.indent, -10*shape.height-9, (float) (-10*renderEpsilon)-10*shape.raise);
|
||||
//if (visibleThroughWalls) RenderSystem.disableDepthTest();
|
||||
RenderSystem.scalef(-1.0F, 1.0F, 1.0F);
|
||||
|
||||
@@ -4,11 +4,13 @@ import carpet.CarpetServer;
|
||||
import carpet.CarpetSettings;
|
||||
import carpet.script.CarpetContext;
|
||||
import carpet.script.CarpetScriptHost;
|
||||
import carpet.script.value.BooleanValue;
|
||||
import carpet.script.value.ListValue;
|
||||
import carpet.script.value.MapValue;
|
||||
import carpet.script.value.NumericValue;
|
||||
import carpet.script.value.StringValue;
|
||||
import carpet.script.value.Value;
|
||||
import carpet.script.value.ValueConversions;
|
||||
import carpet.settings.ParsedRule;
|
||||
import carpet.settings.SettingsManager;
|
||||
import com.sun.management.OperatingSystemMXBean;
|
||||
@@ -16,6 +18,7 @@ import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.minecraft.SharedConstants;
|
||||
import net.minecraft.util.WorldSavePath;
|
||||
import net.minecraft.world.GameRules;
|
||||
import net.minecraft.world.WorldProperties;
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.nio.file.Path;
|
||||
@@ -49,6 +52,12 @@ public class SystemInfo {
|
||||
String tlf = serverPath.getName(nodeCount-2).toString();
|
||||
return StringValue.of(tlf);
|
||||
});
|
||||
put("world_dimensions", c -> ListValue.wrap(c.s.getMinecraftServer().getWorldRegistryKeys().stream().map(k -> ValueConversions.of(k.getValue())).collect(Collectors.toList())));
|
||||
put("world_spawn_point", c -> {
|
||||
WorldProperties prop = c.s.getMinecraftServer().getOverworld().getLevelProperties();
|
||||
return ListValue.of(NumericValue.of(prop.getSpawnX()), NumericValue.of(prop.getSpawnY()), NumericValue.of(prop.getSpawnZ()));
|
||||
});
|
||||
|
||||
put("game_difficulty", c -> StringValue.of(c.s.getMinecraftServer().getSaveProperties().getDifficulty().getName()));
|
||||
put("game_hardcore", c -> new NumericValue(c.s.getMinecraftServer().getSaveProperties().isHardcore()));
|
||||
put("game_storage_format", c -> StringValue.of(c.s.getMinecraftServer().getSaveProperties().getFormatName(c.s.getMinecraftServer().getSaveProperties().getVersion())));
|
||||
@@ -57,7 +66,19 @@ public class SystemInfo {
|
||||
put("game_view_distance", c -> new NumericValue(c.s.getMinecraftServer().getPlayerManager().getViewDistance()));
|
||||
put("game_mod_name", c -> StringValue.of(c.s.getMinecraftServer().getServerModName()));
|
||||
put("game_version", c -> StringValue.of(c.s.getMinecraftServer().getVersion()));
|
||||
put("game_target", c -> StringValue.of(SharedConstants.getGameVersion().getReleaseTarget()));
|
||||
put("game_protocol", c -> NumericValue.of(SharedConstants.method_31372()));
|
||||
put("game_major_target", c -> {
|
||||
String [] vers = SharedConstants.getGameVersion().getReleaseTarget().split("\\.");
|
||||
return NumericValue.of((vers.length > 1)?Integer.parseInt(vers[1]):0);
|
||||
});
|
||||
put("game_minor_target", c -> {
|
||||
String [] vers = SharedConstants.getGameVersion().getReleaseTarget().split("\\.");
|
||||
return NumericValue.of((vers.length > 2)?Integer.parseInt(vers[2]):0);
|
||||
});
|
||||
put("game_stable", c -> BooleanValue.of(SharedConstants.getGameVersion().isStable()));
|
||||
put("game_data_version", c->NumericValue.of(SharedConstants.getGameVersion().getWorldVersion()));
|
||||
put("game_pack_version", c->NumericValue.of(SharedConstants.getGameVersion().getPackVersion()));
|
||||
|
||||
put("server_ip", c -> StringValue.of(c.s.getMinecraftServer().getServerIp()));
|
||||
put("server_whitelisted", c -> new NumericValue(c.s.getMinecraftServer().isEnforceWhitelist()));
|
||||
|
||||
@@ -157,5 +157,5 @@ public class WorldTools
|
||||
@Override public void stop() { }
|
||||
}
|
||||
|
||||
private static final WorldGenerationProgressListener NOOP_LISTENER = new NoopWorldGenerationProgressListener();
|
||||
public static final WorldGenerationProgressListener NOOP_LISTENER = new NoopWorldGenerationProgressListener();
|
||||
}
|
||||
@@ -2,6 +2,9 @@ package carpet.script.value;
|
||||
|
||||
import carpet.script.CarpetContext;
|
||||
import carpet.script.exception.InternalExpressionException;
|
||||
import carpet.script.exception.ThrowStatement;
|
||||
import carpet.script.exception.Throwables;
|
||||
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import net.minecraft.block.BlockState;
|
||||
@@ -73,7 +76,7 @@ public class BlockValue extends Value
|
||||
catch (CommandSyntaxException ignored)
|
||||
{
|
||||
}
|
||||
throw new InternalExpressionException("Cannot parse block: "+str);
|
||||
throw new ThrowStatement(str, Throwables.UNKNOWN_BLOCK);
|
||||
}
|
||||
|
||||
public static BlockPos locateBlockPos(CarpetContext c, int xpos, int ypos, int zpos)
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
package carpet.script.value;
|
||||
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import net.minecraft.nbt.ByteTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
|
||||
public class BooleanValue extends NumericValue
|
||||
{
|
||||
public static final BooleanValue FALSE = new BooleanValue(false);
|
||||
public static final BooleanValue TRUE = new BooleanValue(true);
|
||||
|
||||
boolean boolValue;
|
||||
private BooleanValue(boolean boolval) {
|
||||
super(boolval);
|
||||
boolValue = boolval;
|
||||
}
|
||||
|
||||
public static BooleanValue of(boolean value)
|
||||
{
|
||||
return value?TRUE:FALSE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString() {
|
||||
return boolValue?"true":"false";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrettyString() {
|
||||
return getString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTypeString() {
|
||||
return "bool";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value clone() {
|
||||
return new BooleanValue(boolValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Boolean.hashCode(boolValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tag toTag(boolean force) {
|
||||
return ByteTag.of(boolValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonElement toJson() {
|
||||
return new JsonPrimitive(boolValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInteger() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -56,6 +56,7 @@ import net.minecraft.server.command.ServerCommandSource;
|
||||
import net.minecraft.server.network.ServerPlayerEntity;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.text.LiteralText;
|
||||
import net.minecraft.util.Hand;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.hit.BlockHitResult;
|
||||
import net.minecraft.util.hit.EntityHitResult;
|
||||
@@ -98,6 +99,12 @@ public class EntityValue extends Value
|
||||
entity = e;
|
||||
}
|
||||
|
||||
public static Value of(Entity e)
|
||||
{
|
||||
if (e == null) return Value.NULL;
|
||||
return new EntityValue(e);
|
||||
}
|
||||
|
||||
private static final Map<String, EntitySelector> selectorCache = new HashMap<>();
|
||||
public static Collection<? extends Entity > getEntitiesFromSelector(ServerCommandSource source, String selector)
|
||||
{
|
||||
@@ -416,7 +423,7 @@ public class EntityValue extends Value
|
||||
put("motion_z", (e, a) -> new NumericValue(e.getVelocity().z));
|
||||
put("on_ground", (e, a) -> new NumericValue(e.isOnGround()));
|
||||
put("name", (e, a) -> new StringValue(e.getName().getString()));
|
||||
put("display_name", (e, a) -> new StringValue(e.getDisplayName().getString()));
|
||||
put("display_name", (e, a) -> new FormattedTextValue(e.getDisplayName()));
|
||||
put("command_name", (e, a) -> new StringValue(e.getEntityName()));
|
||||
put("custom_name", (e, a) -> e.hasCustomName()?new StringValue(e.getCustomName().getString()):Value.NULL);
|
||||
put("type", (e, a) -> new StringValue(nameFromRegistryId(Registry.ENTITY_TYPE.getId(e.getType()))));
|
||||
@@ -429,7 +436,7 @@ public class EntityValue extends Value
|
||||
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())));
|
||||
put("entity_tags", (e, a) -> ListValue.wrap(e.getServer().getTagManager().getEntityTypes().getTags().entrySet().stream().filter(entry -> entry.getValue().contains(e.getType())).map(entry -> ValueConversions.of(entry.getKey())).collect(Collectors.toList())));
|
||||
// deprecated
|
||||
put("has_tag", (e, a) -> new NumericValue(e.getScoreboardTags().contains(a.getString())));
|
||||
|
||||
@@ -1307,7 +1314,7 @@ public class EntityValue extends Value
|
||||
else if (v instanceof ListValue)
|
||||
{
|
||||
List<Value> lv = ((ListValue) v).getItems();
|
||||
if (lv.size() >= 1 && lv.size() <= 5)
|
||||
if (lv.size() >= 1 && lv.size() <= 6)
|
||||
{
|
||||
String effectName = lv.get(0).getString();
|
||||
StatusEffect effect = Registry.STATUS_EFFECT.get(new Identifier(effectName));
|
||||
@@ -1376,6 +1383,19 @@ public class EntityValue extends Value
|
||||
}
|
||||
});
|
||||
|
||||
put("swing", (e, v) -> {
|
||||
if (e instanceof LivingEntity)
|
||||
{
|
||||
Hand hand = Hand.MAIN_HAND;
|
||||
if (v != null)
|
||||
{
|
||||
String handString = v.getString().toLowerCase(Locale.ROOT);
|
||||
if (handString.equals("offhand") || handString.equals("off_hand")) hand = Hand.OFF_HAND;
|
||||
}
|
||||
((LivingEntity)e).swingHand(hand, true);
|
||||
}
|
||||
});
|
||||
|
||||
put("silent",(e,v)-> e.setSilent(v.getBoolean()));
|
||||
|
||||
put("gravity",(e,v)-> e.setNoGravity(!v.getBoolean()));
|
||||
|
||||
@@ -215,7 +215,6 @@ public class FunctionValue extends Value implements Fluff.ILazyFunction
|
||||
|
||||
}
|
||||
Value retVal;
|
||||
boolean rethrow = false;
|
||||
try
|
||||
{
|
||||
retVal = body.evalValue(newFrame, type); // todo not sure if we need to propagete type / consider boolean context in defined functions - answer seems ye
|
||||
@@ -228,15 +227,6 @@ public class FunctionValue extends Value implements Fluff.ILazyFunction
|
||||
{
|
||||
retVal = returnStatement.retval;
|
||||
}
|
||||
catch (ThrowStatement throwStatement) // might not be really necessary
|
||||
{
|
||||
retVal = throwStatement.retval;
|
||||
rethrow = true;
|
||||
}
|
||||
if (rethrow)
|
||||
{
|
||||
throw new ThrowStatement(retVal);
|
||||
}
|
||||
Value otherRetVal = retVal;
|
||||
return (cc, tt) -> otherRetVal;
|
||||
}
|
||||
|
||||
@@ -341,7 +341,7 @@ public class ListValue extends AbstractListValue implements ContainerValueInterf
|
||||
int from = normalizeIndex(fromDesc, size);
|
||||
if (toDesc == null)
|
||||
return new ListValue((Collection<? extends Value>) getItems().subList(from, size));
|
||||
int to = normalizeIndex(toDesc, size);
|
||||
int to = normalizeIndex(toDesc, size+1);
|
||||
if (from > to)
|
||||
return ListValue.of();
|
||||
return new ListValue((Collection<? extends Value>) getItems().subList(from, to));
|
||||
|
||||
@@ -7,6 +7,7 @@ import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
@@ -16,7 +17,7 @@ import java.util.stream.Collectors;
|
||||
|
||||
public class MapValue extends AbstractListValue implements ContainerValueInterface
|
||||
{
|
||||
private Map<Value, Value> map;
|
||||
private final Map<Value, Value> map;
|
||||
|
||||
private MapValue()
|
||||
{
|
||||
@@ -268,7 +269,9 @@ public class MapValue extends AbstractListValue implements ContainerValueInterfa
|
||||
public JsonElement toJson()
|
||||
{
|
||||
JsonObject jsonMap = new JsonObject();
|
||||
map.forEach((k, v) -> jsonMap.add(k.getString(), v.toJson()));
|
||||
List<Value> keys = new ArrayList<>(map.keySet());
|
||||
Collections.sort(keys);
|
||||
keys.forEach( k -> jsonMap.add(k.getString(), map.get(k).toJson()));
|
||||
return jsonMap;
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,8 @@ import carpet.fakes.InventoryBearerInterface;
|
||||
import carpet.script.CarpetContext;
|
||||
import carpet.script.LazyValue;
|
||||
import carpet.script.exception.InternalExpressionException;
|
||||
import carpet.script.exception.ThrowStatement;
|
||||
import carpet.script.exception.Throwables;
|
||||
import carpet.script.utils.EquipmentInventory;
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
@@ -297,7 +299,7 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa
|
||||
}
|
||||
catch (CommandSyntaxException e)
|
||||
{
|
||||
throw new InternalExpressionException("Incorrect item: "+itemString);
|
||||
throw new ThrowStatement(itemString, Throwables.UNKNOWN_ITEM);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ import net.minecraft.nbt.Tag;
|
||||
|
||||
public class NullValue extends NumericValue // TODO check nonsingleton code
|
||||
{
|
||||
public static final NullValue NULL = new NullValue();
|
||||
@Override
|
||||
public String getString()
|
||||
{
|
||||
@@ -32,7 +33,7 @@ public class NullValue extends NumericValue // TODO check nonsingleton code
|
||||
{
|
||||
return new NullValue();
|
||||
}
|
||||
public NullValue() {super(0.0D);}
|
||||
private NullValue() {super(false);}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o)
|
||||
|
||||
@@ -15,12 +15,12 @@ import java.util.stream.Collectors;
|
||||
|
||||
public abstract class Value implements Comparable<Value>, Cloneable
|
||||
{
|
||||
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 NumericValue FALSE = BooleanValue.FALSE;
|
||||
public static NumericValue TRUE = BooleanValue.TRUE;
|
||||
public static NumericValue ZERO = new NumericValue(0);
|
||||
public static NumericValue ONE = new NumericValue(1);
|
||||
|
||||
public static NullValue NULL = new NullValue();
|
||||
public static NullValue NULL = NullValue.NULL;
|
||||
|
||||
public String boundVariable;
|
||||
|
||||
@@ -141,12 +141,12 @@ public abstract class Value implements Comparable<Value>, Cloneable
|
||||
|
||||
public void assertAssignable()
|
||||
{
|
||||
if (boundVariable == null || boundVariable.startsWith("_"))
|
||||
if (boundVariable == null)// || boundVariable.startsWith("_"))
|
||||
{
|
||||
if (boundVariable != null)
|
||||
/*if (boundVariable != null)
|
||||
{
|
||||
throw new InternalExpressionException(boundVariable+ " cannot be assigned a new value");
|
||||
}
|
||||
}*/
|
||||
throw new InternalExpressionException(getString()+ " is not a variable");
|
||||
|
||||
}
|
||||
@@ -186,7 +186,7 @@ public abstract class Value implements Comparable<Value>, Cloneable
|
||||
int size = value.length();
|
||||
int from = ListValue.normalizeIndex(fromDesc, size);
|
||||
if (toDesc == null) return new StringValue(value.substring(from));
|
||||
int to = ListValue.normalizeIndex(toDesc, size);
|
||||
int to = ListValue.normalizeIndex(toDesc, size+1);
|
||||
if (from > to) return StringValue.EMPTY;
|
||||
return new StringValue(value.substring(from, to));
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ package carpet.script.value;
|
||||
|
||||
import carpet.fakes.BlockPredicateInterface;
|
||||
import carpet.script.exception.InternalExpressionException;
|
||||
import carpet.script.exception.ThrowStatement;
|
||||
import carpet.script.exception.Throwables;
|
||||
import carpet.utils.BlockInfo;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.MaterialColor;
|
||||
@@ -158,7 +160,7 @@ public class ValueConversions
|
||||
}
|
||||
}
|
||||
if (dim == null)
|
||||
throw new InternalExpressionException("Incorrect dimension string: "+dimString);
|
||||
throw new ThrowStatement(dimString, Throwables.UNKNOWN_DIMENSION);
|
||||
return server.getWorld(dim);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user