Merge branch 'master' into display-title-additions

This commit is contained in:
replaceitem authored and GitHub committed 2021-04-11 21:21:46 +02:00
commit 396e844dce
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) { }
+144 -28
View File
@@ -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 -> {}));
}
}
+9 -5
View File
@@ -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);
}
+5 -1
View File
@@ -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();
+253 -69
View File
@@ -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;
});
+106 -20
View File
@@ -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;
}
}
}
+12 -85
View File
@@ -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)
+9 -9
View File
@@ -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);
}
}