Merge branch 'master' into 1.17
# Conflicts: # src/main/java/carpet/script/bundled/FileModule.java
This commit is contained in:
commit
4616bf4333
13 files changed
+487
-587
No files matched your search
@@ -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;
|
||||
@@ -20,6 +21,7 @@ import carpet.script.value.Value;
|
||||
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 +34,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 +46,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
|
||||
@@ -311,6 +316,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"));
|
||||
@@ -680,31 +786,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 +823,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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -272,35 +272,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
|
||||
@@ -317,89 +305,9 @@ public class CarpetScriptServer
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -558,4 +466,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 -> {}));
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import carpet.fakes.ServerWorldInterface;
|
||||
import carpet.fakes.StatTypeInterface;
|
||||
import carpet.fakes.ThreadedAnvilChunkStorageInterface;
|
||||
import carpet.helpers.FeatureGenerator;
|
||||
import carpet.script.bundled.Module;
|
||||
import carpet.script.argument.FileArgument;
|
||||
import carpet.script.CarpetContext;
|
||||
import carpet.script.CarpetEventServer;
|
||||
import carpet.script.CarpetScriptHost;
|
||||
@@ -105,7 +105,6 @@ 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;
|
||||
@@ -140,6 +139,7 @@ public class Auxiliary {
|
||||
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();
|
||||
|
||||
@Deprecated
|
||||
public static String recognizeResource(Value value, boolean isFloder)
|
||||
{
|
||||
String origfile = value.getString();
|
||||
@@ -152,20 +152,6 @@ 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) -> {
|
||||
@@ -878,49 +864,27 @@ 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;
|
||||
try
|
||||
{
|
||||
state = ((CarpetScriptHost) c.host).readFileTag(fdesc.getLeft(), fdesc.getRight());
|
||||
} catch (CrashException e)
|
||||
{
|
||||
throw new ThrowStatement((fdesc.getRight()?"shared/":"")+ fdesc.getLeft(), Throwables.NBT_READ);
|
||||
}
|
||||
|
||||
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 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((fdesc.getRight()?"shared/":"")+ fdesc.getLeft())
|
||||
)), Throwables.JSON_READ);
|
||||
}
|
||||
json = ((CarpetScriptHost) c.host).readJsonFile(fdesc);
|
||||
Value parsedJson = GSON.fromJson(json, Value.class);
|
||||
if (parsedJson == null)
|
||||
retVal = Value.NULL;
|
||||
@@ -929,7 +893,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()));
|
||||
}
|
||||
@@ -937,32 +901,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);
|
||||
((CarpetScriptHost) c.host).removeResourceFile(fdesc);
|
||||
success = ((CarpetScriptHost) c.host).appendLogFile(fdesc, data);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -987,34 +948,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();
|
||||
}
|
||||
}
|
||||
Tag state;
|
||||
try
|
||||
{
|
||||
state = ((CarpetScriptHost)((CarpetContext)c).host).readFileTag(file, shared);
|
||||
} catch (CrashException e)
|
||||
{
|
||||
throw new ThrowStatement("app data", Throwables.NBT_READ);
|
||||
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) c.host).readFileTag(fdesc);
|
||||
if (state == null)
|
||||
return (cc, tt) -> Value.NULL;
|
||||
Value retVal = new NBTSerializableValue(state);
|
||||
@@ -1026,22 +975,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;
|
||||
});
|
||||
|
||||
|
||||
@@ -5,8 +5,14 @@ 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;
|
||||
@@ -15,24 +21,30 @@ import net.minecraft.nbt.NbtTagSizeTracker;
|
||||
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.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
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;
|
||||
@@ -47,9 +59,22 @@ public class FileArgument
|
||||
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"),
|
||||
@@ -75,7 +100,7 @@ public class FileArgument
|
||||
READ, CREATE, DELETE
|
||||
}
|
||||
|
||||
private FileArgument(String resource, Type type, String zipContainer, boolean isFolder, boolean isShared, Reason reason)
|
||||
public FileArgument(String resource, Type type, String zipContainer, boolean isFolder, boolean isShared, Reason reason)
|
||||
{
|
||||
this.resource = resource;
|
||||
this.type = type;
|
||||
@@ -83,47 +108,103 @@ public class FileArgument
|
||||
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");
|
||||
String resource = recognizeResource(lv.get(0).evalValue(c), isFolder);
|
||||
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.of.containsKey(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, type,null, isFolder, shared, reason);
|
||||
return new FileArgument(resource.getLeft(), type,resource.getRight(), isFolder, shared, reason);
|
||||
|
||||
}
|
||||
|
||||
public static String recognizeResource(Value value, boolean isFolder)
|
||||
public static Pair<String,String> recognizeResource(Value value, boolean isFolder)
|
||||
{
|
||||
String origfile = value.getString();
|
||||
String file = origfile.toLowerCase(Locale.ROOT).replaceAll("[^A-Za-z0-9\\-+_/]", "");
|
||||
file = Arrays.stream(file.split("/+")).filter(s -> !s.isEmpty()).collect(Collectors.joining("/"));
|
||||
if (file.isEmpty() && !isFolder)
|
||||
String[] pathElements = origfile.toLowerCase(Locale.ROOT).split("[/\\\\]+");
|
||||
List<String> path = new ArrayList<>();
|
||||
String zipPath = null;
|
||||
for (String token : pathElements)
|
||||
{
|
||||
throw new InternalExpressionException("Cannot use "+origfile+" as resource name - must have some letters and numbers");
|
||||
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();
|
||||
}
|
||||
}
|
||||
return file;
|
||||
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;
|
||||
return CarpetServer.minecraft_server.getSavePath(WorldSavePath.ROOT).resolve("scripts/"+getDescriptor(module, resource)+type.extension);
|
||||
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 CarpetServer.minecraft_server.getSavePath(WorldSavePath.ROOT).resolve("scripts/"+getDescriptor(module, ""));
|
||||
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)
|
||||
@@ -136,7 +217,7 @@ public class FileArgument
|
||||
{
|
||||
return module.getName()+".data"+(res==null || res.isEmpty()?"":"/"+res);
|
||||
}
|
||||
throw new RuntimeException("Invalid file descriptor: "+res);
|
||||
throw new InternalExpressionException("Invalid file descriptor: "+res);
|
||||
}
|
||||
|
||||
public Stream<Path> listFiles(Module module)
|
||||
@@ -146,8 +227,7 @@ public class FileArgument
|
||||
String ext = type.extension;
|
||||
try
|
||||
{
|
||||
return Files.list(dir).
|
||||
filter(path -> isFolder?Files.isDirectory(path):path.toString().endsWith(ext));
|
||||
return Files.list(dir).filter(path -> (type==Type.FOLDER)?path.toString().endsWith("/"):path.toString().endsWith(ext));
|
||||
}
|
||||
catch (IOException ignored)
|
||||
{
|
||||
@@ -157,73 +237,106 @@ public class FileArgument
|
||||
|
||||
public Stream<String> listFolder(Module module)
|
||||
{
|
||||
Stream<Path> result;
|
||||
synchronized (writeIOSync) { result = listFiles(module); }
|
||||
if (result == null) return null;
|
||||
Path rootPath = moduleRootPath(module);
|
||||
Stream<String> strings = result.map(p -> rootPath.relativize(p).toString().replaceAll("[\\\\/]","/"));
|
||||
if (type != Type.FOLDER)
|
||||
return strings.map(FilenameUtils::removeExtension);
|
||||
return strings;
|
||||
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)
|
||||
{
|
||||
Path dataFile = toPath(module);//, resourceName, supportedTypes.get(type), isShared);
|
||||
if (dataFile == null) return false;
|
||||
if (!Files.exists(dataFile.getParent()) && !dataFile.toFile().getParentFile().mkdirs()) return false;
|
||||
synchronized (writeIOSync)
|
||||
try { synchronized (writeIOSync)
|
||||
{
|
||||
try
|
||||
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)))
|
||||
{
|
||||
OutputStream out = Files.newOutputStream(dataFile, StandardOpenOption.APPEND, StandardOpenOption.CREATE);
|
||||
try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out, StandardCharsets.UTF_8)))
|
||||
for (String line: message)
|
||||
{
|
||||
for (String line: message)
|
||||
{
|
||||
writer.append(line);
|
||||
if (type == Type.TEXT) writer.newLine();
|
||||
}
|
||||
writer.append(line);
|
||||
if (type == Type.TEXT) writer.newLine();
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} }
|
||||
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
|
||||
{
|
||||
Path dataFile = toPath(module);
|
||||
if (dataFile == null) return null;
|
||||
if (!Files.exists(dataFile) || !(dataFile.toFile().isFile())) return null;
|
||||
synchronized (writeIOSync) { return readTag(dataFile.toFile()); }
|
||||
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(File file)
|
||||
public static Tag readTag(Path path)
|
||||
{
|
||||
try
|
||||
{
|
||||
return NbtIo.readCompressed(file);
|
||||
return NbtIo.readCompressed(Files.newInputStream(path));
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
// Copy of NbtIo.read(File) because that's now client-side only
|
||||
if (!file.exists())
|
||||
if (!Files.exists(path))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
try (DataInputStream in = new DataInputStream(new FileInputStream(file)))
|
||||
try (DataInputStream in = new DataInputStream(new BufferedInputStream(Files.newInputStream(path))))
|
||||
{
|
||||
return NbtIo.read(in);
|
||||
}
|
||||
catch (IOException ioException)
|
||||
{
|
||||
try (DataInputStream dataInput_1 = new DataInputStream(new FileInputStream(file)))
|
||||
// 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)
|
||||
@@ -238,71 +351,101 @@ public class FileArgument
|
||||
}
|
||||
catch (IOException ignored)
|
||||
{
|
||||
throw new ThrowStatement("Not a valid NBT tag in "+path.toString(), Throwables.NBT_ERROR);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
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
|
||||
{
|
||||
Path dataFile = toPath(module);
|
||||
if (dataFile == null) return false;
|
||||
if (!Files.exists(dataFile.getParent()) && !dataFile.toFile().getParentFile().mkdirs()) return false;
|
||||
synchronized (writeIOSync) { return writeTag(tag, dataFile.toFile()); }
|
||||
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 writeTag(Tag tag_1, File file)
|
||||
public static boolean writeTagDisk(Tag tag_1, Path path, boolean zipped)
|
||||
{
|
||||
File file_2 = new File(file.getAbsolutePath() + "_tmp");
|
||||
if (file_2.exists()) file_2.delete();
|
||||
Path original = path;
|
||||
try
|
||||
{
|
||||
if (!zipped)
|
||||
{
|
||||
path = new File(path.toFile().getAbsolutePath() + "_tmp").toPath();
|
||||
Files.deleteIfExists(path);
|
||||
}
|
||||
|
||||
if (tag_1 instanceof CompoundTag)
|
||||
{
|
||||
try
|
||||
if (tag_1 instanceof CompoundTag)
|
||||
{
|
||||
NbtIo.writeCompressed((CompoundTag) tag_1, file_2);
|
||||
NbtIo.writeCompressed((CompoundTag) tag_1, Files.newOutputStream(path));
|
||||
}
|
||||
catch (IOException e)
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
try (DataOutputStream dataOutputStream_1 = new DataOutputStream(new FileOutputStream(file_2)))
|
||||
{
|
||||
dataOutputStream_1.writeByte(tag_1.getType());
|
||||
if (tag_1.getType() != 0)
|
||||
try (DataOutputStream dataOutputStream_1 = new DataOutputStream(Files.newOutputStream(path)))
|
||||
{
|
||||
dataOutputStream_1.writeUTF("");
|
||||
tag_1.write(dataOutputStream_1);
|
||||
dataOutputStream_1.writeByte(tag_1.getType());
|
||||
if (tag_1.getType() != 0)
|
||||
{
|
||||
dataOutputStream_1.writeUTF("");
|
||||
tag_1.write(dataOutputStream_1);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
if (!zipped)
|
||||
{
|
||||
return false;
|
||||
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);
|
||||
}
|
||||
if (file.exists()) file.delete();
|
||||
if (!file.exists()) file_2.renameTo(file);
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean dropExistingFile(Module module)
|
||||
{
|
||||
Path dataFile = toPath(module);
|
||||
if (dataFile == null) return false;
|
||||
synchronized (writeIOSync) { return dataFile.toFile().delete(); }
|
||||
|
||||
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)
|
||||
{
|
||||
Path dataFile = toPath(module);
|
||||
if (dataFile == null) return null;
|
||||
if (!dataFile.toFile().exists()) return null;
|
||||
synchronized (writeIOSync) { return listFileContent(dataFile); }
|
||||
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)
|
||||
@@ -319,15 +462,19 @@ public class FileArgument
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
return null;
|
||||
throw new ThrowStatement("Failed to read text file "+filePath.toString(), Throwables.IO_EXCEPTION);
|
||||
}
|
||||
}
|
||||
|
||||
public JsonElement readJsonFile(Module module) {
|
||||
Path dataFile = toPath(module);
|
||||
if (dataFile == null) return null;
|
||||
if (!dataFile.toFile().exists()) return null;
|
||||
synchronized (writeIOSync) { return readJsonContent(dataFile); }
|
||||
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)
|
||||
@@ -336,9 +483,19 @@ public class FileArgument
|
||||
{
|
||||
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)
|
||||
{
|
||||
return null;
|
||||
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.NbtTagSizeTracker;
|
||||
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,155 +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
|
||||
@Deprecated
|
||||
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, NbtTagSizeTracker.EMPTY);
|
||||
}
|
||||
}
|
||||
catch (IOException ignored)
|
||||
{
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//copied private method from net.minecraft.nbt.NbtIo.write() and client method safe_write
|
||||
@Deprecated
|
||||
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;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
@Deprecated
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
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,123 +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();
|
||||
@Deprecated
|
||||
public final static Object writeIOSync = new Object();
|
||||
@Deprecated
|
||||
public final static Map<String, String> supportedTypes = ImmutableMap.of(
|
||||
"raw", ".txt",
|
||||
"text", ".txt",
|
||||
"nbt", ".nbt",
|
||||
"json", ".json",
|
||||
"folder", "folder"
|
||||
);
|
||||
|
||||
@Deprecated
|
||||
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);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
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); }
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
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); }
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
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); }
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
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(); }
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
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); }
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
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); }
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
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;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
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
|
||||
|
||||
@@ -25,8 +25,8 @@ public class Throwables {
|
||||
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_READ = register("nbt_read_error", IO_EXCEPTION);
|
||||
public static final Throwables JSON_READ = register("json_read_error", IO_EXCEPTION);
|
||||
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);
|
||||
|
||||
/**
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -4,6 +4,7 @@ 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;
|
||||
@@ -59,7 +60,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()));
|
||||
|
||||
@@ -16,6 +16,11 @@ public class BooleanValue extends NumericValue
|
||||
boolValue = boolval;
|
||||
}
|
||||
|
||||
public static BooleanValue of(boolean value)
|
||||
{
|
||||
return value?TRUE:FALSE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString() {
|
||||
return boolValue?"true":"false";
|
||||
|
||||
Reference in new issue
Block a user