semi-auto cleanup
This commit is contained in:
1 parent
cad80f82c6
commit
8f61271606
99 files changed
+11463
-8007
No files matched your search
@@ -18,11 +18,12 @@ public class CarpetContext extends Context
|
||||
public CommandSourceStack s;
|
||||
private final BlockPos origin;
|
||||
|
||||
public CarpetContext(CarpetScriptHost host, CommandSourceStack source) {
|
||||
public CarpetContext(final CarpetScriptHost host, final CommandSourceStack source)
|
||||
{
|
||||
this(host, source, BlockPos.ZERO);
|
||||
}
|
||||
|
||||
public CarpetContext(ScriptHost host, CommandSourceStack source, BlockPos origin)
|
||||
public CarpetContext(final ScriptHost host, final CommandSourceStack source, final BlockPos origin)
|
||||
{
|
||||
super(host);
|
||||
s = source;
|
||||
@@ -59,7 +60,8 @@ public class CarpetContext extends Context
|
||||
return s.getLevel().registryAccess();
|
||||
}
|
||||
|
||||
public <T> Registry<T> registry(ResourceKey<? extends Registry<? extends T>> resourceKey) {
|
||||
public <T> Registry<T> registry(final ResourceKey<? extends Registry<? extends T>> resourceKey)
|
||||
{
|
||||
return registryAccess().registryOrThrow(resourceKey);
|
||||
}
|
||||
|
||||
@@ -73,7 +75,7 @@ public class CarpetContext extends Context
|
||||
return origin;
|
||||
}
|
||||
|
||||
public void swapSource(CommandSourceStack source)
|
||||
public void swapSource(final CommandSourceStack source)
|
||||
{
|
||||
s = source;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -25,12 +25,24 @@ public class CarpetExpression
|
||||
private final CommandSourceStack source;
|
||||
private final BlockPos origin;
|
||||
private final Expression expr;
|
||||
// these are for extensions
|
||||
public Expression getExpr() {return expr;}
|
||||
public CommandSourceStack getSource() {return source;}
|
||||
public BlockPos getOrigin() {return origin;}
|
||||
|
||||
public CarpetExpression(Module module, String expression, CommandSourceStack source, BlockPos origin)
|
||||
// these are for extensions
|
||||
public Expression getExpr()
|
||||
{
|
||||
return expr;
|
||||
}
|
||||
|
||||
public CommandSourceStack getSource()
|
||||
{
|
||||
return source;
|
||||
}
|
||||
|
||||
public BlockPos getOrigin()
|
||||
{
|
||||
return origin;
|
||||
}
|
||||
|
||||
public CarpetExpression(final Module module, final String expression, final CommandSourceStack source, final BlockPos origin)
|
||||
{
|
||||
this.origin = origin;
|
||||
this.source = source;
|
||||
@@ -49,76 +61,80 @@ public class CarpetExpression
|
||||
CarpetServer.extensions.forEach(e -> e.scarpetApi(this));
|
||||
}
|
||||
|
||||
public boolean fillAndScanCommand(ScriptHost host, int x, int y, int z)
|
||||
public boolean fillAndScanCommand(final ScriptHost host, final int x, final int y, final int z)
|
||||
{
|
||||
if (CarpetServer.scriptServer.stopAll)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
try
|
||||
{
|
||||
Context context = new CarpetContext(host, source, origin).
|
||||
final Context context = new CarpetContext(host, source, origin).
|
||||
with("x", (c, t) -> new NumericValue(x - origin.getX()).bindTo("x")).
|
||||
with("y", (c, t) -> new NumericValue(y - origin.getY()).bindTo("y")).
|
||||
with("z", (c, t) -> new NumericValue(z - origin.getZ()).bindTo("z")).
|
||||
with("_", (c, t) -> new BlockValue(null, source.getLevel(), new BlockPos(x, y, z)).bindTo("_"));
|
||||
Entity e = source.getEntity();
|
||||
if (e==null)
|
||||
final Entity e = source.getEntity();
|
||||
if (e == null)
|
||||
{
|
||||
Value nullPlayer = Value.NULL.reboundedTo("p");
|
||||
context.with("p", (cc, tt) -> nullPlayer );
|
||||
final Value nullPlayer = Value.NULL.reboundedTo("p");
|
||||
context.with("p", (cc, tt) -> nullPlayer);
|
||||
}
|
||||
else
|
||||
{
|
||||
Value playerValue = new EntityValue(e).bindTo("p");
|
||||
final Value playerValue = new EntityValue(e).bindTo("p");
|
||||
context.with("p", (cc, tt) -> playerValue);
|
||||
}
|
||||
return CarpetServer.scriptServer.events.handleEvents.getWhileDisabled(()-> this.expr.eval(context).getBoolean());
|
||||
return CarpetServer.scriptServer.events.handleEvents.getWhileDisabled(() -> this.expr.eval(context).getBoolean());
|
||||
}
|
||||
catch (ExpressionException e)
|
||||
catch (final ExpressionException e)
|
||||
{
|
||||
throw new CarpetExpressionException(e.getMessage(), e.stack);
|
||||
}
|
||||
catch (ArithmeticException ae)
|
||||
catch (final ArithmeticException ae)
|
||||
{
|
||||
throw new CarpetExpressionException("Math doesn't compute... "+ae.getMessage(), null);
|
||||
throw new CarpetExpressionException("Math doesn't compute... " + ae.getMessage(), null);
|
||||
}
|
||||
catch (StackOverflowError soe)
|
||||
catch (final StackOverflowError soe)
|
||||
{
|
||||
throw new CarpetExpressionException("Your thoughts are too deep", null);
|
||||
}
|
||||
}
|
||||
|
||||
public Value scriptRunCommand(ScriptHost host, BlockPos pos)
|
||||
public Value scriptRunCommand(final ScriptHost host, final BlockPos pos)
|
||||
{
|
||||
if (CarpetServer.scriptServer.stopAll)
|
||||
{
|
||||
throw new CarpetExpressionException("SCRIPTING PAUSED (unpause with /script resume)", null);
|
||||
}
|
||||
try
|
||||
{
|
||||
Context context = new CarpetContext(host, source, origin).
|
||||
final Context context = new CarpetContext(host, source, origin).
|
||||
with("x", (c, t) -> new NumericValue(pos.getX() - origin.getX()).bindTo("x")).
|
||||
with("y", (c, t) -> new NumericValue(pos.getY() - origin.getY()).bindTo("y")).
|
||||
with("z", (c, t) -> new NumericValue(pos.getZ() - origin.getZ()).bindTo("z"));
|
||||
Entity e = source.getEntity();
|
||||
if (e==null)
|
||||
final Entity e = source.getEntity();
|
||||
if (e == null)
|
||||
{
|
||||
Value nullPlayer = Value.NULL.reboundedTo("p");
|
||||
context.with("p", (cc, tt) -> nullPlayer );
|
||||
final Value nullPlayer = Value.NULL.reboundedTo("p");
|
||||
context.with("p", (cc, tt) -> nullPlayer);
|
||||
}
|
||||
else
|
||||
{
|
||||
Value playerValue = new EntityValue(e).bindTo("p");
|
||||
final Value playerValue = new EntityValue(e).bindTo("p");
|
||||
context.with("p", (cc, tt) -> playerValue);
|
||||
}
|
||||
return CarpetServer.scriptServer.events.handleEvents.getWhileDisabled(()-> this.expr.eval(context));
|
||||
return CarpetServer.scriptServer.events.handleEvents.getWhileDisabled(() -> this.expr.eval(context));
|
||||
}
|
||||
catch (ExpressionException e)
|
||||
catch (final ExpressionException e)
|
||||
{
|
||||
throw new CarpetExpressionException(e.getMessage(), e.stack);
|
||||
}
|
||||
catch (ArithmeticException ae)
|
||||
catch (final ArithmeticException ae)
|
||||
{
|
||||
throw new CarpetExpressionException("Math doesn't compute... "+ae.getMessage(), null);
|
||||
throw new CarpetExpressionException("Math doesn't compute... " + ae.getMessage(), null);
|
||||
}
|
||||
catch (StackOverflowError soe)
|
||||
catch (final StackOverflowError soe)
|
||||
{
|
||||
throw new CarpetExpressionException("Your thoughts are too deep", null);
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -59,35 +59,38 @@ public class CarpetScriptServer extends ScriptServer
|
||||
//make static for now, but will change that later:
|
||||
public static final Logger LOG = LoggerFactory.getLogger("Scarpet");
|
||||
public final MinecraftServer server;
|
||||
public CarpetScriptHost globalHost;
|
||||
public Map<String, CarpetScriptHost> modules;
|
||||
public Set<String> unloadableModules;
|
||||
public CarpetScriptHost globalHost;
|
||||
public Map<String, CarpetScriptHost> modules;
|
||||
public Set<String> unloadableModules;
|
||||
public long tickStart;
|
||||
public boolean stopAll;
|
||||
public int tickDepth;
|
||||
private Set<String> holyMoly;
|
||||
public CarpetEventServer events;
|
||||
private Set<String> holyMoly;
|
||||
public CarpetEventServer events;
|
||||
|
||||
private static final List<Module> bundledModuleData = new ArrayList<>();
|
||||
private static final List<Module> ruleModuleData = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Registers a Scarpet App to be always available under the {@code /script load} list.
|
||||
* @see Module#fromJarPath(String, String, boolean)
|
||||
*
|
||||
*
|
||||
* @param app The {@link Module} of the app
|
||||
* @see Module#fromJarPath(String, String, boolean)
|
||||
*/
|
||||
public static void registerBuiltInApp(Module app) {
|
||||
public static void registerBuiltInApp(final Module app)
|
||||
{
|
||||
bundledModuleData.add(app);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a Scarpet App to be used as a Rule App (to be controlled with the value of a Carpet rule).
|
||||
* Libraries should be registered with {@link #registerBuiltInScript(BundledModule)} instead
|
||||
* @see Module#fromJarPath(String, String, boolean)
|
||||
*
|
||||
*
|
||||
* @param app The {@link Module} of the app.
|
||||
* @see Module#fromJarPath(String, String, boolean)
|
||||
*/
|
||||
public static void registerSettingsApp(Module app) {
|
||||
public static void registerSettingsApp(final Module app)
|
||||
{
|
||||
ruleModuleData.add(app);
|
||||
}
|
||||
|
||||
@@ -105,7 +108,7 @@ public class CarpetScriptServer extends ScriptServer
|
||||
registerBuiltInApp(Module.carpetNative("distance_beta", false));
|
||||
}
|
||||
|
||||
public CarpetScriptServer(MinecraftServer server)
|
||||
public CarpetScriptServer(final MinecraftServer server)
|
||||
{
|
||||
this.server = server;
|
||||
init();
|
||||
@@ -126,13 +129,14 @@ public class CarpetScriptServer extends ScriptServer
|
||||
{
|
||||
CarpetServer.settingsManager.initializeScarpetRules();
|
||||
CarpetServer.extensions.forEach(e -> {
|
||||
if (e.extensionSettingsManager() != null) {
|
||||
if (e.extensionSettingsManager() != null)
|
||||
{
|
||||
e.extensionSettingsManager().initializeScarpetRules();
|
||||
}
|
||||
});
|
||||
if (CarpetSettings.scriptsAutoload)
|
||||
{
|
||||
for (String moduleName: listAvailableModules(false))
|
||||
for (final String moduleName : listAvailableModules(false))
|
||||
{
|
||||
addScriptHost(server.createCommandSourceStack(), moduleName, null, true, true, false, null);
|
||||
}
|
||||
@@ -140,40 +144,53 @@ public class CarpetScriptServer extends ScriptServer
|
||||
CarpetEventServer.Event.START.onTick();
|
||||
}
|
||||
|
||||
public Module getModule(String name, boolean allowLibraries)
|
||||
public Module getModule(final String name, final boolean allowLibraries)
|
||||
{
|
||||
try {
|
||||
Path folder = server.getWorldPath(LevelResource.ROOT).resolve("scripts");
|
||||
if (!Files.exists(folder))
|
||||
try
|
||||
{
|
||||
final Path folder = server.getWorldPath(LevelResource.ROOT).resolve("scripts");
|
||||
if (!Files.exists(folder))
|
||||
{
|
||||
Files.createDirectories(folder);
|
||||
try (Stream<Path> folderLister = Files.list(folder)) {
|
||||
Optional<Path> scriptPath = folderLister
|
||||
.filter(script ->
|
||||
script.getFileName().toString().equalsIgnoreCase(name + ".sc") ||
|
||||
(allowLibraries && script.getFileName().toString().equalsIgnoreCase(name+".scl"))
|
||||
).findFirst();
|
||||
}
|
||||
try (final Stream<Path> folderLister = Files.list(folder))
|
||||
{
|
||||
final Optional<Path> scriptPath = folderLister
|
||||
.filter(script ->
|
||||
script.getFileName().toString().equalsIgnoreCase(name + ".sc") ||
|
||||
(allowLibraries && script.getFileName().toString().equalsIgnoreCase(name + ".scl"))
|
||||
).findFirst();
|
||||
if (scriptPath.isPresent())
|
||||
{
|
||||
return Module.fromPath(scriptPath.get());
|
||||
}
|
||||
}
|
||||
|
||||
if (FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT)
|
||||
{
|
||||
Path globalFolder = FabricLoader.getInstance().getConfigDir().resolve("carpet/scripts");
|
||||
if (!Files.exists(globalFolder))
|
||||
final Path globalFolder = FabricLoader.getInstance().getConfigDir().resolve("carpet/scripts");
|
||||
if (!Files.exists(globalFolder))
|
||||
{
|
||||
Files.createDirectories(globalFolder);
|
||||
try (Stream<Path> folderWalker = Files.walk(globalFolder)) {
|
||||
Optional<Path> scriptPath = folderWalker
|
||||
}
|
||||
try (final Stream<Path> folderWalker = Files.walk(globalFolder))
|
||||
{
|
||||
final Optional<Path> scriptPath = folderWalker
|
||||
.filter(script -> script.getFileName().toString().equalsIgnoreCase(name + ".sc") ||
|
||||
(allowLibraries && script.getFileName().toString().equalsIgnoreCase(name + ".scl")))
|
||||
.findFirst();
|
||||
if (scriptPath.isPresent())
|
||||
{
|
||||
return Module.fromPath(scriptPath.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
}
|
||||
catch (final IOException e)
|
||||
{
|
||||
CarpetSettings.LOG.error("Exception while loading the app: ", e);
|
||||
}
|
||||
for (Module moduleData : bundledModuleData)
|
||||
for (final Module moduleData : bundledModuleData)
|
||||
{
|
||||
if (moduleData.name().equalsIgnoreCase(name) && (allowLibraries || !moduleData.library()))
|
||||
{
|
||||
@@ -182,10 +199,10 @@ public class CarpetScriptServer extends ScriptServer
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Module getRuleModule(String name)
|
||||
|
||||
public Module getRuleModule(final String name)
|
||||
{
|
||||
for (Module moduleData : ruleModuleData)
|
||||
for (final Module moduleData : ruleModuleData)
|
||||
{
|
||||
if (moduleData.name().equalsIgnoreCase(name))
|
||||
{
|
||||
@@ -195,101 +212,116 @@ public class CarpetScriptServer extends ScriptServer
|
||||
return null;
|
||||
}
|
||||
|
||||
public List<String> listAvailableModules(boolean includeBuiltIns)
|
||||
public List<String> listAvailableModules(final boolean includeBuiltIns)
|
||||
{
|
||||
List<String> moduleNames = new ArrayList<>();
|
||||
final List<String> moduleNames = new ArrayList<>();
|
||||
if (includeBuiltIns)
|
||||
{
|
||||
for (Module mi : bundledModuleData)
|
||||
for (final Module mi : bundledModuleData)
|
||||
{
|
||||
if (!mi.library() && !mi.name().endsWith("_beta")) moduleNames.add(mi.name());
|
||||
if (!mi.library() && !mi.name().endsWith("_beta"))
|
||||
{
|
||||
moduleNames.add(mi.name());
|
||||
}
|
||||
}
|
||||
}
|
||||
try {
|
||||
Path worldScripts = server.getWorldPath(LevelResource.ROOT).resolve("scripts");
|
||||
if (!Files.exists(worldScripts))
|
||||
try
|
||||
{
|
||||
final Path worldScripts = server.getWorldPath(LevelResource.ROOT).resolve("scripts");
|
||||
if (!Files.exists(worldScripts))
|
||||
{
|
||||
Files.createDirectories(worldScripts);
|
||||
try (Stream<Path> folderLister = Files.list(worldScripts)) {
|
||||
}
|
||||
try (final Stream<Path> folderLister = Files.list(worldScripts))
|
||||
{
|
||||
folderLister
|
||||
.filter(f -> f.toString().endsWith(".sc"))
|
||||
.forEach(f -> moduleNames.add(f.getFileName().toString().replaceFirst("\\.sc$","").toLowerCase(Locale.ROOT)));
|
||||
.filter(f -> f.toString().endsWith(".sc"))
|
||||
.forEach(f -> moduleNames.add(f.getFileName().toString().replaceFirst("\\.sc$", "").toLowerCase(Locale.ROOT)));
|
||||
}
|
||||
|
||||
if (includeBuiltIns && (FabricLoader.getInstance().getEnvironmentType() == EnvType.CLIENT))
|
||||
{
|
||||
Path globalScripts = FabricLoader.getInstance().getConfigDir().resolve("carpet/scripts");
|
||||
final Path globalScripts = FabricLoader.getInstance().getConfigDir().resolve("carpet/scripts");
|
||||
if (!Files.exists(globalScripts))
|
||||
{
|
||||
Files.createDirectories(globalScripts);
|
||||
try (Stream<Path> folderWalker = Files.walk(globalScripts, FileVisitOption.FOLLOW_LINKS)) {
|
||||
}
|
||||
try (final Stream<Path> folderWalker = Files.walk(globalScripts, FileVisitOption.FOLLOW_LINKS))
|
||||
{
|
||||
folderWalker
|
||||
.filter(f -> f.toString().endsWith(".sc"))
|
||||
.forEach(f -> moduleNames.add(f.getFileName().toString().replaceFirst("\\.sc$","").toLowerCase(Locale.ROOT)));
|
||||
.filter(f -> f.toString().endsWith(".sc"))
|
||||
.forEach(f -> moduleNames.add(f.getFileName().toString().replaceFirst("\\.sc$", "").toLowerCase(Locale.ROOT)));
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
}
|
||||
catch (final IOException e)
|
||||
{
|
||||
CarpetSettings.LOG.error("Exception while searching for apps: ", e);
|
||||
}
|
||||
return moduleNames;
|
||||
}
|
||||
|
||||
public CarpetScriptHost getAppHostByName(String name)
|
||||
public CarpetScriptHost getAppHostByName(final String name)
|
||||
{
|
||||
if (name == null)
|
||||
return globalHost;
|
||||
return modules.get(name);
|
||||
return name == null ? globalHost : modules.get(name);
|
||||
}
|
||||
|
||||
public boolean addScriptHost(CommandSourceStack source, String name, Predicate<CommandSourceStack> commandValidator,
|
||||
boolean perPlayer, boolean autoload, boolean isRuleApp, AppStoreManager.StoreNode installer)
|
||||
public boolean addScriptHost(final CommandSourceStack source, String name, Predicate<CommandSourceStack> commandValidator,
|
||||
final boolean perPlayer, final boolean autoload, final boolean isRuleApp, final AppStoreManager.StoreNode installer)
|
||||
{
|
||||
CarpetProfiler.ProfilerToken currentSection = CarpetProfiler.start_section(null, "Scarpet load", CarpetProfiler.TYPE.GENERAL);
|
||||
if (commandValidator == null) commandValidator = p -> true;
|
||||
long start = System.nanoTime();
|
||||
final CarpetProfiler.ProfilerToken currentSection = CarpetProfiler.start_section(null, "Scarpet load", CarpetProfiler.TYPE.GENERAL);
|
||||
if (commandValidator == null)
|
||||
{
|
||||
commandValidator = p -> true;
|
||||
}
|
||||
final long start = System.nanoTime();
|
||||
name = name.toLowerCase(Locale.ROOT);
|
||||
boolean reload = false;
|
||||
if (modules.containsKey(name))
|
||||
{
|
||||
if (isRuleApp) return false;
|
||||
if (isRuleApp)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
removeScriptHost(source, name, false, isRuleApp);
|
||||
reload = true;
|
||||
}
|
||||
Module module = isRuleApp ? getRuleModule(name) : getModule(name, false);
|
||||
final Module module = isRuleApp ? getRuleModule(name) : getModule(name, false);
|
||||
if (module == null)
|
||||
{
|
||||
Messenger.m(source, "r Failed to add "+name+" app: App not found");
|
||||
Messenger.m(source, "r Failed to add " + name + " app: App not found");
|
||||
return false;
|
||||
}
|
||||
CarpetScriptHost newHost;
|
||||
final CarpetScriptHost newHost;
|
||||
try
|
||||
{
|
||||
newHost = CarpetScriptHost.create(this, module, perPlayer, source, commandValidator, isRuleApp, installer);
|
||||
}
|
||||
catch (LoadException e)
|
||||
catch (final LoadException e)
|
||||
{
|
||||
Messenger.m(source, "r Failed to add " + name + " app" + (e.getMessage() == null ? "" : ": " + e.getMessage()));
|
||||
return false;
|
||||
}
|
||||
// config needs to be read as we load the app since some events use that info
|
||||
//if (!newHost.readConfig())
|
||||
//{
|
||||
// Messenger.m(source, "r Failed to add "+name+" app: invalid app config (via '__config()' function)");
|
||||
// return false;
|
||||
//}
|
||||
|
||||
modules.put(name, newHost);
|
||||
if (!isRuleApp) unloadableModules.add(name);
|
||||
if (!isRuleApp)
|
||||
{
|
||||
unloadableModules.add(name);
|
||||
}
|
||||
|
||||
if (autoload && !newHost.persistenceRequired)
|
||||
{
|
||||
removeScriptHost(source, name, false, false);
|
||||
return false;
|
||||
}
|
||||
//addEvents(source, name);
|
||||
String action = (installer!=null)?(reload?"reinstalled":"installed"):(reload?"reloaded":"loaded");
|
||||
final String action = (installer != null) ? (reload ? "reinstalled" : "installed") : (reload ? "reloaded" : "loaded");
|
||||
|
||||
String finalName = name;
|
||||
Boolean isCommandAdded = newHost.addAppCommands(s -> {
|
||||
if (!isRuleApp) Messenger.m(source, Messenger.c("r Failed to add app '" + finalName + "': ", s));
|
||||
final String finalName = name;
|
||||
final Boolean isCommandAdded = newHost.addAppCommands(s -> {
|
||||
if (!isRuleApp)
|
||||
{
|
||||
Messenger.m(source, Messenger.c("r Failed to add app '" + finalName + "': ", s));
|
||||
}
|
||||
});
|
||||
if (isCommandAdded == null) // error should be dispatched
|
||||
{
|
||||
@@ -299,17 +331,23 @@ public class CarpetScriptServer extends ScriptServer
|
||||
else if (isCommandAdded)
|
||||
{
|
||||
CommandHelper.notifyPlayersCommandsChanged(server);
|
||||
if (!isRuleApp) Messenger.m(source, "gi "+name+" app "+action+" with /"+name+" command");
|
||||
if (!isRuleApp)
|
||||
{
|
||||
Messenger.m(source, "gi " + name + " app " + action + " with /" + name + " command");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!isRuleApp) Messenger.m(source, "gi "+name+" app "+action);
|
||||
if (!isRuleApp)
|
||||
{
|
||||
Messenger.m(source, "gi " + name + " app " + action);
|
||||
}
|
||||
}
|
||||
|
||||
if (newHost.isPerUser())
|
||||
{
|
||||
// that will provide player hosts right at the startup
|
||||
for (ServerPlayer player : source.getServer().getPlayerList().getPlayers())
|
||||
for (final ServerPlayer player : source.getServer().getPlayerList().getPlayers())
|
||||
{
|
||||
newHost.retrieveForExecution(player.createCommandSourceStack(), player);
|
||||
}
|
||||
@@ -317,59 +355,76 @@ public class CarpetScriptServer extends ScriptServer
|
||||
else
|
||||
{
|
||||
// global app - calling start now.
|
||||
FunctionValue onStart = newHost.getFunction("__on_start");
|
||||
if (onStart != null) newHost.callNow(onStart, Collections.emptyList());
|
||||
final 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");
|
||||
final long end = System.nanoTime();
|
||||
CarpetSettings.LOG.info("App " + name + " loaded in " + (end - start) / 1000000 + " ms");
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isInvalidCommandRoot(String appName)
|
||||
public boolean isInvalidCommandRoot(final String appName)
|
||||
{
|
||||
return holyMoly.contains(appName);
|
||||
}
|
||||
|
||||
|
||||
public boolean removeScriptHost(CommandSourceStack source, String name, boolean notifySource, boolean isRuleApp)
|
||||
public boolean removeScriptHost(final CommandSourceStack source, String name, final boolean notifySource, final boolean isRuleApp)
|
||||
{
|
||||
name = name.toLowerCase(Locale.ROOT);
|
||||
if (!modules.containsKey(name) || (!isRuleApp && !unloadableModules.contains(name)))
|
||||
{
|
||||
if (notifySource) Messenger.m(source, "r No such app found: ", "wb " + name);
|
||||
if (notifySource)
|
||||
{
|
||||
Messenger.m(source, "r No such app found: ", "wb " + name);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// stop all events associated with name
|
||||
CarpetScriptHost host = modules.remove(name);
|
||||
final CarpetScriptHost host = modules.remove(name);
|
||||
events.removeAllHostEvents(host);
|
||||
host.onClose();
|
||||
if (host.hasCommand)
|
||||
((CommandDispatcherInterface)server.getCommands().getDispatcher()).carpet$unregister(name);
|
||||
if (!isRuleApp) unloadableModules.remove(name);
|
||||
{
|
||||
((CommandDispatcherInterface) server.getCommands().getDispatcher()).carpet$unregister(name);
|
||||
}
|
||||
if (!isRuleApp)
|
||||
{
|
||||
unloadableModules.remove(name);
|
||||
}
|
||||
CommandHelper.notifyPlayersCommandsChanged(server);
|
||||
if (notifySource) Messenger.m(source, "gi Removed "+name+" app");
|
||||
if (notifySource)
|
||||
{
|
||||
Messenger.m(source, "gi Removed " + name + " app");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean uninstallApp(CommandSourceStack source, String name)
|
||||
public boolean uninstallApp(final CommandSourceStack source, String name)
|
||||
{
|
||||
try
|
||||
{
|
||||
name = name.toLowerCase(Locale.ROOT);
|
||||
Path folder = server.getWorldPath(LevelResource.ROOT).resolve("scripts/trash");
|
||||
if (!Files.exists(folder)) Files.createDirectories(folder);
|
||||
if (!Files.exists(folder.getParent().resolve(name+".sc")))
|
||||
final Path folder = server.getWorldPath(LevelResource.ROOT).resolve("scripts/trash");
|
||||
if (!Files.exists(folder))
|
||||
{
|
||||
Files.createDirectories(folder);
|
||||
}
|
||||
if (!Files.exists(folder.getParent().resolve(name + ".sc")))
|
||||
{
|
||||
Messenger.m(source, "App doesn't exist in the world scripts folder, so can only be unloaded");
|
||||
return false;
|
||||
}
|
||||
removeScriptHost(source, name, false, false);
|
||||
Files.move(folder.getParent().resolve(name+".sc"), folder.resolve(name+".sc"), StandardCopyOption.REPLACE_EXISTING);
|
||||
Messenger.m(source, "gi Removed "+name+" app");
|
||||
Files.move(folder.getParent().resolve(name + ".sc"), folder.resolve(name + ".sc"), StandardCopyOption.REPLACE_EXISTING);
|
||||
Messenger.m(source, "gi Removed " + name + " app");
|
||||
return true;
|
||||
}
|
||||
catch (IOException exc)
|
||||
catch (final IOException exc)
|
||||
{
|
||||
Messenger.m(source, "rb Failed to uninstall the app");
|
||||
}
|
||||
@@ -380,10 +435,13 @@ public class CarpetScriptServer extends ScriptServer
|
||||
{
|
||||
CarpetProfiler.ProfilerToken token;
|
||||
token = CarpetProfiler.start_section(null, "Scarpet schedule", CarpetProfiler.TYPE.GENERAL);
|
||||
events.handleEvents.getWhileDisabled( () -> {events.tick(); return null;});
|
||||
events.handleEvents.getWhileDisabled(() -> {
|
||||
events.tick();
|
||||
return null;
|
||||
});
|
||||
CarpetProfiler.end_current_section(token);
|
||||
token = CarpetProfiler.start_section(null, "Scarpet app data", CarpetProfiler.TYPE.GENERAL);
|
||||
for (CarpetScriptHost host : modules.values())
|
||||
for (final CarpetScriptHost host : modules.values())
|
||||
{
|
||||
host.tick();
|
||||
}
|
||||
@@ -393,14 +451,14 @@ public class CarpetScriptServer extends ScriptServer
|
||||
public void onClose()
|
||||
{
|
||||
CarpetEventServer.Event.SHUTDOWN.onTick();
|
||||
for (CarpetScriptHost host : modules.values())
|
||||
for (final CarpetScriptHost host : modules.values())
|
||||
{
|
||||
host.onClose();
|
||||
events.removeAllHostEvents(host);
|
||||
}
|
||||
}
|
||||
|
||||
public void onPlayerJoin(ServerPlayer player)
|
||||
public void onPlayerJoin(final ServerPlayer player)
|
||||
{
|
||||
modules.values().forEach(h ->
|
||||
{
|
||||
@@ -410,36 +468,38 @@ public class CarpetScriptServer extends ScriptServer
|
||||
{
|
||||
h.retrieveOwnForExecution(player.createCommandSourceStack());
|
||||
}
|
||||
catch (CommandSyntaxException ignored)
|
||||
catch (final CommandSyntaxException ignored)
|
||||
{
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static record TransferData(boolean perUser, Predicate<CommandSourceStack> commandValidator, boolean isRuleApp)
|
||||
private record TransferData(boolean perUser, Predicate<CommandSourceStack> commandValidator,
|
||||
boolean isRuleApp)
|
||||
{
|
||||
private TransferData(CarpetScriptHost host)
|
||||
private TransferData(final CarpetScriptHost host)
|
||||
{
|
||||
this(host.perUser, host.commandValidator, host.isRuleApp);
|
||||
}
|
||||
}
|
||||
|
||||
public void reload(MinecraftServer server)
|
||||
public void reload(final MinecraftServer server)
|
||||
{
|
||||
Map<String, TransferData> apps = new HashMap<>();
|
||||
final Map<String, TransferData> apps = new HashMap<>();
|
||||
modules.forEach((s, h) -> apps.put(s, new TransferData(h)));
|
||||
apps.keySet().forEach(s -> removeScriptHost(server.createCommandSourceStack(), s, false, false));
|
||||
CarpetEventServer.Event.clearAllBuiltinEvents();
|
||||
init();
|
||||
apps.forEach((s, data) -> addScriptHost(server.createCommandSourceStack(), s,data.commandValidator, data.perUser,false, data.isRuleApp, null));
|
||||
apps.forEach((s, data) -> addScriptHost(server.createCommandSourceStack(), s, data.commandValidator, data.perUser, false, data.isRuleApp, null));
|
||||
}
|
||||
|
||||
public void reAddCommands()
|
||||
{
|
||||
modules.values().forEach(host -> host.addAppCommands(s -> {}));
|
||||
modules.values().forEach(host -> host.addAppCommands(s -> {
|
||||
}));
|
||||
}
|
||||
|
||||
|
||||
public static void parseFunctionClasses()
|
||||
{
|
||||
ExpressionException.prepareForDoom(); // see fc-#1172
|
||||
@@ -451,7 +511,7 @@ public class CarpetScriptServer extends ScriptServer
|
||||
AnnotationParser.parseFunctionClass(Loops.class);
|
||||
AnnotationParser.parseFunctionClass(Sys.class);
|
||||
AnnotationParser.parseFunctionClass(Threading.class);
|
||||
|
||||
|
||||
// API
|
||||
AnnotationParser.parseFunctionClass(Auxiliary.class);
|
||||
AnnotationParser.parseFunctionClass(BlockIterators.class);
|
||||
|
||||
@@ -13,6 +13,7 @@ public class Context
|
||||
{
|
||||
NONE, VOID, BOOLEAN, NUMBER, STRING, LIST, ITERATOR, SIGNATURE, LOCALIZATION, LVALUE, MAPDEF
|
||||
}
|
||||
|
||||
public static final Type NONE = Type.NONE;
|
||||
public static final Type VOID = Type.VOID;
|
||||
public static final Type BOOLEAN = Type.BOOLEAN;
|
||||
@@ -29,32 +30,32 @@ public class Context
|
||||
|
||||
public final ScriptHost host;
|
||||
|
||||
public Context(ScriptHost host)
|
||||
public Context(final ScriptHost host)
|
||||
{
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
public LazyValue getVariable(String name)
|
||||
public LazyValue getVariable(final String name)
|
||||
{
|
||||
return variables.get(name);
|
||||
}
|
||||
|
||||
public void setVariable(String name, LazyValue lv)
|
||||
public void setVariable(final String name, final LazyValue lv)
|
||||
{
|
||||
variables.put(name, lv);
|
||||
}
|
||||
|
||||
public void delVariable(String variable)
|
||||
public void delVariable(final String variable)
|
||||
{
|
||||
variables.remove(variable);
|
||||
}
|
||||
|
||||
public void removeVariablesMatching(String varname)
|
||||
public void removeVariablesMatching(final String varname)
|
||||
{
|
||||
variables.entrySet().removeIf(e -> e.getKey().startsWith(varname));
|
||||
}
|
||||
|
||||
public Context with(String variable, LazyValue lv)
|
||||
public Context with(final String variable, final LazyValue lv)
|
||||
{
|
||||
variables.put(variable, lv);
|
||||
return this;
|
||||
@@ -67,7 +68,7 @@ public class Context
|
||||
|
||||
public Context recreate()
|
||||
{
|
||||
Context ctx = duplicate();
|
||||
final Context ctx = duplicate();
|
||||
ctx.initialize();
|
||||
return ctx;
|
||||
}
|
||||
@@ -84,12 +85,12 @@ public class Context
|
||||
{
|
||||
return new Context(this.host);
|
||||
}
|
||||
|
||||
public ScriptHost.ErrorSnooper getErrorSnooper()
|
||||
{
|
||||
return host.errorSnooper;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* immutable context only for reason on reporting access violations in evaluating expressions in optimizization
|
||||
* mode detecting any potential violations that may happen on the way
|
||||
@@ -97,10 +98,11 @@ public class Context
|
||||
public static class ContextForErrorReporting extends Context
|
||||
{
|
||||
public ScriptHost.ErrorSnooper optmizerEerrorSnooper;
|
||||
public ContextForErrorReporting(Context parent)
|
||||
|
||||
public ContextForErrorReporting(final Context parent)
|
||||
{
|
||||
super(null);
|
||||
optmizerEerrorSnooper = parent.host.errorSnooper;
|
||||
optmizerEerrorSnooper = parent.host.errorSnooper;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -116,31 +118,64 @@ public class Context
|
||||
"of scarpet authors. Please report this issue directly to the scarpet issue tracker");
|
||||
|
||||
}
|
||||
@Override
|
||||
public LazyValue getVariable(String name) { badProgrammer(); return null;}
|
||||
|
||||
@Override
|
||||
public void setVariable(String name, LazyValue lv) { badProgrammer(); }
|
||||
public LazyValue getVariable(final String name)
|
||||
{
|
||||
badProgrammer();
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delVariable(String variable) { badProgrammer(); }
|
||||
public void setVariable(final String name, final LazyValue lv)
|
||||
{
|
||||
badProgrammer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeVariablesMatching(String varname) { badProgrammer(); }
|
||||
public void delVariable(final String variable)
|
||||
{
|
||||
badProgrammer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Context with(String variable, LazyValue lv) { badProgrammer(); return this; }
|
||||
public void removeVariablesMatching(final String varname)
|
||||
{
|
||||
badProgrammer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getAllVariableNames() { badProgrammer(); return null;}
|
||||
public Context with(final String variable, final LazyValue lv)
|
||||
{
|
||||
badProgrammer();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Context recreate() { badProgrammer(); return null;}
|
||||
public Set<String> getAllVariableNames()
|
||||
{
|
||||
badProgrammer();
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initialize() { badProgrammer();}
|
||||
public Context recreate()
|
||||
{
|
||||
badProgrammer();
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Context duplicate() { badProgrammer(); return null;}
|
||||
protected void initialize()
|
||||
{
|
||||
badProgrammer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Context duplicate()
|
||||
{
|
||||
badProgrammer();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,72 +8,93 @@ import carpet.script.value.NumericValue;
|
||||
import carpet.script.value.StringValue;
|
||||
import carpet.script.value.Value;
|
||||
import carpet.script.value.ValueConversions;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import net.minecraft.world.damagesource.DamageSource;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
public class EntityEventsGroup
|
||||
{
|
||||
private static record EventKey(String host, String user) {}
|
||||
private record EventKey(String host, String user)
|
||||
{
|
||||
}
|
||||
|
||||
private final Map<Event, Map<EventKey, CarpetEventServer.Callback>> actions;
|
||||
private final Entity entity;
|
||||
public EntityEventsGroup(Entity e)
|
||||
|
||||
public EntityEventsGroup(final Entity e)
|
||||
{
|
||||
actions = new HashMap<>();
|
||||
entity = e;
|
||||
}
|
||||
|
||||
public void onEvent(Event type, Object... args)
|
||||
public void onEvent(final Event type, final Object... args)
|
||||
{
|
||||
if (actions.isEmpty()) return; // most of the cases, trying to be nice
|
||||
Map<EventKey, CarpetEventServer.Callback> actionSet = actions.get(type);
|
||||
if (actionSet == null) return;
|
||||
if (CarpetServer.scriptServer == null) return; // executed after world is closin down
|
||||
for (Iterator<Map.Entry<EventKey, CarpetEventServer.Callback>> iterator = actionSet.entrySet().iterator(); iterator.hasNext(); )
|
||||
if (actions.isEmpty())
|
||||
{
|
||||
Map.Entry<EventKey, CarpetEventServer.Callback> action = iterator.next();
|
||||
EventKey key = action.getKey();
|
||||
ScriptHost host = CarpetServer.scriptServer.getAppHostByName(key.host());
|
||||
return; // most of the cases, trying to be nice
|
||||
}
|
||||
final Map<EventKey, CarpetEventServer.Callback> actionSet = actions.get(type);
|
||||
if (actionSet == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (CarpetServer.scriptServer == null)
|
||||
{
|
||||
return; // executed after world is closin down
|
||||
}
|
||||
for (final Iterator<Map.Entry<EventKey, CarpetEventServer.Callback>> iterator = actionSet.entrySet().iterator(); iterator.hasNext(); )
|
||||
{
|
||||
final Map.Entry<EventKey, CarpetEventServer.Callback> action = iterator.next();
|
||||
final EventKey key = action.getKey();
|
||||
final ScriptHost host = CarpetServer.scriptServer.getAppHostByName(key.host());
|
||||
if (host == null)
|
||||
{
|
||||
iterator.remove();
|
||||
continue;
|
||||
}
|
||||
if (key.user() != null)
|
||||
if (key.user() != null && entity.getServer().getPlayerList().getPlayerByName(key.user()) == null)
|
||||
{
|
||||
if (entity.getServer().getPlayerList().getPlayerByName(key.user())==null)
|
||||
{
|
||||
iterator.remove();
|
||||
continue;
|
||||
}
|
||||
iterator.remove();
|
||||
continue;
|
||||
}
|
||||
if (type.call(action.getValue(), entity, args) == CarpetEventServer.CallbackResult.FAIL)
|
||||
{
|
||||
iterator.remove();
|
||||
}
|
||||
}
|
||||
if (actionSet.isEmpty())
|
||||
{
|
||||
actions.remove(type);
|
||||
}
|
||||
if (actionSet.isEmpty()) actions.remove(type);
|
||||
}
|
||||
|
||||
public void addEvent(Event type, ScriptHost host, FunctionValue fun, List<Value> extraargs)
|
||||
public void addEvent(final Event type, final ScriptHost host, final FunctionValue fun, final List<Value> extraargs)
|
||||
{
|
||||
EventKey key = new EventKey(host.getName(), host.user);
|
||||
final EventKey key = new EventKey(host.getName(), host.user);
|
||||
if (fun != null)
|
||||
{
|
||||
CarpetEventServer.Callback call = type.create(key, fun, extraargs);
|
||||
final CarpetEventServer.Callback call = type.create(key, fun, extraargs);
|
||||
if (call == null)
|
||||
throw new InternalExpressionException("wrong number of arguments for callback, required "+type.argcount);
|
||||
{
|
||||
throw new InternalExpressionException("wrong number of arguments for callback, required " + type.argcount);
|
||||
}
|
||||
actions.computeIfAbsent(type, k -> new HashMap<>()).put(key, call);
|
||||
}
|
||||
else
|
||||
{
|
||||
actions.computeIfAbsent(type, k -> new HashMap<>()).remove(key);
|
||||
if (actions.get(type).isEmpty())
|
||||
{
|
||||
actions.remove(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,11 +105,11 @@ public class EntityEventsGroup
|
||||
public static final Event ON_DEATH = new Event("on_death", 1)
|
||||
{
|
||||
@Override
|
||||
public List<Value> makeArgs(Entity entity, Object... providedArgs)
|
||||
public List<Value> makeArgs(final Entity entity, final Object... providedArgs)
|
||||
{
|
||||
return Arrays.asList(
|
||||
new EntityValue(entity),
|
||||
new StringValue((String)providedArgs[0])
|
||||
new StringValue((String) providedArgs[0])
|
||||
);
|
||||
}
|
||||
};
|
||||
@@ -97,22 +118,22 @@ public class EntityEventsGroup
|
||||
public static final Event ON_DAMAGE = new Event("on_damaged", 3)
|
||||
{
|
||||
@Override
|
||||
public List<Value> makeArgs(Entity entity, Object... providedArgs)
|
||||
public List<Value> makeArgs(final Entity entity, final Object... providedArgs)
|
||||
{
|
||||
float amount = (Float)providedArgs[0];
|
||||
DamageSource source = (DamageSource)providedArgs[1];
|
||||
final float amount = (Float) providedArgs[0];
|
||||
final DamageSource source = (DamageSource) providedArgs[1];
|
||||
return Arrays.asList(
|
||||
new EntityValue(entity),
|
||||
new NumericValue(amount),
|
||||
new StringValue(source.getMsgId()),
|
||||
source.getEntity()==null?Value.NULL:new EntityValue(source.getEntity())
|
||||
source.getEntity() == null ? Value.NULL : new EntityValue(source.getEntity())
|
||||
);
|
||||
}
|
||||
};
|
||||
public static final Event ON_MOVE = new Event("on_move", 3)
|
||||
{
|
||||
@Override
|
||||
public List<Value> makeArgs(Entity entity, Object... providedArgs)
|
||||
public List<Value> makeArgs(final Entity entity, final Object... providedArgs)
|
||||
{
|
||||
return Arrays.asList(
|
||||
new EntityValue(entity),
|
||||
@@ -125,26 +146,30 @@ public class EntityEventsGroup
|
||||
|
||||
public final int argcount;
|
||||
public final String id;
|
||||
public Event(String identifier, int args)
|
||||
|
||||
public Event(final String identifier, final int args)
|
||||
{
|
||||
id = identifier;
|
||||
argcount = args+1; // entity is not extra
|
||||
argcount = args + 1; // entity is not extra
|
||||
byName.put(identifier, this);
|
||||
}
|
||||
public CarpetEventServer.Callback create(EventKey key, FunctionValue function, List<Value> extraArgs)
|
||||
|
||||
public CarpetEventServer.Callback create(final EventKey key, final FunctionValue function, final List<Value> extraArgs)
|
||||
{
|
||||
if ((function.getArguments().size()-(extraArgs == null ? 0 : extraArgs.size())) != argcount)
|
||||
if ((function.getArguments().size() - (extraArgs == null ? 0 : extraArgs.size())) != argcount)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return new CarpetEventServer.Callback(key.host(), key.user(), function, extraArgs);
|
||||
}
|
||||
public CarpetEventServer.CallbackResult call(CarpetEventServer.Callback tickCall, Entity entity, Object ... args)
|
||||
|
||||
public CarpetEventServer.CallbackResult call(final CarpetEventServer.Callback tickCall, final Entity entity, final Object... args)
|
||||
{
|
||||
assert args.length == argcount-1;
|
||||
assert args.length == argcount - 1;
|
||||
return tickCall.execute(entity.createCommandSourceStack(), makeArgs(entity, args));
|
||||
}
|
||||
protected List<Value> makeArgs(Entity entity, Object ... args)
|
||||
|
||||
protected List<Value> makeArgs(final Entity entity, final Object... args)
|
||||
{
|
||||
return Collections.singletonList(new EntityValue(entity));
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -13,24 +13,33 @@ import java.util.List;
|
||||
public abstract class Fluff
|
||||
{
|
||||
@FunctionalInterface
|
||||
public interface TriFunction<A, B, C, R> { R apply(A a, B b, C c); }
|
||||
public interface TriFunction<A, B, C, R>
|
||||
{
|
||||
R apply(A a, B b, C c);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface TriConsumer<A, B, C> { void accept(A a, B b, C c); }
|
||||
public interface QuadFunction<A, B, C, D, R>
|
||||
{
|
||||
R apply(A a, B b, C c, D d);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface QuadConsumer<A, B, C, D> { void accept(A a, B b, C c, D d); }
|
||||
public interface QuinnFunction<A, B, C, D, E, R>
|
||||
{
|
||||
R apply(A a, B b, C c, D d, E e);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface QuadFunction<A, B, C, D, R> { R apply(A a, B b, C c, D d);}
|
||||
public interface SexFunction<A, B, C, D, E, F, R>
|
||||
{
|
||||
R apply(A a, B b, C c, D d, E e, F f);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface QuinnFunction<A, B, C, D, E, R> { R apply(A a, B b, C c, D d, E e);}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface SexFunction<A, B, C, D, E, F, R> { R apply(A a, B b, C c, D d, E e, F f);}
|
||||
|
||||
public interface UsageProvider { String getUsage();}
|
||||
public interface UsageProvider
|
||||
{
|
||||
String getUsage();
|
||||
}
|
||||
|
||||
public interface EvalNode
|
||||
{
|
||||
@@ -49,7 +58,11 @@ public abstract class Fluff
|
||||
/**
|
||||
* @return required argument eval type in case its evaluated statically without context but with a given context type
|
||||
*/
|
||||
default Context.Type staticType(Context.Type outerType) {return transitive()?outerType:Context.NONE;};
|
||||
default Context.Type staticType(final Context.Type outerType)
|
||||
{
|
||||
return transitive() ? outerType : Context.NONE;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public interface ILazyFunction extends EvalNode
|
||||
@@ -63,9 +76,11 @@ public abstract class Fluff
|
||||
static void checkInterrupts()
|
||||
{
|
||||
if (ScriptHost.mainThread != Thread.currentThread() && Thread.currentThread().isInterrupted())
|
||||
{
|
||||
throw new InternalExpressionException("Thread interrupted");
|
||||
}
|
||||
}
|
||||
// lazy function has a chance to change execution based on contxt
|
||||
// lazy function has a chance to change execution based on context
|
||||
}
|
||||
|
||||
public interface IFunction extends ILazyFunction
|
||||
@@ -92,94 +107,109 @@ public abstract class Fluff
|
||||
protected String name;
|
||||
int numParams;
|
||||
|
||||
public AbstractLazyFunction(int numParams, String name)
|
||||
public AbstractLazyFunction(final int numParams, final String name)
|
||||
{
|
||||
super();
|
||||
this.numParams = numParams;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
|
||||
public String getName() {
|
||||
public String getName()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getNumParams() {
|
||||
public int getNumParams()
|
||||
{
|
||||
return numParams;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean numParamsVaries() {
|
||||
public boolean numParamsVaries()
|
||||
{
|
||||
return numParams < 0;
|
||||
}
|
||||
|
||||
public static List<Value> unpackLazy(List<LazyValue> lzargs, Context c, Context.Type contextType)
|
||||
public static List<Value> unpackLazy(final List<LazyValue> lzargs, final Context c, final Context.Type contextType)
|
||||
{
|
||||
List<Value> args = new ArrayList<>();
|
||||
for (LazyValue lv : lzargs)
|
||||
final List<Value> args = new ArrayList<>();
|
||||
for (final LazyValue lv : lzargs)
|
||||
{
|
||||
Value arg = lv.evalValue(c, contextType);
|
||||
final Value arg = lv.evalValue(c, contextType);
|
||||
if (arg instanceof FunctionUnpackedArgumentsValue)
|
||||
{
|
||||
args.addAll(((ListValue) arg).getItems());
|
||||
}
|
||||
else
|
||||
{
|
||||
args.add(arg);
|
||||
}
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
public List<Value> unpackArgs(List<LazyValue> lzargs, Context c, Context.Type contextType)
|
||||
public List<Value> unpackArgs(final List<LazyValue> lzargs, final Context c, final Context.Type contextType)
|
||||
{
|
||||
List<Value> args = unpackLazy(lzargs, c, contextType);
|
||||
final List<Value> args = unpackLazy(lzargs, c, contextType);
|
||||
if (!numParamsVaries() && getNumParams() != args.size())
|
||||
{
|
||||
throw new InternalExpressionException("Function " + getName() + " expected " + getNumParams() + " parameters, got " + args.size());
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
public static List<LazyValue> lazify(List<Value> args)
|
||||
public static List<LazyValue> lazify(final List<Value> args)
|
||||
{
|
||||
List<LazyValue> lzargs = new ArrayList<>(args.size());
|
||||
args.forEach( v -> lzargs.add( (c, t) -> v));
|
||||
final List<LazyValue> lzargs = new ArrayList<>(args.size());
|
||||
args.forEach(v -> lzargs.add((c, t) -> v));
|
||||
return lzargs;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract static class AbstractFunction extends AbstractLazyFunction implements IFunction
|
||||
{
|
||||
AbstractFunction(int numParams, String name) {
|
||||
AbstractFunction(final int numParams, final String name)
|
||||
{
|
||||
super(numParams, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean pure() {
|
||||
public boolean pure()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean transitive() {
|
||||
public boolean transitive()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LazyValue lazyEval(Context cc, Context.Type type, Expression e, Tokenizer.Token t, final List<LazyValue> lazyParams)
|
||||
public LazyValue lazyEval(final Context cc, final Context.Type type, final Expression e, final Tokenizer.Token t, final List<LazyValue> lazyParams)
|
||||
{
|
||||
|
||||
return new LazyValue()
|
||||
{ // eager evaluation always ignores the required type and evals params by none default
|
||||
private List<Value> params;
|
||||
|
||||
@Override
|
||||
public Value evalValue(Context c, Context.Type type)
|
||||
public Value evalValue(final Context c, final Context.Type type)
|
||||
{
|
||||
ILazyFunction.checkInterrupts();
|
||||
try
|
||||
{
|
||||
return AbstractFunction.this.eval(getParams(c));
|
||||
}
|
||||
catch (RuntimeException exc)
|
||||
catch (final RuntimeException exc)
|
||||
{
|
||||
throw Expression.handleCodeException(cc, exc, e, t);
|
||||
}
|
||||
}
|
||||
private List<Value> getParams(Context c)
|
||||
|
||||
private List<Value> getParams(final Context c)
|
||||
{
|
||||
if (params == null)
|
||||
{
|
||||
@@ -202,18 +232,22 @@ public abstract class Fluff
|
||||
|
||||
boolean leftAssoc;
|
||||
|
||||
AbstractLazyOperator(int precedence, boolean leftAssoc) {
|
||||
AbstractLazyOperator(final int precedence, final boolean leftAssoc)
|
||||
{
|
||||
super();
|
||||
this.precedence = precedence;
|
||||
this.leftAssoc = leftAssoc;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPrecedence() {
|
||||
public int getPrecedence()
|
||||
{
|
||||
return precedence;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLeftAssoc() {
|
||||
public boolean isLeftAssoc()
|
||||
{
|
||||
return leftAssoc;
|
||||
}
|
||||
|
||||
@@ -222,28 +256,32 @@ public abstract class Fluff
|
||||
public abstract static class AbstractOperator extends AbstractLazyOperator implements IOperator
|
||||
{
|
||||
|
||||
AbstractOperator(int precedence, boolean leftAssoc) {
|
||||
AbstractOperator(final int precedence, final boolean leftAssoc)
|
||||
{
|
||||
super(precedence, leftAssoc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean pure() {
|
||||
public boolean pure()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean transitive() {
|
||||
public boolean transitive()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LazyValue lazyEval(Context cc, Context.Type type, Expression e, Tokenizer.Token t, final LazyValue v1, final LazyValue v2)
|
||||
public LazyValue lazyEval(final Context cc, final Context.Type type, final Expression e, final Tokenizer.Token t, final LazyValue v1, final LazyValue v2)
|
||||
{
|
||||
return (c, typeIgnored) -> {
|
||||
try
|
||||
{
|
||||
return AbstractOperator.this.eval(v1.evalValue(c, Context.Type.NONE), v2.evalValue(c, Context.Type.NONE));
|
||||
}
|
||||
catch (RuntimeException exc)
|
||||
catch (final RuntimeException exc)
|
||||
{
|
||||
throw Expression.handleCodeException(cc, exc, e, t);
|
||||
}
|
||||
@@ -253,21 +291,25 @@ public abstract class Fluff
|
||||
|
||||
public abstract static class AbstractUnaryOperator extends AbstractOperator
|
||||
{
|
||||
AbstractUnaryOperator(int precedence, boolean leftAssoc) {
|
||||
AbstractUnaryOperator(final int precedence, final boolean leftAssoc)
|
||||
{
|
||||
super(precedence, leftAssoc);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean pure() {
|
||||
public boolean pure()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean transitive() {
|
||||
public boolean transitive()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LazyValue lazyEval(Context cc, Context.Type type, Expression e, Tokenizer.Token t, final LazyValue v1, final LazyValue v2)
|
||||
public LazyValue lazyEval(final Context cc, final Context.Type type, final Expression e, final Tokenizer.Token t, final LazyValue v1, final LazyValue v2)
|
||||
{
|
||||
if (v2 != null)
|
||||
{
|
||||
@@ -278,7 +320,7 @@ public abstract class Fluff
|
||||
{
|
||||
return AbstractUnaryOperator.this.evalUnary(v1.evalValue(c, Context.Type.NONE));
|
||||
}
|
||||
catch (RuntimeException exc)
|
||||
catch (final RuntimeException exc)
|
||||
{
|
||||
throw Expression.handleCodeException(cc, exc, e, t);
|
||||
}
|
||||
@@ -286,7 +328,7 @@ public abstract class Fluff
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value eval(Value v1, Value v2)
|
||||
public Value eval(final Value v1, final Value v2)
|
||||
{
|
||||
throw new IllegalStateException("Shouldn't end up here");
|
||||
}
|
||||
|
||||
@@ -2,7 +2,9 @@ package carpet.script;
|
||||
|
||||
import carpet.script.value.Value;
|
||||
|
||||
/** LazyNumber interface created for lazily evaluated functions */
|
||||
/**
|
||||
* LazyNumber interface created for lazily evaluated functions
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface LazyValue
|
||||
{
|
||||
@@ -11,13 +13,15 @@ public interface LazyValue
|
||||
LazyValue NULL = (c, t) -> Value.NULL;
|
||||
LazyValue ZERO = (c, t) -> Value.ZERO;
|
||||
|
||||
public static LazyValue ofConstant(Value val) {
|
||||
static LazyValue ofConstant(final Value val)
|
||||
{
|
||||
return new Constant(val);
|
||||
}
|
||||
|
||||
Value evalValue(Context c, Context.Type type);
|
||||
|
||||
default Value evalValue(Context c){
|
||||
default Value evalValue(final Context c)
|
||||
{
|
||||
return evalValue(c, Context.Type.NONE);
|
||||
}
|
||||
|
||||
@@ -28,7 +32,8 @@ public interface LazyValue
|
||||
Value evalType(Context.Type type);
|
||||
|
||||
@Override
|
||||
default Value evalValue(Context c, Context.Type type) {
|
||||
default Value evalValue(final Context c, final Context.Type type)
|
||||
{
|
||||
return evalType(type);
|
||||
}
|
||||
}
|
||||
@@ -38,21 +43,26 @@ public interface LazyValue
|
||||
{
|
||||
Value result;
|
||||
|
||||
public Constant(Value value)
|
||||
public Constant(final Value value)
|
||||
{
|
||||
result = value;
|
||||
}
|
||||
|
||||
public Value get() {return result;}
|
||||
public Value get()
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value evalType(Context.Type type) {
|
||||
public Value evalType(final Context.Type type)
|
||||
{
|
||||
|
||||
return result.fromConstant();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value evalValue(Context c, Context.Type type) {
|
||||
public Value evalValue(final Context c, final Context.Type type)
|
||||
{
|
||||
return result.fromConstant();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,23 +14,24 @@ import carpet.script.argument.FileArgument;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.world.level.storage.LevelResource;
|
||||
|
||||
public record Module(String name, String code, boolean library) {
|
||||
public record Module(String name, String code, boolean library)
|
||||
{
|
||||
public Module
|
||||
{
|
||||
Objects.requireNonNull(name);
|
||||
Objects.requireNonNull(code);
|
||||
}
|
||||
|
||||
public static Module fromPath(Path path)
|
||||
public static Module fromPath(final Path path)
|
||||
{
|
||||
boolean library = path.getFileName().toString().endsWith(".scl");
|
||||
final boolean library = path.getFileName().toString().endsWith(".scl");
|
||||
try
|
||||
{
|
||||
String name = path.getFileName().toString().replaceFirst("\\.scl?","").toLowerCase(Locale.ROOT);
|
||||
String code = new String(Files.readAllBytes(path), StandardCharsets.UTF_8);
|
||||
final String name = path.getFileName().toString().replaceFirst("\\.scl?", "").toLowerCase(Locale.ROOT);
|
||||
final String code = new String(Files.readAllBytes(path), StandardCharsets.UTF_8);
|
||||
return new Module(name, code, library);
|
||||
}
|
||||
catch (IOException e)
|
||||
catch (final IOException e)
|
||||
{
|
||||
throw new IllegalArgumentException("Failed to load scarpet module", e);
|
||||
}
|
||||
@@ -38,79 +39,96 @@ public record Module(String name, String code, boolean library) {
|
||||
|
||||
/**
|
||||
* Creates a new {@link Module} with an app located in Carpet's JAR.
|
||||
*
|
||||
* @param scriptName A {@link String} being the name of the script. The extension will be autocompleted
|
||||
* @param isLibrary A {@link boolean} indicating whether or not the script is a library
|
||||
* @param isLibrary A {@link boolean} indicating whether or not the script is a library
|
||||
* @return The created {@link BundledModule}
|
||||
*/
|
||||
public static Module carpetNative(String scriptName, boolean isLibrary)
|
||||
public static Module carpetNative(final String scriptName, final boolean isLibrary)
|
||||
{
|
||||
return fromJarPath("assets/carpet/scripts/", scriptName, isLibrary);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new {@link Module} with an app located at a specified path inside some mod's JAR.
|
||||
* @see #fromJarPathWithCustomName(String, String, boolean)
|
||||
*
|
||||
* @param path A {@link String} being the path to the directory where the app is located.
|
||||
*
|
||||
* @param path A {@link String} being the path to the directory where the app is located.
|
||||
* @param scriptName A {@link String} being the name of the script. The extension will be autocompleted
|
||||
* @param isLibrary A {@link boolean} indicating whether or not the script is a library
|
||||
* @param isLibrary A {@link boolean} indicating whether or not the script is a library
|
||||
* @return The created {@link BundledModule}
|
||||
* @see #fromJarPathWithCustomName(String, String, boolean)
|
||||
*/
|
||||
public static Module fromJarPath(String path, String scriptName, boolean isLibrary) {
|
||||
return fromJarPathWithCustomName(path + scriptName + (isLibrary ? ".scl":".sc"), scriptName, isLibrary);
|
||||
public static Module fromJarPath(final String path, final String scriptName, final boolean isLibrary)
|
||||
{
|
||||
return fromJarPathWithCustomName(path + scriptName + (isLibrary ? ".scl" : ".sc"), scriptName, isLibrary);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new {@link Module} with an app located at the specified fullPath (inside a mod jar)with a custom name.
|
||||
* @see #fromJarPath(String, String, boolean)
|
||||
*
|
||||
* @param fullPath A {@link String} being the full path to the app's code, including file and extension.
|
||||
*
|
||||
* @param fullPath A {@link String} being the full path to the app's code, including file and extension.
|
||||
* @param customName A {@link String} being the custom name for the script.
|
||||
* @param isLibrary A {@link boolean} indicating whether or not the script is a library
|
||||
* @param isLibrary A {@link boolean} indicating whether or not the script is a library
|
||||
* @return The created {@link Module}
|
||||
* @see #fromJarPath(String, String, boolean)
|
||||
*/
|
||||
public static Module fromJarPathWithCustomName(String fullPath, String customName, boolean isLibrary) {
|
||||
public static Module fromJarPathWithCustomName(final String fullPath, final String customName, final boolean isLibrary)
|
||||
{
|
||||
try
|
||||
{
|
||||
String name = customName.toLowerCase(Locale.ROOT);
|
||||
String code = IOUtils.toString(
|
||||
final String name = customName.toLowerCase(Locale.ROOT);
|
||||
final String code = IOUtils.toString(
|
||||
Module.class.getClassLoader().getResourceAsStream(fullPath),
|
||||
StandardCharsets.UTF_8
|
||||
);
|
||||
return new Module(name, code, isLibrary);
|
||||
}
|
||||
catch (IOException e)
|
||||
catch (final IOException e)
|
||||
{
|
||||
throw new IllegalArgumentException("Failed to load bundled module", e);
|
||||
}
|
||||
}
|
||||
|
||||
public static Tag getData(Module module)
|
||||
|
||||
public static Tag getData(final Module module)
|
||||
{
|
||||
Path dataFile = resolveResource(module);
|
||||
if (dataFile == null) return null;
|
||||
if (!Files.exists(dataFile) || !(Files.isRegularFile(dataFile))) return null;
|
||||
synchronized (FileArgument.writeIOSync) { return FileArgument.readTag(dataFile); }
|
||||
final Path dataFile = resolveResource(module);
|
||||
if (dataFile == null || !Files.exists(dataFile) || !(Files.isRegularFile(dataFile)))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
synchronized (FileArgument.writeIOSync)
|
||||
{
|
||||
return FileArgument.readTag(dataFile);
|
||||
}
|
||||
}
|
||||
|
||||
public static void saveData(Module module, Tag globalState)
|
||||
public static void saveData(final Module module, final Tag globalState)
|
||||
{
|
||||
Path dataFile = resolveResource(module);
|
||||
if (dataFile == null) return;
|
||||
if (!Files.exists(dataFile.getParent())) {
|
||||
try {
|
||||
final Path dataFile = resolveResource(module);
|
||||
if (dataFile == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!Files.exists(dataFile.getParent()))
|
||||
{
|
||||
try
|
||||
{
|
||||
Files.createDirectories(dataFile.getParent());
|
||||
} catch (IOException e) {
|
||||
}
|
||||
catch (final IOException e)
|
||||
{
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
synchronized (FileArgument.writeIOSync) { FileArgument.writeTagDisk(globalState, dataFile, false); }
|
||||
synchronized (FileArgument.writeIOSync)
|
||||
{
|
||||
FileArgument.writeTagDisk(globalState, dataFile, false);
|
||||
}
|
||||
}
|
||||
|
||||
private static Path resolveResource(Module module)
|
||||
private static Path resolveResource(final Module module)
|
||||
{
|
||||
if (module == null) return null; // commandline app
|
||||
return CarpetServer.minecraft_server.getWorldPath(LevelResource.ROOT).resolve("scripts/"+module.name()+".data.nbt");
|
||||
return module == null ? null : CarpetServer.minecraft_server.getWorldPath(LevelResource.ROOT).resolve("scripts/" + module.name() + ".data.nbt"); // commandline app
|
||||
}
|
||||
|
||||
}
|
||||
@@ -38,41 +38,45 @@ public abstract class ScriptHost
|
||||
|
||||
private final Set<String> deprecations = new HashSet<>();
|
||||
|
||||
public Random getRandom(long aLong)
|
||||
public Random getRandom(final long aLong)
|
||||
{
|
||||
if (randomizers.size() > 65536)
|
||||
{
|
||||
randomizers.clear();
|
||||
}
|
||||
return randomizers.computeIfAbsent(aLong, Random::new);
|
||||
}
|
||||
|
||||
public boolean resetRandom(long aLong) {
|
||||
public boolean resetRandom(final long aLong)
|
||||
{
|
||||
return randomizers.remove(aLong) != null;
|
||||
}
|
||||
|
||||
public static class ModuleData
|
||||
{
|
||||
Module parent;
|
||||
public Map<String, FunctionValue> globalFunctions = new Object2ObjectOpenHashMap<>();
|
||||
public Map<String, LazyValue> globalVariables = new Object2ObjectOpenHashMap<>();
|
||||
public Map<String, ModuleData> functionImports = new Object2ObjectOpenHashMap<>(); // imported functions string to module
|
||||
public Map<String, ModuleData> globalsImports = new Object2ObjectOpenHashMap<>(); // imported global variables string to module
|
||||
public Map<String, ModuleData> futureImports = new Object2ObjectOpenHashMap<>(); // imports not known before used
|
||||
public final Map<String, FunctionValue> globalFunctions = new Object2ObjectOpenHashMap<>();
|
||||
public final Map<String, LazyValue> globalVariables = new Object2ObjectOpenHashMap<>();
|
||||
public final Map<String, ModuleData> functionImports = new Object2ObjectOpenHashMap<>(); // imported functions string to module
|
||||
public final Map<String, ModuleData> globalsImports = new Object2ObjectOpenHashMap<>(); // imported global variables string to module
|
||||
public final Map<String, ModuleData> futureImports = new Object2ObjectOpenHashMap<>(); // imports not known before used
|
||||
|
||||
public ModuleData(Module parent, ModuleData other)
|
||||
public ModuleData(final Module parent, final ModuleData other)
|
||||
{
|
||||
super();
|
||||
// imports are just pointers, but they still point to the wrong modules (point to the parent)
|
||||
this.parent = parent;
|
||||
globalFunctions.putAll(other.globalFunctions);
|
||||
other.globalVariables.forEach((key, value) ->
|
||||
{
|
||||
Value var = value.evalValue(null);
|
||||
Value copy = var.deepcopy();
|
||||
final Value var = value.evalValue(null);
|
||||
final Value copy = var.deepcopy();
|
||||
copy.boundVariable = var.boundVariable;
|
||||
globalVariables.put(key, (c, t) -> copy);
|
||||
});
|
||||
}
|
||||
|
||||
public void setImportsBasedOn(ScriptHost host, ModuleData other)
|
||||
public void setImportsBasedOn(final ScriptHost host, final ModuleData other)
|
||||
{
|
||||
// fixing imports
|
||||
other.functionImports.forEach((name, targetData) -> {
|
||||
@@ -87,20 +91,24 @@ public abstract class ScriptHost
|
||||
|
||||
}
|
||||
|
||||
public ModuleData(Module parent)
|
||||
public ModuleData(final Module parent)
|
||||
{
|
||||
this.parent = parent;
|
||||
}
|
||||
}
|
||||
|
||||
protected final Map<String, ScriptHost> userHosts = new Object2ObjectOpenHashMap<>();
|
||||
private Map<Module,ModuleData> moduleData = new HashMap<>(); // marking imports
|
||||
private Map<String,Module> modules = new HashMap<>();
|
||||
private final Map<Module, ModuleData> moduleData = new HashMap<>(); // marking imports
|
||||
private final Map<String, Module> modules = new HashMap<>();
|
||||
|
||||
protected ScriptHost parent;
|
||||
protected boolean perUser;
|
||||
public String user;
|
||||
|
||||
public String getName() {return main ==null?null: main.name();}
|
||||
public String getName()
|
||||
{
|
||||
return main == null ? null : main.name();
|
||||
}
|
||||
|
||||
public final Module main;
|
||||
|
||||
@@ -109,9 +117,10 @@ public abstract class ScriptHost
|
||||
{
|
||||
List<String> apply(Expression expression, Tokenizer.Token token, Context context, String message);
|
||||
}
|
||||
|
||||
public ErrorSnooper errorSnooper = null;
|
||||
|
||||
protected ScriptHost(Module code, ScriptServer scriptServer, boolean perUser, ScriptHost parent)
|
||||
protected ScriptHost(final Module code, final ScriptServer scriptServer, final boolean perUser, final ScriptHost parent)
|
||||
{
|
||||
this.parent = parent;
|
||||
this.main = code;
|
||||
@@ -119,24 +128,30 @@ public abstract class ScriptHost
|
||||
this.user = null;
|
||||
this.strict = false;
|
||||
this.scriptServer = scriptServer;
|
||||
ModuleData moduleData = new ModuleData(code);
|
||||
final ModuleData moduleData = new ModuleData(code);
|
||||
initializeModuleGlobals(moduleData);
|
||||
this.moduleData.put(code, moduleData);
|
||||
this.modules.put(code==null?null:code.name(), code);
|
||||
this.modules.put(code == null ? null : code.name(), code);
|
||||
mainThread = Thread.currentThread();
|
||||
}
|
||||
|
||||
void initializeModuleGlobals(ModuleData md)
|
||||
void initializeModuleGlobals(final ModuleData md)
|
||||
{
|
||||
}
|
||||
|
||||
public void importModule(Context c, String moduleName)
|
||||
public void importModule(final Context c, final String moduleName)
|
||||
{
|
||||
if (modules.containsKey(moduleName.toLowerCase(Locale.ROOT))) return; // aready imported
|
||||
Module module = getModuleOrLibraryByName(moduleName);
|
||||
if (modules.containsKey(module.name())) return; // aready imported, once again, in case some discrepancies in names?
|
||||
if (modules.containsKey(moduleName.toLowerCase(Locale.ROOT)))
|
||||
{
|
||||
return; // aready imported
|
||||
}
|
||||
final Module module = getModuleOrLibraryByName(moduleName);
|
||||
if (modules.containsKey(module.name()))
|
||||
{
|
||||
return; // aready imported, once again, in case some discrepancies in names?
|
||||
}
|
||||
modules.put(module.name(), module);
|
||||
ModuleData data = new ModuleData(module);
|
||||
final ModuleData data = new ModuleData(module);
|
||||
initializeModuleGlobals(data);
|
||||
moduleData.put(module, data);
|
||||
runModuleCode(c, module);
|
||||
@@ -144,20 +159,21 @@ public abstract class ScriptHost
|
||||
//modules.remove(module.getName());
|
||||
//throw new InternalExpressionException("Failed to import a module "+moduleName);
|
||||
}
|
||||
public void importNames(Context c, Module targetModule, String sourceModuleName, List<String> identifiers )
|
||||
|
||||
public void importNames(final Context c, final Module targetModule, final String sourceModuleName, final List<String> identifiers)
|
||||
{
|
||||
if (!moduleData.containsKey(targetModule))
|
||||
{
|
||||
throw new InternalExpressionException("Cannot import to module that doesn't exist");
|
||||
}
|
||||
Module source = modules.get(sourceModuleName);
|
||||
ModuleData sourceData = moduleData.get(source);
|
||||
ModuleData targetData = moduleData.get(targetModule);
|
||||
final Module source = modules.get(sourceModuleName);
|
||||
final ModuleData sourceData = moduleData.get(source);
|
||||
final ModuleData targetData = moduleData.get(targetModule);
|
||||
if (sourceData == null || targetData == null)
|
||||
{
|
||||
throw new InternalExpressionException("Cannot import from module that is not imported");
|
||||
}
|
||||
for (String identifier: identifiers)
|
||||
for (final String identifier : identifiers)
|
||||
{
|
||||
if (sourceData.globalFunctions.containsKey(identifier))
|
||||
{
|
||||
@@ -174,10 +190,10 @@ public abstract class ScriptHost
|
||||
}
|
||||
}
|
||||
|
||||
public Stream<String> availableImports(String moduleName)
|
||||
public Stream<String> availableImports(final String moduleName)
|
||||
{
|
||||
Module source = modules.get(moduleName);
|
||||
ModuleData sourceData = moduleData.get(source);
|
||||
final Module source = modules.get(moduleName);
|
||||
final ModuleData sourceData = moduleData.get(source);
|
||||
if (sourceData == null)
|
||||
{
|
||||
throw new InternalExpressionException("Cannot import from module that is not imported");
|
||||
@@ -192,136 +208,195 @@ public abstract class ScriptHost
|
||||
|
||||
protected abstract void runModuleCode(Context c, Module module); // this should be shell out in the executor
|
||||
|
||||
public FunctionValue getFunction(String name) { return getFunction(main, name); }
|
||||
public FunctionValue getAssertFunction(Module module, String name)
|
||||
public FunctionValue getFunction(final String name)
|
||||
{
|
||||
FunctionValue ret = getFunction(module, name);
|
||||
return getFunction(main, name);
|
||||
}
|
||||
|
||||
public FunctionValue getAssertFunction(final Module module, final String name)
|
||||
{
|
||||
final FunctionValue ret = getFunction(module, name);
|
||||
if (ret == null)
|
||||
{
|
||||
if (module == main)
|
||||
throw new InternalExpressionException("Function '"+name+"' is not defined yet");
|
||||
{
|
||||
throw new InternalExpressionException("Function '" + name + "' is not defined yet");
|
||||
}
|
||||
else
|
||||
throw new InternalExpressionException("Function '"+name+"' is not defined nor visible by its name in the imported module '"+module.name()+"'");
|
||||
{
|
||||
throw new InternalExpressionException("Function '" + name + "' is not defined nor visible by its name in the imported module '" + module.name() + "'");
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
private FunctionValue getFunction(Module module, String name)
|
||||
|
||||
private FunctionValue getFunction(final Module module, final String name)
|
||||
{
|
||||
ModuleData local = getModuleData(module);
|
||||
final ModuleData local = getModuleData(module);
|
||||
FunctionValue ret = local.globalFunctions.get(name); // most uses would be from local scope anyways
|
||||
if (ret != null) return ret;
|
||||
if (ret != null)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
ModuleData target = local.functionImports.get(name);
|
||||
if (target != null)
|
||||
{
|
||||
ret = target.globalFunctions.get(name);
|
||||
if (ret != null) return ret;
|
||||
if (ret != null)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
// not in local scope - will need to travel over import links
|
||||
target = local.futureImports.get(name);
|
||||
if (target == null) return null;
|
||||
if (target == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
target = findModuleDataFromFunctionImports(name, target, 0);
|
||||
if (target == null) return null;
|
||||
if (target == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
local.futureImports.remove(name);
|
||||
local.functionImports.put(name, target);
|
||||
return target.globalFunctions.get(name);
|
||||
}
|
||||
|
||||
private ModuleData findModuleDataFromFunctionImports(String name, ModuleData source, int ttl)
|
||||
private ModuleData findModuleDataFromFunctionImports(final String name, final ModuleData source, final int ttl)
|
||||
{
|
||||
if (ttl > 64) throw new InternalExpressionException("Cannot import "+name+", either your imports are too deep or too loopy");
|
||||
if (ttl > 64)
|
||||
{
|
||||
throw new InternalExpressionException("Cannot import " + name + ", either your imports are too deep or too loopy");
|
||||
}
|
||||
if (source.globalFunctions.containsKey(name))
|
||||
{
|
||||
return source;
|
||||
}
|
||||
if (source.functionImports.containsKey(name))
|
||||
return findModuleDataFromFunctionImports(name, source.functionImports.get(name), ttl+1);
|
||||
{
|
||||
return findModuleDataFromFunctionImports(name, source.functionImports.get(name), ttl + 1);
|
||||
}
|
||||
if (source.futureImports.containsKey(name))
|
||||
return findModuleDataFromFunctionImports(name, source.futureImports.get(name), ttl+1);
|
||||
{
|
||||
return findModuleDataFromFunctionImports(name, source.futureImports.get(name), ttl + 1);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public LazyValue getGlobalVariable(String name) { return getGlobalVariable(main, name); }
|
||||
public LazyValue getGlobalVariable(Module module, String name)
|
||||
public LazyValue getGlobalVariable(final String name)
|
||||
{
|
||||
ModuleData local = getModuleData(module);
|
||||
return getGlobalVariable(main, name);
|
||||
}
|
||||
|
||||
public LazyValue getGlobalVariable(final Module module, final String name)
|
||||
{
|
||||
final ModuleData local = getModuleData(module);
|
||||
LazyValue ret = local.globalVariables.get(name); // most uses would be from local scope anyways
|
||||
if (ret != null) return ret;
|
||||
if (ret != null)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
ModuleData target = local.globalsImports.get(name);
|
||||
if (target != null)
|
||||
{
|
||||
ret = target.globalVariables.get(name);
|
||||
if (ret != null) return ret;
|
||||
if (ret != null)
|
||||
{
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
// not in local scope - will need to travel over import links
|
||||
target = local.futureImports.get(name);
|
||||
if (target == null) return null;
|
||||
if (target == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
target = findModuleDataFromGlobalImports(name, target, 0);
|
||||
if (target == null) return null;
|
||||
if (target == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
local.futureImports.remove(name);
|
||||
local.globalsImports.put(name, target);
|
||||
return target.globalVariables.get(name);
|
||||
}
|
||||
|
||||
private ModuleData findModuleDataFromGlobalImports(String name, ModuleData source, int ttl)
|
||||
private ModuleData findModuleDataFromGlobalImports(final String name, final ModuleData source, final int ttl)
|
||||
{
|
||||
if (ttl > 64) throw new InternalExpressionException("Cannot import "+name+", either your imports are too deep or too loopy");
|
||||
if (ttl > 64)
|
||||
{
|
||||
throw new InternalExpressionException("Cannot import " + name + ", either your imports are too deep or too loopy");
|
||||
}
|
||||
if (source.globalVariables.containsKey(name))
|
||||
{
|
||||
return source;
|
||||
}
|
||||
if (source.globalsImports.containsKey(name))
|
||||
return findModuleDataFromGlobalImports(name, source.globalsImports.get(name), ttl+1);
|
||||
{
|
||||
return findModuleDataFromGlobalImports(name, source.globalsImports.get(name), ttl + 1);
|
||||
}
|
||||
if (source.futureImports.containsKey(name))
|
||||
return findModuleDataFromGlobalImports(name, source.futureImports.get(name), ttl+1);
|
||||
{
|
||||
return findModuleDataFromGlobalImports(name, source.futureImports.get(name), ttl + 1);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void delFunctionWithPrefix(Module module, String prefix)
|
||||
public void delFunctionWithPrefix(final Module module, final String prefix)
|
||||
{
|
||||
ModuleData data = getModuleData(module);
|
||||
final ModuleData data = getModuleData(module);
|
||||
data.globalFunctions.entrySet().removeIf(e -> e.getKey().startsWith(prefix));
|
||||
data.functionImports.entrySet().removeIf(e -> e.getKey().startsWith(prefix));
|
||||
}
|
||||
public void delFunction(Module module, String funName)
|
||||
|
||||
public void delFunction(final Module module, final String funName)
|
||||
{
|
||||
ModuleData data = getModuleData(module);
|
||||
final ModuleData data = getModuleData(module);
|
||||
data.globalFunctions.remove(funName);
|
||||
data.functionImports.remove(funName);
|
||||
}
|
||||
|
||||
public void delGlobalVariableWithPrefix(Module module, String prefix)
|
||||
public void delGlobalVariableWithPrefix(final Module module, final String prefix)
|
||||
{
|
||||
ModuleData data = getModuleData(module);
|
||||
final ModuleData data = getModuleData(module);
|
||||
data.globalVariables.entrySet().removeIf(e -> e.getKey().startsWith(prefix));
|
||||
data.globalsImports.entrySet().removeIf(e -> e.getKey().startsWith(prefix));
|
||||
}
|
||||
|
||||
public void delGlobalVariable(Module module, String varName)
|
||||
public void delGlobalVariable(final Module module, final String varName)
|
||||
{
|
||||
ModuleData data = getModuleData(module);
|
||||
final ModuleData data = getModuleData(module);
|
||||
data.globalFunctions.remove(varName);
|
||||
data.functionImports.remove(varName);
|
||||
}
|
||||
|
||||
private ModuleData getModuleData(Module module)
|
||||
private ModuleData getModuleData(final Module module)
|
||||
{
|
||||
ModuleData data = moduleData.get(module);
|
||||
if (data == null) throw new IntegrityException("Module structure changed for the app. Did you reload the app with tasks running?");
|
||||
final ModuleData data = moduleData.get(module);
|
||||
if (data == null)
|
||||
{
|
||||
throw new IntegrityException("Module structure changed for the app. Did you reload the app with tasks running?");
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
protected void assertAppIntegrity(Module module)
|
||||
protected void assertAppIntegrity(final Module module)
|
||||
{
|
||||
getModuleData(module);
|
||||
}
|
||||
|
||||
public void addUserDefinedFunction(Context ctx, Module module, String name, FunctionValue fun)
|
||||
public void addUserDefinedFunction(final Context ctx, final Module module, final String name, final FunctionValue fun)
|
||||
{
|
||||
getModuleData(module).globalFunctions.put(name, fun);
|
||||
}
|
||||
|
||||
public void setGlobalVariable(Module module, String name, LazyValue lv)
|
||||
public void setGlobalVariable(final Module module, final String name, final LazyValue lv)
|
||||
{
|
||||
getModuleData(module).globalVariables.put(name, lv);
|
||||
}
|
||||
|
||||
public Stream<String> globalVariableNames(Module module, Predicate<String> predicate)
|
||||
public Stream<String> globalVariableNames(final Module module, final Predicate<String> predicate)
|
||||
{
|
||||
return Stream.concat(Stream.concat(
|
||||
getModuleData(module).globalVariables.keySet().stream(),
|
||||
@@ -329,27 +404,33 @@ public abstract class ScriptHost
|
||||
), getModuleData(module).futureImports.keySet().stream().filter(s -> s.startsWith("global_"))).filter(predicate);
|
||||
}
|
||||
|
||||
public Stream<String> globalFunctionNames(Module module, Predicate<String> predicate)
|
||||
public Stream<String> globalFunctionNames(final Module module, final Predicate<String> predicate)
|
||||
{
|
||||
return Stream.concat(Stream.concat(
|
||||
getModuleData(module).globalFunctions.keySet().stream(),
|
||||
getModuleData(module).functionImports.keySet().stream()
|
||||
),getModuleData(module).futureImports.keySet().stream().filter(s -> !s.startsWith("global_"))).filter(predicate);
|
||||
), getModuleData(module).futureImports.keySet().stream().filter(s -> !s.startsWith("global_"))).filter(predicate);
|
||||
}
|
||||
|
||||
public ScriptHost retrieveForExecution(String /*Nullable*/ user)
|
||||
public ScriptHost retrieveForExecution(final String /*Nullable*/ user)
|
||||
{
|
||||
if (!perUser) return this;
|
||||
ScriptHost oldUserHost = userHosts.get(user);
|
||||
if (oldUserHost != null) return oldUserHost;
|
||||
ScriptHost userHost = this.duplicate();
|
||||
if (!perUser)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
final ScriptHost oldUserHost = userHosts.get(user);
|
||||
if (oldUserHost != null)
|
||||
{
|
||||
return oldUserHost;
|
||||
}
|
||||
final ScriptHost userHost = this.duplicate();
|
||||
userHost.user = user;
|
||||
this.setupUserHost(userHost);
|
||||
userHosts.put(user, userHost);
|
||||
return userHost;
|
||||
}
|
||||
|
||||
protected void setupUserHost(ScriptHost host)
|
||||
protected void setupUserHost(final ScriptHost host)
|
||||
{
|
||||
// adding imports
|
||||
host.modules.putAll(this.modules);
|
||||
@@ -358,57 +439,65 @@ public abstract class ScriptHost
|
||||
host.moduleData.forEach((module, data) -> data.setImportsBasedOn(host, this.moduleData.get(data.parent)));
|
||||
}
|
||||
|
||||
synchronized public void handleExpressionException(String msg, ExpressionException exc)
|
||||
public synchronized void handleExpressionException(final String msg, final ExpressionException exc)
|
||||
{
|
||||
System.out.println(msg+": "+exc);
|
||||
System.out.println(msg + ": " + exc);
|
||||
}
|
||||
|
||||
|
||||
protected abstract ScriptHost duplicate();
|
||||
|
||||
public Object getLock(Value name)
|
||||
public Object getLock(final Value name)
|
||||
{
|
||||
return locks.computeIfAbsent(name, (n) -> new Object());
|
||||
return locks.computeIfAbsent(name, n -> new Object());
|
||||
}
|
||||
|
||||
public ThreadPoolExecutor getExecutor(Value pool)
|
||||
public ThreadPoolExecutor getExecutor(final Value pool)
|
||||
{
|
||||
if (inTermination) return null;
|
||||
return executorServices.computeIfAbsent(pool, (v) -> (ThreadPoolExecutor) Executors.newCachedThreadPool());
|
||||
if (inTermination)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return executorServices.computeIfAbsent(pool, v -> (ThreadPoolExecutor) Executors.newCachedThreadPool());
|
||||
}
|
||||
|
||||
public int taskCount()
|
||||
{
|
||||
return executorServices.values().stream().map(ThreadPoolExecutor::getActiveCount).reduce(0, Integer::sum);
|
||||
}
|
||||
public int taskCount(Value pool)
|
||||
|
||||
public int taskCount(final Value pool)
|
||||
{
|
||||
if (executorServices.containsKey(pool))
|
||||
{
|
||||
return executorServices.get(pool).getActiveCount();
|
||||
}
|
||||
return 0;
|
||||
return executorServices.containsKey(pool) ? executorServices.get(pool).getActiveCount() : 0;
|
||||
}
|
||||
|
||||
public void onClose()
|
||||
{
|
||||
inTermination = true;
|
||||
executorServices.values().forEach(ThreadPoolExecutor::shutdown);
|
||||
for (ScriptHost uh : userHosts.values()) uh.onClose();
|
||||
for (final ScriptHost uh : userHosts.values())
|
||||
{
|
||||
uh.onClose();
|
||||
}
|
||||
if (taskCount() > 0)
|
||||
{
|
||||
executorServices.values().forEach(e -> {
|
||||
ExecutorService stopper = Executors.newSingleThreadExecutor();
|
||||
stopper.submit( () -> {
|
||||
try {
|
||||
final ExecutorService stopper = Executors.newSingleThreadExecutor();
|
||||
stopper.submit(() -> {
|
||||
try
|
||||
{
|
||||
// Wait a while for existing tasks to terminate
|
||||
if (!e.awaitTermination(1500, TimeUnit.MILLISECONDS)) {
|
||||
if (!e.awaitTermination(1500, TimeUnit.MILLISECONDS))
|
||||
{
|
||||
e.shutdownNow(); // Cancel currently executing tasks
|
||||
// Wait a while for tasks to respond to being cancelled
|
||||
if (!e.awaitTermination(1500, TimeUnit.MILLISECONDS))
|
||||
{
|
||||
CarpetScriptServer.LOG.error("Failed to stop app's thread");
|
||||
}
|
||||
}
|
||||
} catch (InterruptedException ie) {
|
||||
}
|
||||
catch (final InterruptedException ie)
|
||||
{
|
||||
// (Re-)Cancel if current thread also interrupted
|
||||
e.shutdownNow();
|
||||
// Preserve interrupt status
|
||||
@@ -421,12 +510,15 @@ public abstract class ScriptHost
|
||||
}
|
||||
}
|
||||
|
||||
public void setPerPlayer(boolean isPerUser)
|
||||
public void setPerPlayer(final boolean isPerUser)
|
||||
{
|
||||
perUser = isPerUser;
|
||||
}
|
||||
|
||||
public boolean isPerUser() {return perUser;}
|
||||
public boolean isPerUser()
|
||||
{
|
||||
return perUser;
|
||||
}
|
||||
|
||||
public Set<String> getUserList()
|
||||
{
|
||||
@@ -436,21 +528,24 @@ public abstract class ScriptHost
|
||||
|
||||
public void resetErrorSnooper()
|
||||
{
|
||||
errorSnooper=null;
|
||||
errorSnooper = null;
|
||||
}
|
||||
|
||||
public static final Logger DEPRECATION_LOG = LoggerFactory.getLogger("Scarpet Deprecation Warnings");
|
||||
|
||||
public boolean issueDeprecation(String feature)
|
||||
public boolean issueDeprecation(final String feature)
|
||||
{
|
||||
if (deprecations.contains(feature)) return false;
|
||||
if (deprecations.contains(feature))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
deprecations.add(feature);
|
||||
DEPRECATION_LOG.warn("'"+feature+"' is deprecated and soon will be removed. Please consult the docs for their replacement");
|
||||
DEPRECATION_LOG.warn("'" + feature + "' is deprecated and soon will be removed. Please consult the docs for their replacement");
|
||||
return true;
|
||||
}
|
||||
|
||||
public ScriptServer scriptServer()
|
||||
{
|
||||
return scriptServer;
|
||||
}
|
||||
public ScriptServer scriptServer()
|
||||
{
|
||||
return scriptServer;
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
import carpet.script.value.Value;
|
||||
|
||||
// WIP
|
||||
public class ScriptServer {
|
||||
public class ScriptServer
|
||||
{
|
||||
public final Map<Value, Value> systemGlobals = new ConcurrentHashMap<>();
|
||||
}
|
||||
@@ -16,25 +16,35 @@ import java.util.stream.StreamSupport;
|
||||
*/
|
||||
public class Tokenizer implements Iterator<Tokenizer.Token>
|
||||
{
|
||||
/** What character to use for decimal separators. */
|
||||
/**
|
||||
* What character to use for decimal separators.
|
||||
*/
|
||||
private static final char decimalSeparator = '.';
|
||||
/** What character to use for minus sign (negative values). */
|
||||
/**
|
||||
* What character to use for minus sign (negative values).
|
||||
*/
|
||||
private static final char minusSign = '-';
|
||||
/** Actual position in expression string. */
|
||||
/**
|
||||
* Actual position in expression string.
|
||||
*/
|
||||
private int pos = 0;
|
||||
private int lineno = 0;
|
||||
private int linepos = 0;
|
||||
private boolean comments;
|
||||
private boolean newLinesMarkers;
|
||||
/** The original input expression. */
|
||||
private String input;
|
||||
/** The previous token or <code>null</code> if none. */
|
||||
private final boolean comments;
|
||||
private final boolean newLinesMarkers;
|
||||
/**
|
||||
* The original input expression.
|
||||
*/
|
||||
private final String input;
|
||||
/**
|
||||
* The previous token or <code>null</code> if none.
|
||||
*/
|
||||
private Token previousToken;
|
||||
|
||||
private Expression expression;
|
||||
private Context context;
|
||||
private final Expression expression;
|
||||
private final Context context;
|
||||
|
||||
Tokenizer(Context c, Expression expr, String input, boolean allowComments, boolean allowNewLineMakers)
|
||||
Tokenizer(final Context c, final Expression expr, final String input, final boolean allowComments, final boolean allowNewLineMakers)
|
||||
{
|
||||
this.input = input;
|
||||
this.expression = expr;
|
||||
@@ -45,16 +55,18 @@ public class Tokenizer implements Iterator<Tokenizer.Token>
|
||||
|
||||
public List<Token> postProcess()
|
||||
{
|
||||
Iterable<Token> iterable = () -> this;
|
||||
List<Token> originalTokens = StreamSupport.stream(iterable.spliterator(), false).collect(Collectors.toList());
|
||||
List<Token> cleanedTokens = new ArrayList<>();
|
||||
final Iterable<Token> iterable = () -> this;
|
||||
final List<Token> originalTokens = StreamSupport.stream(iterable.spliterator(), false).collect(Collectors.toList());
|
||||
final List<Token> cleanedTokens = new ArrayList<>();
|
||||
Token last = null;
|
||||
while (originalTokens.size() > 0)
|
||||
while (!originalTokens.isEmpty())
|
||||
{
|
||||
Token current = originalTokens.remove(originalTokens.size()-1);
|
||||
final Token current = originalTokens.remove(originalTokens.size() - 1);
|
||||
if (current.type == Token.TokenType.MARKER && current.surface.startsWith("//"))
|
||||
{
|
||||
continue;
|
||||
// skipping comments
|
||||
}
|
||||
// skipping comments
|
||||
if (!isSemicolon(current)
|
||||
|| (last != null && last.type != Token.TokenType.CLOSE_PAREN && last.type != Token.TokenType.COMMA && !isSemicolon(last)))
|
||||
{
|
||||
@@ -84,7 +96,9 @@ public class Tokenizer implements Iterator<Tokenizer.Token>
|
||||
cleanedTokens.add(current);
|
||||
}
|
||||
if (!(current.type == Token.TokenType.MARKER && current.surface.equals("$")))
|
||||
{
|
||||
last = current;
|
||||
}
|
||||
}
|
||||
Collections.reverse(cleanedTokens);
|
||||
return cleanedTokens;
|
||||
@@ -106,30 +120,33 @@ public class Tokenizer implements Iterator<Tokenizer.Token>
|
||||
return (pos < (input.length() - 1)) ? input.charAt(pos + 1) : 0;
|
||||
}
|
||||
|
||||
private boolean isHexDigit(char ch)
|
||||
private boolean isHexDigit(final char ch)
|
||||
{
|
||||
return ch == 'x' || ch == 'X' || (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f')
|
||||
|| (ch >= 'A' && ch <= 'F');
|
||||
}
|
||||
|
||||
private static boolean isSemicolon(Token tok)
|
||||
private static boolean isSemicolon(final Token tok)
|
||||
{
|
||||
return ( tok.type == Token.TokenType.OPERATOR && tok.surface.equals(";") )
|
||||
|| (tok.type == Token.TokenType.UNARY_OPERATOR && tok.surface.equals(";u") );
|
||||
return (tok.type == Token.TokenType.OPERATOR && tok.surface.equals(";"))
|
||||
|| (tok.type == Token.TokenType.UNARY_OPERATOR && tok.surface.equals(";u"));
|
||||
}
|
||||
|
||||
public static List<Token> simplepass(String input)
|
||||
public static List<Token> simplepass(final String input)
|
||||
{
|
||||
Tokenizer tok = new Tokenizer(null, null, input, false, false);
|
||||
List<Token> res = new ArrayList<>();
|
||||
while (tok.hasNext()) res.add(tok.next());
|
||||
final Tokenizer tok = new Tokenizer(null, null, input, false, false);
|
||||
final List<Token> res = new ArrayList<>();
|
||||
while (tok.hasNext())
|
||||
{
|
||||
res.add(tok.next());
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Token next()
|
||||
{
|
||||
Token token = new Token();
|
||||
final Token token = new Token();
|
||||
|
||||
if (pos >= input.length())
|
||||
{
|
||||
@@ -139,7 +156,7 @@ public class Tokenizer implements Iterator<Tokenizer.Token>
|
||||
while (Character.isWhitespace(ch) && pos < input.length())
|
||||
{
|
||||
linepos++;
|
||||
if (ch=='\n')
|
||||
if (ch == '\n')
|
||||
{
|
||||
lineno++;
|
||||
linepos = 0;
|
||||
@@ -153,10 +170,12 @@ public class Tokenizer implements Iterator<Tokenizer.Token>
|
||||
boolean isHex = false;
|
||||
|
||||
if (Character.isDigit(ch)) // || (ch == decimalSeparator && Character.isDigit(peekNextChar())))
|
||||
// decided to no support this notation to favour element access via . operator
|
||||
// decided to no support this notation to favour element access via . operator
|
||||
{
|
||||
if (ch == '0' && (peekNextChar() == 'x' || peekNextChar() == 'X'))
|
||||
{
|
||||
isHex = true;
|
||||
}
|
||||
while ((isHex
|
||||
&& isHexDigit(
|
||||
ch))
|
||||
@@ -181,13 +200,15 @@ public class Tokenizer implements Iterator<Tokenizer.Token>
|
||||
linepos++;
|
||||
token.type = Token.TokenType.STRINGPARAM;
|
||||
if (pos == input.length() && expression != null && context != null)
|
||||
{
|
||||
throw new ExpressionException(context, this.expression, token, "Program truncated");
|
||||
}
|
||||
ch = input.charAt(pos);
|
||||
while (ch != '\'')
|
||||
{
|
||||
if (ch == '\\')
|
||||
{
|
||||
char nextChar = peekNextChar();
|
||||
final char nextChar = peekNextChar();
|
||||
if (nextChar == 'n')
|
||||
{
|
||||
token.append('\n');
|
||||
@@ -213,22 +234,26 @@ public class Tokenizer implements Iterator<Tokenizer.Token>
|
||||
pos--;
|
||||
linepos--;
|
||||
}
|
||||
pos+=2;
|
||||
linepos+=2;
|
||||
pos += 2;
|
||||
linepos += 2;
|
||||
if (pos == input.length() && expression != null && context != null)
|
||||
{
|
||||
throw new ExpressionException(context, this.expression, token, "Program truncated");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
token.append(input.charAt(pos++));
|
||||
linepos++;
|
||||
if (ch=='\n')
|
||||
if (ch == '\n')
|
||||
{
|
||||
lineno++;
|
||||
linepos = 0;
|
||||
}
|
||||
if (pos == input.length() && expression != null && context != null)
|
||||
{
|
||||
throw new ExpressionException(context, this.expression, token, "Program truncated");
|
||||
}
|
||||
}
|
||||
ch = input.charAt(pos);
|
||||
}
|
||||
@@ -252,7 +277,7 @@ public class Tokenizer implements Iterator<Tokenizer.Token>
|
||||
{
|
||||
ch = input.charAt(pos++);
|
||||
linepos++;
|
||||
if (ch=='\n')
|
||||
if (ch == '\n')
|
||||
{
|
||||
lineno++;
|
||||
linepos = 0;
|
||||
@@ -291,14 +316,16 @@ public class Tokenizer implements Iterator<Tokenizer.Token>
|
||||
(ch == ')' || ch == ',' || ch == ']' || ch == '}') &&
|
||||
!previousToken.surface.equalsIgnoreCase(";")
|
||||
)
|
||||
{
|
||||
throw new ExpressionException(context, this.expression, previousToken,
|
||||
"Can't have operator " + previousToken.surface + " at the end of a subexpression");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
String greedyMatch = "";
|
||||
int initialPos = pos;
|
||||
int initialLinePos = linepos;
|
||||
final int initialPos = pos;
|
||||
final int initialLinePos = linepos;
|
||||
ch = input.charAt(pos);
|
||||
int validOperatorSeenUntil = -1;
|
||||
while (!Character.isLetter(ch) && !Character.isDigit(ch) && "_".indexOf(ch) < 0
|
||||
@@ -309,13 +336,13 @@ public class Tokenizer implements Iterator<Tokenizer.Token>
|
||||
if (comments && "//".equals(greedyMatch))
|
||||
{
|
||||
|
||||
while ( ch != '\n' && pos < input.length())
|
||||
while (ch != '\n' && pos < input.length())
|
||||
{
|
||||
ch = input.charAt(pos++);
|
||||
linepos++;
|
||||
greedyMatch += ch;
|
||||
}
|
||||
if (ch=='\n')
|
||||
if (ch == '\n')
|
||||
{
|
||||
lineno++;
|
||||
linepos = 0;
|
||||
@@ -344,7 +371,7 @@ public class Tokenizer implements Iterator<Tokenizer.Token>
|
||||
{
|
||||
token.append(input.substring(initialPos, validOperatorSeenUntil));
|
||||
pos = validOperatorSeenUntil;
|
||||
linepos = initialLinePos+validOperatorSeenUntil-initialPos;
|
||||
linepos = initialLinePos + validOperatorSeenUntil - initialPos;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -353,7 +380,7 @@ public class Tokenizer implements Iterator<Tokenizer.Token>
|
||||
|
||||
if (previousToken == null || previousToken.type == Token.TokenType.OPERATOR
|
||||
|| previousToken.type == Token.TokenType.OPEN_PAREN || previousToken.type == Token.TokenType.COMMA
|
||||
|| (previousToken.type == Token.TokenType.MARKER && ( previousToken.surface.equals("{") || previousToken.surface.equals("[") ) )
|
||||
|| (previousToken.type == Token.TokenType.MARKER && (previousToken.surface.equals("{") || previousToken.surface.equals("[")))
|
||||
)
|
||||
{
|
||||
token.surface += "u";
|
||||
@@ -365,25 +392,25 @@ public class Tokenizer implements Iterator<Tokenizer.Token>
|
||||
}
|
||||
}
|
||||
if (expression != null && context != null && previousToken != null &&
|
||||
(
|
||||
token.type == Token.TokenType.LITERAL ||
|
||||
token.type == Token.TokenType.HEX_LITERAL ||
|
||||
token.type == Token.TokenType.VARIABLE ||
|
||||
token.type == Token.TokenType.STRINGPARAM ||
|
||||
( token.type == Token.TokenType.MARKER && ( previousToken.surface.equalsIgnoreCase("{") || previousToken.surface.equalsIgnoreCase("["))) ||
|
||||
token.type == Token.TokenType.FUNCTION
|
||||
) &&(
|
||||
(
|
||||
token.type == Token.TokenType.LITERAL ||
|
||||
token.type == Token.TokenType.HEX_LITERAL ||
|
||||
token.type == Token.TokenType.VARIABLE ||
|
||||
token.type == Token.TokenType.STRINGPARAM ||
|
||||
(token.type == Token.TokenType.MARKER && (previousToken.surface.equalsIgnoreCase("{") || previousToken.surface.equalsIgnoreCase("["))) ||
|
||||
token.type == Token.TokenType.FUNCTION
|
||||
) && (
|
||||
previousToken.type == Token.TokenType.VARIABLE ||
|
||||
previousToken.type == Token.TokenType.FUNCTION ||
|
||||
previousToken.type == Token.TokenType.LITERAL ||
|
||||
previousToken.type == Token.TokenType.CLOSE_PAREN ||
|
||||
( previousToken.type == Token.TokenType.MARKER && ( previousToken.surface.equalsIgnoreCase("}") || previousToken.surface.equalsIgnoreCase("]"))) ||
|
||||
previousToken.type == Token.TokenType.HEX_LITERAL ||
|
||||
previousToken.type == Token.TokenType.STRINGPARAM
|
||||
)
|
||||
previousToken.type == Token.TokenType.FUNCTION ||
|
||||
previousToken.type == Token.TokenType.LITERAL ||
|
||||
previousToken.type == Token.TokenType.CLOSE_PAREN ||
|
||||
(previousToken.type == Token.TokenType.MARKER && (previousToken.surface.equalsIgnoreCase("}") || previousToken.surface.equalsIgnoreCase("]"))) ||
|
||||
previousToken.type == Token.TokenType.HEX_LITERAL ||
|
||||
previousToken.type == Token.TokenType.STRINGPARAM
|
||||
)
|
||||
)
|
||||
{
|
||||
throw new ExpressionException(context, this.expression, previousToken, "'"+token.surface +"' is not allowed after '"+previousToken.surface+"'");
|
||||
throw new ExpressionException(context, this.expression, previousToken, "'" + token.surface + "' is not allowed after '" + previousToken.surface + "'");
|
||||
}
|
||||
return previousToken = token;
|
||||
}
|
||||
@@ -403,10 +430,10 @@ public class Tokenizer implements Iterator<Tokenizer.Token>
|
||||
LITERAL(false, true), HEX_LITERAL(false, true), STRINGPARAM(false, true),
|
||||
OPEN_PAREN(false, true), COMMA(false, true), CLOSE_PAREN(false, true), MARKER(false, true);
|
||||
|
||||
boolean functional;
|
||||
boolean constant;
|
||||
final boolean functional;
|
||||
final boolean constant;
|
||||
|
||||
TokenType(boolean functional, boolean constant)
|
||||
TokenType(final boolean functional, final boolean constant)
|
||||
{
|
||||
this.functional = functional;
|
||||
this.constant = constant;
|
||||
@@ -417,44 +444,47 @@ public class Tokenizer implements Iterator<Tokenizer.Token>
|
||||
return functional;
|
||||
}
|
||||
|
||||
public boolean isConstant() {
|
||||
public boolean isConstant()
|
||||
{
|
||||
return constant;
|
||||
}
|
||||
}
|
||||
|
||||
public String surface = "";
|
||||
public TokenType type;
|
||||
public int pos;
|
||||
public int linepos;
|
||||
public int lineno;
|
||||
public static final Token NONE = new Token();
|
||||
public Token morphedInto(TokenType newType, String newSurface)
|
||||
|
||||
public Token morphedInto(final TokenType newType, final String newSurface)
|
||||
{
|
||||
Token created = new Token();
|
||||
final Token created = new Token();
|
||||
created.surface = newSurface;
|
||||
created.type = newType;
|
||||
created.pos = pos;
|
||||
created.linepos = linepos;
|
||||
created.lineno= lineno;
|
||||
created.lineno = lineno;
|
||||
return created;
|
||||
}
|
||||
|
||||
public void morph(TokenType type, String s)
|
||||
public void morph(final TokenType type, final String s)
|
||||
{
|
||||
this.type = type;
|
||||
this.surface = s;
|
||||
}
|
||||
|
||||
public void append(char c)
|
||||
public void append(final char c)
|
||||
{
|
||||
surface += c;
|
||||
}
|
||||
|
||||
public void append(String s)
|
||||
public void append(final String s)
|
||||
{
|
||||
surface += s;
|
||||
}
|
||||
|
||||
public char charAt(int pos)
|
||||
public char charAt(final int pos)
|
||||
{
|
||||
return surface.charAt(pos);
|
||||
}
|
||||
|
||||
@@ -105,32 +105,38 @@ public final class AnnotationParser
|
||||
* @see ScarpetFunction
|
||||
* @param clazz The class to parse
|
||||
*/
|
||||
public static void parseFunctionClass(Class<?> clazz)
|
||||
public static void parseFunctionClass(final Class<?> clazz)
|
||||
{
|
||||
// Only try to instantiate or require concrete classes if there are non-static annotated methods
|
||||
Supplier<Object> instanceSupplier = Suppliers.memoize(() -> {
|
||||
final Supplier<Object> instanceSupplier = Suppliers.memoize(() -> {
|
||||
if (Modifier.isAbstract(clazz.getModifiers()))
|
||||
{
|
||||
throw new IllegalArgumentException("Function class must be concrete to support non-static methods! Class: " + clazz.getSimpleName());
|
||||
}
|
||||
try
|
||||
{
|
||||
return clazz.getConstructor().newInstance();
|
||||
}
|
||||
catch (ReflectiveOperationException e)
|
||||
catch (final ReflectiveOperationException e)
|
||||
{
|
||||
throw new IllegalArgumentException(
|
||||
"Couldn't create instance of given " + clazz + ". This is needed for non-static methods. Make sure default constructor is available", e);
|
||||
}
|
||||
});
|
||||
Method[] methodz = clazz.getDeclaredMethods();
|
||||
for (Method method : methodz)
|
||||
final Method[] methodz = clazz.getDeclaredMethods();
|
||||
for (final Method method : methodz)
|
||||
{
|
||||
if (!method.isAnnotationPresent(ScarpetFunction.class))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (method.getExceptionTypes().length != 0)
|
||||
{
|
||||
throw new IllegalArgumentException("Annotated method '" + method.getName() +"', provided in '"+clazz+"' must not declare checked exceptions");
|
||||
}
|
||||
|
||||
ParsedFunction function = new ParsedFunction(method, clazz, instanceSupplier);
|
||||
final ParsedFunction function = new ParsedFunction(method, clazz, instanceSupplier);
|
||||
functionList.add(function);
|
||||
}
|
||||
}
|
||||
@@ -141,10 +147,12 @@ public final class AnnotationParser
|
||||
*
|
||||
* @param expr The expression to add every function to
|
||||
*/
|
||||
public static void apply(Expression expr)
|
||||
public static void apply(final Expression expr)
|
||||
{
|
||||
for (ParsedFunction function : functionList)
|
||||
for (final ParsedFunction function : functionList)
|
||||
{
|
||||
expr.addLazyFunction(function.name, function.scarpetParamCount, function);
|
||||
}
|
||||
}
|
||||
|
||||
private static class ParsedFunction implements TriFunction<Context, Context.Type, List<LazyValue>, LazyValue>, UsageProvider
|
||||
@@ -164,25 +172,27 @@ public final class AnnotationParser
|
||||
private final int scarpetParamCount;
|
||||
private final Context.Type contextType;
|
||||
|
||||
private ParsedFunction(Method method, Class<?> originClass, Supplier<Object> instance)
|
||||
private ParsedFunction(final Method method, final Class<?> originClass, final Supplier<Object> instance)
|
||||
{
|
||||
this.name = method.getName();
|
||||
this.isMethodVarArgs = method.isVarArgs();
|
||||
this.methodParamCount = method.getParameterCount();
|
||||
|
||||
Parameter[] methodParameters = method.getParameters();
|
||||
final Parameter[] methodParameters = method.getParameters();
|
||||
this.valueConverters = new ValueConverter[isMethodVarArgs ? methodParamCount - 1 : methodParamCount];
|
||||
for (int i = 0; i < this.methodParamCount; i++)
|
||||
{
|
||||
Parameter param = methodParameters[i];
|
||||
final Parameter param = methodParameters[i];
|
||||
if (!isMethodVarArgs || i != this.methodParamCount - 1) // Varargs converter is separate
|
||||
{
|
||||
this.valueConverters[i] = ValueConverter.fromAnnotatedType(param.getAnnotatedType());
|
||||
}
|
||||
}
|
||||
Class<?> originalVarArgsType = isMethodVarArgs ? methodParameters[methodParamCount - 1].getType().getComponentType() : null;
|
||||
final Class<?> originalVarArgsType = isMethodVarArgs ? methodParameters[methodParamCount - 1].getType().getComponentType() : null;
|
||||
this.varArgsType = ClassUtils.primitiveToWrapper(originalVarArgsType); // Primitive array cannot be cast to Obj[]
|
||||
this.primitiveVarArgs = originalVarArgsType != null && originalVarArgsType.isPrimitive();
|
||||
this.varArgsConverter = isMethodVarArgs ? ValueConverter.fromAnnotatedType(methodParameters[methodParamCount - 1].getAnnotatedType()) : null;
|
||||
@SuppressWarnings("unchecked") // Yes. Making a T is not worth
|
||||
@SuppressWarnings("unchecked") final // Yes. Making a T is not worth
|
||||
OutputConverter<Object> converter = OutputConverter.get((Class<Object>) method.getReturnType());
|
||||
this.outputConverter = converter;
|
||||
|
||||
@@ -193,13 +203,19 @@ public final class AnnotationParser
|
||||
{
|
||||
maxParams = method.getAnnotation(ScarpetFunction.class).maxParams();
|
||||
if (maxParams == UNDEFINED_PARAMS)
|
||||
{
|
||||
throw new IllegalArgumentException("No maximum number of params specified for " + name + ", use ScarpetFunction.UNLIMITED_PARAMS for unlimited. "
|
||||
+ "Provided in " + originClass);
|
||||
}
|
||||
if (maxParams == ScarpetFunction.UNLIMITED_PARAMS)
|
||||
{
|
||||
maxParams = Integer.MAX_VALUE;
|
||||
}
|
||||
if (maxParams < this.minParams)
|
||||
{
|
||||
throw new IllegalArgumentException("Provided maximum number of params for " + name + " is smaller than method's param count."
|
||||
+ "Provided in " + originClass);
|
||||
}
|
||||
}
|
||||
this.maxParams = maxParams;
|
||||
|
||||
@@ -227,56 +243,66 @@ public final class AnnotationParser
|
||||
}
|
||||
|
||||
@Override
|
||||
public LazyValue apply(Context context, Context.Type t, List<LazyValue> lazyValues)
|
||||
public LazyValue apply(final Context context, final Context.Type t, final List<LazyValue> lazyValues)
|
||||
{
|
||||
List<Value> lv = AbstractLazyFunction.unpackLazy(lazyValues, context, contextType);
|
||||
final List<Value> lv = AbstractLazyFunction.unpackLazy(lazyValues, context, contextType);
|
||||
if (isEffectivelyVarArgs)
|
||||
{
|
||||
if (lv.size() < minParams)
|
||||
throw new InternalExpressionException("Function '" + name + "' expected at least " + minParams + " arguments, got " + lv.size() + ". "
|
||||
{
|
||||
throw new InternalExpressionException("Function '" + name + "' expected at least " + minParams + " arguments, got " + lv.size() + ". "
|
||||
+ getUsage());
|
||||
}
|
||||
if (lv.size() > maxParams)
|
||||
throw new InternalExpressionException("Function '" + name + " expected up to " + maxParams + " arguments, got " + lv.size() + ". "
|
||||
{
|
||||
throw new InternalExpressionException("Function '" + name + " expected up to " + maxParams + " arguments, got " + lv.size() + ". "
|
||||
+ getUsage());
|
||||
}
|
||||
}
|
||||
Object[] params = getMethodParams(lv, context, t);
|
||||
final Object[] params = getMethodParams(lv, context, t);
|
||||
try
|
||||
{
|
||||
return outputConverter.convert(handle.invokeExact(params));
|
||||
}
|
||||
catch (Throwable e)
|
||||
catch (final Throwable e)
|
||||
{
|
||||
if (e instanceof RuntimeException)
|
||||
throw (RuntimeException) e;
|
||||
if (e instanceof RuntimeException re)
|
||||
{
|
||||
throw re;
|
||||
}
|
||||
throw (Error) e; // Stack overflow or something. Methods are guaranteed not to throw checked exceptions
|
||||
}
|
||||
}
|
||||
|
||||
// Hot code: Must be optimized
|
||||
private Object[] getMethodParams(final List<Value> lv, final Context context, Context.Type theLazyT)
|
||||
private Object[] getMethodParams(final List<Value> lv, final Context context, final Context.Type theLazyT)
|
||||
{
|
||||
Object[] params = new Object[methodParamCount];
|
||||
ListIterator<Value> lvIterator = lv.listIterator();
|
||||
final Object[] params = new Object[methodParamCount];
|
||||
final ListIterator<Value> lvIterator = lv.listIterator();
|
||||
|
||||
int regularArgs = isMethodVarArgs ? methodParamCount - 1 : methodParamCount;
|
||||
final int regularArgs = isMethodVarArgs ? methodParamCount - 1 : methodParamCount;
|
||||
for (int i = 0; i < regularArgs; i++)
|
||||
{
|
||||
params[i] = valueConverters[i].checkAndConvert(lvIterator, context, theLazyT);
|
||||
if (params[i] == null)
|
||||
{
|
||||
throw new InternalExpressionException("Incorrect argument passsed to '" + name + "' function.\n" + getUsage());
|
||||
}
|
||||
}
|
||||
if (isMethodVarArgs)
|
||||
{
|
||||
int remaining = lv.size() - lvIterator.nextIndex();
|
||||
Object[] varArgs;
|
||||
final int remaining = lv.size() - lvIterator.nextIndex();
|
||||
final Object[] varArgs;
|
||||
if (varArgsConverter.consumesVariableArgs())
|
||||
{
|
||||
List<Object> varArgsList = new ArrayList<>(); // fastutil's is extremely slow in toArray, and we use that
|
||||
final List<Object> varArgsList = new ArrayList<>(); // fastutil's is extremely slow in toArray, and we use that
|
||||
while (lvIterator.hasNext())
|
||||
{
|
||||
Object obj = varArgsConverter.checkAndConvert(lvIterator, context, theLazyT);
|
||||
final Object obj = varArgsConverter.checkAndConvert(lvIterator, context, theLazyT);
|
||||
if (obj == null)
|
||||
{
|
||||
throw new InternalExpressionException("Incorrect argument passsed to '" + name + "' function.\n" + getUsage());
|
||||
}
|
||||
varArgsList.add(obj);
|
||||
}
|
||||
varArgs = varArgsList.toArray((Object[])Array.newInstance(varArgsType, 0));
|
||||
@@ -287,7 +313,9 @@ public final class AnnotationParser
|
||||
{
|
||||
varArgs[i] = varArgsConverter.checkAndConvert(lvIterator, context, theLazyT);
|
||||
if (varArgs[i] == null)
|
||||
{
|
||||
throw new InternalExpressionException("Incorrect argument passsed to '" + name + "' function.\n" + getUsage());
|
||||
}
|
||||
}
|
||||
}
|
||||
params[methodParamCount - 1] = primitiveVarArgs ? ArrayUtils.toPrimitive(varArgs) : varArgs; // Copies the array
|
||||
@@ -299,7 +327,7 @@ public final class AnnotationParser
|
||||
public String getUsage()
|
||||
{
|
||||
// Possibility: More descriptive messages using param.getName()? Would need changing gradle setup to keep those
|
||||
StringBuilder builder = new StringBuilder("Usage: '");
|
||||
final StringBuilder builder = new StringBuilder("Usage: '");
|
||||
builder.append(name);
|
||||
builder.append('(');
|
||||
builder.append(Arrays.stream(valueConverters).map(ValueConverter::getTypeName).filter(Objects::nonNull).collect(Collectors.joining(", ")));
|
||||
|
||||
@@ -32,34 +32,38 @@ final class ListConverter<T> implements ValueConverter<List<T>>
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<T> convert(Value value)
|
||||
public List<T> convert(final Value value)
|
||||
{
|
||||
return value instanceof ListValue ? convertListValue((ListValue) value) : allowSingletonCreation ? convertSingleton(value) : null;
|
||||
}
|
||||
|
||||
private List<T> convertListValue(ListValue values)
|
||||
private List<T> convertListValue(final ListValue values)
|
||||
{
|
||||
List<T> list = new ArrayList<>(values.getItems().size());
|
||||
for (Value value : values)
|
||||
final List<T> list = new ArrayList<>(values.getItems().size());
|
||||
for (final Value value : values)
|
||||
{
|
||||
T converted = itemConverter.convert(value);
|
||||
final T converted = itemConverter.convert(value);
|
||||
if (converted == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
list.add(converted);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private List<T> convertSingleton(Value val)
|
||||
private List<T> convertSingleton(final Value val)
|
||||
{
|
||||
T converted = itemConverter.convert(val);
|
||||
final T converted = itemConverter.convert(val);
|
||||
if (converted == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return Collections.singletonList(converted);
|
||||
|
||||
}
|
||||
|
||||
private ListConverter(AnnotatedType itemType, boolean allowSingletonCreation)
|
||||
private ListConverter(final AnnotatedType itemType, final boolean allowSingletonCreation)
|
||||
{
|
||||
itemConverter = ValueConverter.fromAnnotatedType(itemType);
|
||||
this.allowSingletonCreation = allowSingletonCreation;
|
||||
@@ -79,11 +83,11 @@ final class ListConverter<T> implements ValueConverter<List<T>>
|
||||
* @param annotatedType The type to get generics information from
|
||||
* @return A new {@link ListConverter} for the data specified in the {@link AnnotatedType}
|
||||
*/
|
||||
static ListConverter<?> fromAnnotatedType(AnnotatedType annotatedType)
|
||||
static ListConverter<?> fromAnnotatedType(final AnnotatedType annotatedType)
|
||||
{
|
||||
AnnotatedParameterizedType paramType = (AnnotatedParameterizedType) annotatedType;
|
||||
AnnotatedType itemType = paramType.getAnnotatedActualTypeArguments()[0];
|
||||
boolean allowSingletonCreation = annotatedType.isAnnotationPresent(Param.AllowSingleton.class);
|
||||
final AnnotatedParameterizedType paramType = (AnnotatedParameterizedType) annotatedType;
|
||||
final AnnotatedType itemType = paramType.getAnnotatedActualTypeArguments()[0];
|
||||
final boolean allowSingletonCreation = annotatedType.isAnnotationPresent(Param.AllowSingleton.class);
|
||||
return new ListConverter<>(itemType, allowSingletonCreation);
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ public interface Locator
|
||||
@Documented
|
||||
@Retention(RUNTIME)
|
||||
@Target({ PARAMETER, TYPE_USE })
|
||||
public @interface Block
|
||||
@interface Block
|
||||
{
|
||||
/**
|
||||
* <p>Whether or not should the locator accept a single {@link String} as the parameter and let parsing to {@link BlockValue}.</p>
|
||||
@@ -66,7 +66,7 @@ public interface Locator
|
||||
@Documented
|
||||
@Retention(RUNTIME)
|
||||
@Target({ PARAMETER, TYPE_USE })
|
||||
public @interface Vec3d
|
||||
@interface Vec3d
|
||||
{
|
||||
/**
|
||||
* <p>Whether or not should the {@link Vector3Argument} locator accept an optional direction aside from the
|
||||
@@ -94,7 +94,7 @@ public interface Locator
|
||||
@Documented
|
||||
@Retention(RUNTIME)
|
||||
@Target({ PARAMETER, TYPE_USE })
|
||||
public @interface Function
|
||||
@interface Function
|
||||
{
|
||||
/**
|
||||
* <p>Whether this Locator should allow no function to be passed.</p> <p>This is not compatible with {@link FunctionValue} type, since a
|
||||
@@ -115,18 +115,27 @@ public interface Locator
|
||||
*
|
||||
* <p>Not part of the public API, just that interfaces must have all members public</p>
|
||||
*/
|
||||
static final class Locators
|
||||
final class Locators
|
||||
{
|
||||
private Locators() {}
|
||||
private Locators()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
static <R> ValueConverter<R> fromAnnotatedType(AnnotatedType annoType, Class<R> type)
|
||||
static <R> ValueConverter<R> fromAnnotatedType(final AnnotatedType annoType, final Class<R> type)
|
||||
{
|
||||
if (annoType.isAnnotationPresent(Block.class))
|
||||
return new BlockLocator<R>(annoType.getAnnotation(Block.class), type);
|
||||
{
|
||||
return new BlockLocator<>(annoType.getAnnotation(Block.class), type);
|
||||
}
|
||||
if (annoType.isAnnotationPresent(Function.class))
|
||||
return new FunctionLocator<R>(annoType.getAnnotation(Function.class), type);
|
||||
{
|
||||
return new FunctionLocator<>(annoType.getAnnotation(Function.class), type);
|
||||
}
|
||||
if (annoType.isAnnotationPresent(Vec3d.class))
|
||||
return new Vec3dLocator<R>(annoType.getAnnotation(Vec3d.class), type);
|
||||
{
|
||||
return new Vec3dLocator<>(annoType.getAnnotation(Vec3d.class), type);
|
||||
}
|
||||
throw new IllegalStateException("Locator#fromAnnotatedType got called with an incompatible AnnotatedType");
|
||||
}
|
||||
|
||||
@@ -137,28 +146,42 @@ public interface Locator
|
||||
private final boolean anyString;
|
||||
private final boolean optional;
|
||||
|
||||
public BlockLocator(Block annotation, Class<R> type)
|
||||
public BlockLocator(final Block annotation, final Class<R> type)
|
||||
{
|
||||
super();
|
||||
this.acceptString = annotation.acceptString();
|
||||
this.anyString = annotation.anyString();
|
||||
this.optional = annotation.optional();
|
||||
if (type != BlockArgument.class && (anyString || optional))
|
||||
{
|
||||
throw new IllegalArgumentException("Can only use anyString or optional parameters of Locator.Block if targeting a BlockArgument");
|
||||
}
|
||||
this.returnFunction = getReturnFunction(type);
|
||||
if (returnFunction == null)
|
||||
{
|
||||
throw new IllegalArgumentException("Locator.Block can only be used against BlockArgument, BlockValue, BlockPos or BlockState types!");
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <R> java.util.function.Function<BlockArgument, R> getReturnFunction(Class<R> type) {
|
||||
private static <R> java.util.function.Function<BlockArgument, R> getReturnFunction(final Class<R> type)
|
||||
{
|
||||
if (type == BlockArgument.class)
|
||||
{
|
||||
return r -> (R) r;
|
||||
}
|
||||
if (type == BlockValue.class)
|
||||
{
|
||||
return r -> (R) r.block;
|
||||
}
|
||||
if (type == BlockPos.class)
|
||||
{
|
||||
return r -> (R) r.block.getPos();
|
||||
}
|
||||
if (type == BlockState.class)
|
||||
{
|
||||
return r -> (R) r.block.getBlockState();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -169,9 +192,9 @@ public interface Locator
|
||||
}
|
||||
|
||||
@Override
|
||||
public R checkAndConvert(Iterator<Value> valueIterator, Context context, Context.Type theLazyT)
|
||||
public R checkAndConvert(final Iterator<Value> valueIterator, final Context context, final Context.Type theLazyT)
|
||||
{
|
||||
BlockArgument locator = BlockArgument.findIn((CarpetContext) context, valueIterator, 0, acceptString, optional, anyString);
|
||||
final BlockArgument locator = BlockArgument.findIn((CarpetContext) context, valueIterator, 0, acceptString, optional, anyString);
|
||||
return returnFunction.apply(locator);
|
||||
}
|
||||
}
|
||||
@@ -182,15 +205,19 @@ public interface Locator
|
||||
private final boolean optionalEntity;
|
||||
private final boolean returnVec3d;
|
||||
|
||||
public Vec3dLocator(Vec3d annotation, Class<R> type)
|
||||
public Vec3dLocator(final Vec3d annotation, final Class<R> type)
|
||||
{
|
||||
this.optionalDirection = annotation.optionalDirection();
|
||||
this.optionalEntity = annotation.optionalEntity();
|
||||
this.returnVec3d = type == net.minecraft.world.phys.Vec3.class; // Because of the locator
|
||||
if (returnVec3d && optionalDirection)
|
||||
{
|
||||
throw new IllegalArgumentException("optionalDirection Locator.Vec3d cannot be used for Vec3d type, use Vector3Argument instead");
|
||||
}
|
||||
if (!returnVec3d && type != Vector3Argument.class)
|
||||
{
|
||||
throw new IllegalArgumentException("Locator.Vec3d can only be used in Vector3Argument or Vec3d types");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -200,11 +227,10 @@ public interface Locator
|
||||
}
|
||||
|
||||
@Override
|
||||
public R checkAndConvert(Iterator<Value> valueIterator, Context context, Context.Type theLazyT)
|
||||
public R checkAndConvert(final Iterator<Value> valueIterator, final Context context, final Context.Type theLazyT)
|
||||
{
|
||||
Vector3Argument locator = Vector3Argument.findIn(valueIterator, 0, optionalDirection, optionalEntity);
|
||||
@SuppressWarnings("unchecked")
|
||||
R ret = (R) (returnVec3d ? locator.vec : locator);
|
||||
final Vector3Argument locator = Vector3Argument.findIn(valueIterator, 0, optionalDirection, optionalEntity);
|
||||
@SuppressWarnings("unchecked") final R ret = (R) (returnVec3d ? locator.vec : locator);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
@@ -215,24 +241,28 @@ public interface Locator
|
||||
private final boolean allowNone;
|
||||
private final boolean checkArgs;
|
||||
|
||||
FunctionLocator(Function annotation, Class<R> type)
|
||||
FunctionLocator(final Function annotation, final Class<R> type)
|
||||
{
|
||||
super();
|
||||
this.returnFunctionValue = type == FunctionValue.class;
|
||||
if (!returnFunctionValue && type != FunctionArgument.class)
|
||||
{
|
||||
throw new IllegalArgumentException("Params annotated with Locator.Function must be of either FunctionArgument or FunctionValue type");
|
||||
}
|
||||
this.allowNone = annotation.allowNone();
|
||||
this.checkArgs = annotation.checkArgs();
|
||||
if (returnFunctionValue && allowNone)
|
||||
{
|
||||
throw new IllegalArgumentException("Cannot use allowNone of Locator.Function in FunctionValue types, use FunctionArgument");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public R checkAndConvert(Iterator<Value> valueIterator, Context context, Context.Type theLazyT)
|
||||
public R checkAndConvert(final Iterator<Value> valueIterator, final Context context, final Context.Type theLazyT)
|
||||
{
|
||||
Module module = context.host.main;
|
||||
FunctionArgument locator = FunctionArgument.findIn(context, module, Lists.newArrayList(valueIterator), 0, allowNone, checkArgs);
|
||||
@SuppressWarnings("unchecked")
|
||||
R ret = (R) (returnFunctionValue ? locator.function : locator);
|
||||
final Module module = context.host.main;
|
||||
final FunctionArgument locator = FunctionArgument.findIn(context, module, Lists.newArrayList(valueIterator), 0, allowNone, checkArgs);
|
||||
@SuppressWarnings("unchecked") final R ret = (R) (returnFunctionValue ? locator.function : locator);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@@ -243,10 +273,10 @@ public interface Locator
|
||||
}
|
||||
}
|
||||
|
||||
private static abstract class AbstractLocator<R> implements ValueConverter<R>, Locator
|
||||
private abstract static class AbstractLocator<R> implements ValueConverter<R>, Locator
|
||||
{
|
||||
@Override
|
||||
public R convert(Value value)
|
||||
public R convert(final Value value)
|
||||
{
|
||||
throw new UnsupportedOperationException("Cannot call a locator in a parameter that doesn't contain a context!");
|
||||
}
|
||||
|
||||
@@ -37,17 +37,19 @@ class MapConverter<K, V> implements ValueConverter<Map<K, V>>
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<K, V> convert(Value value)
|
||||
public Map<K, V> convert(final Value value)
|
||||
{
|
||||
Map<K, V> result = new HashMap<>();
|
||||
final Map<K, V> result = new HashMap<>();
|
||||
if (value instanceof MapValue)
|
||||
{
|
||||
for (Entry<Value, Value> entry : ((MapValue) value).getMap().entrySet())
|
||||
for (final Entry<Value, Value> entry : ((MapValue) value).getMap().entrySet())
|
||||
{
|
||||
K key = keyConverter.convert(entry.getKey());
|
||||
V val = valueConverter.convert(entry.getValue());
|
||||
final K key = keyConverter.convert(entry.getKey());
|
||||
final V val = valueConverter.convert(entry.getValue());
|
||||
if (key == null || val == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
result.put(key, val);
|
||||
}
|
||||
return result;
|
||||
@@ -55,8 +57,9 @@ class MapConverter<K, V> implements ValueConverter<Map<K, V>>
|
||||
return null;
|
||||
}
|
||||
|
||||
private MapConverter(AnnotatedType keyType, AnnotatedType valueType)
|
||||
private MapConverter(final AnnotatedType keyType, final AnnotatedType valueType)
|
||||
{
|
||||
super();
|
||||
keyConverter = ValueConverter.fromAnnotatedType(keyType);
|
||||
valueConverter = ValueConverter.fromAnnotatedType(valueType);
|
||||
}
|
||||
@@ -77,9 +80,9 @@ class MapConverter<K, V> implements ValueConverter<Map<K, V>>
|
||||
* @param annotatedType The type to get generics information from
|
||||
* @return A new {@link MapConverter} for the data specified in the {@link AnnotatedType}
|
||||
*/
|
||||
static MapConverter<?, ?> fromAnnotatedType(AnnotatedType annotatedType)
|
||||
static MapConverter<?, ?> fromAnnotatedType(final AnnotatedType annotatedType)
|
||||
{
|
||||
AnnotatedType[] annotatedGenerics = ((AnnotatedParameterizedType) annotatedType).getAnnotatedActualTypeArguments();
|
||||
final AnnotatedType[] annotatedGenerics = ((AnnotatedParameterizedType) annotatedType).getAnnotatedActualTypeArguments();
|
||||
return annotatedType.isAnnotationPresent(Param.KeyValuePairs.class)
|
||||
? new PairConverter<>(annotatedGenerics[0], annotatedGenerics[1], annotatedType.getAnnotation(Param.KeyValuePairs.class))
|
||||
: new MapConverter<>(annotatedGenerics[0], annotatedGenerics[1]);
|
||||
@@ -89,7 +92,7 @@ class MapConverter<K, V> implements ValueConverter<Map<K, V>>
|
||||
{
|
||||
private final boolean acceptMultiParam;
|
||||
|
||||
private PairConverter(AnnotatedType keyType, AnnotatedType valueType, Param.KeyValuePairs config)
|
||||
private PairConverter(final AnnotatedType keyType, final AnnotatedType valueType, final Param.KeyValuePairs config)
|
||||
{
|
||||
super(keyType, valueType);
|
||||
acceptMultiParam = config.allowMultiparam();
|
||||
@@ -102,51 +105,63 @@ class MapConverter<K, V> implements ValueConverter<Map<K, V>>
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<K, V> convert(Value value) {
|
||||
public Map<K, V> convert(final Value value) {
|
||||
return value instanceof MapValue ? super.convert(value)
|
||||
: value instanceof ListValue ? convertList(((ListValue)value).getItems())
|
||||
: null; // Multiparam mode can only be used in evalAndConvert
|
||||
}
|
||||
|
||||
|
||||
private Map<K, V> convertList(List<Value> valueList)
|
||||
private Map<K, V> convertList(final List<Value> valueList)
|
||||
{
|
||||
if (valueList.size() % 2 == 1)
|
||||
{
|
||||
return null;
|
||||
Map<K, V> map = new HashMap<>();
|
||||
Iterator<Value> val = valueList.iterator();
|
||||
}
|
||||
final Map<K, V> map = new HashMap<>();
|
||||
final Iterator<Value> val = valueList.iterator();
|
||||
while (val.hasNext())
|
||||
{
|
||||
K key = keyConverter.convert(val.next());
|
||||
V value = valueConverter.convert(val.next());
|
||||
final K key = keyConverter.convert(val.next());
|
||||
final V value = valueConverter.convert(val.next());
|
||||
if (key == null || value == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
map.put(key, value);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<K, V> checkAndConvert(Iterator<Value> valueIterator, Context context, Context.Type theLazyT)
|
||||
public Map<K, V> checkAndConvert(final Iterator<Value> valueIterator, final Context context, final Context.Type theLazyT)
|
||||
{
|
||||
if (!valueIterator.hasNext())
|
||||
{
|
||||
return null;
|
||||
Value val = valueIterator.next();
|
||||
}
|
||||
final Value val = valueIterator.next();
|
||||
if (!acceptMultiParam || val instanceof MapValue || (val instanceof ListValue && !(keyConverter instanceof ListConverter)))
|
||||
{
|
||||
return convert(val); // @KeyValuePairs Map<List<Something>, Boolean> will not support list consumption
|
||||
}
|
||||
|
||||
Map<K, V> map = new HashMap<>();
|
||||
final Map<K, V> map = new HashMap<>();
|
||||
K key = keyConverter.convert(val); //First pair is manual since we got it to check for a different conversion mode
|
||||
V value = valueConverter.checkAndConvert(valueIterator, context, theLazyT);
|
||||
if (key == null || value == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
map.put(key, value);
|
||||
while (valueIterator.hasNext())
|
||||
{
|
||||
key = keyConverter.checkAndConvert(valueIterator, context, theLazyT);
|
||||
value = valueConverter.checkAndConvert(valueIterator, context, theLazyT);
|
||||
if (key == null || value == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
map.put(key, value);
|
||||
}
|
||||
return map;
|
||||
|
||||
@@ -50,25 +50,33 @@ final class OptionalConverter<R> implements ValueConverter<Optional<R>>
|
||||
* {@link #checkAndConvert(Iterator, Context, Context.Type)} and is only used as a fallback in types that don't support it.
|
||||
*/
|
||||
@Override
|
||||
public Optional<R> convert(Value value)
|
||||
public Optional<R> convert(final Value value)
|
||||
{
|
||||
if (value.isNull())
|
||||
{
|
||||
return Optional.empty();
|
||||
R converted = typeConverter.convert(value);
|
||||
}
|
||||
final R converted = typeConverter.convert(value);
|
||||
if (converted == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return Optional.of(converted);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Optional<R> checkAndConvert(Iterator<Value> valueIterator, Context context, Context.Type theLazyT)
|
||||
public Optional<R> checkAndConvert(final Iterator<Value> valueIterator, final Context context, final Context.Type theLazyT)
|
||||
{
|
||||
if (!valueIterator.hasNext() || valueIterator.next().isNull())
|
||||
{
|
||||
return Optional.empty();
|
||||
}
|
||||
((ListIterator<Value>) valueIterator).previous();
|
||||
R converted = typeConverter.checkAndConvert(valueIterator, context, theLazyT);
|
||||
final R converted = typeConverter.checkAndConvert(valueIterator, context, theLazyT);
|
||||
if (converted == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return Optional.of(converted);
|
||||
}
|
||||
|
||||
@@ -98,10 +106,10 @@ final class OptionalConverter<R> implements ValueConverter<Optional<R>>
|
||||
* @param annotatedType The type to get generics information from
|
||||
* @return A new {@link OptionalConverter} for the data specified in the {@link AnnotatedType}
|
||||
*/
|
||||
static OptionalConverter<?> fromAnnotatedType(AnnotatedType annotatedType)
|
||||
static OptionalConverter<?> fromAnnotatedType(final AnnotatedType annotatedType)
|
||||
{
|
||||
AnnotatedParameterizedType paramType = (AnnotatedParameterizedType) annotatedType;
|
||||
AnnotatedType wrappedType = paramType.getAnnotatedActualTypeArguments()[0];
|
||||
final AnnotatedParameterizedType paramType = (AnnotatedParameterizedType) annotatedType;
|
||||
final AnnotatedType wrappedType = paramType.getAnnotatedActualTypeArguments()[0];
|
||||
return new OptionalConverter<>(wrappedType);
|
||||
}
|
||||
}
|
||||
@@ -58,7 +58,7 @@ public final class OutputConverter<T>
|
||||
|
||||
private final Function<T, LazyValue> converter;
|
||||
|
||||
private OutputConverter(Function<T, LazyValue> converter)
|
||||
private OutputConverter(final Function<T, LazyValue> converter)
|
||||
{
|
||||
this.converter = converter;
|
||||
}
|
||||
@@ -75,7 +75,9 @@ public final class OutputConverter<T>
|
||||
public static <T> OutputConverter<T> get(Class<T> returnType)
|
||||
{
|
||||
if (Value.class.isAssignableFrom(returnType))
|
||||
{
|
||||
return (OutputConverter<T>) VALUE;
|
||||
}
|
||||
returnType = (Class<T>) ClassUtils.primitiveToWrapper(returnType); // wrapper holds same generic as primitive: wrapped
|
||||
return (OutputConverter<T>) Objects.requireNonNull(byResult.get(returnType),
|
||||
"Unregistered output type: " + returnType + ". Register in OutputConverter");
|
||||
@@ -89,7 +91,7 @@ public final class OutputConverter<T>
|
||||
* @param input The value to convert
|
||||
* @return The converted value
|
||||
*/
|
||||
public LazyValue convert(T input)
|
||||
public LazyValue convert(final T input)
|
||||
{
|
||||
return input == null ? LazyValue.NULL : converter.apply(input);
|
||||
}
|
||||
@@ -102,11 +104,13 @@ public final class OutputConverter<T>
|
||||
* @param inputType The class of T
|
||||
* @param converter The function that converts the an instance of T to a {@link LazyValue}
|
||||
*/
|
||||
public static <T> void register(Class<T> inputType, Function<T, LazyValue> converter)
|
||||
public static <T> void register(final Class<T> inputType, final Function<T, LazyValue> converter)
|
||||
{
|
||||
OutputConverter<T> instance = new OutputConverter<T>(converter);
|
||||
final OutputConverter<T> instance = new OutputConverter<>(converter);
|
||||
if (byResult.containsKey(inputType))
|
||||
{
|
||||
throw new IllegalArgumentException(inputType + " already has a registered OutputConverter");
|
||||
}
|
||||
byResult.put(inputType, instance);
|
||||
}
|
||||
|
||||
@@ -119,11 +123,13 @@ public final class OutputConverter<T>
|
||||
* @param inputType The class of T
|
||||
* @param converter The function that converts an instance of T to a {@link Value}
|
||||
*/
|
||||
public static <T> void registerToValue(Class<T> inputType, Function<T, Value> converter)
|
||||
public static <T> void registerToValue(final Class<T> inputType, final Function<T, Value> converter)
|
||||
{
|
||||
OutputConverter<T> instance = new OutputConverter<>(converter.andThen(v -> (c, t) -> v));
|
||||
final OutputConverter<T> instance = new OutputConverter<>(converter.andThen(v -> (c, t) -> v));
|
||||
if (byResult.containsKey(inputType))
|
||||
{
|
||||
throw new IllegalArgumentException(inputType + " already has a registered OutputConverter");
|
||||
}
|
||||
byResult.put(inputType, instance);
|
||||
}
|
||||
}
|
||||
@@ -51,7 +51,7 @@ public interface Param
|
||||
@Documented
|
||||
@Retention(RUNTIME)
|
||||
@Target({ PARAMETER, TYPE_USE })
|
||||
public @interface AllowSingleton
|
||||
@interface AllowSingleton
|
||||
{
|
||||
|
||||
}
|
||||
@@ -71,7 +71,7 @@ public interface Param
|
||||
@Documented
|
||||
@Retention(RUNTIME)
|
||||
@Target({ PARAMETER, TYPE_USE })
|
||||
public @interface KeyValuePairs
|
||||
@interface KeyValuePairs
|
||||
{
|
||||
/**
|
||||
* <p>Whether or not this accepts the key-value pairs directly in the function call as myFunction(..., key, value, key2, value2)</p>
|
||||
@@ -91,7 +91,7 @@ public interface Param
|
||||
@Documented
|
||||
@Retention(RUNTIME)
|
||||
@Target({ PARAMETER, TYPE_USE })
|
||||
public @interface Custom
|
||||
@interface Custom
|
||||
{
|
||||
|
||||
}
|
||||
@@ -111,7 +111,7 @@ public interface Param
|
||||
@Documented
|
||||
@Retention(RUNTIME)
|
||||
@Target({ PARAMETER, TYPE_USE })
|
||||
public @interface Strict
|
||||
@interface Strict
|
||||
{
|
||||
/**
|
||||
* <p>Defines whether this parameter can accept types with "shallow strictness", that is, in order to get a {@link Component}, accepting either a
|
||||
@@ -137,7 +137,7 @@ public interface Param
|
||||
* @see #registerCustomConverterFactory(BiFunction)
|
||||
*
|
||||
*/
|
||||
public static final class Params
|
||||
final class Params
|
||||
{
|
||||
/**
|
||||
* <p>A {@link ValueConverter} that outputs the {@link Context} in which the function has been called, and throws {@link UnsupportedOperationException} when trying to convert a {@link Value}
|
||||
@@ -148,13 +148,13 @@ public interface Param
|
||||
@Override public String getTypeName() { return null; }
|
||||
|
||||
@Override
|
||||
public Context convert(Value value)
|
||||
public Context convert(final Value value)
|
||||
{
|
||||
throw new UnsupportedOperationException("Called convert() with Value in Context Provider converter, where only checkAndConvert is supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Context checkAndConvert(Iterator<Value> valueIterator, Context context, Context.Type theLazyT)
|
||||
public Context checkAndConvert(final Iterator<Value> valueIterator, final Context context, final Context.Type theLazyT)
|
||||
{
|
||||
return context;
|
||||
}
|
||||
@@ -175,13 +175,13 @@ public interface Param
|
||||
@Override public String getTypeName() { return null; }
|
||||
|
||||
@Override
|
||||
public Context.Type convert(Value value)
|
||||
public Context.Type convert(final Value value)
|
||||
{
|
||||
throw new UnsupportedOperationException("Called convert() with a Value in TheLazyT Provider, where only checkAndConvert is supported");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Context.Type checkAndConvert(Iterator<Value> valueIterator, Context context, Context.Type theLazyT)
|
||||
public Context.Type checkAndConvert(final Iterator<Value> valueIterator, final Context context, final Context.Type theLazyT)
|
||||
{
|
||||
return theLazyT;
|
||||
}
|
||||
@@ -193,7 +193,7 @@ public interface Param
|
||||
}
|
||||
};
|
||||
|
||||
static record StrictConverterInfo(Class<?> type, boolean shallow) {}
|
||||
record StrictConverterInfo(Class<?> type, boolean shallow) {}
|
||||
private static final Map<StrictConverterInfo, ValueConverter<?>> strictParamsByClassAndShallowness = new HashMap<>();
|
||||
static
|
||||
{ // TODO Specify strictness in name?
|
||||
@@ -214,14 +214,16 @@ public interface Param
|
||||
* @throws IllegalArgumentException If the type doesn't accept the {@link Strict} annotation or if it has been used incorrectly (shallow in
|
||||
* unsupported places)
|
||||
*/
|
||||
static ValueConverter<?> getStrictConverter(AnnotatedType type)
|
||||
static ValueConverter<?> getStrictConverter(final AnnotatedType type)
|
||||
{
|
||||
boolean shallow = type.getAnnotation(Strict.class).shallow();
|
||||
Class<?> clazz = (Class<?>) type.getType();
|
||||
var key = new StrictConverterInfo(clazz, shallow);
|
||||
ValueConverter<?> converter = strictParamsByClassAndShallowness.get(key);
|
||||
final boolean shallow = type.getAnnotation(Strict.class).shallow();
|
||||
final Class<?> clazz = (Class<?>) type.getType();
|
||||
final StrictConverterInfo key = new StrictConverterInfo(clazz, shallow);
|
||||
final ValueConverter<?> converter = strictParamsByClassAndShallowness.get(key);
|
||||
if (converter != null)
|
||||
{
|
||||
return converter;
|
||||
}
|
||||
throw new IllegalArgumentException("Incorrect use of @Param.Strict annotation");
|
||||
}
|
||||
|
||||
@@ -237,11 +239,13 @@ public interface Param
|
||||
* @param shallow {@code true} if you are registering a shallow-strict parameter, {@code false} if a "fully" strict one
|
||||
* @param converter The {@link ValueConverter} for the given type and shallowness.
|
||||
*/
|
||||
public static <T> void registerStrictConverter(Class<T> type, boolean shallow, ValueConverter<T> converter)
|
||||
public static <T> void registerStrictConverter(final Class<T> type, final boolean shallow, final ValueConverter<T> converter)
|
||||
{
|
||||
var key = new StrictConverterInfo(type, shallow);
|
||||
final StrictConverterInfo key = new StrictConverterInfo(type, shallow);
|
||||
if (strictParamsByClassAndShallowness.containsKey(key))
|
||||
{
|
||||
throw new IllegalArgumentException(type + " already has a registered " + (shallow ? "" : "non-") + "shallow StrictConverter");
|
||||
}
|
||||
strictParamsByClassAndShallowness.put(key, converter);
|
||||
}
|
||||
|
||||
@@ -267,19 +271,21 @@ public interface Param
|
||||
* avoid possible collisions with other extensions.
|
||||
*/
|
||||
@SuppressWarnings("unchecked") // this makes no sense... But I guess its preferable to enforce typesafety in callers
|
||||
public static <T> void registerCustomConverterFactory(BiFunction<AnnotatedType, Class<T>, ValueConverter<T>> factory)
|
||||
public static <T> void registerCustomConverterFactory(final BiFunction<AnnotatedType, Class<T>, ValueConverter<T>> factory)
|
||||
{
|
||||
customFactories.add((BiFunction<AnnotatedType, Class<?>, ValueConverter<?>>) (Object) factory);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked") // Stored correctly
|
||||
static <R> ValueConverter<R> getCustomConverter(AnnotatedType annoType, Class<R> type)
|
||||
static <R> ValueConverter<R> getCustomConverter(final AnnotatedType annoType, final Class<R> type)
|
||||
{
|
||||
ValueConverter<R> result;
|
||||
for (BiFunction<AnnotatedType, Class<?>, ValueConverter<?>> factory : customFactories)
|
||||
for (final BiFunction<AnnotatedType, Class<?>, ValueConverter<?>> factory : customFactories)
|
||||
{
|
||||
if ((result = (ValueConverter<R>) factory.apply(annoType, type)) != null)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("No custom converter found for Param.Custom annotated param with type " + annoType.getType().getTypeName());
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ public @interface ScarpetFunction
|
||||
/**
|
||||
* <p>Used to define that this {@link ScarpetFunction} can accept an unlimited number of parameters</p>
|
||||
*/
|
||||
public static final int UNLIMITED_PARAMS = -1;
|
||||
int UNLIMITED_PARAMS = -1;
|
||||
|
||||
/**
|
||||
* <p>If the function can accept a variable number of parameters, either by declaring its last parameter as a varargs parameter or by having one
|
||||
|
||||
@@ -58,8 +58,9 @@ public final class SimpleTypeConverter<T extends Value, R> implements ValueConve
|
||||
* @param inputType The required type for the input {@link Value}
|
||||
* @param converter The function to convert an instance of inputType into R.
|
||||
*/
|
||||
public SimpleTypeConverter(Class<T> inputType, Function<T, R> converter, String typeName)
|
||||
public SimpleTypeConverter(final Class<T> inputType, final Function<T, R> converter, final String typeName)
|
||||
{
|
||||
super();
|
||||
this.converter = converter;
|
||||
this.valueClass = inputType;
|
||||
this.typeName = typeName;
|
||||
@@ -79,14 +80,14 @@ public final class SimpleTypeConverter<T extends Value, R> implements ValueConve
|
||||
* @return The {@link SimpleTypeConverter} for the specified outputType
|
||||
*/
|
||||
@SuppressWarnings("unchecked") // T always extends Value, R is always the same as map's key, since map is private.
|
||||
static <R> SimpleTypeConverter<Value, R> get(Class<R> outputType)
|
||||
static <R> SimpleTypeConverter<Value, R> get(final Class<R> outputType)
|
||||
{
|
||||
return (SimpleTypeConverter<Value, R>) byResult.get(outputType);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked") // more than checked. not using class.cast because then "method is too big" for inlining, because javac is useless
|
||||
public R convert(Value value) // and adds millions of casts. This one is even removed
|
||||
public R convert(final Value value) // and adds millions of casts. This one is even removed
|
||||
{
|
||||
return valueClass.isInstance(value) ? converter.apply((T)value) : null;
|
||||
}
|
||||
@@ -103,10 +104,10 @@ public final class SimpleTypeConverter<T extends Value, R> implements ValueConve
|
||||
* can also throw an {@link InternalExpressionException} by itself if really necessary.
|
||||
* @param typeName The name of the type, following the conventions of {@link ValueConverter#getTypeName()}
|
||||
*/
|
||||
public static <T extends Value, R> void registerType(Class<T> requiredInputType, Class<R> outputType,
|
||||
Function<T, R> converter, String typeName)
|
||||
public static <T extends Value, R> void registerType(final Class<T> requiredInputType, final Class<R> outputType,
|
||||
final Function<T, R> converter, final String typeName)
|
||||
{
|
||||
SimpleTypeConverter<T, R> type = new SimpleTypeConverter<>(requiredInputType, converter, typeName);
|
||||
final SimpleTypeConverter<T, R> type = new SimpleTypeConverter<>(requiredInputType, converter, typeName);
|
||||
byResult.put(outputType, type);
|
||||
}
|
||||
}
|
||||
@@ -50,8 +50,9 @@ public final class ValueCaster<R> implements ValueConverter<R> // R always exten
|
||||
private final Class<R> outputType;
|
||||
private final String typeName;
|
||||
|
||||
private ValueCaster(Class<R> outputType, String typeName)
|
||||
private ValueCaster(final Class<R> outputType, final String typeName)
|
||||
{
|
||||
super();
|
||||
this.outputType = outputType;
|
||||
this.typeName = typeName;
|
||||
}
|
||||
@@ -71,17 +72,19 @@ public final class ValueCaster<R> implements ValueConverter<R> // R always exten
|
||||
*/
|
||||
@SuppressWarnings("unchecked") // Casters are stored with their exact class, for sure since the map is private (&& class has same generic as
|
||||
// caster)
|
||||
public static <R> ValueCaster<R> get(Class<R> outputType)
|
||||
public static <R> ValueCaster<R> get(final Class<R> outputType)
|
||||
{
|
||||
return (ValueCaster<R>) byResult.get(outputType);
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked") // more than checked, see SimpleTypeConverter#converter for reasoning
|
||||
public R convert(Value value)
|
||||
public R convert(final Value value)
|
||||
{
|
||||
if (!outputType.isInstance(value))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return (R)value;
|
||||
}
|
||||
|
||||
@@ -94,9 +97,9 @@ public final class ValueCaster<R> implements ValueConverter<R> // R always exten
|
||||
* required<!--, with the form //Outdated concept <code>(function name) requires a (typeName) to be passed as (argName, if
|
||||
* available)</code>-->
|
||||
*/
|
||||
public static <R extends Value> void register(Class<R> valueClass, String typeName)
|
||||
public static <R extends Value> void register(final Class<R> valueClass, final String typeName)
|
||||
{
|
||||
ValueCaster<R> caster = new ValueCaster<R>(valueClass, typeName);
|
||||
final ValueCaster<R> caster = new ValueCaster<>(valueClass, typeName);
|
||||
byResult.putIfAbsent(valueClass, caster);
|
||||
}
|
||||
}
|
||||
@@ -37,7 +37,7 @@ public interface ValueConverter<R>
|
||||
* @apiNote This method is intended to only be called when an error has occurred and therefore there is a need to print a stacktrace with some
|
||||
* helpful usage instructions.
|
||||
*/
|
||||
public String getTypeName();
|
||||
String getTypeName();
|
||||
|
||||
/**
|
||||
* <p>Converts the given {@link Value} to {@code <R>}, which was defined when being registered.</p>
|
||||
@@ -62,8 +62,7 @@ public interface ValueConverter<R>
|
||||
* <p>Even with the above reasons, {@link ValueConverter} users should try to implement {@link #convert(Value)} whenever possible instead of
|
||||
* {@link #checkAndConvert(Iterator, Context, Context.Type)}, since it allows its usage in generics of lists and maps.</p>
|
||||
*/
|
||||
@Nullable
|
||||
public R convert(Value value);
|
||||
@Nullable R convert(Value value);
|
||||
|
||||
/**
|
||||
* <p>Returns whether this {@link ValueConverter} consumes a variable number of elements from the {@link Iterator} passed to it via
|
||||
@@ -72,7 +71,7 @@ public interface ValueConverter<R>
|
||||
* @implNote The default implementation returns {@code false}
|
||||
* @see #valueConsumption()
|
||||
*/
|
||||
default public boolean consumesVariableArgs()
|
||||
default boolean consumesVariableArgs()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -87,7 +86,7 @@ public interface ValueConverter<R>
|
||||
* @implNote The default implementation returns {@code 1}
|
||||
*
|
||||
*/
|
||||
default public int valueConsumption()
|
||||
default int valueConsumption()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
@@ -107,7 +106,7 @@ public interface ValueConverter<R>
|
||||
* @return A usable {@link ValueConverter} to convert from a {@link Value} to {@code <R>}
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <R> ValueConverter<R> fromAnnotatedType(AnnotatedType annoType)
|
||||
static <R> ValueConverter<R> fromAnnotatedType(final AnnotatedType annoType)
|
||||
{
|
||||
Class<R> type = annoType.getType() instanceof ParameterizedType ? // We are defining R here.
|
||||
(Class<R>) ((ParameterizedType) annoType.getType()).getRawType() :
|
||||
@@ -118,33 +117,51 @@ public interface ValueConverter<R>
|
||||
// Example: AnnotatedGenericTypeArray (or similar) being (@Paran.KeyValuePairs Map<String, String>... name)
|
||||
// Those will just fail with a ClassCastException.
|
||||
if (type.isArray())
|
||||
{
|
||||
type = (Class<R>) type.getComponentType(); // Varargs
|
||||
}
|
||||
type = (Class<R>) ClassUtils.primitiveToWrapper(type); // It will be boxed anyway, this saves unboxing-boxing
|
||||
if (type == List.class)
|
||||
{
|
||||
return (ValueConverter<R>) ListConverter.fromAnnotatedType(annoType); // Already checked that type is List
|
||||
}
|
||||
if (type == Map.class)
|
||||
{
|
||||
return (ValueConverter<R>) MapConverter.fromAnnotatedType(annoType); // Already checked that type is Map
|
||||
}
|
||||
if (type == Optional.class)
|
||||
{
|
||||
return (ValueConverter<R>) OptionalConverter.fromAnnotatedType(annoType);
|
||||
}
|
||||
if (annoType.getDeclaredAnnotations().length != 0)
|
||||
{
|
||||
if (annoType.isAnnotationPresent(Param.Custom.class))
|
||||
return Param.Params.getCustomConverter(annoType, type); // Throws if incorrect usage
|
||||
{
|
||||
return Params.getCustomConverter(annoType, type); // Throws if incorrect usage
|
||||
}
|
||||
if (annoType.isAnnotationPresent(Param.Strict.class))
|
||||
{
|
||||
return (ValueConverter<R>) Params.getStrictConverter(annoType); // Throws if incorrect usage
|
||||
}
|
||||
if (annoType.getAnnotations()[0].annotationType().getEnclosingClass() == Locator.class)
|
||||
{
|
||||
return Locator.Locators.fromAnnotatedType(annoType, type);
|
||||
}
|
||||
}
|
||||
|
||||
// Class only checks
|
||||
if (Value.class.isAssignableFrom(type))
|
||||
{
|
||||
return Objects.requireNonNull(ValueCaster.get(type), "Value subclass " + type + " is not registered. Register it in ValueCaster to use it");
|
||||
// if (type == LazyValue.class) // No longer supported
|
||||
// return (ValueConverter<R>) Params.LAZY_VALUE_IDENTITY;
|
||||
}
|
||||
if (type == Context.class)
|
||||
{
|
||||
return (ValueConverter<R>) Params.CONTEXT_PROVIDER;
|
||||
}
|
||||
if (type == Context.Type.class)
|
||||
{
|
||||
return (ValueConverter<R>) Params.CONTEXT_TYPE_PROVIDER;
|
||||
}
|
||||
return Objects.requireNonNull(SimpleTypeConverter.get(type), "Type " + type + " is not registered. Register it in SimpleTypeConverter to use it");
|
||||
}
|
||||
|
||||
@@ -168,10 +185,8 @@ public interface ValueConverter<R>
|
||||
* @implNote This method's default implementation runs the {@link #convert(Value)} function in the next {@link Value} ignoring {@link Context} and
|
||||
* {@code theLazyT}.
|
||||
*/
|
||||
default public R checkAndConvert(Iterator<Value> valueIterator, Context context, Context.Type contextType)
|
||||
default R checkAndConvert(final Iterator<Value> valueIterator, final Context context, final Context.Type contextType)
|
||||
{
|
||||
if (!valueIterator.hasNext())
|
||||
return null;
|
||||
return convert(valueIterator.next());
|
||||
return !valueIterator.hasNext() ? null : convert(valueIterator.next());
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -15,9 +15,11 @@ import carpet.script.value.LazyListValue;
|
||||
import carpet.script.value.ListValue;
|
||||
import carpet.script.value.NumericValue;
|
||||
import carpet.script.value.Value;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Vec3i;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
@@ -27,19 +29,23 @@ import static java.lang.Math.abs;
|
||||
import static java.lang.Math.max;
|
||||
import static java.lang.Math.min;
|
||||
|
||||
public class BlockIterators {
|
||||
public static void apply(Expression expression)
|
||||
public class BlockIterators
|
||||
{
|
||||
public static void apply(final Expression expression)
|
||||
{
|
||||
// lazy cause of lazy expression
|
||||
expression.addLazyFunction("scan", (c, t, llv) ->
|
||||
{
|
||||
if (llv.size() < 3) throw new InternalExpressionException("'scan' needs many more arguments");
|
||||
List<Value> lv = Fluff.AbstractFunction.unpackLazy(llv.subList(0, llv.size()-1), c, Context.NONE);
|
||||
CarpetContext cc = (CarpetContext)c;
|
||||
BlockArgument centerLocator = BlockArgument.findIn(cc, lv, 0);
|
||||
if (llv.size() < 3)
|
||||
{
|
||||
throw new InternalExpressionException("'scan' needs many more arguments");
|
||||
}
|
||||
final List<Value> lv = Fluff.AbstractFunction.unpackLazy(llv.subList(0, llv.size() - 1), c, Context.NONE);
|
||||
final CarpetContext cc = (CarpetContext) c;
|
||||
final BlockArgument centerLocator = BlockArgument.findIn(cc, lv, 0);
|
||||
Vector3Argument rangeLocator = Vector3Argument.findIn(lv, centerLocator.offset);
|
||||
BlockPos center = centerLocator.block.getPos();
|
||||
Vec3i range;
|
||||
final BlockPos center = centerLocator.block.getPos();
|
||||
final Vec3i range;
|
||||
|
||||
if (rangeLocator.fromBlock)
|
||||
{
|
||||
@@ -48,14 +54,13 @@ public class BlockIterators {
|
||||
abs(rangeLocator.vec.y - center.getY()),
|
||||
abs(rangeLocator.vec.z - center.getZ())
|
||||
);
|
||||
//if (lv.size() > rangeLocator.offset+1) throw new InternalExpressionException("'scan' takes two block positions, and the expression")
|
||||
}
|
||||
else
|
||||
{
|
||||
range = new Vec3i(abs(rangeLocator.vec.x), abs(rangeLocator.vec.y), abs(rangeLocator.vec.z));
|
||||
}
|
||||
Vec3i upperRange = range;
|
||||
if (lv.size() > rangeLocator.offset+1) // +1 cause we still need the expression
|
||||
if (lv.size() > rangeLocator.offset + 1) // +1 cause we still need the expression
|
||||
{
|
||||
rangeLocator = Vector3Argument.findIn(lv, rangeLocator.offset);
|
||||
if (rangeLocator.fromBlock)
|
||||
@@ -65,60 +70,60 @@ public class BlockIterators {
|
||||
abs(rangeLocator.vec.y - center.getY()),
|
||||
abs(rangeLocator.vec.z - center.getZ())
|
||||
);
|
||||
//if (lv.size() > rangeLocator.offset+1) throw new InternalExpressionException("'scan' takes two block positions, and the expression")
|
||||
}
|
||||
else
|
||||
{
|
||||
upperRange = new Vec3i(abs(rangeLocator.vec.x), abs(rangeLocator.vec.y), abs(rangeLocator.vec.z));
|
||||
}
|
||||
}
|
||||
if (llv.size() != rangeLocator.offset+1)
|
||||
if (llv.size() != rangeLocator.offset + 1)
|
||||
{
|
||||
throw new InternalExpressionException("'scan' takes two, or three block positions, and an expression: "+lv.size()+" "+rangeLocator.offset);
|
||||
throw new InternalExpressionException("'scan' takes two, or three block positions, and an expression: " + lv.size() + " " + rangeLocator.offset);
|
||||
}
|
||||
LazyValue expr = llv.get(rangeLocator.offset);
|
||||
final LazyValue expr = llv.get(rangeLocator.offset);
|
||||
|
||||
int cx = center.getX();
|
||||
int cy = center.getY();
|
||||
int cz = center.getZ();
|
||||
int xrange = range.getX();
|
||||
int yrange = range.getY();
|
||||
int zrange = range.getZ();
|
||||
int xprange = upperRange.getX();
|
||||
int yprange = upperRange.getY();
|
||||
int zprange = upperRange.getZ();
|
||||
final int cx = center.getX();
|
||||
final int cy = center.getY();
|
||||
final int cz = center.getZ();
|
||||
final int xrange = range.getX();
|
||||
final int yrange = range.getY();
|
||||
final int zrange = range.getZ();
|
||||
final int xprange = upperRange.getX();
|
||||
final int yprange = upperRange.getY();
|
||||
final int zprange = upperRange.getZ();
|
||||
|
||||
//saving outer scope
|
||||
LazyValue _x = c.getVariable("_x");
|
||||
LazyValue _y = c.getVariable("_y");
|
||||
LazyValue _z = c.getVariable("_z");
|
||||
LazyValue __ = c.getVariable("_");
|
||||
final LazyValue _x = c.getVariable("_x");
|
||||
final LazyValue _y = c.getVariable("_y");
|
||||
final LazyValue _z = c.getVariable("_z");
|
||||
final LazyValue __ = c.getVariable("_");
|
||||
int sCount = 0;
|
||||
outer:for (int y=cy-yrange; y <= cy+yprange; y++)
|
||||
outer:
|
||||
for (int y = cy - yrange; y <= cy + yprange; y++)
|
||||
{
|
||||
int yFinal = y;
|
||||
final int yFinal = y;
|
||||
c.setVariable("_y", (c_, t_) -> new NumericValue(yFinal).bindTo("_y"));
|
||||
for (int x=cx-xrange; x <= cx+xprange; x++)
|
||||
for (int x = cx - xrange; x <= cx + xprange; x++)
|
||||
{
|
||||
int xFinal = x;
|
||||
final int xFinal = x;
|
||||
c.setVariable("_x", (c_, t_) -> new NumericValue(xFinal).bindTo("_x"));
|
||||
for (int z=cz-zrange; z <= cz+zprange; z++)
|
||||
for (int z = cz - zrange; z <= cz + zprange; z++)
|
||||
{
|
||||
int zFinal = z;
|
||||
final int zFinal = z;
|
||||
|
||||
c.setVariable("_z", (c_, t_) -> new NumericValue(zFinal).bindTo("_z"));
|
||||
Value blockValue = BlockValue.fromCoords(((CarpetContext)c), xFinal,yFinal,zFinal).bindTo("_");
|
||||
c.setVariable( "_", (cc_, t_c) -> blockValue);
|
||||
final Value blockValue = BlockValue.fromCoords(((CarpetContext) c), xFinal, yFinal, zFinal).bindTo("_");
|
||||
c.setVariable("_", (cc_, t_c) -> blockValue);
|
||||
Value result;
|
||||
try
|
||||
{
|
||||
result = expr.evalValue(c, t);
|
||||
}
|
||||
catch (ContinueStatement notIgnored)
|
||||
catch (final ContinueStatement notIgnored)
|
||||
{
|
||||
result = notIgnored.retval;
|
||||
}
|
||||
catch (BreakStatement notIgnored)
|
||||
catch (final BreakStatement notIgnored)
|
||||
{
|
||||
break outer;
|
||||
}
|
||||
@@ -134,66 +139,70 @@ public class BlockIterators {
|
||||
c.setVariable("_y", _y);
|
||||
c.setVariable("_z", _z);
|
||||
c.setVariable("_", __);
|
||||
int finalSCount = sCount;
|
||||
final int finalSCount = sCount;
|
||||
return (c_, t_) -> new NumericValue(finalSCount);
|
||||
});
|
||||
|
||||
// must be lazy
|
||||
expression.addLazyFunction("volume", (c, t, llv) ->
|
||||
{
|
||||
CarpetContext cc = (CarpetContext)c;
|
||||
if (llv.size() < 3) throw new InternalExpressionException("'volume' needs many more arguments");
|
||||
List<Value> lv = Fluff.AbstractFunction.unpackLazy(llv.subList(0, llv.size()-1), c, Context.NONE);
|
||||
final CarpetContext cc = (CarpetContext) c;
|
||||
if (llv.size() < 3)
|
||||
{
|
||||
throw new InternalExpressionException("'volume' needs many more arguments");
|
||||
}
|
||||
final List<Value> lv = Fluff.AbstractFunction.unpackLazy(llv.subList(0, llv.size() - 1), c, Context.NONE);
|
||||
|
||||
BlockArgument pos1Locator = BlockArgument.findIn(cc, lv, 0);
|
||||
BlockArgument pos2Locator = BlockArgument.findIn(cc, lv, pos1Locator.offset);
|
||||
BlockPos pos1 = pos1Locator.block.getPos();
|
||||
BlockPos pos2 = pos2Locator.block.getPos();
|
||||
final BlockArgument pos1Locator = BlockArgument.findIn(cc, lv, 0);
|
||||
final BlockArgument pos2Locator = BlockArgument.findIn(cc, lv, pos1Locator.offset);
|
||||
final BlockPos pos1 = pos1Locator.block.getPos();
|
||||
final BlockPos pos2 = pos2Locator.block.getPos();
|
||||
|
||||
int x1 = pos1.getX();
|
||||
int y1 = pos1.getY();
|
||||
int z1 = pos1.getZ();
|
||||
int x2 = pos2.getX();
|
||||
int y2 = pos2.getY();
|
||||
int z2 = pos2.getZ();
|
||||
int minx = min(x1, x2);
|
||||
int miny = min(y1, y2);
|
||||
int minz = min(z1, z2);
|
||||
int maxx = max(x1, x2);
|
||||
int maxy = max(y1, y2);
|
||||
int maxz = max(z1, z2);
|
||||
LazyValue expr = llv.get(pos2Locator.offset);
|
||||
final int x1 = pos1.getX();
|
||||
final int y1 = pos1.getY();
|
||||
final int z1 = pos1.getZ();
|
||||
final int x2 = pos2.getX();
|
||||
final int y2 = pos2.getY();
|
||||
final int z2 = pos2.getZ();
|
||||
final int minx = min(x1, x2);
|
||||
final int miny = min(y1, y2);
|
||||
final int minz = min(z1, z2);
|
||||
final int maxx = max(x1, x2);
|
||||
final int maxy = max(y1, y2);
|
||||
final int maxz = max(z1, z2);
|
||||
final LazyValue expr = llv.get(pos2Locator.offset);
|
||||
|
||||
//saving outer scope
|
||||
LazyValue _x = c.getVariable("_x");
|
||||
LazyValue _y = c.getVariable("_y");
|
||||
LazyValue _z = c.getVariable("_z");
|
||||
LazyValue __ = c.getVariable("_");
|
||||
final LazyValue _x = c.getVariable("_x");
|
||||
final LazyValue _y = c.getVariable("_y");
|
||||
final LazyValue _z = c.getVariable("_z");
|
||||
final LazyValue __ = c.getVariable("_");
|
||||
int sCount = 0;
|
||||
outer:for (int y=miny; y <= maxy; y++)
|
||||
outer:
|
||||
for (int y = miny; y <= maxy; y++)
|
||||
{
|
||||
int yFinal = y;
|
||||
final int yFinal = y;
|
||||
c.setVariable("_y", (c_, t_) -> new NumericValue(yFinal).bindTo("_y"));
|
||||
for (int x=minx; x <= maxx; x++)
|
||||
for (int x = minx; x <= maxx; x++)
|
||||
{
|
||||
int xFinal = x;
|
||||
final int xFinal = x;
|
||||
c.setVariable("_x", (c_, t_) -> new NumericValue(xFinal).bindTo("_x"));
|
||||
for (int z=minz; z <= maxz; z++)
|
||||
for (int z = minz; z <= maxz; z++)
|
||||
{
|
||||
int zFinal = z;
|
||||
final int zFinal = z;
|
||||
c.setVariable("_z", (c_, t_) -> new NumericValue(zFinal).bindTo("_z"));
|
||||
Value blockValue = BlockValue.fromCoords(((CarpetContext)c), xFinal,yFinal,zFinal).bindTo("_");
|
||||
c.setVariable( "_", (cc_, t_c) -> blockValue);
|
||||
final Value blockValue = BlockValue.fromCoords(((CarpetContext) c), xFinal, yFinal, zFinal).bindTo("_");
|
||||
c.setVariable("_", (cc_, t_c) -> blockValue);
|
||||
Value result;
|
||||
try
|
||||
{
|
||||
result = expr.evalValue(c, t);
|
||||
}
|
||||
catch (ContinueStatement notIgnored)
|
||||
catch (final ContinueStatement notIgnored)
|
||||
{
|
||||
result = notIgnored.retval;
|
||||
}
|
||||
catch (BreakStatement notIgnored)
|
||||
catch (final BreakStatement notIgnored)
|
||||
{
|
||||
break outer;
|
||||
}
|
||||
@@ -209,16 +218,16 @@ public class BlockIterators {
|
||||
c.setVariable("_y", _y);
|
||||
c.setVariable("_z", _z);
|
||||
c.setVariable("_", __);
|
||||
int finalSCount = sCount;
|
||||
final int finalSCount = sCount;
|
||||
return (c_, t_) -> new NumericValue(finalSCount);
|
||||
});
|
||||
|
||||
expression.addContextFunction("neighbours", -1, (c, t, lv)->
|
||||
expression.addContextFunction("neighbours", -1, (c, t, lv) ->
|
||||
{
|
||||
BlockPos center = BlockArgument.findIn((CarpetContext) c, lv,0).block.getPos();
|
||||
ServerLevel world = ((CarpetContext) c).level();
|
||||
final BlockPos center = BlockArgument.findIn((CarpetContext) c, lv, 0).block.getPos();
|
||||
final ServerLevel world = ((CarpetContext) c).level();
|
||||
|
||||
List<Value> neighbours = new ArrayList<>();
|
||||
final List<Value> neighbours = new ArrayList<>();
|
||||
neighbours.add(new BlockValue(null, world, center.above()));
|
||||
neighbours.add(new BlockValue(null, world, center.below()));
|
||||
neighbours.add(new BlockValue(null, world, center.north()));
|
||||
@@ -228,20 +237,26 @@ public class BlockIterators {
|
||||
return ListValue.wrap(neighbours);
|
||||
});
|
||||
|
||||
expression.addContextFunction("rect", -1, (c, t, lv)->
|
||||
expression.addContextFunction("rect", -1, (c, t, lv) ->
|
||||
{
|
||||
CarpetContext cc = (CarpetContext) c;
|
||||
int cx, cy, cz;
|
||||
int sminx, sminy, sminz;
|
||||
int smaxx, smaxy, smaxz;
|
||||
BlockArgument cposLocator = BlockArgument.findIn(cc, lv, 0);
|
||||
BlockPos cpos = cposLocator.block.getPos();
|
||||
final CarpetContext cc = (CarpetContext) c;
|
||||
final int cx;
|
||||
final int cy;
|
||||
final int cz;
|
||||
final int sminx;
|
||||
final int sminy;
|
||||
final int sminz;
|
||||
final int smaxx;
|
||||
final int smaxy;
|
||||
final int smaxz;
|
||||
final BlockArgument cposLocator = BlockArgument.findIn(cc, lv, 0);
|
||||
final BlockPos cpos = cposLocator.block.getPos();
|
||||
cx = cpos.getX();
|
||||
cy = cpos.getY();
|
||||
cz = cpos.getZ();
|
||||
if (lv.size() > cposLocator.offset)
|
||||
{
|
||||
Vector3Argument diffLocator = Vector3Argument.findIn(lv, cposLocator.offset);
|
||||
final Vector3Argument diffLocator = Vector3Argument.findIn(lv, cposLocator.offset);
|
||||
if (diffLocator.fromBlock)
|
||||
{
|
||||
sminx = Mth.floor(abs(diffLocator.vec.x - cx));
|
||||
@@ -256,7 +271,7 @@ public class BlockIterators {
|
||||
}
|
||||
if (lv.size() > diffLocator.offset)
|
||||
{
|
||||
Vector3Argument posDiff = Vector3Argument.findIn(lv, diffLocator.offset);
|
||||
final Vector3Argument posDiff = Vector3Argument.findIn(lv, diffLocator.offset);
|
||||
if (posDiff.fromBlock)
|
||||
{
|
||||
smaxx = Mth.floor(abs(posDiff.vec.x - cx));
|
||||
@@ -289,19 +304,21 @@ public class BlockIterators {
|
||||
|
||||
return new LazyListValue()
|
||||
{
|
||||
final int minx = cx-sminx;
|
||||
final int miny = cy-sminy;
|
||||
final int minz = cz-sminz;
|
||||
final int maxx = cx+smaxx;
|
||||
final int maxy = cy+smaxy;
|
||||
final int maxz = cz+smaxz;
|
||||
final int minx = cx - sminx;
|
||||
final int miny = cy - sminy;
|
||||
final int minz = cz - sminz;
|
||||
final int maxx = cx + smaxx;
|
||||
final int maxy = cy + smaxy;
|
||||
final int maxz = cz + smaxz;
|
||||
|
||||
int x;
|
||||
int y;
|
||||
int z;
|
||||
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext()
|
||||
{
|
||||
@@ -311,7 +328,7 @@ public class BlockIterators {
|
||||
@Override
|
||||
public Value next()
|
||||
{
|
||||
Value r = BlockValue.fromCoords(cc, x,y,z);
|
||||
final Value r = BlockValue.fromCoords(cc, x, y, z);
|
||||
//possibly reroll context
|
||||
x++;
|
||||
if (x > maxx)
|
||||
@@ -347,56 +364,57 @@ public class BlockIterators {
|
||||
@Override
|
||||
public String getString()
|
||||
{
|
||||
return String.format(Locale.ROOT, "rect[(%d,%d,%d),..,(%d,%d,%d)]",minx, miny, minz, maxx, maxy, maxz);
|
||||
return String.format(Locale.ROOT, "rect[(%d,%d,%d),..,(%d,%d,%d)]", minx, miny, minz, maxx, maxy, maxz);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
expression.addContextFunction("diamond", -1, (c, t, lv)->
|
||||
expression.addContextFunction("diamond", -1, (c, t, lv) ->
|
||||
{
|
||||
CarpetContext cc = (CarpetContext)c;
|
||||
final CarpetContext cc = (CarpetContext) c;
|
||||
|
||||
BlockArgument cposLocator=BlockArgument.findIn((CarpetContext)c,lv,0);
|
||||
BlockPos cpos = cposLocator.block.getPos();
|
||||
final BlockArgument cposLocator = BlockArgument.findIn((CarpetContext) c, lv, 0);
|
||||
final BlockPos cpos = cposLocator.block.getPos();
|
||||
|
||||
int cx;
|
||||
int cy;
|
||||
int cz;
|
||||
int width;
|
||||
int height;
|
||||
final int cx;
|
||||
final int cy;
|
||||
final int cz;
|
||||
final int width;
|
||||
final int height;
|
||||
try
|
||||
{
|
||||
cx = cpos.getX();
|
||||
cy = cpos.getY();
|
||||
cz = cpos.getZ();
|
||||
|
||||
if (lv.size()==cposLocator.offset)
|
||||
if (lv.size() == cposLocator.offset)
|
||||
{
|
||||
Value retval = ListValue.of(
|
||||
BlockValue.fromCoords(cc, cx, cy-1, cz),
|
||||
return ListValue.of(
|
||||
BlockValue.fromCoords(cc, cx, cy - 1, cz),
|
||||
BlockValue.fromCoords(cc, cx, cy, cz),
|
||||
BlockValue.fromCoords(cc, cx-1, cy, cz),
|
||||
BlockValue.fromCoords(cc, cx, cy, cz-1),
|
||||
BlockValue.fromCoords(cc, cx+1, cy, cz),
|
||||
BlockValue.fromCoords(cc, cx, cy, cz+1),
|
||||
BlockValue.fromCoords(cc, cx, cy+1, cz)
|
||||
BlockValue.fromCoords(cc, cx - 1, cy, cz),
|
||||
BlockValue.fromCoords(cc, cx, cy, cz - 1),
|
||||
BlockValue.fromCoords(cc, cx + 1, cy, cz),
|
||||
BlockValue.fromCoords(cc, cx, cy, cz + 1),
|
||||
BlockValue.fromCoords(cc, cx, cy + 1, cz)
|
||||
);
|
||||
return retval;
|
||||
}
|
||||
else if (lv.size()==1+cposLocator.offset)
|
||||
else if (lv.size() == 1 + cposLocator.offset)
|
||||
{
|
||||
width = (int) ((NumericValue) lv.get(cposLocator.offset)).getLong();
|
||||
height = 0;
|
||||
}
|
||||
else if(lv.size()==2+cposLocator.offset)
|
||||
else if (lv.size() == 2 + cposLocator.offset)
|
||||
{
|
||||
width = (int) ((NumericValue) lv.get(cposLocator.offset)).getLong();
|
||||
height = (int) ((NumericValue) lv.get(cposLocator.offset+1)).getLong();
|
||||
} else{
|
||||
height = (int) ((NumericValue) lv.get(cposLocator.offset + 1)).getLong();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InternalExpressionException("Incorrect number of arguments for 'diamond'");
|
||||
}
|
||||
}
|
||||
catch (ClassCastException exc)
|
||||
catch (final ClassCastException ignored)
|
||||
{
|
||||
throw new InternalExpressionException("Attempted to pass a non-number to 'diamond'");
|
||||
}
|
||||
@@ -406,9 +424,11 @@ public class BlockIterators {
|
||||
{
|
||||
int curradius;
|
||||
int curpos;
|
||||
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext()
|
||||
{
|
||||
@@ -425,9 +445,9 @@ public class BlockIterators {
|
||||
}
|
||||
// x = 3-|i-6|
|
||||
// z = |( (i-3)%12-6|-3
|
||||
Value block = BlockValue.fromCoords(cc, cx+(curradius-abs(curpos-2*curradius)), cy, cz-curradius+abs( abs(curpos-curradius)%(4*curradius) -2*curradius ));
|
||||
final Value block = BlockValue.fromCoords(cc, cx + (curradius - abs(curpos - 2 * curradius)), cy, cz - curradius + abs(abs(curpos - curradius) % (4 * curradius) - 2 * curradius));
|
||||
curpos++;
|
||||
if (curpos>=curradius*4)
|
||||
if (curpos >= curradius * 4)
|
||||
{
|
||||
curradius++;
|
||||
curpos = 0;
|
||||
@@ -446,7 +466,7 @@ public class BlockIterators {
|
||||
@Override
|
||||
public String getString()
|
||||
{
|
||||
return String.format(Locale.ROOT, "diamond[(%d,%d,%d),%d,0]",cx, cy, cz, width);
|
||||
return String.format(Locale.ROOT, "diamond[(%d,%d,%d),%d,0]", cx, cy, cz, width);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -457,9 +477,11 @@ public class BlockIterators {
|
||||
int curradius;
|
||||
int curpos;
|
||||
int curheight;
|
||||
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext()
|
||||
{
|
||||
@@ -471,27 +493,26 @@ public class BlockIterators {
|
||||
{
|
||||
if (curheight == -height || curheight == height)
|
||||
{
|
||||
return BlockValue.fromCoords(cc, cx, cy+curheight++, cz);
|
||||
return BlockValue.fromCoords(cc, cx, cy + curheight++, cz);
|
||||
}
|
||||
if (curradius == 0)
|
||||
{
|
||||
curradius++;
|
||||
return BlockValue.fromCoords(cc, cx, cy+curheight, cz);
|
||||
return BlockValue.fromCoords(cc, cx, cy + curheight, cz);
|
||||
}
|
||||
// x = 3-|i-6|
|
||||
// z = |( (i-3)%12-6|-3
|
||||
|
||||
Value block = BlockValue.fromCoords(cc, cx+(curradius-abs(curpos-2*curradius)), cy+curheight, cz-curradius+abs( abs(curpos-curradius)%(4*curradius) -2*curradius ));
|
||||
final Value block = BlockValue.fromCoords(cc, cx + (curradius - abs(curpos - 2 * curradius)), cy + curheight, cz - curradius + abs(abs(curpos - curradius) % (4 * curradius) - 2 * curradius));
|
||||
curpos++;
|
||||
if (curpos>=curradius*4)
|
||||
if (curpos >= curradius * 4)
|
||||
{
|
||||
curradius++;
|
||||
curpos = 0;
|
||||
if (curradius>width -abs(width*curheight/height))
|
||||
if (curradius > width - abs(width * curheight / height))
|
||||
{
|
||||
curheight++;
|
||||
curradius = 0;
|
||||
curpos = 0;
|
||||
}
|
||||
}
|
||||
return block;
|
||||
@@ -508,7 +529,7 @@ public class BlockIterators {
|
||||
@Override
|
||||
public String getString()
|
||||
{
|
||||
return String.format(Locale.ROOT, "diamond[(%d,%d,%d),%d,%d]",cx, cy, cz, width, height);
|
||||
return String.format(Locale.ROOT, "diamond[(%d,%d,%d),%d,%d]", cx, cy, cz, width, height);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import carpet.script.value.NumericValue;
|
||||
import carpet.script.value.Value;
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
@@ -23,6 +24,7 @@ import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
@@ -38,159 +40,183 @@ import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
public class Entities {
|
||||
private static ListValue getPlayersFromWorldMatching(Context c, Predicate<ServerPlayer> condition) {
|
||||
List<Value> ret = new ArrayList<>();
|
||||
for (ServerPlayer player: ((CarpetContext) c).level().players()) {
|
||||
if (condition.test(player)) {
|
||||
public class Entities
|
||||
{
|
||||
private static ListValue getPlayersFromWorldMatching(final Context c, final Predicate<ServerPlayer> condition)
|
||||
{
|
||||
final List<Value> ret = new ArrayList<>();
|
||||
for (final ServerPlayer player : ((CarpetContext) c).level().players())
|
||||
{
|
||||
if (condition.test(player))
|
||||
{
|
||||
ret.add(new EntityValue(player));
|
||||
}
|
||||
}
|
||||
return ListValue.wrap(ret);
|
||||
}
|
||||
|
||||
public static void apply(Expression expression)
|
||||
public static void apply(final Expression expression)
|
||||
{
|
||||
expression.addContextFunction("player", -1, (c, t, lv) ->
|
||||
{
|
||||
if (lv.size() == 0)
|
||||
{
|
||||
CarpetContext cc = (CarpetContext)c;
|
||||
final CarpetContext cc = (CarpetContext) c;
|
||||
if (cc.host.user != null)
|
||||
{
|
||||
ServerPlayer player = cc.server().getPlayerList().getPlayerByName(cc.host.user);
|
||||
final ServerPlayer player = cc.server().getPlayerList().getPlayerByName(cc.host.user);
|
||||
return EntityValue.of(player);
|
||||
}
|
||||
Entity callingEntity = cc.source().getEntity();
|
||||
if (callingEntity instanceof Player) return EntityValue.of(callingEntity);
|
||||
Vec3 pos = ((CarpetContext)c).source().getPosition();
|
||||
Player closestPlayer = ((CarpetContext)c).level().getNearestPlayer(pos.x, pos.y, pos.z, -1.0, EntitySelector.ENTITY_STILL_ALIVE);
|
||||
final Entity callingEntity = cc.source().getEntity();
|
||||
if (callingEntity instanceof Player)
|
||||
{
|
||||
return EntityValue.of(callingEntity);
|
||||
}
|
||||
final Vec3 pos = ((CarpetContext) c).source().getPosition();
|
||||
final Player closestPlayer = ((CarpetContext) c).level().getNearestPlayer(pos.x, pos.y, pos.z, -1.0, EntitySelector.ENTITY_STILL_ALIVE);
|
||||
return EntityValue.of(closestPlayer);
|
||||
}
|
||||
String playerName = lv.get(0).getString();
|
||||
return switch (playerName) {
|
||||
case "all" -> {
|
||||
List<Value> ret = new ArrayList<>();
|
||||
for (ServerPlayer player: ((CarpetContext)c).server().getPlayerList().getPlayers())
|
||||
ret.add(new EntityValue(player));
|
||||
yield ListValue.wrap(ret);
|
||||
}
|
||||
case "*" -> getPlayersFromWorldMatching(c, p -> true);
|
||||
case "survival" -> getPlayersFromWorldMatching(c, p -> p.gameMode.isSurvival()); // todo assert correct
|
||||
case "creative" -> getPlayersFromWorldMatching(c, ServerPlayer::isCreative);
|
||||
case "spectating" -> getPlayersFromWorldMatching(c, ServerPlayer::isSpectator);
|
||||
case "!spectating" -> getPlayersFromWorldMatching(c, p -> !p.isSpectator());
|
||||
default -> {
|
||||
ServerPlayer player = ((CarpetContext) c).server().getPlayerList().getPlayerByName(playerName);
|
||||
if (player != null) {
|
||||
yield new EntityValue(player);
|
||||
}
|
||||
yield Value.NULL;
|
||||
}
|
||||
};
|
||||
final String playerName = lv.get(0).getString();
|
||||
return switch (playerName)
|
||||
{
|
||||
case "all" -> {
|
||||
final List<Value> ret = new ArrayList<>();
|
||||
for (final ServerPlayer player : ((CarpetContext) c).server().getPlayerList().getPlayers())
|
||||
{
|
||||
ret.add(new EntityValue(player));
|
||||
}
|
||||
yield ListValue.wrap(ret);
|
||||
}
|
||||
case "*" -> getPlayersFromWorldMatching(c, p -> true);
|
||||
case "survival" -> getPlayersFromWorldMatching(c, p -> p.gameMode.isSurvival()); // todo assert correct
|
||||
case "creative" -> getPlayersFromWorldMatching(c, ServerPlayer::isCreative);
|
||||
case "spectating" -> getPlayersFromWorldMatching(c, ServerPlayer::isSpectator);
|
||||
case "!spectating" -> getPlayersFromWorldMatching(c, p -> !p.isSpectator());
|
||||
default -> {
|
||||
final ServerPlayer player = ((CarpetContext) c).server().getPlayerList().getPlayerByName(playerName);
|
||||
yield player != null ? new EntityValue(player) : Value.NULL;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
expression.addContextFunction("spawn", -1, (c, t, lv) ->
|
||||
{
|
||||
CarpetContext cc = (CarpetContext)c;
|
||||
final CarpetContext cc = (CarpetContext) c;
|
||||
if (lv.size() < 2)
|
||||
{
|
||||
throw new InternalExpressionException("'spawn' function takes mob name, and position to spawn");
|
||||
String entityString = lv.get(0).getString();
|
||||
ResourceLocation entityId;
|
||||
}
|
||||
final String entityString = lv.get(0).getString();
|
||||
final ResourceLocation entityId;
|
||||
try
|
||||
{
|
||||
entityId = ResourceLocation.read(new StringReader(entityString));
|
||||
EntityType<? extends Entity> type = cc.registry(Registries.ENTITY_TYPE).getOptional(entityId).orElse(null);
|
||||
final EntityType<? extends Entity> type = cc.registry(Registries.ENTITY_TYPE).getOptional(entityId).orElse(null);
|
||||
if (type == null || !type.canSummon())
|
||||
{
|
||||
return Value.NULL;
|
||||
}
|
||||
}
|
||||
catch (CommandSyntaxException exception)
|
||||
catch (final CommandSyntaxException ignored)
|
||||
{
|
||||
return Value.NULL;
|
||||
return Value.NULL;
|
||||
}
|
||||
|
||||
Vector3Argument position = Vector3Argument.findIn(lv, 1);
|
||||
final Vector3Argument position = Vector3Argument.findIn(lv, 1);
|
||||
if (position.fromBlock)
|
||||
{
|
||||
position.vec = position.vec.subtract(0, 0.5, 0);
|
||||
}
|
||||
CompoundTag tag = new CompoundTag();
|
||||
boolean hasTag = false;
|
||||
if (lv.size() > position.offset)
|
||||
{
|
||||
Value nbt = lv.get(position.offset);
|
||||
NBTSerializableValue v = (nbt instanceof NBTSerializableValue) ? (NBTSerializableValue) nbt
|
||||
final Value nbt = lv.get(position.offset);
|
||||
final NBTSerializableValue v = (nbt instanceof final NBTSerializableValue nbtsv)
|
||||
? nbtsv
|
||||
: NBTSerializableValue.parseString(nbt.getString(), true);
|
||||
hasTag = true;
|
||||
tag = v.getCompoundTag();
|
||||
|
||||
}
|
||||
tag.putString("id", entityId.toString());
|
||||
Vec3 vec3d = position.vec;
|
||||
final Vec3 vec3d = position.vec;
|
||||
|
||||
ServerLevel serverWorld = cc.level();
|
||||
Entity entity_1 = EntityType.loadEntityRecursive(tag, serverWorld, (entity_1x) -> {
|
||||
entity_1x.moveTo(vec3d.x, vec3d.y, vec3d.z, entity_1x.getYRot(), entity_1x.getXRot());
|
||||
return !serverWorld.addWithUUID(entity_1x) ? null : entity_1x;
|
||||
final ServerLevel serverWorld = cc.level();
|
||||
final Entity entity = EntityType.loadEntityRecursive(tag, serverWorld, e -> {
|
||||
e.moveTo(vec3d.x, vec3d.y, vec3d.z, e.getYRot(), e.getXRot());
|
||||
return !serverWorld.addWithUUID(e) ? null : e;
|
||||
});
|
||||
if (entity_1 == null) {
|
||||
if (entity == null)
|
||||
{
|
||||
return Value.NULL;
|
||||
} else {
|
||||
if (!hasTag && entity_1 instanceof Mob) {
|
||||
((Mob)entity_1).finalizeSpawn(serverWorld, serverWorld.getCurrentDifficultyAt(entity_1.blockPosition()), MobSpawnType.COMMAND, null, null);
|
||||
}
|
||||
return new EntityValue(entity_1);
|
||||
}
|
||||
if (!hasTag && entity instanceof final Mob mob)
|
||||
{
|
||||
mob.finalizeSpawn(serverWorld, serverWorld.getCurrentDifficultyAt(entity.blockPosition()), MobSpawnType.COMMAND, null, null);
|
||||
}
|
||||
return new EntityValue(entity);
|
||||
});
|
||||
|
||||
expression.addContextFunction("entity_id", 1, (c, t, lv) ->
|
||||
{
|
||||
Value who = lv.get(0);
|
||||
if (who instanceof NumericValue)
|
||||
return EntityValue.of(((CarpetContext)c).level().getEntity((int)((NumericValue) who).getLong()));
|
||||
return EntityValue.of(((CarpetContext)c).level().getEntity(UUID.fromString(who.getString())));
|
||||
final Value who = lv.get(0);
|
||||
if (who instanceof final NumericValue numericValue)
|
||||
{
|
||||
return EntityValue.of(((CarpetContext) c).level().getEntity((int) numericValue.getLong()));
|
||||
}
|
||||
return EntityValue.of(((CarpetContext) c).level().getEntity(UUID.fromString(who.getString())));
|
||||
});
|
||||
|
||||
expression.addContextFunction("entity_list", 1, (c, t, lv) ->
|
||||
{
|
||||
String who = lv.get(0).getString();
|
||||
CommandSourceStack source = ((CarpetContext)c).source();
|
||||
EntityValue.EntityClassDescriptor eDesc = EntityValue.getEntityDescriptor(who, source.getServer());
|
||||
List<? extends Entity> entityList = source.getLevel().getEntities(eDesc.directType, eDesc.filteringPredicate);
|
||||
final String who = lv.get(0).getString();
|
||||
final CommandSourceStack source = ((CarpetContext) c).source();
|
||||
final EntityValue.EntityClassDescriptor eDesc = EntityValue.getEntityDescriptor(who, source.getServer());
|
||||
final List<? extends Entity> entityList = source.getLevel().getEntities(eDesc.directType, eDesc.filteringPredicate);
|
||||
return ListValue.wrap(entityList.stream().map(EntityValue::new));
|
||||
});
|
||||
|
||||
expression.addContextFunction("entity_area", -1, (c, t, lv) ->
|
||||
{
|
||||
if (lv.size()<3) throw new InternalExpressionException("'entity_area' requires entity type, center and range arguments");
|
||||
String who = lv.get(0).getString();
|
||||
CarpetContext cc = (CarpetContext)c;
|
||||
Vector3Argument centerLocator = Vector3Argument.findIn(lv, 1, false, true);
|
||||
if (lv.size() < 3)
|
||||
{
|
||||
throw new InternalExpressionException("'entity_area' requires entity type, center and range arguments");
|
||||
}
|
||||
final String who = lv.get(0).getString();
|
||||
final CarpetContext cc = (CarpetContext) c;
|
||||
final Vector3Argument centerLocator = Vector3Argument.findIn(lv, 1, false, true);
|
||||
|
||||
AABB centerBox;
|
||||
final AABB centerBox;
|
||||
if (centerLocator.entity != null)
|
||||
{
|
||||
centerBox = centerLocator.entity.getBoundingBox();
|
||||
}
|
||||
else
|
||||
{
|
||||
Vec3 center = centerLocator.vec;
|
||||
if (centerLocator.fromBlock) center.add(0.5, 0.5, 0.5);
|
||||
final Vec3 center = centerLocator.vec;
|
||||
if (centerLocator.fromBlock)
|
||||
{
|
||||
center.add(0.5, 0.5, 0.5);
|
||||
}
|
||||
centerBox = new AABB(center, center);
|
||||
}
|
||||
Vector3Argument rangeLocator = Vector3Argument.findIn(lv, centerLocator.offset);
|
||||
final Vector3Argument rangeLocator = Vector3Argument.findIn(lv, centerLocator.offset);
|
||||
if (rangeLocator.fromBlock)
|
||||
{
|
||||
throw new InternalExpressionException("Range of 'entity_area' cannot come from a block argument");
|
||||
Vec3 range = rangeLocator.vec;
|
||||
AABB area = centerBox.inflate(range.x, range.y, range.z);
|
||||
EntityValue.EntityClassDescriptor eDesc = EntityValue.getEntityDescriptor(who, cc.server());
|
||||
List<? extends Entity> entityList = cc.level().getEntities(eDesc.directType, area,eDesc.filteringPredicate);
|
||||
}
|
||||
final Vec3 range = rangeLocator.vec;
|
||||
final AABB area = centerBox.inflate(range.x, range.y, range.z);
|
||||
final EntityValue.EntityClassDescriptor eDesc = EntityValue.getEntityDescriptor(who, cc.server());
|
||||
final List<? extends Entity> entityList = cc.level().getEntities(eDesc.directType, area, eDesc.filteringPredicate);
|
||||
return ListValue.wrap(entityList.stream().map(EntityValue::new));
|
||||
});
|
||||
|
||||
expression.addContextFunction("entity_selector", -1, (c, t, lv) ->
|
||||
{
|
||||
String selector = lv.get(0).getString();
|
||||
List<Value> retlist = new ArrayList<>();
|
||||
for (Entity e: EntityValue.getEntitiesFromSelector(((CarpetContext)c).source(), selector))
|
||||
final String selector = lv.get(0).getString();
|
||||
final List<Value> retlist = new ArrayList<>();
|
||||
for (final Entity e : EntityValue.getEntitiesFromSelector(((CarpetContext) c).source(), selector))
|
||||
{
|
||||
retlist.add(new EntityValue(e));
|
||||
}
|
||||
@@ -199,64 +225,74 @@ public class Entities {
|
||||
|
||||
expression.addContextFunction("query", -1, (c, t, lv) ->
|
||||
{
|
||||
if (lv.size()<2)
|
||||
if (lv.size() < 2)
|
||||
{
|
||||
throw new InternalExpressionException("'query' takes entity as a first argument, and queried feature as a second");
|
||||
}
|
||||
Value v = lv.get(0);
|
||||
if (!(v instanceof EntityValue))
|
||||
final Value v = lv.get(0);
|
||||
if (!(v instanceof final EntityValue ev))
|
||||
{
|
||||
throw new InternalExpressionException("First argument to query should be an entity");
|
||||
String what = lv.get(1).getString().toLowerCase(Locale.ROOT);
|
||||
}
|
||||
final String what = lv.get(1).getString().toLowerCase(Locale.ROOT);
|
||||
if (what.equals("tags"))
|
||||
{
|
||||
c.host.issueDeprecation("'tags' for entity querying");
|
||||
Value retval;
|
||||
if (lv.size()==2)
|
||||
retval = ((EntityValue) v).get(what, null);
|
||||
else if (lv.size()==3)
|
||||
retval = ((EntityValue) v).get(what, lv.get(2));
|
||||
else
|
||||
retval = ((EntityValue) v).get(what, ListValue.wrap(lv.subList(2, lv.size())));
|
||||
return retval;
|
||||
}
|
||||
return switch (lv.size())
|
||||
{
|
||||
case 2 -> ev.get(what, null);
|
||||
case 3 -> ev.get(what, lv.get(2));
|
||||
default -> ev.get(what, ListValue.wrap(lv.subList(2, lv.size())));
|
||||
};
|
||||
});
|
||||
|
||||
// or update
|
||||
expression.addContextFunction("modify", -1, (c, t, lv) ->
|
||||
{
|
||||
if (lv.size()<2)
|
||||
if (lv.size() < 2)
|
||||
{
|
||||
throw new InternalExpressionException("'modify' takes entity as a first argument, and queried feature as a second");
|
||||
}
|
||||
Value v = lv.get(0);
|
||||
if (!(v instanceof EntityValue))
|
||||
final Value v = lv.get(0);
|
||||
if (!(v instanceof final EntityValue ev))
|
||||
{
|
||||
throw new InternalExpressionException("First argument to modify should be an entity");
|
||||
String what = lv.get(1).getString();
|
||||
if (lv.size()==2)
|
||||
((EntityValue) v).set(what, null);
|
||||
else if (lv.size()==3)
|
||||
((EntityValue) v).set(what, lv.get(2));
|
||||
else
|
||||
((EntityValue) v).set(what, ListValue.wrap(lv.subList(2, lv.size())));
|
||||
}
|
||||
final String what = lv.get(1).getString();
|
||||
switch (lv.size())
|
||||
{
|
||||
case 2 -> ev.set(what, null);
|
||||
case 3 -> ev.set(what, lv.get(2));
|
||||
default -> ev.set(what, ListValue.wrap(lv.subList(2, lv.size())));
|
||||
}
|
||||
return v;
|
||||
});
|
||||
|
||||
expression.addContextFunction("entity_types", -1, (c, t, lv) ->
|
||||
{
|
||||
if (lv.size() > 1) throw new InternalExpressionException("'entity_types' requires one or no arguments");
|
||||
String desc = (lv.size() == 1)?lv.get(0).getString():"*";
|
||||
return EntityValue.getEntityDescriptor(desc, ((CarpetContext) c).server()).listValue(((CarpetContext)c).registryAccess());
|
||||
if (lv.size() > 1)
|
||||
{
|
||||
throw new InternalExpressionException("'entity_types' requires one or no arguments");
|
||||
}
|
||||
final String desc = (lv.size() == 1) ? lv.get(0).getString() : "*";
|
||||
return EntityValue.getEntityDescriptor(desc, ((CarpetContext) c).server()).listValue(((CarpetContext) c).registryAccess());
|
||||
});
|
||||
|
||||
expression.addContextFunction("entity_load_handler", -1, (c, t, lv) ->
|
||||
{
|
||||
if (lv.size() < 2) throw new InternalExpressionException("'entity_load_handler' required the entity type, and a function to call");
|
||||
Value entityValue = lv.get(0);
|
||||
List<String> descriptors = (entityValue instanceof ListValue)
|
||||
? ((ListValue) entityValue).getItems().stream().map(Value::getString).toList()
|
||||
if (lv.size() < 2)
|
||||
{
|
||||
throw new InternalExpressionException("'entity_load_handler' required the entity type, and a function to call");
|
||||
}
|
||||
final Value entityValue = lv.get(0);
|
||||
final List<String> descriptors = (entityValue instanceof final ListValue list)
|
||||
? list.getItems().stream().map(Value::getString).toList()
|
||||
: Collections.singletonList(entityValue.getString());
|
||||
Set<EntityType<? extends Entity>> types = new HashSet<>();
|
||||
final Set<EntityType<? extends Entity>> types = new HashSet<>();
|
||||
descriptors.forEach(s -> types.addAll(EntityValue.getEntityDescriptor(s, ((CarpetContext) c).server()).types));
|
||||
FunctionArgument funArg = FunctionArgument.findIn(c, expression.module, lv, 1, true, false);
|
||||
CarpetEventServer events = ((CarpetScriptHost)c.host).scriptServer().events;
|
||||
final FunctionArgument funArg = FunctionArgument.findIn(c, expression.module, lv, 1, true, false);
|
||||
final CarpetEventServer events = ((CarpetScriptHost) c.host).scriptServer().events;
|
||||
if (funArg.function == null)
|
||||
{
|
||||
types.forEach(et -> events.removeBuiltInEvent(CarpetEventServer.Event.getEntityLoadEventName(et), (CarpetScriptHost) c.host));
|
||||
@@ -265,8 +301,8 @@ public class Entities {
|
||||
else
|
||||
{
|
||||
///compat
|
||||
int argno = funArg.function.getArguments().size() - funArg.args.size();
|
||||
if (argno == 1)
|
||||
final int numberOfArguments = funArg.function.getArguments().size() - funArg.args.size();
|
||||
if (numberOfArguments == 1)
|
||||
{
|
||||
c.host.issueDeprecation("entity_load_handler() with single argument callback");
|
||||
types.forEach(et -> events.addBuiltInEvent(CarpetEventServer.Event.getEntityLoadEventName(et), c.host, funArg.function, funArg.args));
|
||||
@@ -282,16 +318,20 @@ public class Entities {
|
||||
// or update
|
||||
expression.addContextFunction("entity_event", -1, (c, t, lv) ->
|
||||
{
|
||||
if (lv.size()<3)
|
||||
if (lv.size() < 3)
|
||||
{
|
||||
throw new InternalExpressionException("'entity_event' requires at least 3 arguments, entity, event to be handled, and function name, with optional arguments");
|
||||
Value v = lv.get(0);
|
||||
if (!(v instanceof EntityValue))
|
||||
}
|
||||
final Value v = lv.get(0);
|
||||
if (!(v instanceof final EntityValue ev))
|
||||
{
|
||||
throw new InternalExpressionException("First argument to entity_event should be an entity");
|
||||
String what = lv.get(1).getString();
|
||||
}
|
||||
final String what = lv.get(1).getString();
|
||||
|
||||
FunctionArgument funArg = FunctionArgument.findIn(c, expression.module, lv, 2, true, false);
|
||||
final FunctionArgument funArg = FunctionArgument.findIn(c, expression.module, lv, 2, true, false);
|
||||
|
||||
((EntityValue) v).setEvent((CarpetContext)c, what, funArg.function, funArg.args);
|
||||
ev.setEvent((CarpetContext) c, what, funArg.function, funArg.args);
|
||||
|
||||
return Value.NULL;
|
||||
});
|
||||
|
||||
@@ -21,11 +21,13 @@ import carpet.script.value.StringValue;
|
||||
import carpet.script.value.Value;
|
||||
import carpet.script.value.ValueConversions;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import net.minecraft.commands.arguments.item.ItemInput;
|
||||
import net.minecraft.core.HolderSet;
|
||||
import net.minecraft.core.Registry;
|
||||
@@ -51,129 +53,110 @@ import net.minecraft.world.item.crafting.ShapelessRecipe;
|
||||
import net.minecraft.world.item.crafting.SingleItemRecipe;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
public class Inventories {
|
||||
public static void apply(Expression expression)
|
||||
public class Inventories
|
||||
{
|
||||
public static void apply(final Expression expression)
|
||||
{
|
||||
expression.addContextFunction("stack_limit", 1, (c, t, lv) ->
|
||||
new NumericValue(NBTSerializableValue.parseItem(lv.get(0).getString(), ((CarpetContext) c).registryAccess() ).getItem().getMaxStackSize()));
|
||||
new NumericValue(NBTSerializableValue.parseItem(lv.get(0).getString(), ((CarpetContext) c).registryAccess()).getItem().getMaxStackSize()));
|
||||
|
||||
expression.addContextFunction("item_category", -1, (c, t, lv) -> {
|
||||
CarpetContext cc = (CarpetContext)c;
|
||||
cc.host.issueDeprecation("item_category in 1.19.3+");
|
||||
c.host.issueDeprecation("item_category in 1.19.3+");
|
||||
return Value.NULL;
|
||||
});
|
||||
|
||||
expression.addContextFunction("item_list", -1, (c, t, lv) ->
|
||||
{
|
||||
CarpetContext cc = (CarpetContext)c;
|
||||
Registry<Item> items = cc.registry(Registries.ITEM);
|
||||
final CarpetContext cc = (CarpetContext) c;
|
||||
final Registry<Item> items = cc.registry(Registries.ITEM);
|
||||
if (lv.size() == 0)
|
||||
{
|
||||
return ListValue.wrap(items.keySet().stream().map(ValueConversions::of));
|
||||
String tag = lv.get(0).getString();
|
||||
Optional<HolderSet.Named<Item>> itemTag = items.getTag(TagKey.create(Registries.ITEM, InputValidator.identifierOf(tag)));
|
||||
if (itemTag.isEmpty()) return Value.NULL;
|
||||
return ListValue.wrap(itemTag.get().stream().map(b -> ValueConversions.of(items.getKey(b.value()))));
|
||||
/*
|
||||
TagContainer tagManager = cc.s.getServer(). getTags();
|
||||
String tag = lv.get(0).getString();
|
||||
net.minecraft.tags.Tag<Item> itemTag = tagManager.getOrEmpty(Registry.ITEM_REGISTRY).getTag(InputValidator.identifierOf(tag));
|
||||
if (itemTag == null) return Value.NULL;
|
||||
return ListValue.wrap(itemTag.getValues().stream().map(b -> ValueConversions.of(Registry.ITEM.getKey(b))));
|
||||
*/
|
||||
}
|
||||
final String tag = lv.get(0).getString();
|
||||
final Optional<HolderSet.Named<Item>> itemTag = items.getTag(TagKey.create(Registries.ITEM, InputValidator.identifierOf(tag)));
|
||||
return itemTag.isEmpty() ? Value.NULL : ListValue.wrap(itemTag.get().stream().map(b -> ValueConversions.of(items.getKey(b.value()))));
|
||||
});
|
||||
|
||||
expression.addContextFunction("item_tags", -1, (c, t, lv) ->
|
||||
{
|
||||
CarpetContext cc = (CarpetContext)c;
|
||||
final CarpetContext cc = (CarpetContext) c;
|
||||
|
||||
Registry<Item> blocks = cc.registry(Registries.ITEM);
|
||||
final Registry<Item> blocks = cc.registry(Registries.ITEM);
|
||||
if (lv.size() == 0)
|
||||
{
|
||||
return ListValue.wrap(blocks.getTagNames().map(ValueConversions::of));
|
||||
Item item = NBTSerializableValue.parseItem(lv.get(0).getString(), cc.registryAccess()).getItem();
|
||||
}
|
||||
final Item item = NBTSerializableValue.parseItem(lv.get(0).getString(), cc.registryAccess()).getItem();
|
||||
if (lv.size() == 1)
|
||||
{
|
||||
return ListValue.wrap( blocks.getTags().filter(e -> e.getSecond().stream().anyMatch(h -> (h.value() == item))).map(e -> ValueConversions.of(e.getFirst())));
|
||||
return ListValue.wrap(blocks.getTags().filter(e -> e.getSecond().stream().anyMatch(h -> (h.value() == item))).map(e -> ValueConversions.of(e.getFirst())));
|
||||
}
|
||||
String tag = lv.get(1).getString();
|
||||
Optional<HolderSet.Named<Item>> tagSet = blocks.getTag(TagKey.create(Registries.ITEM, InputValidator.identifierOf(tag)));
|
||||
if (tagSet.isEmpty()) return Value.NULL;
|
||||
|
||||
//return BooleanValue.of(tagSet.get().contains(item.builtInRegistryHolder()));
|
||||
return BooleanValue.of(tagSet.get().stream().anyMatch(h -> h.value() == item));
|
||||
|
||||
/*
|
||||
TagContainer tagManager = cc.s.getServer().getTags();
|
||||
if (lv.size() == 0)
|
||||
return ListValue.wrap(tagManager.getOrEmpty(Registry.ITEM_REGISTRY).getAvailableTags().stream().map(ValueConversions::of));
|
||||
Item item = NBTSerializableValue.parseItem(lv.get(0).getString()).getItem();
|
||||
if (lv.size() == 1)
|
||||
return ListValue.wrap(tagManager.getOrEmpty(Registry.ITEM_REGISTRY).getAllTags().entrySet().stream().filter(e -> e.getValue().contains(item)).map(e -> ValueConversions.of(e.getKey())));
|
||||
String tag = lv.get(1).getString();
|
||||
net.minecraft.tags.Tag<Item> itemTag = tagManager.getOrEmpty(Registry.ITEM_REGISTRY).getTag(InputValidator.identifierOf(tag));
|
||||
if (itemTag == null) return Value.NULL;
|
||||
return BooleanValue.of(itemTag.contains(item));
|
||||
*/
|
||||
final String tag = lv.get(1).getString();
|
||||
final Optional<HolderSet.Named<Item>> tagSet = blocks.getTag(TagKey.create(Registries.ITEM, InputValidator.identifierOf(tag)));
|
||||
return tagSet.isEmpty() ? Value.NULL : BooleanValue.of(tagSet.get().stream().anyMatch(h -> h.value() == item));
|
||||
});
|
||||
|
||||
expression.addContextFunction("recipe_data", -1, (c, t, lv) ->
|
||||
{
|
||||
CarpetContext cc = (CarpetContext)c;
|
||||
if (lv.size() < 1) throw new InternalExpressionException("'recipe_data' requires at least one argument");
|
||||
String recipeName = lv.get(0).getString();
|
||||
final CarpetContext cc = (CarpetContext) c;
|
||||
if (lv.size() < 1)
|
||||
{
|
||||
throw new InternalExpressionException("'recipe_data' requires at least one argument");
|
||||
}
|
||||
final String recipeName = lv.get(0).getString();
|
||||
RecipeType<?> type = RecipeType.CRAFTING;
|
||||
if (lv.size() > 1)
|
||||
{
|
||||
String recipeType = lv.get(1).getString();
|
||||
final String recipeType = lv.get(1).getString();
|
||||
type = cc.registry(Registries.RECIPE_TYPE).get(InputValidator.identifierOf(recipeType));
|
||||
}
|
||||
List<Recipe<?>> recipes;
|
||||
recipes = ((RecipeManagerInterface) cc.server().getRecipeManager()).getAllMatching(type, InputValidator.identifierOf(recipeName), cc.registryAccess());
|
||||
final List<Recipe<?>> recipes = ((RecipeManagerInterface) cc.server().getRecipeManager()).getAllMatching(type, InputValidator.identifierOf(recipeName), cc.registryAccess());
|
||||
if (recipes.isEmpty())
|
||||
return Value.NULL;
|
||||
List<Value> recipesOutput = new ArrayList<>();
|
||||
RegistryAccess regs = cc.registryAccess();
|
||||
for (Recipe<?> recipe: recipes)
|
||||
{
|
||||
ItemStack result = recipe.getResultItem(regs);
|
||||
List<Value> ingredientValue = new ArrayList<>();
|
||||
recipe.getIngredients().forEach(
|
||||
ingredient ->
|
||||
{
|
||||
// I am flattening ingredient lists per slot.
|
||||
// consider recipe_data('wooden_sword','crafting') and ('iron_nugget', 'blasting') and notice difference
|
||||
// in depths of lists.
|
||||
List<Collection<ItemStack>> stacks = ((IngredientInterface) (Object) ingredient).getRecipeStacks();
|
||||
if (stacks.isEmpty())
|
||||
{
|
||||
ingredientValue.add(Value.NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
List<Value> alternatives = new ArrayList<>();
|
||||
stacks.forEach(col -> col.stream().map(is -> ValueConversions.of(is, regs)).forEach(alternatives::add));
|
||||
ingredientValue.add(ListValue.wrap(alternatives));
|
||||
}
|
||||
}
|
||||
);
|
||||
Value recipeSpec;
|
||||
if (recipe instanceof ShapedRecipe)
|
||||
return Value.NULL;
|
||||
}
|
||||
final List<Value> recipesOutput = new ArrayList<>();
|
||||
final RegistryAccess regs = cc.registryAccess();
|
||||
for (final Recipe<?> recipe : recipes)
|
||||
{
|
||||
final ItemStack result = recipe.getResultItem(regs);
|
||||
final List<Value> ingredientValue = new ArrayList<>();
|
||||
recipe.getIngredients().forEach( ingredient -> {
|
||||
// I am flattening ingredient lists per slot.
|
||||
// consider recipe_data('wooden_sword','crafting') and ('iron_nugget', 'blasting') and notice difference
|
||||
// in depths of lists.
|
||||
final List<Collection<ItemStack>> stacks = ((IngredientInterface) (Object) ingredient).getRecipeStacks();
|
||||
if (stacks.isEmpty())
|
||||
{
|
||||
ingredientValue.add(Value.NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
final List<Value> alternatives = new ArrayList<>();
|
||||
stacks.forEach(col -> col.stream().map(is -> ValueConversions.of(is, regs)).forEach(alternatives::add));
|
||||
ingredientValue.add(ListValue.wrap(alternatives));
|
||||
}
|
||||
});
|
||||
final Value recipeSpec;
|
||||
if (recipe instanceof final ShapedRecipe shapedRecipe)
|
||||
{
|
||||
recipeSpec = ListValue.of(
|
||||
new StringValue("shaped"),
|
||||
new NumericValue(((ShapedRecipe) recipe).getWidth()),
|
||||
new NumericValue(((ShapedRecipe) recipe).getHeight())
|
||||
new NumericValue(shapedRecipe.getWidth()),
|
||||
new NumericValue(shapedRecipe.getHeight())
|
||||
);
|
||||
}
|
||||
else if (recipe instanceof ShapelessRecipe)
|
||||
{
|
||||
recipeSpec = ListValue.of(new StringValue("shapeless"));
|
||||
}
|
||||
else if (recipe instanceof AbstractCookingRecipe)
|
||||
else if (recipe instanceof final AbstractCookingRecipe abstractCookingRecipe)
|
||||
{
|
||||
recipeSpec = ListValue.of(
|
||||
new StringValue("smelting"),
|
||||
new NumericValue(((AbstractCookingRecipe) recipe).getCookingTime()),
|
||||
new NumericValue(((AbstractCookingRecipe) recipe).getExperience())
|
||||
new NumericValue(abstractCookingRecipe.getCookingTime()),
|
||||
new NumericValue(abstractCookingRecipe.getExperience())
|
||||
);
|
||||
}
|
||||
else if (recipe instanceof SingleItemRecipe)
|
||||
@@ -196,100 +179,117 @@ public class Inventories {
|
||||
|
||||
expression.addContextFunction("crafting_remaining_item", 1, (c, t, v) ->
|
||||
{
|
||||
String itemStr = v.get(0).getString();
|
||||
Item item;
|
||||
ResourceLocation id = InputValidator.identifierOf(itemStr);
|
||||
Registry<Item> registry = ((CarpetContext)c).registry(Registries.ITEM);
|
||||
item = registry.getOptional(id).orElseThrow(() -> new ThrowStatement(itemStr, Throwables.UNKNOWN_ITEM));
|
||||
if (!item.hasCraftingRemainingItem()) return Value.NULL;
|
||||
return new StringValue(NBTSerializableValue.nameFromRegistryId(registry.getKey(item.getCraftingRemainingItem())));
|
||||
final String itemStr = v.get(0).getString();
|
||||
final ResourceLocation id = InputValidator.identifierOf(itemStr);
|
||||
final Registry<Item> registry = ((CarpetContext) c).registry(Registries.ITEM);
|
||||
final Item item = registry.getOptional(id).orElseThrow(() -> new ThrowStatement(itemStr, Throwables.UNKNOWN_ITEM));
|
||||
return !item.hasCraftingRemainingItem()
|
||||
? Value.NULL
|
||||
: new StringValue(NBTSerializableValue.nameFromRegistryId(registry.getKey(item.getCraftingRemainingItem())));
|
||||
});
|
||||
|
||||
expression.addContextFunction("inventory_size", -1, (c, t, lv) ->
|
||||
{
|
||||
CarpetContext cc = (CarpetContext) c;
|
||||
NBTSerializableValue.InventoryLocator inventoryLocator = NBTSerializableValue.locateInventory(cc, lv, 0);
|
||||
if (inventoryLocator == null) return Value.NULL;
|
||||
return new NumericValue(inventoryLocator.inventory().getContainerSize());
|
||||
final CarpetContext cc = (CarpetContext) c;
|
||||
final NBTSerializableValue.InventoryLocator inventoryLocator = NBTSerializableValue.locateInventory(cc, lv, 0);
|
||||
return inventoryLocator == null ? Value.NULL : new NumericValue(inventoryLocator.inventory().getContainerSize());
|
||||
});
|
||||
|
||||
expression.addContextFunction("inventory_has_items", -1, (c, t, lv) ->
|
||||
{
|
||||
CarpetContext cc = (CarpetContext) c;
|
||||
NBTSerializableValue.InventoryLocator inventoryLocator = NBTSerializableValue.locateInventory(cc, lv, 0);
|
||||
if (inventoryLocator == null) return Value.NULL;
|
||||
return BooleanValue.of(!inventoryLocator.inventory().isEmpty());
|
||||
final CarpetContext cc = (CarpetContext) c;
|
||||
final NBTSerializableValue.InventoryLocator inventoryLocator = NBTSerializableValue.locateInventory(cc, lv, 0);
|
||||
return inventoryLocator == null ? Value.NULL : BooleanValue.of(!inventoryLocator.inventory().isEmpty());
|
||||
});
|
||||
|
||||
//inventory_get(<b, e>, <n>) -> item_triple
|
||||
expression.addContextFunction("inventory_get", -1, (c, t, lv) ->
|
||||
{
|
||||
CarpetContext cc = (CarpetContext) c;
|
||||
NBTSerializableValue.InventoryLocator inventoryLocator = NBTSerializableValue.locateInventory(cc, lv, 0);
|
||||
if (inventoryLocator == null) return Value.NULL;
|
||||
RegistryAccess regs = cc.registryAccess();
|
||||
final CarpetContext cc = (CarpetContext) c;
|
||||
final NBTSerializableValue.InventoryLocator inventoryLocator = NBTSerializableValue.locateInventory(cc, lv, 0);
|
||||
if (inventoryLocator == null)
|
||||
{
|
||||
return Value.NULL;
|
||||
}
|
||||
final RegistryAccess regs = cc.registryAccess();
|
||||
if (lv.size() == inventoryLocator.offset())
|
||||
{
|
||||
List<Value> fullInventory = new ArrayList<>();
|
||||
final List<Value> fullInventory = new ArrayList<>();
|
||||
for (int i = 0, maxi = inventoryLocator.inventory().getContainerSize(); i < maxi; i++)
|
||||
{
|
||||
fullInventory.add(ValueConversions.of(inventoryLocator.inventory().getItem(i), regs));
|
||||
}
|
||||
return ListValue.wrap(fullInventory);
|
||||
}
|
||||
int slot = (int)NumericValue.asNumber(lv.get(inventoryLocator.offset())).getLong();
|
||||
int slot = (int) NumericValue.asNumber(lv.get(inventoryLocator.offset())).getLong();
|
||||
slot = NBTSerializableValue.validateSlot(slot, inventoryLocator.inventory());
|
||||
if (slot == inventoryLocator.inventory().getContainerSize()) return Value.NULL;
|
||||
return ValueConversions.of(inventoryLocator.inventory().getItem(slot), regs);
|
||||
return slot == inventoryLocator.inventory().getContainerSize()
|
||||
? Value.NULL
|
||||
: ValueConversions.of(inventoryLocator.inventory().getItem(slot), regs);
|
||||
});
|
||||
|
||||
//inventory_set(<b,e>, <n>, <count>, <item>, <nbt>)
|
||||
expression.addContextFunction("inventory_set", -1, (c, t, lv) ->
|
||||
{
|
||||
CarpetContext cc = (CarpetContext) c;
|
||||
NBTSerializableValue.InventoryLocator inventoryLocator = NBTSerializableValue.locateInventory(cc, lv, 0);
|
||||
if (inventoryLocator == null) return Value.NULL;
|
||||
if (lv.size() < inventoryLocator.offset()+2)
|
||||
final CarpetContext cc = (CarpetContext) c;
|
||||
final NBTSerializableValue.InventoryLocator inventoryLocator = NBTSerializableValue.locateInventory(cc, lv, 0);
|
||||
if (inventoryLocator == null)
|
||||
{
|
||||
return Value.NULL;
|
||||
}
|
||||
if (lv.size() < inventoryLocator.offset() + 2)
|
||||
{
|
||||
throw new InternalExpressionException("'inventory_set' requires at least slot number and new stack size, and optional new item");
|
||||
int slot = (int) NumericValue.asNumber(lv.get(inventoryLocator.offset()+0)).getLong();
|
||||
}
|
||||
int slot = (int) NumericValue.asNumber(lv.get(inventoryLocator.offset() + 0)).getLong();
|
||||
slot = NBTSerializableValue.validateSlot(slot, inventoryLocator.inventory());
|
||||
if (slot == inventoryLocator.inventory().getContainerSize()) return Value.NULL;
|
||||
int count = (int) NumericValue.asNumber(lv.get(inventoryLocator.offset()+1)).getLong();
|
||||
RegistryAccess regs = cc.registryAccess();
|
||||
if (slot == inventoryLocator.inventory().getContainerSize())
|
||||
{
|
||||
return Value.NULL;
|
||||
}
|
||||
final int count = (int) NumericValue.asNumber(lv.get(inventoryLocator.offset() + 1)).getLong();
|
||||
final RegistryAccess regs = cc.registryAccess();
|
||||
if (count == 0)
|
||||
{
|
||||
// clear slot
|
||||
ItemStack removedStack = inventoryLocator.inventory().removeItemNoUpdate(slot);
|
||||
final ItemStack removedStack = inventoryLocator.inventory().removeItemNoUpdate(slot);
|
||||
syncPlayerInventory(inventoryLocator, slot);
|
||||
//Value res = ListValue.fromItemStack(removedStack); // that tuple will be read only but cheaper if noone cares
|
||||
return ValueConversions.of(removedStack, regs);
|
||||
}
|
||||
if (lv.size() < inventoryLocator.offset()+3)
|
||||
if (lv.size() < inventoryLocator.offset() + 3)
|
||||
{
|
||||
ItemStack previousStack = inventoryLocator.inventory().getItem(slot);
|
||||
ItemStack newStack = previousStack.copy();
|
||||
final ItemStack previousStack = inventoryLocator.inventory().getItem(slot);
|
||||
final ItemStack newStack = previousStack.copy();
|
||||
newStack.setCount(count);
|
||||
inventoryLocator.inventory().setItem(slot, newStack);
|
||||
syncPlayerInventory(inventoryLocator, slot);
|
||||
return ValueConversions.of(previousStack, regs);
|
||||
}
|
||||
CompoundTag nbt = null; // skipping one argument
|
||||
if (lv.size() > inventoryLocator.offset()+3)
|
||||
if (lv.size() > inventoryLocator.offset() + 3)
|
||||
{
|
||||
Value nbtValue = lv.get(inventoryLocator.offset()+3);
|
||||
if (nbtValue instanceof NBTSerializableValue)
|
||||
nbt = ((NBTSerializableValue)nbtValue).getCompoundTag();
|
||||
final Value nbtValue = lv.get(inventoryLocator.offset() + 3);
|
||||
if (nbtValue instanceof final NBTSerializableValue nbtsv)
|
||||
{
|
||||
nbt = nbtsv.getCompoundTag();
|
||||
}
|
||||
else if (nbtValue.isNull())
|
||||
{
|
||||
nbt = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
nbt = new NBTSerializableValue(nbtValue.getString()).getCompoundTag();
|
||||
}
|
||||
}
|
||||
ItemInput newitem = NBTSerializableValue.parseItem(lv.get(inventoryLocator.offset()+2).getString(), nbt, cc.registryAccess());
|
||||
ItemStack previousStack = inventoryLocator.inventory().getItem(slot);
|
||||
final ItemInput newitem = NBTSerializableValue.parseItem(lv.get(inventoryLocator.offset() + 2).getString(), nbt, cc.registryAccess());
|
||||
final ItemStack previousStack = inventoryLocator.inventory().getItem(slot);
|
||||
try
|
||||
{
|
||||
inventoryLocator.inventory().setItem(slot, newitem.createItemStack(count, false));
|
||||
syncPlayerInventory(inventoryLocator, slot);
|
||||
}
|
||||
catch (CommandSyntaxException e)
|
||||
catch (final CommandSyntaxException e)
|
||||
{
|
||||
throw new InternalExpressionException(e.getMessage());
|
||||
}
|
||||
@@ -299,27 +299,34 @@ public class Inventories {
|
||||
//inventory_find(<b, e>, <item> or null (first empty slot), <start_from=0> ) -> <N> or null
|
||||
expression.addContextFunction("inventory_find", -1, (c, t, lv) ->
|
||||
{
|
||||
CarpetContext cc = (CarpetContext) c;
|
||||
NBTSerializableValue.InventoryLocator inventoryLocator = NBTSerializableValue.locateInventory(cc, lv, 0);
|
||||
if (inventoryLocator == null) return Value.NULL;
|
||||
final CarpetContext cc = (CarpetContext) c;
|
||||
final NBTSerializableValue.InventoryLocator inventoryLocator = NBTSerializableValue.locateInventory(cc, lv, 0);
|
||||
if (inventoryLocator == null)
|
||||
{
|
||||
return Value.NULL;
|
||||
}
|
||||
ItemInput itemArg = null;
|
||||
if (lv.size() > inventoryLocator.offset())
|
||||
{
|
||||
Value secondArg = lv.get(inventoryLocator.offset()+0);
|
||||
final Value secondArg = lv.get(inventoryLocator.offset() + 0);
|
||||
if (!secondArg.isNull())
|
||||
{
|
||||
itemArg = NBTSerializableValue.parseItem(secondArg.getString(), cc.registryAccess());
|
||||
}
|
||||
}
|
||||
int startIndex = 0;
|
||||
if (lv.size() > inventoryLocator.offset()+1)
|
||||
if (lv.size() > inventoryLocator.offset() + 1)
|
||||
{
|
||||
startIndex = (int) NumericValue.asNumber(lv.get(inventoryLocator.offset()+1)).getLong();
|
||||
startIndex = (int) NumericValue.asNumber(lv.get(inventoryLocator.offset() + 1)).getLong();
|
||||
}
|
||||
startIndex = NBTSerializableValue.validateSlot(startIndex, inventoryLocator.inventory());
|
||||
for (int i = startIndex, maxi = inventoryLocator.inventory().getContainerSize(); i < maxi; i++)
|
||||
{
|
||||
ItemStack stack = inventoryLocator.inventory().getItem(i);
|
||||
if ( (itemArg == null && stack.isEmpty()) || (itemArg != null && itemArg.getItem().equals(stack.getItem())) )
|
||||
final ItemStack stack = inventoryLocator.inventory().getItem(i);
|
||||
if ((itemArg == null && stack.isEmpty()) || (itemArg != null && itemArg.getItem().equals(stack.getItem())))
|
||||
{
|
||||
return new NumericValue(i);
|
||||
}
|
||||
}
|
||||
return Value.NULL;
|
||||
});
|
||||
@@ -327,26 +334,36 @@ public class Inventories {
|
||||
//inventory_remove(<b, e>, <item>, <amount=1>) -> bool
|
||||
expression.addContextFunction("inventory_remove", -1, (c, t, lv) ->
|
||||
{
|
||||
CarpetContext cc = (CarpetContext) c;
|
||||
NBTSerializableValue.InventoryLocator inventoryLocator = NBTSerializableValue.locateInventory(cc, lv, 0);
|
||||
if (inventoryLocator == null) return Value.NULL;
|
||||
final CarpetContext cc = (CarpetContext) c;
|
||||
final NBTSerializableValue.InventoryLocator inventoryLocator = NBTSerializableValue.locateInventory(cc, lv, 0);
|
||||
if (inventoryLocator == null)
|
||||
{
|
||||
return Value.NULL;
|
||||
}
|
||||
if (lv.size() <= inventoryLocator.offset())
|
||||
{
|
||||
throw new InternalExpressionException("'inventory_remove' requires at least an item to be removed");
|
||||
ItemInput searchItem = NBTSerializableValue.parseItem(lv.get(inventoryLocator.offset()).getString(), cc.registryAccess());
|
||||
}
|
||||
final ItemInput searchItem = NBTSerializableValue.parseItem(lv.get(inventoryLocator.offset()).getString(), cc.registryAccess());
|
||||
int amount = 1;
|
||||
if (lv.size() > inventoryLocator.offset()+1)
|
||||
amount = (int)NumericValue.asNumber(lv.get(inventoryLocator.offset()+1)).getLong();
|
||||
if (lv.size() > inventoryLocator.offset() + 1)
|
||||
{
|
||||
amount = (int) NumericValue.asNumber(lv.get(inventoryLocator.offset() + 1)).getLong();
|
||||
}
|
||||
// not enough
|
||||
if (((amount == 1) && (!inventoryLocator.inventory().hasAnyOf(Set.of(searchItem.getItem()))))
|
||||
|| (inventoryLocator.inventory().countItem(searchItem.getItem()) < amount)) return Value.FALSE;
|
||||
|| (inventoryLocator.inventory().countItem(searchItem.getItem()) < amount))
|
||||
{
|
||||
return Value.FALSE;
|
||||
}
|
||||
for (int i = 0, maxi = inventoryLocator.inventory().getContainerSize(); i < maxi; i++)
|
||||
{
|
||||
ItemStack stack = inventoryLocator.inventory().getItem(i);
|
||||
if (stack.isEmpty())
|
||||
final ItemStack stack = inventoryLocator.inventory().getItem(i);
|
||||
if (stack.isEmpty() || !stack.getItem().equals(searchItem.getItem()))
|
||||
{
|
||||
continue;
|
||||
if (!stack.getItem().equals(searchItem.getItem()))
|
||||
continue;
|
||||
int left = stack.getCount()-amount;
|
||||
}
|
||||
final int left = stack.getCount() - amount;
|
||||
if (left > 0)
|
||||
{
|
||||
stack.setCount(left);
|
||||
@@ -354,58 +371,78 @@ public class Inventories {
|
||||
syncPlayerInventory(inventoryLocator, i);
|
||||
return Value.TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
inventoryLocator.inventory().removeItemNoUpdate(i);
|
||||
syncPlayerInventory(inventoryLocator, i);
|
||||
amount -= stack.getCount();
|
||||
}
|
||||
inventoryLocator.inventory().removeItemNoUpdate(i);
|
||||
syncPlayerInventory(inventoryLocator, i);
|
||||
amount -= stack.getCount();
|
||||
}
|
||||
if (amount > 0)
|
||||
{
|
||||
throw new InternalExpressionException("Something bad happened - cannot pull all items from inventory");
|
||||
}
|
||||
return Value.TRUE;
|
||||
});
|
||||
|
||||
//inventory_drop(<b, e>, <n>, <amount=1, 0-whatever's there>) -> entity_item (and sets slot) or null if cannot
|
||||
expression.addContextFunction("drop_item", -1, (c, t, lv) ->
|
||||
{
|
||||
CarpetContext cc = (CarpetContext) c;
|
||||
NBTSerializableValue.InventoryLocator inventoryLocator = NBTSerializableValue.locateInventory(cc, lv, 0);
|
||||
if (inventoryLocator == null) return Value.NULL;
|
||||
if (lv.size() == inventoryLocator.offset())
|
||||
throw new InternalExpressionException("Slot number is required for inventory_drop");
|
||||
int slot = (int)NumericValue.asNumber(lv.get(inventoryLocator.offset())).getLong();
|
||||
slot = NBTSerializableValue.validateSlot(slot, inventoryLocator.inventory());
|
||||
if (slot == inventoryLocator.inventory().getContainerSize()) return Value.NULL;
|
||||
int amount = 0;
|
||||
if (lv.size() > inventoryLocator.offset()+1)
|
||||
amount = (int)NumericValue.asNumber(lv.get(inventoryLocator.offset()+1)).getLong();
|
||||
if (amount < 0)
|
||||
throw new InternalExpressionException("Cannot throw negative number of items");
|
||||
ItemStack stack = inventoryLocator.inventory().getItem(slot);
|
||||
if (stack == null || stack.isEmpty()) return Value.ZERO;
|
||||
if (amount == 0) amount = stack.getCount();
|
||||
ItemStack droppedStack = inventoryLocator.inventory().removeItem(slot, amount);
|
||||
if (droppedStack.isEmpty()) return Value.ZERO;
|
||||
Object owner = inventoryLocator.owner();
|
||||
ItemEntity item;
|
||||
if (owner instanceof Player)
|
||||
final CarpetContext cc = (CarpetContext) c;
|
||||
final NBTSerializableValue.InventoryLocator inventoryLocator = NBTSerializableValue.locateInventory(cc, lv, 0);
|
||||
if (inventoryLocator == null)
|
||||
{
|
||||
item = ((Player) owner).drop(droppedStack, false, true);
|
||||
return Value.NULL;
|
||||
}
|
||||
else if (owner instanceof LivingEntity villager)
|
||||
if (lv.size() == inventoryLocator.offset())
|
||||
{
|
||||
throw new InternalExpressionException("Slot number is required for inventory_drop");
|
||||
}
|
||||
int slot = (int) NumericValue.asNumber(lv.get(inventoryLocator.offset())).getLong();
|
||||
slot = NBTSerializableValue.validateSlot(slot, inventoryLocator.inventory());
|
||||
if (slot == inventoryLocator.inventory().getContainerSize())
|
||||
{
|
||||
return Value.NULL;
|
||||
}
|
||||
int amount = 0;
|
||||
if (lv.size() > inventoryLocator.offset() + 1)
|
||||
{
|
||||
amount = (int) NumericValue.asNumber(lv.get(inventoryLocator.offset() + 1)).getLong();
|
||||
}
|
||||
if (amount < 0)
|
||||
{
|
||||
throw new InternalExpressionException("Cannot throw negative number of items");
|
||||
}
|
||||
final ItemStack stack = inventoryLocator.inventory().getItem(slot);
|
||||
if (stack == null || stack.isEmpty())
|
||||
{
|
||||
return Value.ZERO;
|
||||
}
|
||||
if (amount == 0)
|
||||
{
|
||||
amount = stack.getCount();
|
||||
}
|
||||
final ItemStack droppedStack = inventoryLocator.inventory().removeItem(slot, amount);
|
||||
if (droppedStack.isEmpty())
|
||||
{
|
||||
return Value.ZERO;
|
||||
}
|
||||
final Object owner = inventoryLocator.owner();
|
||||
final ItemEntity item;
|
||||
if (owner instanceof final Player player)
|
||||
{
|
||||
item = player.drop(droppedStack, false, true);
|
||||
}
|
||||
else if (owner instanceof LivingEntity livingEntity)
|
||||
{
|
||||
// stolen from LookTargetUtil.give((VillagerEntity)owner, droppedStack, (LivingEntity) owner);
|
||||
double double_1 = villager.getY() - 0.30000001192092896D + villager.getEyeHeight();
|
||||
item = new ItemEntity(villager.level, villager.getX(), double_1, villager.getZ(), droppedStack);
|
||||
Vec3 vec3d_1 = villager.getViewVector(1.0F).normalize().scale(0.3);// new Vec3d(0, 0.3, 0);
|
||||
item.setDeltaMovement(vec3d_1);
|
||||
double double_1 = livingEntity.getY() - 0.30000001192092896D + livingEntity.getEyeHeight();
|
||||
item = new ItemEntity(livingEntity.level, livingEntity.getX(), double_1, livingEntity.getZ(), droppedStack);
|
||||
final Vec3 vec3d = livingEntity.getViewVector(1.0F).normalize().scale(0.3);// new Vec3d(0, 0.3, 0);
|
||||
item.setDeltaMovement(vec3d);
|
||||
item.setDefaultPickUpDelay();
|
||||
cc.level().addFreshEntity(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
Vec3 point = Vec3.atCenterOf(inventoryLocator.position()); //pos+0.5v
|
||||
final Vec3 point = Vec3.atCenterOf(inventoryLocator.position()); //pos+0.5v
|
||||
item = new ItemEntity(cc.level(), point.x, point.y, point.z, droppedStack);
|
||||
item.setDefaultPickUpDelay();
|
||||
cc.level().addFreshEntity(item);
|
||||
@@ -413,49 +450,64 @@ public class Inventories {
|
||||
return new NumericValue(item.getItem().getCount());
|
||||
});
|
||||
|
||||
expression.addContextFunction("create_screen",-1, (c, t, lv) ->
|
||||
expression.addContextFunction("create_screen", -1, (c, t, lv) ->
|
||||
{
|
||||
if(lv.size() < 3) throw new InternalExpressionException("'create_screen' requires at least three arguments");
|
||||
Value playerValue = lv.get(0);
|
||||
ServerPlayer player = EntityValue.getPlayerByValue(((CarpetContext) c).server(), playerValue);
|
||||
if(player == null) throw new InternalExpressionException("'create_screen' requires a valid online player as the first argument.");
|
||||
String type = lv.get(1).getString();
|
||||
Component name = FormattedTextValue.getTextByValue(lv.get(2));
|
||||
if (lv.size() < 3)
|
||||
{
|
||||
throw new InternalExpressionException("'create_screen' requires at least three arguments");
|
||||
}
|
||||
final Value playerValue = lv.get(0);
|
||||
final ServerPlayer player = EntityValue.getPlayerByValue(((CarpetContext) c).server(), playerValue);
|
||||
if (player == null)
|
||||
{
|
||||
throw new InternalExpressionException("'create_screen' requires a valid online player as the first argument.");
|
||||
}
|
||||
final String type = lv.get(1).getString();
|
||||
final Component name = FormattedTextValue.getTextByValue(lv.get(2));
|
||||
FunctionValue function = null;
|
||||
if(lv.size() > 3)
|
||||
if (lv.size() > 3)
|
||||
{
|
||||
function = FunctionArgument.findIn(c, expression.module, lv, 3, true, false).function;
|
||||
}
|
||||
|
||||
return new ScreenValue(player,type,name,function,c);
|
||||
return new ScreenValue(player, type, name, function, c);
|
||||
});
|
||||
|
||||
expression.addContextFunction("close_screen",1, (c, t, lv) ->
|
||||
expression.addContextFunction("close_screen", 1, (c, t, lv) ->
|
||||
{
|
||||
Value value = lv.get(0);
|
||||
if(!(value instanceof ScreenValue screenValue)) throw new InternalExpressionException("'close_screen' requires a screen value as the first argument.");
|
||||
if(!screenValue.isOpen()) return Value.FALSE;
|
||||
final Value value = lv.get(0);
|
||||
if (!(value instanceof final ScreenValue screenValue))
|
||||
{
|
||||
throw new InternalExpressionException("'close_screen' requires a screen value as the first argument.");
|
||||
}
|
||||
if (!screenValue.isOpen())
|
||||
{
|
||||
return Value.FALSE;
|
||||
}
|
||||
screenValue.close();
|
||||
return Value.TRUE;
|
||||
});
|
||||
|
||||
expression.addContextFunction("screen_property",-1, (c, t, lv) ->
|
||||
expression.addContextFunction("screen_property", -1, (c, t, lv) ->
|
||||
{
|
||||
if(lv.size()<2) throw new InternalExpressionException("'screen_property' requires at least a screen and a property name");
|
||||
if(!(lv.get(0) instanceof ScreenValue screenValue)) throw new InternalExpressionException("'screen_property' requires a screen value as the first argument");
|
||||
String propertyName = lv.get(1).getString();
|
||||
if(lv.size()>=3)
|
||||
if (lv.size() < 2)
|
||||
{
|
||||
return screenValue.modifyProperty(propertyName,lv.subList(2,lv.size()));
|
||||
throw new InternalExpressionException("'screen_property' requires at least a screen and a property name");
|
||||
}
|
||||
else
|
||||
if (!(lv.get(0) instanceof final ScreenValue screenValue))
|
||||
{
|
||||
return screenValue.queryProperty(propertyName);
|
||||
throw new InternalExpressionException("'screen_property' requires a screen value as the first argument");
|
||||
}
|
||||
final String propertyName = lv.get(1).getString();
|
||||
return lv.size() >= 3
|
||||
? screenValue.modifyProperty(propertyName, lv.subList(2, lv.size()))
|
||||
: screenValue.queryProperty(propertyName);
|
||||
});
|
||||
}
|
||||
|
||||
private static void syncPlayerInventory(NBTSerializableValue.InventoryLocator inventory, int int_1)
|
||||
private static void syncPlayerInventory(final NBTSerializableValue.InventoryLocator inventory, final int int_1)
|
||||
{
|
||||
if (inventory.owner() instanceof ServerPlayer player && !inventory.isEnder() && !(inventory.inventory() instanceof ScreenValue.ScreenHandlerInventory))
|
||||
if (inventory.owner() instanceof final ServerPlayer player && !inventory.isEnder() && !(inventory.inventory() instanceof ScreenValue.ScreenHandlerInventory))
|
||||
{
|
||||
player.connection.send(new ClientboundContainerSetSlotPacket(
|
||||
-2, 0, // resolve mystery argument
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package carpet.script.api;
|
||||
|
||||
import carpet.fakes.SpawnHelperInnerInterface;
|
||||
import carpet.script.CarpetContext;
|
||||
import carpet.script.Expression;
|
||||
import carpet.script.exception.InternalExpressionException;
|
||||
@@ -24,10 +23,11 @@ import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.entity.MobCategory;
|
||||
import net.minecraft.world.level.NaturalSpawner;
|
||||
|
||||
public class Monitoring {
|
||||
public class Monitoring
|
||||
{
|
||||
private static final Map<String, MobCategory> MOB_CATEGORY_MAP = Arrays.stream(MobCategory.values()).collect(Collectors.toMap(MobCategory::getName, Function.identity()));
|
||||
|
||||
public static void apply(Expression expression)
|
||||
public static void apply(final Expression expression)
|
||||
{
|
||||
expression.addContextFunction("system_info", -1, (c, t, lv) ->
|
||||
{
|
||||
@@ -35,10 +35,14 @@ public class Monitoring {
|
||||
{
|
||||
return SystemInfo.getAll();
|
||||
}
|
||||
if (lv.size() == 1) {
|
||||
String what = lv.get(0).getString();
|
||||
Value res = SystemInfo.get(what, (CarpetContext) c);
|
||||
if (res == null) throw new InternalExpressionException("Unknown option for 'system_info': " + what);
|
||||
if (lv.size() == 1)
|
||||
{
|
||||
final String what = lv.get(0).getString();
|
||||
final Value res = SystemInfo.get(what, (CarpetContext) c);
|
||||
if (res == null)
|
||||
{
|
||||
throw new InternalExpressionException("Unknown option for 'system_info': " + what);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
throw new InternalExpressionException("'system_info' requires one or no parameters");
|
||||
@@ -46,18 +50,21 @@ public class Monitoring {
|
||||
// game processed snooper functions
|
||||
expression.addContextFunction("get_mob_counts", -1, (c, t, lv) ->
|
||||
{
|
||||
CarpetContext cc = (CarpetContext)c;
|
||||
ServerLevel world = cc.level();
|
||||
NaturalSpawner.SpawnState info = world.getChunkSource().getLastSpawnState();
|
||||
if (info == null) return Value.NULL;
|
||||
Object2IntMap<MobCategory> mobcounts = info.getMobCategoryCounts();
|
||||
int chunks = info.getSpawnableChunkCount();
|
||||
final CarpetContext cc = (CarpetContext) c;
|
||||
final ServerLevel world = cc.level();
|
||||
final NaturalSpawner.SpawnState info = world.getChunkSource().getLastSpawnState();
|
||||
if (info == null)
|
||||
{
|
||||
return Value.NULL;
|
||||
}
|
||||
final Object2IntMap<MobCategory> mobcounts = info.getMobCategoryCounts();
|
||||
final int chunks = info.getSpawnableChunkCount();
|
||||
if (lv.size() == 0)
|
||||
{
|
||||
Map<Value, Value> retDict = new HashMap<>();
|
||||
for (MobCategory category: mobcounts.keySet())
|
||||
final Map<Value, Value> retDict = new HashMap<>();
|
||||
for (final MobCategory category : mobcounts.keySet())
|
||||
{
|
||||
int currentCap = (int)(category.getMaxInstancesPerChunk() * chunks / SpawnReporter.MAGIC_NUMBER);
|
||||
final int currentCap = category.getMaxInstancesPerChunk() * chunks / SpawnReporter.MAGIC_NUMBER;
|
||||
retDict.put(
|
||||
new StringValue(category.getSerializedName().toLowerCase(Locale.ROOT)),
|
||||
ListValue.of(
|
||||
@@ -67,12 +74,15 @@ public class Monitoring {
|
||||
}
|
||||
return MapValue.wrap(retDict);
|
||||
}
|
||||
String catString = lv.get(0).getString();
|
||||
MobCategory cat = MOB_CATEGORY_MAP.get(catString.toLowerCase(Locale.ROOT));
|
||||
if (cat == null) throw new InternalExpressionException("Unreconized mob category: "+catString);
|
||||
final String catString = lv.get(0).getString();
|
||||
final MobCategory cat = MOB_CATEGORY_MAP.get(catString.toLowerCase(Locale.ROOT));
|
||||
if (cat == null)
|
||||
{
|
||||
throw new InternalExpressionException("Unreconized mob category: " + catString);
|
||||
}
|
||||
return ListValue.of(
|
||||
new NumericValue(mobcounts.getInt(cat)),
|
||||
new NumericValue((int)(cat.getMaxInstancesPerChunk() * chunks / SpawnReporter.MAGIC_NUMBER))
|
||||
new NumericValue((int) (cat.getMaxInstancesPerChunk() * chunks / SpawnReporter.MAGIC_NUMBER))
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -16,8 +16,10 @@ import carpet.script.value.StringValue;
|
||||
import carpet.script.value.FormattedTextValue;
|
||||
import carpet.script.value.Value;
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
@@ -35,80 +37,90 @@ import net.minecraft.world.scores.Scoreboard;
|
||||
import net.minecraft.world.scores.Team;
|
||||
import net.minecraft.world.scores.criteria.ObjectiveCriteria;
|
||||
|
||||
public class Scoreboards {
|
||||
private static String getScoreboardKeyFromValue(Value keyValue)
|
||||
public class Scoreboards
|
||||
{
|
||||
private static String getScoreboardKeyFromValue(final Value keyValue)
|
||||
{
|
||||
if (keyValue instanceof EntityValue)
|
||||
if (keyValue instanceof final EntityValue ev)
|
||||
{
|
||||
Entity e = ((EntityValue) keyValue).getEntity();
|
||||
if (e instanceof Player)
|
||||
{
|
||||
return e.getName().getString();
|
||||
}
|
||||
else
|
||||
{
|
||||
return e.getStringUUID();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return keyValue.getString();
|
||||
final Entity e = ev.getEntity();
|
||||
return e instanceof Player ? e.getName().getString() : e.getStringUUID();
|
||||
}
|
||||
return keyValue.getString();
|
||||
}
|
||||
|
||||
public static void apply(Expression expression)
|
||||
public static void apply(final Expression expression)
|
||||
{
|
||||
// scoreboard(player,'objective')
|
||||
// scoreboard(player, objective, newValue)
|
||||
expression.addContextFunction("scoreboard", -1, (c, t, lv) ->
|
||||
{
|
||||
CarpetContext cc = (CarpetContext)c;
|
||||
Scoreboard scoreboard = cc.server().getScoreboard();
|
||||
if (lv.size()==0)
|
||||
return ListValue.wrap(scoreboard.getObjectiveNames().stream().map(StringValue::new));
|
||||
String objectiveName = lv.get(0).getString();
|
||||
Objective objective = scoreboard.getOrCreateObjective(objectiveName);
|
||||
if (objective == null) return Value.NULL;
|
||||
if (lv.size()==1)
|
||||
return ListValue.wrap(scoreboard.getPlayerScores(objective).stream().map(s -> new StringValue(s.getOwner())));
|
||||
String key = getScoreboardKeyFromValue(lv.get(1));
|
||||
if(lv.size()==2)
|
||||
final CarpetContext cc = (CarpetContext) c;
|
||||
final Scoreboard scoreboard = cc.server().getScoreboard();
|
||||
if (lv.size() == 0)
|
||||
{
|
||||
if(!scoreboard.hasPlayerScore(key, objective)) return Value.NULL;
|
||||
return NumericValue.of(scoreboard.getOrCreatePlayerScore(key,objective).getScore());
|
||||
return ListValue.wrap(scoreboard.getObjectiveNames().stream().map(StringValue::new));
|
||||
}
|
||||
final String objectiveName = lv.get(0).getString();
|
||||
final Objective objective = scoreboard.getOrCreateObjective(objectiveName);
|
||||
if (objective == null)
|
||||
{
|
||||
return Value.NULL;
|
||||
}
|
||||
if (lv.size() == 1)
|
||||
{
|
||||
return ListValue.wrap(scoreboard.getPlayerScores(objective).stream().map(s -> new StringValue(s.getOwner())));
|
||||
}
|
||||
final String key = getScoreboardKeyFromValue(lv.get(1));
|
||||
if (lv.size() == 2)
|
||||
{
|
||||
return !scoreboard.hasPlayerScore(key, objective)
|
||||
? Value.NULL
|
||||
: NumericValue.of(scoreboard.getOrCreatePlayerScore(key, objective).getScore());
|
||||
}
|
||||
|
||||
Value value = lv.get(2);
|
||||
if(value.isNull()) {
|
||||
Score score = scoreboard.getOrCreatePlayerScore(key, objective);
|
||||
scoreboard.resetPlayerScore(key,objective);
|
||||
final Value value = lv.get(2);
|
||||
if (value.isNull())
|
||||
{
|
||||
final Score score = scoreboard.getOrCreatePlayerScore(key, objective);
|
||||
scoreboard.resetPlayerScore(key, objective);
|
||||
return NumericValue.of(score.getScore());
|
||||
}
|
||||
if (value instanceof NumericValue) {
|
||||
Score score = scoreboard.getOrCreatePlayerScore(key, objective);
|
||||
if (value instanceof NumericValue)
|
||||
{
|
||||
final Score score = scoreboard.getOrCreatePlayerScore(key, objective);
|
||||
score.setScore(NumericValue.asNumber(value).getInt());
|
||||
return NumericValue.of(score.getScore());
|
||||
}
|
||||
throw new InternalExpressionException("'scoreboard' requires a number or null as the third parameter");
|
||||
});
|
||||
|
||||
expression.addContextFunction("scoreboard_remove", -1, (c, t, lv)->
|
||||
expression.addContextFunction("scoreboard_remove", -1, (c, t, lv) ->
|
||||
{
|
||||
if (lv.size()==0) throw new InternalExpressionException("'scoreboard_remove' requires at least one parameter");
|
||||
CarpetContext cc = (CarpetContext)c;
|
||||
Scoreboard scoreboard = cc.server().getScoreboard();
|
||||
String objectiveName = lv.get(0).getString();
|
||||
Objective objective = scoreboard.getOrCreateObjective(objectiveName);
|
||||
if (objective == null) return Value.FALSE;
|
||||
if (lv.size() == 0)
|
||||
{
|
||||
throw new InternalExpressionException("'scoreboard_remove' requires at least one parameter");
|
||||
}
|
||||
final CarpetContext cc = (CarpetContext) c;
|
||||
final Scoreboard scoreboard = cc.server().getScoreboard();
|
||||
final String objectiveName = lv.get(0).getString();
|
||||
final Objective objective = scoreboard.getOrCreateObjective(objectiveName);
|
||||
if (objective == null)
|
||||
{
|
||||
return Value.FALSE;
|
||||
}
|
||||
if (lv.size() == 1)
|
||||
{
|
||||
scoreboard.removeObjective(objective);
|
||||
return Value.TRUE;
|
||||
}
|
||||
String key = getScoreboardKeyFromValue(lv.get(1));
|
||||
if (!scoreboard.hasPlayerScore(key, objective)) return Value.NULL;
|
||||
Score scoreboardPlayerScore = scoreboard.getOrCreatePlayerScore(key, objective);
|
||||
Value previous = new NumericValue(scoreboardPlayerScore.getScore());
|
||||
final String key = getScoreboardKeyFromValue(lv.get(1));
|
||||
if (!scoreboard.hasPlayerScore(key, objective))
|
||||
{
|
||||
return Value.NULL;
|
||||
}
|
||||
final Score scoreboardPlayerScore = scoreboard.getOrCreatePlayerScore(key, objective);
|
||||
final Value previous = new NumericValue(scoreboardPlayerScore.getScore());
|
||||
scoreboard.resetPlayerScore(key, objective);
|
||||
return previous;
|
||||
});
|
||||
@@ -116,175 +128,247 @@ public class Scoreboards {
|
||||
// objective_add('lvl','level')
|
||||
// objective_add('counter')
|
||||
|
||||
expression.addContextFunction("scoreboard_add", -1, (c, t, lv)->
|
||||
expression.addContextFunction("scoreboard_add", -1, (c, t, lv) ->
|
||||
{
|
||||
CarpetContext cc = (CarpetContext)c;
|
||||
Scoreboard scoreboard = cc.server().getScoreboard();
|
||||
if (lv.size() == 0 || lv.size()>2) throw new InternalExpressionException("'scoreboard_add' should have one or two parameters");
|
||||
String objectiveName = lv.get(0).getString();
|
||||
ObjectiveCriteria criterion;
|
||||
if (lv.size() == 1 )
|
||||
final CarpetContext cc = (CarpetContext) c;
|
||||
final Scoreboard scoreboard = cc.server().getScoreboard();
|
||||
if (lv.size() == 0 || lv.size() > 2)
|
||||
{
|
||||
throw new InternalExpressionException("'scoreboard_add' should have one or two parameters");
|
||||
}
|
||||
final String objectiveName = lv.get(0).getString();
|
||||
final ObjectiveCriteria criterion;
|
||||
if (lv.size() == 1)
|
||||
{
|
||||
criterion = ObjectiveCriteria.DUMMY;
|
||||
}
|
||||
else
|
||||
{
|
||||
String critetionName = lv.get(1).getString();
|
||||
final String critetionName = lv.get(1).getString();
|
||||
criterion = ObjectiveCriteria.byName(critetionName).orElse(null);
|
||||
if (criterion==null)
|
||||
if (criterion == null)
|
||||
{
|
||||
throw new ThrowStatement(critetionName, Throwables.UNKNOWN_CRITERION);
|
||||
}
|
||||
}
|
||||
|
||||
Objective objective = scoreboard.getOrCreateObjective(objectiveName);
|
||||
if (objective != null) {
|
||||
final Objective objective = scoreboard.getOrCreateObjective(objectiveName);
|
||||
if (objective != null)
|
||||
{
|
||||
c.host.issueDeprecation("reading or modifying an objective's criterion with scoreboard_add");
|
||||
if(lv.size() == 1) return StringValue.of(objective.getCriteria().getName());
|
||||
if(objective.getCriteria().equals(criterion) || lv.size() == 1) return Value.NULL;
|
||||
((Scoreboard_scarpetMixin)scoreboard).getObjectivesByCriterion().get(objective.getCriteria()).remove(objective);
|
||||
if (lv.size() == 1)
|
||||
{
|
||||
return StringValue.of(objective.getCriteria().getName());
|
||||
}
|
||||
if (objective.getCriteria().equals(criterion) || lv.size() == 1)
|
||||
{
|
||||
return Value.NULL;
|
||||
}
|
||||
((Scoreboard_scarpetMixin) scoreboard).getObjectivesByCriterion().get(objective.getCriteria()).remove(objective);
|
||||
((Objective_scarpetMixin) objective).setCriterion(criterion);
|
||||
(((Scoreboard_scarpetMixin)scoreboard).getObjectivesByCriterion().computeIfAbsent(criterion, (criterion1) -> Lists.newArrayList())).add(objective);
|
||||
(((Scoreboard_scarpetMixin) scoreboard).getObjectivesByCriterion().computeIfAbsent(criterion, (criterion1) -> Lists.newArrayList())).add(objective);
|
||||
scoreboard.onObjectiveAdded(objective);
|
||||
return Value.FALSE;
|
||||
}
|
||||
|
||||
scoreboard.addObjective(objectiveName, criterion, Component.literal(objectiveName), criterion.getDefaultRenderType());
|
||||
return Value.TRUE;
|
||||
});
|
||||
|
||||
expression.addContextFunction("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.server().getScoreboard();
|
||||
Objective objective = scoreboard.getOrCreateObjective(lv.get(0).getString());
|
||||
if(objective == null) return Value.NULL;
|
||||
if (lv.size() < 2)
|
||||
{
|
||||
throw new InternalExpressionException("'scoreboard_property' requires at least two parameters");
|
||||
}
|
||||
final CarpetContext cc = (CarpetContext) c;
|
||||
final Scoreboard scoreboard = cc.server().getScoreboard();
|
||||
final Objective objective = scoreboard.getOrCreateObjective(lv.get(0).getString());
|
||||
if (objective == null)
|
||||
{
|
||||
return Value.NULL;
|
||||
}
|
||||
|
||||
boolean modify = lv.size() > 2;
|
||||
final boolean modify = lv.size() > 2;
|
||||
Value setValue = null;
|
||||
if(modify) {
|
||||
if (modify)
|
||||
{
|
||||
setValue = lv.get(2);
|
||||
}
|
||||
String property = lv.get(1).getString();
|
||||
switch (property) {
|
||||
case "criterion":
|
||||
if(modify) {
|
||||
ObjectiveCriteria criterion = ObjectiveCriteria.byName(setValue.getString()).orElse(null);
|
||||
if (criterion==null) throw new InternalExpressionException("Unknown scoreboard criterion: "+ setValue.getString());
|
||||
if(objective.getCriteria().equals(criterion) || lv.size() == 1) return Value.FALSE;
|
||||
((Scoreboard_scarpetMixin)scoreboard).getObjectivesByCriterion().get(objective.getCriteria()).remove(objective);
|
||||
final String property = lv.get(1).getString();
|
||||
switch (property)
|
||||
{
|
||||
case "criterion" -> {
|
||||
if (modify)
|
||||
{
|
||||
final ObjectiveCriteria criterion = ObjectiveCriteria.byName(setValue.getString()).orElse(null);
|
||||
if (criterion == null)
|
||||
{
|
||||
throw new InternalExpressionException("Unknown scoreboard criterion: " + setValue.getString());
|
||||
}
|
||||
if (objective.getCriteria().equals(criterion) || lv.size() == 1)
|
||||
{
|
||||
return Value.FALSE;
|
||||
}
|
||||
((Scoreboard_scarpetMixin) scoreboard).getObjectivesByCriterion().get(objective.getCriteria()).remove(objective);
|
||||
((Objective_scarpetMixin) objective).setCriterion(criterion);
|
||||
(((Scoreboard_scarpetMixin)scoreboard).getObjectivesByCriterion().computeIfAbsent(criterion, (criterion1) -> Lists.newArrayList())).add(objective);
|
||||
(((Scoreboard_scarpetMixin) scoreboard).getObjectivesByCriterion().computeIfAbsent(criterion, (criterion1) -> Lists.newArrayList())).add(objective);
|
||||
scoreboard.onObjectiveAdded(objective);
|
||||
return Value.TRUE;
|
||||
}
|
||||
return StringValue.of(objective.getCriteria().getName());
|
||||
case "display_name":
|
||||
if(modify) {
|
||||
Component text = FormattedTextValue.getTextByValue(setValue);
|
||||
}
|
||||
case "display_name" -> {
|
||||
if (modify)
|
||||
{
|
||||
final Component text = FormattedTextValue.getTextByValue(setValue);
|
||||
objective.setDisplayName(text);
|
||||
return Value.TRUE;
|
||||
}
|
||||
return new FormattedTextValue(objective.getDisplayName());
|
||||
case "display_slot":
|
||||
if(modify) {
|
||||
int slotId = Scoreboard.getDisplaySlotByName(setValue.getString());
|
||||
if(slotId == -1) throw new InternalExpressionException("Unknown scoreboard display slot: " + setValue.getString());
|
||||
if(objective.equals(scoreboard.getDisplayObjective(slotId))) {
|
||||
}
|
||||
case "display_slot" -> {
|
||||
if (modify)
|
||||
{
|
||||
final int slotId = Scoreboard.getDisplaySlotByName(setValue.getString());
|
||||
if (slotId == -1)
|
||||
{
|
||||
throw new InternalExpressionException("Unknown scoreboard display slot: " + setValue.getString());
|
||||
}
|
||||
if (objective.equals(scoreboard.getDisplayObjective(slotId)))
|
||||
{
|
||||
return Value.FALSE;
|
||||
}
|
||||
scoreboard.setDisplayObjective(slotId,objective);
|
||||
scoreboard.setDisplayObjective(slotId, objective);
|
||||
return Value.TRUE;
|
||||
}
|
||||
|
||||
List<Value> slots = new ArrayList<>();
|
||||
for(int i = 0; i < 19; i++) {
|
||||
if (scoreboard.getDisplayObjective(i) == objective) {
|
||||
String slotName = Scoreboard.getDisplaySlotName(i);
|
||||
final List<Value> slots = new ArrayList<>();
|
||||
for (int i = 0; i < 19; i++)
|
||||
{
|
||||
if (scoreboard.getDisplayObjective(i) == objective)
|
||||
{
|
||||
final String slotName = Scoreboard.getDisplaySlotName(i);
|
||||
slots.add(StringValue.of(slotName));
|
||||
}
|
||||
}
|
||||
return ListValue.wrap(slots);
|
||||
case "render_type":
|
||||
if(modify) {
|
||||
ObjectiveCriteria.RenderType renderType = ObjectiveCriteria.RenderType.byId(setValue.getString().toLowerCase());
|
||||
if(objective.getRenderType().equals(renderType)) return Value.FALSE;
|
||||
}
|
||||
case "render_type" -> {
|
||||
if (modify)
|
||||
{
|
||||
final ObjectiveCriteria.RenderType renderType = ObjectiveCriteria.RenderType.byId(setValue.getString().toLowerCase());
|
||||
if (objective.getRenderType().equals(renderType))
|
||||
{
|
||||
return Value.FALSE;
|
||||
}
|
||||
objective.setRenderType(renderType);
|
||||
return Value.TRUE;
|
||||
}
|
||||
return StringValue.of(objective.getRenderType().getId());
|
||||
default:
|
||||
throw new InternalExpressionException("scoreboard property '" + property + "' is not a valid property");
|
||||
}
|
||||
default -> throw new InternalExpressionException("scoreboard property '" + property + "' is not a valid property");
|
||||
}
|
||||
});
|
||||
|
||||
expression.addContextFunction("scoreboard_display", 2, (c, t, lv) ->
|
||||
{
|
||||
CarpetContext cc = (CarpetContext)c;
|
||||
Scoreboard scoreboard = cc.server().getScoreboard();
|
||||
String location = lv.get(0).getString();
|
||||
int slot = Scoreboard.getDisplaySlotByName(location);
|
||||
if (slot < 0) throw new InternalExpressionException("Invalid objective slot: "+location);
|
||||
Value target = lv.get(1);
|
||||
final CarpetContext cc = (CarpetContext) c;
|
||||
final Scoreboard scoreboard = cc.server().getScoreboard();
|
||||
final String location = lv.get(0).getString();
|
||||
final int slot = Scoreboard.getDisplaySlotByName(location);
|
||||
if (slot < 0)
|
||||
{
|
||||
throw new InternalExpressionException("Invalid objective slot: " + location);
|
||||
}
|
||||
final Value target = lv.get(1);
|
||||
if (target.isNull())
|
||||
{
|
||||
scoreboard.setDisplayObjective(slot, null);
|
||||
return new NumericValue(slot);
|
||||
}
|
||||
String objectiveString = target.getString();
|
||||
Objective objective = scoreboard.getOrCreateObjective(objectiveString);
|
||||
if (objective == null) return Value.NULL;
|
||||
final String objectiveString = target.getString();
|
||||
final Objective objective = scoreboard.getOrCreateObjective(objectiveString);
|
||||
if (objective == null)
|
||||
{
|
||||
return Value.NULL;
|
||||
}
|
||||
scoreboard.setDisplayObjective(slot, objective);
|
||||
return new NumericValue(slot);
|
||||
});
|
||||
|
||||
expression.addContextFunction("team_list", -1, (c, t, lv) ->
|
||||
{
|
||||
if(lv.size() > 1) throw new InternalExpressionException("'team_list' requires zero or one parameters");
|
||||
CarpetContext cc = (CarpetContext)c;
|
||||
ServerScoreboard scoreboard = cc.server().getScoreboard();
|
||||
if(lv.size() == 0)
|
||||
if (lv.size() > 1)
|
||||
{
|
||||
throw new InternalExpressionException("'team_list' requires zero or one parameters");
|
||||
}
|
||||
final CarpetContext cc = (CarpetContext) c;
|
||||
final ServerScoreboard scoreboard = cc.server().getScoreboard();
|
||||
if (lv.size() == 0)
|
||||
{
|
||||
return ListValue.wrap(scoreboard.getTeamNames().stream().map(StringValue::of));
|
||||
if (lv.size() != 1) return Value.NULL;
|
||||
PlayerTeam team = scoreboard.getPlayerTeam(lv.get(0).getString());
|
||||
if(team == null) return Value.NULL;
|
||||
return ListValue.wrap(team.getPlayers().stream().map(StringValue::of));
|
||||
}
|
||||
if (lv.size() != 1)
|
||||
{
|
||||
return Value.NULL;
|
||||
}
|
||||
final PlayerTeam team = scoreboard.getPlayerTeam(lv.get(0).getString());
|
||||
return team == null ? Value.NULL : ListValue.wrap(team.getPlayers().stream().map(StringValue::of));
|
||||
});
|
||||
|
||||
|
||||
expression.addContextFunction("team_add", -1, (c, t, lv) ->
|
||||
{
|
||||
if(!(lv.size() < 3 && lv.size() > 0)) throw new InternalExpressionException("'team_add' requires one or two parameters");
|
||||
|
||||
CarpetContext cc = (CarpetContext)c;
|
||||
ServerScoreboard scoreboard = cc.server().getScoreboard();
|
||||
String teamName = lv.get(0).getString();
|
||||
|
||||
if(lv.size() == 1)
|
||||
if (!(lv.size() < 3 && lv.size() > 0))
|
||||
{
|
||||
if (scoreboard.getPlayerTeam(teamName) != null) return Value.NULL;
|
||||
throw new InternalExpressionException("'team_add' requires one or two parameters");
|
||||
}
|
||||
|
||||
final CarpetContext cc = (CarpetContext) c;
|
||||
final ServerScoreboard scoreboard = cc.server().getScoreboard();
|
||||
final String teamName = lv.get(0).getString();
|
||||
|
||||
if (lv.size() == 1)
|
||||
{
|
||||
if (scoreboard.getPlayerTeam(teamName) != null)
|
||||
{
|
||||
return Value.NULL;
|
||||
}
|
||||
scoreboard.addPlayerTeam(teamName);
|
||||
return new StringValue(teamName);
|
||||
}
|
||||
if(lv.size() != 2) return Value.NULL;
|
||||
Value playerVal = lv.get(1);
|
||||
String player = EntityValue.getPlayerNameByValue(playerVal);
|
||||
if(player == null) return Value.NULL;
|
||||
PlayerTeam team = scoreboard.getPlayerTeam(teamName);
|
||||
if(team == null) return Value.NULL;
|
||||
if(team.isAlliedTo(scoreboard.getPlayersTeam(player))) return Value.FALSE;
|
||||
scoreboard.addPlayerToTeam(player,scoreboard.getPlayerTeam(teamName));
|
||||
if (lv.size() != 2)
|
||||
{
|
||||
return Value.NULL;
|
||||
}
|
||||
final Value playerVal = lv.get(1);
|
||||
final String player = EntityValue.getPlayerNameByValue(playerVal);
|
||||
if (player == null)
|
||||
{
|
||||
return Value.NULL;
|
||||
}
|
||||
final PlayerTeam team = scoreboard.getPlayerTeam(teamName);
|
||||
if (team == null)
|
||||
{
|
||||
return Value.NULL;
|
||||
}
|
||||
if (team.isAlliedTo(scoreboard.getPlayersTeam(player)))
|
||||
{
|
||||
return Value.FALSE;
|
||||
}
|
||||
scoreboard.addPlayerToTeam(player, scoreboard.getPlayerTeam(teamName));
|
||||
return Value.TRUE;
|
||||
});
|
||||
|
||||
expression.addContextFunction("team_remove", 1, (c, t, lv) ->
|
||||
{
|
||||
CarpetContext cc = (CarpetContext)c;
|
||||
ServerScoreboard scoreboard = cc.server().getScoreboard();
|
||||
Value teamVal = lv.get(0);
|
||||
String team = teamVal.getString();
|
||||
if(scoreboard.getPlayerTeam(team) == null) return Value.NULL;
|
||||
final CarpetContext cc = (CarpetContext) c;
|
||||
final ServerScoreboard scoreboard = cc.server().getScoreboard();
|
||||
final Value teamVal = lv.get(0);
|
||||
final String team = teamVal.getString();
|
||||
if (scoreboard.getPlayerTeam(team) == null)
|
||||
{
|
||||
return Value.NULL;
|
||||
}
|
||||
scoreboard.removePlayerTeam(scoreboard.getPlayerTeam(team));
|
||||
return Value.TRUE;
|
||||
});
|
||||
@@ -292,212 +376,328 @@ public class Scoreboards {
|
||||
|
||||
expression.addContextFunction("team_leave", 1, (c, t, lv) ->
|
||||
{
|
||||
CarpetContext cc = (CarpetContext)c;
|
||||
ServerScoreboard scoreboard = cc.server().getScoreboard();
|
||||
Value playerVal = lv.get(0);
|
||||
String player = EntityValue.getPlayerNameByValue(playerVal);
|
||||
if(player == null) return Value.NULL;
|
||||
return BooleanValue.of(scoreboard.removePlayerFromTeam(player));
|
||||
final CarpetContext cc = (CarpetContext) c;
|
||||
final ServerScoreboard scoreboard = cc.server().getScoreboard();
|
||||
final Value playerVal = lv.get(0);
|
||||
final String player = EntityValue.getPlayerNameByValue(playerVal);
|
||||
return player == null ? Value.NULL : BooleanValue.of(scoreboard.removePlayerFromTeam(player));
|
||||
});
|
||||
|
||||
expression.addContextFunction("team_property", -1, (c, t, lv) ->
|
||||
{
|
||||
CarpetContext cc = (CarpetContext)c;
|
||||
ServerScoreboard scoreboard = cc.server().getScoreboard();
|
||||
|
||||
if(lv.size() < 2 || lv.size() > 3) throw new InternalExpressionException("'team_property' requires two or three arguments");
|
||||
|
||||
Value teamVal = lv.get(0);
|
||||
Value propertyVal = lv.get(1);
|
||||
final CarpetContext cc = (CarpetContext) c;
|
||||
final ServerScoreboard scoreboard = cc.server().getScoreboard();
|
||||
if (lv.size() < 2 || lv.size() > 3)
|
||||
{
|
||||
throw new InternalExpressionException("'team_property' requires two or three arguments");
|
||||
}
|
||||
final Value teamVal = lv.get(0);
|
||||
final Value propertyVal = lv.get(1);
|
||||
|
||||
Value settingVal = null;
|
||||
boolean modifying = false;
|
||||
if(lv.size() == 3)
|
||||
if (lv.size() == 3)
|
||||
{
|
||||
modifying = true;
|
||||
settingVal = lv.get(2);
|
||||
}
|
||||
|
||||
PlayerTeam team = scoreboard.getPlayerTeam(teamVal.getString());
|
||||
if(team == null) return Value.NULL;
|
||||
final PlayerTeam team = scoreboard.getPlayerTeam(teamVal.getString());
|
||||
if (team == null)
|
||||
{
|
||||
return Value.NULL;
|
||||
}
|
||||
|
||||
if(!(propertyVal instanceof StringValue)) throw new InternalExpressionException("'team_property' requires a string as the second argument");
|
||||
if (!(propertyVal instanceof StringValue))
|
||||
{
|
||||
throw new InternalExpressionException("'team_property' requires a string as the second argument");
|
||||
}
|
||||
|
||||
switch (propertyVal.getString()) {
|
||||
case "collisionRule":
|
||||
if(!modifying) return new StringValue(team.getCollisionRule().name);
|
||||
if(!(settingVal instanceof StringValue)) throw new InternalExpressionException("'team_property' requires a string as the third argument for the property " + propertyVal.getString());
|
||||
Team.CollisionRule collisionRule = Team.CollisionRule.byName(settingVal.getString());
|
||||
if(collisionRule == null) throw new InternalExpressionException("Unknown value for property " + propertyVal.getString() + ": " + settingVal.getString());
|
||||
switch (propertyVal.getString())
|
||||
{
|
||||
case "collisionRule" -> {
|
||||
if (!modifying)
|
||||
{
|
||||
return new StringValue(team.getCollisionRule().name);
|
||||
}
|
||||
if (!(settingVal instanceof StringValue))
|
||||
{
|
||||
throw new InternalExpressionException("'team_property' requires a string as the third argument for the property " + propertyVal.getString());
|
||||
}
|
||||
final Team.CollisionRule collisionRule = Team.CollisionRule.byName(settingVal.getString());
|
||||
if (collisionRule == null)
|
||||
{
|
||||
throw new InternalExpressionException("Unknown value for property " + propertyVal.getString() + ": " + settingVal.getString());
|
||||
}
|
||||
team.setCollisionRule(collisionRule);
|
||||
break;
|
||||
case "color":
|
||||
if(!modifying) return new StringValue(team.getColor().getName());
|
||||
if(!(settingVal instanceof StringValue)) throw new InternalExpressionException("'team_property' requires a string as the third argument for the property " + propertyVal.getString());
|
||||
ChatFormatting color = ChatFormatting.getByName(settingVal.getString().toUpperCase());
|
||||
if(color == null || !color.isColor()) throw new InternalExpressionException("Unknown value for property " + propertyVal.getString() + ": " + settingVal.getString());
|
||||
}
|
||||
case "color" -> {
|
||||
if (!modifying)
|
||||
{
|
||||
return new StringValue(team.getColor().getName());
|
||||
}
|
||||
if (!(settingVal instanceof StringValue))
|
||||
{
|
||||
throw new InternalExpressionException("'team_property' requires a string as the third argument for the property " + propertyVal.getString());
|
||||
}
|
||||
final ChatFormatting color = ChatFormatting.getByName(settingVal.getString().toUpperCase());
|
||||
if (color == null || !color.isColor())
|
||||
{
|
||||
throw new InternalExpressionException("Unknown value for property " + propertyVal.getString() + ": " + settingVal.getString());
|
||||
}
|
||||
team.setColor(color);
|
||||
break;
|
||||
case "deathMessageVisibility":
|
||||
if(!modifying) return new StringValue(team.getDeathMessageVisibility().name);
|
||||
if(!(settingVal instanceof StringValue)) throw new InternalExpressionException("'team_property' requires a string as the third argument for the property " + propertyVal.getString());
|
||||
Team.Visibility deathMessageVisibility = Team.Visibility.byName(settingVal.getString());
|
||||
if(deathMessageVisibility == null) throw new InternalExpressionException("Unknown value for property " + propertyVal.getString() + ": " + settingVal.getString());
|
||||
}
|
||||
case "deathMessageVisibility" -> {
|
||||
if (!modifying)
|
||||
{
|
||||
return new StringValue(team.getDeathMessageVisibility().name);
|
||||
}
|
||||
if (!(settingVal instanceof StringValue))
|
||||
{
|
||||
throw new InternalExpressionException("'team_property' requires a string as the third argument for the property " + propertyVal.getString());
|
||||
}
|
||||
final Team.Visibility deathMessageVisibility = Team.Visibility.byName(settingVal.getString());
|
||||
if (deathMessageVisibility == null)
|
||||
{
|
||||
throw new InternalExpressionException("Unknown value for property " + propertyVal.getString() + ": " + settingVal.getString());
|
||||
}
|
||||
team.setDeathMessageVisibility(deathMessageVisibility);
|
||||
break;
|
||||
case "displayName":
|
||||
if(!modifying) return new FormattedTextValue(team.getDisplayName());
|
||||
if(!(settingVal instanceof StringValue)) throw new InternalExpressionException("'team_property' requires a string or formatted text as the third argument for the property " + propertyVal.getString());
|
||||
Component displayName;
|
||||
displayName = FormattedTextValue.getTextByValue(settingVal);
|
||||
team.setDisplayName(displayName);
|
||||
break;
|
||||
case "friendlyFire":
|
||||
if(!modifying) return BooleanValue.of(team.isAllowFriendlyFire());
|
||||
if(!(settingVal instanceof NumericValue)) throw new InternalExpressionException("'team_property' requires a boolean as the third argument for the property " + propertyVal.getString());
|
||||
boolean friendlyFire = settingVal.getBoolean();
|
||||
team.setAllowFriendlyFire(friendlyFire);
|
||||
break;
|
||||
case "nametagVisibility":
|
||||
if(!modifying) return new StringValue(team.getNameTagVisibility().name);
|
||||
if(!(settingVal instanceof StringValue)) throw new InternalExpressionException("'team_property' requires a string as the third argument for the property " + propertyVal.getString());
|
||||
Team.Visibility nametagVisibility = Team.Visibility.byName(settingVal.getString());
|
||||
if(nametagVisibility == null) throw new InternalExpressionException("Unknown value for property " + propertyVal.getString() + ": " + settingVal.getString());
|
||||
}
|
||||
case "displayName" -> {
|
||||
if (!modifying)
|
||||
{
|
||||
return new FormattedTextValue(team.getDisplayName());
|
||||
}
|
||||
if (!(settingVal instanceof StringValue))
|
||||
{
|
||||
throw new InternalExpressionException("'team_property' requires a string or formatted text as the third argument for the property " + propertyVal.getString());
|
||||
}
|
||||
team.setDisplayName(FormattedTextValue.getTextByValue(settingVal));
|
||||
}
|
||||
case "friendlyFire" -> {
|
||||
if (!modifying)
|
||||
{
|
||||
return BooleanValue.of(team.isAllowFriendlyFire());
|
||||
}
|
||||
if (!(settingVal instanceof NumericValue))
|
||||
{
|
||||
throw new InternalExpressionException("'team_property' requires a boolean as the third argument for the property " + propertyVal.getString());
|
||||
}
|
||||
team.setAllowFriendlyFire(settingVal.getBoolean());
|
||||
}
|
||||
case "nametagVisibility" -> {
|
||||
if (!modifying)
|
||||
{
|
||||
return new StringValue(team.getNameTagVisibility().name);
|
||||
}
|
||||
if (!(settingVal instanceof StringValue))
|
||||
{
|
||||
throw new InternalExpressionException("'team_property' requires a string as the third argument for the property " + propertyVal.getString());
|
||||
}
|
||||
final Team.Visibility nametagVisibility = Team.Visibility.byName(settingVal.getString());
|
||||
if (nametagVisibility == null)
|
||||
{
|
||||
throw new InternalExpressionException("Unknown value for property " + propertyVal.getString() + ": " + settingVal.getString());
|
||||
}
|
||||
team.setNameTagVisibility(nametagVisibility);
|
||||
|
||||
break;
|
||||
case "prefix":
|
||||
if(!modifying) return new FormattedTextValue(team.getPlayerPrefix());
|
||||
if(!(settingVal instanceof StringValue)) throw new InternalExpressionException("'team_property ' requires a string or formatted text as the third argument for the property " + propertyVal.getString());
|
||||
Component prefix;
|
||||
prefix = FormattedTextValue.getTextByValue(settingVal);
|
||||
team.setPlayerPrefix(prefix);
|
||||
break;
|
||||
case "seeFriendlyInvisibles":
|
||||
if(!modifying) return BooleanValue.of(team.canSeeFriendlyInvisibles());
|
||||
if(!(settingVal instanceof NumericValue)) throw new InternalExpressionException("'team_property' requires a boolean as the third argument for the property " + propertyVal.getString());
|
||||
boolean seeFriendlyInvisibles = settingVal.getBoolean();
|
||||
team.setSeeFriendlyInvisibles(seeFriendlyInvisibles);
|
||||
break;
|
||||
case "suffix":
|
||||
if(!modifying) return new FormattedTextValue(team.getPlayerSuffix());
|
||||
if(!(settingVal instanceof StringValue)) throw new InternalExpressionException("'team_property' requires a string or formatted text as the third argument for the property " + propertyVal.getString());
|
||||
Component suffix;
|
||||
suffix = FormattedTextValue.getTextByValue(settingVal);
|
||||
team.setPlayerSuffix(suffix);
|
||||
break;
|
||||
default:
|
||||
throw new InternalExpressionException("team property '" + propertyVal.getString() + "' is not a valid property");
|
||||
}
|
||||
case "prefix" -> {
|
||||
if (!modifying)
|
||||
{
|
||||
return new FormattedTextValue(team.getPlayerPrefix());
|
||||
}
|
||||
if (!(settingVal instanceof StringValue))
|
||||
{
|
||||
throw new InternalExpressionException("'team_property ' requires a string or formatted text as the third argument for the property " + propertyVal.getString());
|
||||
}
|
||||
team.setPlayerPrefix(FormattedTextValue.getTextByValue(settingVal));
|
||||
}
|
||||
case "seeFriendlyInvisibles" -> {
|
||||
if (!modifying)
|
||||
{
|
||||
return BooleanValue.of(team.canSeeFriendlyInvisibles());
|
||||
}
|
||||
if (!(settingVal instanceof NumericValue))
|
||||
{
|
||||
throw new InternalExpressionException("'team_property' requires a boolean as the third argument for the property " + propertyVal.getString());
|
||||
}
|
||||
team.setSeeFriendlyInvisibles(settingVal.getBoolean());
|
||||
}
|
||||
case "suffix" -> {
|
||||
if (!modifying)
|
||||
{
|
||||
return new FormattedTextValue(team.getPlayerSuffix());
|
||||
}
|
||||
if (!(settingVal instanceof StringValue))
|
||||
{
|
||||
throw new InternalExpressionException("'team_property' requires a string or formatted text as the third argument for the property " + propertyVal.getString());
|
||||
}
|
||||
team.setPlayerSuffix(FormattedTextValue.getTextByValue(settingVal));
|
||||
}
|
||||
default -> throw new InternalExpressionException("team property '" + propertyVal.getString() + "' is not a valid property");
|
||||
}
|
||||
return Value.TRUE;
|
||||
});
|
||||
|
||||
expression.addContextFunction("bossbar", -1, (c, t, lv) ->
|
||||
{
|
||||
CustomBossEvents bossBarManager = ((CarpetContext)c).server().getCustomBossEvents();
|
||||
if(lv.size() > 3) throw new InternalExpressionException("'bossbar' accepts max three arguments");
|
||||
|
||||
if(lv.size() == 0) return ListValue.wrap(bossBarManager.getEvents().stream().map(CustomBossEvent::getTextId).map(ResourceLocation::toString).map(StringValue::of));
|
||||
|
||||
String id = lv.get(0).getString();
|
||||
ResourceLocation identifier;
|
||||
identifier = InputValidator.identifierOf(id);
|
||||
|
||||
if(lv.size() == 1)
|
||||
final CustomBossEvents bossBarManager = ((CarpetContext) c).server().getCustomBossEvents();
|
||||
if (lv.size() > 3)
|
||||
{
|
||||
if(bossBarManager.get(identifier) != null) return Value.FALSE;
|
||||
return StringValue.of(bossBarManager.create(identifier,Component.literal(id)).getTextId().toString());
|
||||
throw new InternalExpressionException("'bossbar' accepts max three arguments");
|
||||
}
|
||||
|
||||
String property = lv.get(1).getString();
|
||||
if (lv.size() == 0)
|
||||
{
|
||||
return ListValue.wrap(bossBarManager.getEvents().stream().map(CustomBossEvent::getTextId).map(ResourceLocation::toString).map(StringValue::of));
|
||||
}
|
||||
|
||||
CustomBossEvent bossBar = bossBarManager.get(identifier);
|
||||
if(bossBar == null) return Value.NULL;
|
||||
final String id = lv.get(0).getString();
|
||||
final ResourceLocation identifier = InputValidator.identifierOf(id);
|
||||
|
||||
Value propertyValue = (lv.size() == 3)?lv.get(2):null;
|
||||
if (lv.size() == 1)
|
||||
{
|
||||
if (bossBarManager.get(identifier) != null)
|
||||
{
|
||||
return Value.FALSE;
|
||||
}
|
||||
return StringValue.of(bossBarManager.create(identifier, Component.literal(id)).getTextId().toString());
|
||||
}
|
||||
|
||||
switch (property) {
|
||||
case "color":
|
||||
if(propertyValue == null) {
|
||||
BossEvent.BossBarColor color = (bossBar).getColor();
|
||||
if(color == null) return Value.NULL;
|
||||
return StringValue.of(color.getName());
|
||||
final String property = lv.get(1).getString();
|
||||
|
||||
final CustomBossEvent bossBar = bossBarManager.get(identifier);
|
||||
if (bossBar == null)
|
||||
{
|
||||
return Value.NULL;
|
||||
}
|
||||
|
||||
final Value propertyValue = (lv.size() == 3) ? lv.get(2) : null;
|
||||
|
||||
switch (property)
|
||||
{
|
||||
case "color" -> {
|
||||
if (propertyValue == null)
|
||||
{
|
||||
final BossEvent.BossBarColor color = (bossBar).getColor();
|
||||
return color == null ? Value.NULL : StringValue.of(color.getName());
|
||||
}
|
||||
final BossEvent.BossBarColor color = BossEvent.BossBarColor.byName(propertyValue.getString());
|
||||
if (color == null)
|
||||
{
|
||||
return Value.NULL;
|
||||
}
|
||||
|
||||
BossEvent.BossBarColor color = BossEvent.BossBarColor.byName(propertyValue.getString());
|
||||
if(color == null) return Value.NULL;
|
||||
bossBar.setColor(BossEvent.BossBarColor.byName(propertyValue.getString()));
|
||||
return Value.TRUE;
|
||||
case "max":
|
||||
if(propertyValue == null) return NumericValue.of(bossBar.getMax());
|
||||
|
||||
if(!(propertyValue instanceof NumericValue)) throw new InternalExpressionException("'bossbar' requires a number as the value for the property " + property);
|
||||
bossBar.setMax(((NumericValue) propertyValue).getInt());
|
||||
}
|
||||
case "max" -> {
|
||||
if (propertyValue == null)
|
||||
{
|
||||
return NumericValue.of(bossBar.getMax());
|
||||
}
|
||||
if (!(propertyValue instanceof final NumericValue number))
|
||||
{
|
||||
throw new InternalExpressionException("'bossbar' requires a number as the value for the property " + property);
|
||||
}
|
||||
bossBar.setMax(number.getInt());
|
||||
return Value.TRUE;
|
||||
case "name":
|
||||
if(propertyValue == null) return new FormattedTextValue(bossBar.getName());
|
||||
|
||||
}
|
||||
case "name" -> {
|
||||
if (propertyValue == null)
|
||||
{
|
||||
return new FormattedTextValue(bossBar.getName());
|
||||
}
|
||||
bossBar.setName(FormattedTextValue.getTextByValue(propertyValue));
|
||||
return Value.TRUE;
|
||||
case "add_player":
|
||||
if(propertyValue == null) throw new InternalExpressionException("Bossbar property " + property + " can't be queried, add a third parameter");
|
||||
|
||||
if(propertyValue instanceof ListValue) {
|
||||
((ListValue) propertyValue).getItems().forEach((v)->{
|
||||
ServerPlayer player = EntityValue.getPlayerByValue(((CarpetContext)c).server(), propertyValue);
|
||||
if(player != null) bossBar.addPlayer(player);
|
||||
}
|
||||
case "add_player" -> {
|
||||
if (propertyValue == null)
|
||||
{
|
||||
throw new InternalExpressionException("Bossbar property " + property + " can't be queried, add a third parameter");
|
||||
}
|
||||
if (propertyValue instanceof final ListValue list)
|
||||
{
|
||||
list.getItems().forEach((v) -> {
|
||||
final ServerPlayer player = EntityValue.getPlayerByValue(((CarpetContext) c).server(), propertyValue);
|
||||
if (player != null)
|
||||
{
|
||||
bossBar.addPlayer(player);
|
||||
}
|
||||
});
|
||||
return Value.TRUE;
|
||||
}
|
||||
|
||||
ServerPlayer player = EntityValue.getPlayerByValue(((CarpetContext) c).server(), propertyValue);
|
||||
if(player != null) {
|
||||
final ServerPlayer player = EntityValue.getPlayerByValue(((CarpetContext) c).server(), propertyValue);
|
||||
if (player != null)
|
||||
{
|
||||
bossBar.addPlayer(player);
|
||||
return Value.TRUE;
|
||||
}
|
||||
return Value.FALSE;
|
||||
case "players":
|
||||
if (propertyValue == null) return ListValue.wrap(bossBar.getPlayers().stream().map(EntityValue::new));
|
||||
if(propertyValue instanceof ListValue)
|
||||
}
|
||||
case "players" -> {
|
||||
if (propertyValue == null)
|
||||
{
|
||||
return ListValue.wrap(bossBar.getPlayers().stream().map(EntityValue::new));
|
||||
}
|
||||
if (propertyValue instanceof final ListValue list)
|
||||
{
|
||||
bossBar.removeAllPlayers();
|
||||
((ListValue) propertyValue).getItems().forEach((v) -> {
|
||||
list.getItems().forEach(v -> {
|
||||
ServerPlayer p = EntityValue.getPlayerByValue(((CarpetContext) c).server(), v);
|
||||
if (p != null) bossBar.addPlayer(p);
|
||||
if (p != null)
|
||||
{
|
||||
bossBar.addPlayer(p);
|
||||
}
|
||||
});
|
||||
return Value.TRUE;
|
||||
}
|
||||
|
||||
|
||||
ServerPlayer p = EntityValue.getPlayerByValue(((CarpetContext) c).server(), propertyValue);
|
||||
final ServerPlayer p = EntityValue.getPlayerByValue(((CarpetContext) c).server(), propertyValue);
|
||||
bossBar.removeAllPlayers();
|
||||
if (p != null) {
|
||||
if (p != null)
|
||||
{
|
||||
bossBar.addPlayer(p);
|
||||
return Value.TRUE;
|
||||
}
|
||||
return Value.FALSE;
|
||||
case "style":
|
||||
if(propertyValue == null) return StringValue.of(bossBar.getOverlay().getName());
|
||||
BossEvent.BossBarOverlay style = BossEvent.BossBarOverlay.byName(propertyValue.getString());
|
||||
if(style == null) throw new InternalExpressionException("'" + propertyValue.getString() + "' is not a valid value for property " + property);
|
||||
}
|
||||
case "style" -> {
|
||||
if (propertyValue == null)
|
||||
{
|
||||
return StringValue.of(bossBar.getOverlay().getName());
|
||||
}
|
||||
final BossEvent.BossBarOverlay style = BossEvent.BossBarOverlay.byName(propertyValue.getString());
|
||||
if (style == null)
|
||||
{
|
||||
throw new InternalExpressionException("'" + propertyValue.getString() + "' is not a valid value for property " + property);
|
||||
}
|
||||
bossBar.setOverlay(style);
|
||||
return Value.TRUE;
|
||||
case "value":
|
||||
if(propertyValue == null) return NumericValue.of(bossBar.getValue());
|
||||
|
||||
if(!(propertyValue instanceof NumericValue)) throw new InternalExpressionException("'bossbar' requires a number as the value for the property " + property);
|
||||
bossBar.setValue(((NumericValue) propertyValue).getInt());
|
||||
}
|
||||
case "value" -> {
|
||||
if (propertyValue == null)
|
||||
{
|
||||
return NumericValue.of(bossBar.getValue());
|
||||
}
|
||||
if (!(propertyValue instanceof final NumericValue number))
|
||||
{
|
||||
throw new InternalExpressionException("'bossbar' requires a number as the value for the property " + property);
|
||||
}
|
||||
bossBar.setValue(number.getInt());
|
||||
return Value.TRUE;
|
||||
case "visible":
|
||||
if(propertyValue == null) return BooleanValue.of(bossBar.isVisible());
|
||||
|
||||
}
|
||||
case "visible" -> {
|
||||
if (propertyValue == null)
|
||||
{
|
||||
return BooleanValue.of(bossBar.isVisible());
|
||||
}
|
||||
bossBar.setVisible(propertyValue.getBoolean());
|
||||
return Value.TRUE;
|
||||
case "remove":
|
||||
}
|
||||
case "remove" -> {
|
||||
bossBarManager.remove(bossBar);
|
||||
return Value.TRUE;
|
||||
default:
|
||||
throw new InternalExpressionException("Unknown bossbar property " + property);
|
||||
}
|
||||
default -> throw new InternalExpressionException("Unknown bossbar property " + property);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -16,21 +16,28 @@ public class Threading
|
||||
{
|
||||
//"overidden" native call to cancel if on main thread
|
||||
expression.addContextFunction("task_join", 1, (c, t, lv) -> {
|
||||
if (((CarpetContext)c).server().isSameThread())
|
||||
if (((CarpetContext) c).server().isSameThread())
|
||||
{
|
||||
throw new InternalExpressionException("'task_join' cannot be called from main thread to avoid deadlocks");
|
||||
Value v = lv.get(0);
|
||||
if (!(v instanceof ThreadValue))
|
||||
}
|
||||
final Value v = lv.get(0);
|
||||
if (!(v instanceof final ThreadValue tv))
|
||||
{
|
||||
throw new InternalExpressionException("'task_join' could only be used with a task value");
|
||||
return ((ThreadValue) v).join();
|
||||
}
|
||||
return tv.join();
|
||||
});
|
||||
|
||||
// has to be lazy due to deferred execution of the expression
|
||||
expression.addLazyFunctionWithDelegation("task_dock", 1, false,true, (c, t, expr, tok, lv) -> {
|
||||
CarpetContext cc = (CarpetContext)c;
|
||||
MinecraftServer server = cc.server();
|
||||
if (server.isSameThread()) return lv.get(0); // pass through for on thread tasks
|
||||
Value[] result = new Value[]{Value.NULL};
|
||||
RuntimeException[] internal = new RuntimeException[]{null};
|
||||
expression.addLazyFunctionWithDelegation("task_dock", 1, false, true, (c, t, expr, tok, lv) -> {
|
||||
final CarpetContext cc = (CarpetContext) c;
|
||||
final MinecraftServer server = cc.server();
|
||||
if (server.isSameThread())
|
||||
{
|
||||
return lv.get(0); // pass through for on thread tasks
|
||||
}
|
||||
final Value[] result = new Value[]{Value.NULL};
|
||||
final RuntimeException[] internal = new RuntimeException[]{null};
|
||||
try
|
||||
{
|
||||
((CarpetContext) c).server().executeBlocking(() ->
|
||||
@@ -39,22 +46,22 @@ public class Threading
|
||||
{
|
||||
result[0] = lv.get(0).evalValue(c, t);
|
||||
}
|
||||
catch (ExpressionException exc)
|
||||
catch (final ExpressionException exc)
|
||||
{
|
||||
internal[0] = exc;
|
||||
}
|
||||
catch (InternalExpressionException exc)
|
||||
catch (final InternalExpressionException exc)
|
||||
{
|
||||
internal[0] = new ExpressionException(c, expr, tok, exc.getMessage(), exc.stack);
|
||||
}
|
||||
|
||||
catch (ArithmeticException exc)
|
||||
catch (final ArithmeticException exc)
|
||||
{
|
||||
internal[0] = new ExpressionException(c, expr, tok, "Your math is wrong, "+exc.getMessage());
|
||||
internal[0] = new ExpressionException(c, expr, tok, "Your math is wrong, " + exc.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (CompletionException exc)
|
||||
catch (final CompletionException exc)
|
||||
{
|
||||
throw new InternalExpressionException("Error while executing docked task section, internal stack trace is gone");
|
||||
}
|
||||
@@ -62,7 +69,7 @@ public class Threading
|
||||
{
|
||||
throw internal[0];
|
||||
}
|
||||
Value ret = result[0]; // preventing from lazy evaluating of the result in case a future completes later
|
||||
final Value ret = result[0]; // preventing from lazy evaluating of the result in case a future completes later
|
||||
return (_c, _t) -> ret;
|
||||
// pass through placeholder
|
||||
// implmenetation should dock the task on the main thread.
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -3,6 +3,7 @@ package carpet.script.argument;
|
||||
public abstract class Argument
|
||||
{
|
||||
public int offset;
|
||||
|
||||
protected Argument(int offset)
|
||||
{
|
||||
this.offset = offset;
|
||||
|
||||
@@ -7,48 +7,52 @@ import carpet.script.value.ListValue;
|
||||
import carpet.script.value.NumericValue;
|
||||
import carpet.script.value.StringValue;
|
||||
import carpet.script.value.Value;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
import net.minecraft.core.BlockPos;
|
||||
|
||||
public class BlockArgument extends Argument
|
||||
{
|
||||
public final BlockValue block;
|
||||
public final String replacement;
|
||||
private BlockArgument(BlockValue b, int o)
|
||||
|
||||
private BlockArgument(final BlockValue b, final int o)
|
||||
{
|
||||
super(o);
|
||||
block = b;
|
||||
replacement = null;
|
||||
}
|
||||
private BlockArgument(BlockValue b, int o, String replacement)
|
||||
|
||||
private BlockArgument(final BlockValue b, final int o, final String replacement)
|
||||
{
|
||||
super(o);
|
||||
block = b;
|
||||
this.replacement = replacement;
|
||||
}
|
||||
|
||||
public static BlockArgument findIn(CarpetContext c, List<Value> params, int offset)
|
||||
public static BlockArgument findIn(final CarpetContext c, final List<Value> params, final int offset)
|
||||
{
|
||||
return findIn(c, params,offset, false, false, false);
|
||||
return findIn(c, params, offset, false, false, false);
|
||||
}
|
||||
|
||||
public static BlockArgument findIn(CarpetContext c, List<Value> params, int offset, boolean acceptString)
|
||||
public static BlockArgument findIn(final CarpetContext c, final List<Value> params, final int offset, final boolean acceptString)
|
||||
{
|
||||
return findIn(c, params,offset, acceptString, false, false);
|
||||
return findIn(c, params, offset, acceptString, false, false);
|
||||
}
|
||||
|
||||
public static BlockArgument findIn(CarpetContext c, List<Value> params, int offset, boolean acceptString, boolean optional, boolean anyString)
|
||||
public static BlockArgument findIn(final CarpetContext c, final List<Value> params, final int offset, final boolean acceptString, final boolean optional, final boolean anyString)
|
||||
{
|
||||
return findIn(c, params.listIterator(offset), offset, acceptString, optional, anyString);
|
||||
}
|
||||
|
||||
public static BlockArgument findIn(CarpetContext c, Iterator<Value> params, int offset, boolean acceptString, boolean optional, boolean anyString)
|
||||
public static BlockArgument findIn(final CarpetContext c, final Iterator<Value> params, final int offset, final boolean acceptString, final boolean optional, final boolean anyString)
|
||||
{
|
||||
try
|
||||
{
|
||||
Value v1 = params.next();
|
||||
final Value v1 = params.next();
|
||||
//add conditional from string name
|
||||
if (optional && v1.isNull())
|
||||
{
|
||||
@@ -69,10 +73,10 @@ public class BlockArgument extends Argument
|
||||
final BlockPos pos = c.origin();
|
||||
if (v1 instanceof ListValue)
|
||||
{
|
||||
List<Value> args = ((ListValue) v1).getItems();
|
||||
int xpos = (int) NumericValue.asNumber(args.get(0)).getLong();
|
||||
int ypos = (int) NumericValue.asNumber(args.get(1)).getLong();
|
||||
int zpos = (int) NumericValue.asNumber(args.get(2)).getLong();
|
||||
final List<Value> args = ((ListValue) v1).getItems();
|
||||
final int xpos = (int) NumericValue.asNumber(args.get(0)).getLong();
|
||||
final int ypos = (int) NumericValue.asNumber(args.get(1)).getLong();
|
||||
final int zpos = (int) NumericValue.asNumber(args.get(2)).getLong();
|
||||
|
||||
return new BlockArgument(
|
||||
new BlockValue(
|
||||
@@ -82,9 +86,9 @@ public class BlockArgument extends Argument
|
||||
),
|
||||
1 + offset);
|
||||
}
|
||||
int xpos = (int) NumericValue.asNumber(v1).getLong();
|
||||
int ypos = (int) NumericValue.asNumber( params.next()).getLong();
|
||||
int zpos = (int) NumericValue.asNumber( params.next()).getLong();
|
||||
final int xpos = (int) NumericValue.asNumber(v1).getLong();
|
||||
final int ypos = (int) NumericValue.asNumber(params.next()).getLong();
|
||||
final int zpos = (int) NumericValue.asNumber(params.next()).getLong();
|
||||
return new BlockArgument(
|
||||
new BlockValue(
|
||||
null,
|
||||
@@ -100,13 +104,17 @@ public class BlockArgument extends Argument
|
||||
}
|
||||
}
|
||||
|
||||
private static InternalExpressionException handleError(boolean optional, boolean acceptString)
|
||||
private static InternalExpressionException handleError(final boolean optional, final boolean acceptString)
|
||||
{
|
||||
String message = "Block-type argument should be defined either by three coordinates (a triple or by three arguments), or a block value";
|
||||
if (acceptString)
|
||||
message+=", or a string with block description";
|
||||
{
|
||||
message += ", or a string with block description";
|
||||
}
|
||||
if (optional)
|
||||
message+=", or null";
|
||||
{
|
||||
message += ", or null";
|
||||
}
|
||||
return new InternalExpressionException(message);
|
||||
}
|
||||
|
||||
|
||||
@@ -59,14 +59,19 @@ public class FileArgument
|
||||
private FileSystem zfs;
|
||||
private Path zipPath;
|
||||
|
||||
public final static Object writeIOSync = new Object();
|
||||
public static final Object writeIOSync = new Object();
|
||||
|
||||
public void close() {
|
||||
if (zfs != null && zfs.isOpen()) {
|
||||
try {
|
||||
public void close()
|
||||
{
|
||||
if (zfs != null && zfs.isOpen())
|
||||
{
|
||||
try
|
||||
{
|
||||
zfs.close();
|
||||
} catch (IOException e) {
|
||||
throw new InternalExpressionException("Unable to close zip container: "+zipContainer);
|
||||
}
|
||||
catch (final IOException e)
|
||||
{
|
||||
throw new InternalExpressionException("Unable to close zip container: " + zipContainer);
|
||||
}
|
||||
zfs = null;
|
||||
}
|
||||
@@ -86,10 +91,10 @@ public class FileArgument
|
||||
|
||||
public static Map<String, Type> of = Arrays.stream(values()).collect(Collectors.toMap(t -> t.id, t -> t));
|
||||
|
||||
Type(String id, String extension)
|
||||
Type(final String id, final String extension)
|
||||
{
|
||||
this.id = id;
|
||||
this.extension = extension;
|
||||
this.id = id;
|
||||
this.extension = extension;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,7 +103,7 @@ public class FileArgument
|
||||
READ, CREATE, DELETE
|
||||
}
|
||||
|
||||
public FileArgument(String resource, Type type, String zipContainer, boolean isFolder, boolean isShared, Reason reason)
|
||||
public FileArgument(final String resource, final Type type, final String zipContainer, final boolean isFolder, final boolean isShared, final Reason reason)
|
||||
{
|
||||
this.resource = resource;
|
||||
this.type = type;
|
||||
@@ -111,48 +116,68 @@ public class FileArgument
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "path: "+resource+" zip: "+zipContainer+" type: "+type.id+" folder: "+isFolder+" shared: "+isShared+" reason: "+reason.toString();
|
||||
public String toString()
|
||||
{
|
||||
return "path: " + resource + " zip: " + zipContainer + " type: " + type.id + " folder: " + isFolder + " shared: " + isShared + " reason: " + reason.toString();
|
||||
}
|
||||
|
||||
public static FileArgument from(List<Value> lv, boolean isFolder, Reason reason)
|
||||
public static FileArgument from(final List<Value> lv, final boolean isFolder, final Reason reason)
|
||||
{
|
||||
if (lv.size() < 2) throw new InternalExpressionException("File functions require path and type as first two arguments");
|
||||
String origtype = lv.get(1).getString().toLowerCase(Locale.ROOT);
|
||||
boolean shared = origtype.startsWith("shared_");
|
||||
String typeString = shared ? origtype.substring(7) : origtype; //len(shared_)
|
||||
Type type = Type.of.get(typeString);
|
||||
Pair<String, String> resource = recognizeResource(lv.get(0).getString(), isFolder, type);
|
||||
if (type==null)
|
||||
throw new InternalExpressionException("Unsupported file type: "+origtype);
|
||||
if (type==Type.FOLDER && !isFolder)
|
||||
if (lv.size() < 2)
|
||||
{
|
||||
throw new InternalExpressionException("File functions require path and type as first two arguments");
|
||||
}
|
||||
final String origtype = lv.get(1).getString().toLowerCase(Locale.ROOT);
|
||||
final boolean shared = origtype.startsWith("shared_");
|
||||
final String typeString = shared ? origtype.substring(7) : origtype; //len(shared_)
|
||||
final Type type = Type.of.get(typeString);
|
||||
final Pair<String, String> resource = recognizeResource(lv.get(0).getString(), isFolder, type);
|
||||
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);
|
||||
}
|
||||
return new FileArgument(resource.getLeft(), type, resource.getRight(), isFolder, shared, reason);
|
||||
|
||||
}
|
||||
|
||||
public static FileArgument resourceFromPath(String path, Reason reason, boolean shared)
|
||||
public static FileArgument resourceFromPath(final String path, final Reason reason, final boolean shared)
|
||||
{
|
||||
Pair<String, String> resource = recognizeResource(path, false, Type.ANY);
|
||||
return new FileArgument(resource.getLeft(), Type.ANY,resource.getRight(), false, shared, reason);
|
||||
final Pair<String, String> resource = recognizeResource(path, false, Type.ANY);
|
||||
return new FileArgument(resource.getLeft(), Type.ANY, resource.getRight(), false, shared, reason);
|
||||
}
|
||||
|
||||
public static Pair<String,String> recognizeResource(String origfile, boolean isFolder, Type type)
|
||||
public static Pair<String, String> recognizeResource(final String origfile, final boolean isFolder, final Type type)
|
||||
{
|
||||
String[] pathElements = origfile.split("[/\\\\]+");
|
||||
List<String> path = new ArrayList<>();
|
||||
final String[] pathElements = origfile.split("[/\\\\]+");
|
||||
final List<String> path = new ArrayList<>();
|
||||
String zipPath = null;
|
||||
for (int i =0; i < pathElements.length; i++ )
|
||||
for (int i = 0; i < pathElements.length; i++)
|
||||
{
|
||||
String token = pathElements[i];
|
||||
boolean isZip = token.endsWith(".zip") && (isFolder || (i < pathElements.length-1));
|
||||
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 = (type==Type.ANY && i == pathElements.length-1)? // sloppy really, but should work
|
||||
token.replaceAll("[^A-Za-z0-9\\-+_.]", ""):
|
||||
final boolean isZip = token.endsWith(".zip") && (isFolder || (i < pathElements.length - 1));
|
||||
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 = (type == Type.ANY && i == pathElements.length - 1) ? // sloppy really, but should work
|
||||
token.replaceAll("[^A-Za-z0-9\\-+_.]", "") :
|
||||
token.replaceAll("[^A-Za-z0-9\\-+_]", "");
|
||||
if (token.isEmpty()) continue;
|
||||
if (isZip) token = token + ".zip";
|
||||
if (token.isEmpty())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (isZip)
|
||||
{
|
||||
token = token + ".zip";
|
||||
}
|
||||
path.add(token);
|
||||
if (isZip)
|
||||
{
|
||||
@@ -161,80 +186,106 @@ public class FileArgument
|
||||
}
|
||||
}
|
||||
if (path.isEmpty() && !isFolder)
|
||||
{
|
||||
throw new InternalExpressionException(
|
||||
"Cannot use "+origfile+" as resource name: indicated path is empty"+((zipPath==null)?"":" in zip container "+zipPath)
|
||||
"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)
|
||||
private Path resolve(final String suffix)
|
||||
{
|
||||
if (CarpetServer.minecraft_server == null)
|
||||
{
|
||||
throw new InternalExpressionException("Accessing world files without server running");
|
||||
return CarpetServer.minecraft_server.getWorldPath(LevelResource.ROOT).resolve("scripts/"+suffix);
|
||||
}
|
||||
return CarpetServer.minecraft_server.getWorldPath(LevelResource.ROOT).resolve("scripts/" + suffix);
|
||||
}
|
||||
|
||||
private Path toPath(Module module)
|
||||
private Path toPath(final Module module)
|
||||
{
|
||||
if (!isShared && module == null) return null;
|
||||
if (!isShared && module == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (zipContainer == null)
|
||||
return resolve(getDescriptor(module, resource)+(isFolder?"":type.extension));
|
||||
{
|
||||
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));
|
||||
if (!Files.exists(zipPath) && reason != Reason.CREATE) return null; // no zip file
|
||||
try {
|
||||
if (!Files.exists(zipPath.getParent())) Files.createDirectories(zipPath.getParent());
|
||||
zfs = FileSystems.newFileSystem(URI.create("jar:"+ zipPath.toUri().toString()), env);
|
||||
final Map<String, String> env = new HashMap<>();
|
||||
if (reason == Reason.CREATE)
|
||||
{
|
||||
env.put("create", "true");
|
||||
}
|
||||
catch (FileSystemNotFoundException | IOException e)
|
||||
zipPath = resolve(getDescriptor(module, zipContainer));
|
||||
if (!Files.exists(zipPath) && reason != Reason.CREATE)
|
||||
{
|
||||
return null; // no zip file
|
||||
}
|
||||
try
|
||||
{
|
||||
if (!Files.exists(zipPath.getParent()))
|
||||
{
|
||||
Files.createDirectories(zipPath.getParent());
|
||||
}
|
||||
zfs = FileSystems.newFileSystem(URI.create("jar:" + zipPath.toUri()), env);
|
||||
}
|
||||
catch (final FileSystemNotFoundException | IOException e)
|
||||
{
|
||||
CarpetScriptServer.LOG.warn("Exception when opening zip file", e);
|
||||
throw new ThrowStatement("Unable to open zip file: "+zipContainer, Throwables.IO_EXCEPTION);
|
||||
throw new ThrowStatement("Unable to open zip file: " + zipContainer, Throwables.IO_EXCEPTION);
|
||||
}
|
||||
}
|
||||
return zfs.getPath(resource+(isFolder?"/":type.extension));
|
||||
return zfs.getPath(resource + (isFolder ? "/" : type.extension));
|
||||
}
|
||||
}
|
||||
|
||||
private Path moduleRootPath(Module module)
|
||||
private Path moduleRootPath(final Module module)
|
||||
{
|
||||
if (!isShared && module == null) return null;
|
||||
return resolve(isShared?"shared":module.name()+".data");
|
||||
return !isShared && module == null
|
||||
? null
|
||||
: resolve(isShared ? "shared" : module.name() + ".data");
|
||||
}
|
||||
|
||||
public String getDisplayPath()
|
||||
{
|
||||
return (isShared?"shared/":"")+(zipContainer!=null?zipContainer+"/":"")+resource+type.extension;
|
||||
return (isShared ? "shared/" : "") + (zipContainer != null ? zipContainer + "/" : "") + resource + type.extension;
|
||||
}
|
||||
|
||||
private String getDescriptor(Module module, String res)
|
||||
private String getDescriptor(final Module module, final String res)
|
||||
{
|
||||
if (isShared)
|
||||
{
|
||||
return res.isEmpty()?"shared":"shared/"+res;
|
||||
return res.isEmpty() ? "shared" : "shared/" + res;
|
||||
}
|
||||
if (module != null) // appdata
|
||||
{
|
||||
return module.name()+".data"+(res==null || res.isEmpty()?"":"/"+res);
|
||||
return module.name() + ".data" + (res == null || res.isEmpty() ? "" : "/" + res);
|
||||
}
|
||||
throw new InternalExpressionException("Invalid file descriptor: "+res);
|
||||
throw new InternalExpressionException("Invalid file descriptor: " + res);
|
||||
}
|
||||
|
||||
|
||||
public boolean findPathAndApply(Module module, Consumer<Path> action)
|
||||
public boolean findPathAndApply(final Module module, final Consumer<Path> action)
|
||||
{
|
||||
try { synchronized (writeIOSync)
|
||||
try
|
||||
{
|
||||
Path dataFile = toPath(module);//, resourceName, supportedTypes.get(type), isShared);
|
||||
if (dataFile == null) return false;
|
||||
createPaths(dataFile);
|
||||
action.accept(dataFile);
|
||||
} }
|
||||
synchronized (writeIOSync)
|
||||
{
|
||||
final Path dataFile = toPath(module);//, resourceName, supportedTypes.get(type), isShared);
|
||||
if (dataFile == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
createPaths(dataFile);
|
||||
action.accept(dataFile);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
close();
|
||||
@@ -242,83 +293,109 @@ public class FileArgument
|
||||
return true;
|
||||
}
|
||||
|
||||
public Stream<Path> listFiles(Module module)
|
||||
public Stream<Path> listFiles(final Module module)
|
||||
{
|
||||
Path dir = toPath(module);
|
||||
if (dir == null || !Files.exists(dir)) return null;
|
||||
String ext = type.extension;
|
||||
final Path dir = toPath(module);
|
||||
if (dir == null || !Files.exists(dir))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
final String ext = type.extension;
|
||||
try
|
||||
{
|
||||
return Files.list(dir).filter(path -> (type==Type.FOLDER)
|
||||
?Files.isDirectory(path)
|
||||
:(Files.isRegularFile(path) && path.toString().endsWith(ext))
|
||||
return Files.list(dir).filter(path -> (type == Type.FOLDER)
|
||||
? Files.isDirectory(path)
|
||||
: (Files.isRegularFile(path) && path.toString().endsWith(ext))
|
||||
);
|
||||
}
|
||||
catch (IOException ignored)
|
||||
catch (final IOException ignored)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public Stream<String> listFolder(Module module)
|
||||
public Stream<String> listFolder(final Module module)
|
||||
{
|
||||
Stream<String> strings;
|
||||
try (Stream<Path> result = listFiles(module)) { synchronized (writeIOSync)
|
||||
try (final Stream<Path> 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;
|
||||
// need to evaluate the stream before exiting try-with-resources else there'll be no data to stream
|
||||
strings = (zipContainer == null)
|
||||
? result.map(p -> rootPath.relativize(p).toString().replaceAll("[\\\\/]+", "/")).toList().stream()
|
||||
: result.map(p -> (zipComponent + '/'+ p.toString()).replaceAll("[\\\\/]+", "/")).toList().stream();
|
||||
} }
|
||||
synchronized (writeIOSync)
|
||||
{
|
||||
if (result == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
final Path rootPath = moduleRootPath(module);
|
||||
if (rootPath == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
final String zipComponent = (zipContainer != null) ? rootPath.relativize(zipPath).toString() : null;
|
||||
// need to evaluate the stream before exiting try-with-resources else there'll be no data to stream
|
||||
strings = (zipContainer == null)
|
||||
? result.map(p -> rootPath.relativize(p).toString().replaceAll("[\\\\/]+", "/")).toList().stream()
|
||||
: result.map(p -> (zipComponent + '/' + p.toString()).replaceAll("[\\\\/]+", "/")).toList().stream();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
close();
|
||||
}
|
||||
if (type == Type.FOLDER)
|
||||
// java 8 paths are inconsistent. in java 16 they all should not have trailing slashes
|
||||
return strings.map(s -> s.endsWith("/")?s.substring(0, s.length()-1):s);
|
||||
return strings.map(FilenameUtils::removeExtension);
|
||||
// java 8 paths are inconsistent. in java 16 they all should not have trailing slashes
|
||||
return type == Type.FOLDER
|
||||
? strings.map(s -> s.endsWith("/") ? s.substring(0, s.length() - 1) : s)
|
||||
: strings.map(FilenameUtils::removeExtension);
|
||||
}
|
||||
|
||||
private void createPaths(Path file)
|
||||
private void createPaths(final Path file)
|
||||
{
|
||||
try {
|
||||
try
|
||||
{
|
||||
if ((zipContainer == null || file.getParent() != null) &&
|
||||
!Files.exists(file.getParent()) &&
|
||||
Files.createDirectories(file.getParent()) == null
|
||||
) throw new IOException();
|
||||
} catch (IOException e) {
|
||||
)
|
||||
{
|
||||
throw new IOException();
|
||||
}
|
||||
}
|
||||
catch (final IOException e)
|
||||
{
|
||||
CarpetScriptServer.LOG.warn("IOException when creating paths", e);
|
||||
throw new ThrowStatement("Unable to create paths for "+file.toString(), Throwables.IO_EXCEPTION);
|
||||
throw new ThrowStatement("Unable to create paths for " + file, Throwables.IO_EXCEPTION);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public boolean appendToTextFile(Module module, List<String> message)
|
||||
public boolean appendToTextFile(final Module module, final List<String> message)
|
||||
{
|
||||
try { synchronized (writeIOSync)
|
||||
try
|
||||
{
|
||||
Path dataFile = toPath(module);//, resourceName, supportedTypes.get(type), isShared);
|
||||
if (dataFile == null) return false;
|
||||
createPaths(dataFile);
|
||||
OutputStream out = Files.newOutputStream(dataFile, StandardOpenOption.APPEND, StandardOpenOption.CREATE);
|
||||
try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out, StandardCharsets.UTF_8)))
|
||||
synchronized (writeIOSync)
|
||||
{
|
||||
for (String line: message)
|
||||
final Path dataFile = toPath(module);
|
||||
if (dataFile == null)
|
||||
{
|
||||
writer.append(line);
|
||||
if (type == Type.TEXT) writer.newLine();
|
||||
return false;
|
||||
}
|
||||
createPaths(dataFile);
|
||||
final OutputStream out = Files.newOutputStream(dataFile, StandardOpenOption.APPEND, StandardOpenOption.CREATE);
|
||||
try (final BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(out, StandardCharsets.UTF_8)))
|
||||
{
|
||||
for (final String line : message)
|
||||
{
|
||||
writer.append(line);
|
||||
if (type == Type.TEXT)
|
||||
{
|
||||
writer.newLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} }
|
||||
catch (IOException e)
|
||||
}
|
||||
catch (final IOException e)
|
||||
{
|
||||
CarpetScriptServer.LOG.warn("IOException when appending to text file", e);
|
||||
throw new ThrowStatement("Error when writing to the file: "+e, Throwables.IO_EXCEPTION);
|
||||
throw new ThrowStatement("Error when writing to the file: " + e, Throwables.IO_EXCEPTION);
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -327,76 +404,91 @@ public class FileArgument
|
||||
return true;
|
||||
}
|
||||
|
||||
public Tag getNbtData(Module module) // aka getData
|
||||
public Tag getNbtData(final Module module) // aka getData
|
||||
{
|
||||
try { synchronized (writeIOSync) {
|
||||
Path dataFile = toPath(module);
|
||||
if (dataFile == null || !Files.exists(dataFile)) return null;
|
||||
return readTag(dataFile);
|
||||
} }
|
||||
finally {
|
||||
try
|
||||
{
|
||||
synchronized (writeIOSync)
|
||||
{
|
||||
final 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)
|
||||
public static Tag readTag(final Path path)
|
||||
{
|
||||
try
|
||||
{
|
||||
return NbtIo.readCompressed(Files.newInputStream(path));
|
||||
}
|
||||
catch (IOException e)
|
||||
catch (final 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))))
|
||||
try (final DataInputStream in = new DataInputStream(new BufferedInputStream(Files.newInputStream(path))))
|
||||
{
|
||||
return NbtIo.read(in);
|
||||
}
|
||||
catch (IOException ioException)
|
||||
catch (final 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))))
|
||||
try (final DataInputStream dataInputStream = new DataInputStream(new BufferedInputStream(Files.newInputStream(path))))
|
||||
{
|
||||
byte byte_1 = dataInput_1.readByte();
|
||||
if (byte_1 == 0)
|
||||
final byte b = dataInputStream.readByte();
|
||||
if (b == 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
dataInput_1.readUTF();
|
||||
return TagTypes.getType(byte_1).load(dataInput_1, 0, NbtAccounter.UNLIMITED);
|
||||
dataInputStream.readUTF();
|
||||
return TagTypes.getType(b).load(dataInputStream, 0, NbtAccounter.UNLIMITED);
|
||||
}
|
||||
}
|
||||
catch (IOException secondIO)
|
||||
catch (final IOException secondIO)
|
||||
{
|
||||
CarpetScriptServer.LOG.warn("IOException when trying to read nbt file, something may have gone wrong with the fs", e);
|
||||
CarpetScriptServer.LOG.warn("", ioException);
|
||||
CarpetScriptServer.LOG.warn("", secondIO);
|
||||
throw new ThrowStatement("Not a valid NBT tag in "+path.toString(), Throwables.NBT_ERROR);
|
||||
throw new ThrowStatement("Not a valid NBT tag in " + path, Throwables.NBT_ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (ReportedException e)
|
||||
catch (final ReportedException e)
|
||||
{
|
||||
throw new ThrowStatement("Error when reading NBT file "+path.toString(), Throwables.NBT_ERROR);
|
||||
throw new ThrowStatement("Error when reading NBT file " + path.toString(), Throwables.NBT_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean saveNbtData(Module module, Tag tag) // aka saveData
|
||||
public boolean saveNbtData(final Module module, final Tag tag) // aka saveData
|
||||
{
|
||||
try { synchronized (writeIOSync) {
|
||||
Path dataFile = toPath(module);
|
||||
if (dataFile == null) return false;
|
||||
createPaths(dataFile);
|
||||
return writeTagDisk(tag, dataFile, zipContainer != null);
|
||||
} }
|
||||
try
|
||||
{
|
||||
synchronized (writeIOSync)
|
||||
{
|
||||
final Path dataFile = toPath(module);
|
||||
if (dataFile == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
createPaths(dataFile);
|
||||
return writeTagDisk(tag, dataFile, zipContainer != null);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
close();
|
||||
@@ -404,9 +496,9 @@ public class FileArgument
|
||||
}
|
||||
|
||||
//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)
|
||||
public static boolean writeTagDisk(final Tag tag, Path path, final boolean zipped)
|
||||
{
|
||||
Path original = path;
|
||||
final Path original = path;
|
||||
try
|
||||
{
|
||||
if (!zipped)
|
||||
@@ -415,19 +507,19 @@ public class FileArgument
|
||||
Files.deleteIfExists(path);
|
||||
}
|
||||
|
||||
if (tag_1 instanceof CompoundTag)
|
||||
if (tag instanceof final CompoundTag cTag)
|
||||
{
|
||||
NbtIo.writeCompressed((CompoundTag) tag_1, Files.newOutputStream(path));
|
||||
NbtIo.writeCompressed(cTag, Files.newOutputStream(path));
|
||||
}
|
||||
else
|
||||
{
|
||||
try (DataOutputStream dataOutputStream_1 = new DataOutputStream(Files.newOutputStream(path)))
|
||||
try (final DataOutputStream dataOutputStream = new DataOutputStream(Files.newOutputStream(path)))
|
||||
{
|
||||
dataOutputStream_1.writeByte(tag_1.getId());
|
||||
if (tag_1.getId() != 0)
|
||||
dataOutputStream.writeByte(tag.getId());
|
||||
if (tag.getId() != 0)
|
||||
{
|
||||
dataOutputStream_1.writeUTF("");
|
||||
tag_1.write(dataOutputStream_1);
|
||||
dataOutputStream.writeUTF("");
|
||||
tag.write(dataOutputStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -438,26 +530,31 @@ public class FileArgument
|
||||
}
|
||||
return true;
|
||||
}
|
||||
catch (IOException e)
|
||||
catch (final IOException e)
|
||||
{
|
||||
CarpetScriptServer.LOG.warn("IO Exception when writing nbt file", e);
|
||||
throw new ThrowStatement("Unable to write tag to "+original.toString(), Throwables.IO_EXCEPTION);
|
||||
throw new ThrowStatement("Unable to write tag to " + original, Throwables.IO_EXCEPTION);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean dropExistingFile(Module module)
|
||||
public boolean dropExistingFile(final Module module)
|
||||
{
|
||||
|
||||
try { synchronized (writeIOSync)
|
||||
try
|
||||
{
|
||||
Path dataFile = toPath(module);
|
||||
if (dataFile == null) return false;
|
||||
return Files.deleteIfExists(dataFile);
|
||||
} }
|
||||
catch (IOException e)
|
||||
synchronized (writeIOSync)
|
||||
{
|
||||
final Path dataFile = toPath(module);
|
||||
if (dataFile == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return Files.deleteIfExists(dataFile);
|
||||
}
|
||||
}
|
||||
catch (final IOException e)
|
||||
{
|
||||
CarpetScriptServer.LOG.warn("IOException when removing file", e);
|
||||
throw new ThrowStatement("Error while removing file: "+getDisplayPath(), Throwables.IO_EXCEPTION);
|
||||
throw new ThrowStatement("Error while removing file: " + getDisplayPath(), Throwables.IO_EXCEPTION);
|
||||
}
|
||||
finally
|
||||
{
|
||||
@@ -465,69 +562,95 @@ public class FileArgument
|
||||
}
|
||||
}
|
||||
|
||||
public List<String> listFile(Module module)
|
||||
public List<String> listFile(final Module module)
|
||||
{
|
||||
try { synchronized (writeIOSync) {
|
||||
Path dataFile = toPath(module);
|
||||
if (dataFile == null) return null;
|
||||
if (!Files.exists(dataFile)) return null;
|
||||
return listFileContent(dataFile);
|
||||
} }
|
||||
finally {
|
||||
try
|
||||
{
|
||||
synchronized (writeIOSync)
|
||||
{
|
||||
final 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)
|
||||
public static List<String> listFileContent(final Path filePath)
|
||||
{
|
||||
try (BufferedReader reader = Files.newBufferedReader(filePath, StandardCharsets.UTF_8)) {
|
||||
List<String> result = new ArrayList<>();
|
||||
for (;;) {
|
||||
String line = reader.readLine();
|
||||
try (final BufferedReader reader = Files.newBufferedReader(filePath, StandardCharsets.UTF_8))
|
||||
{
|
||||
final List<String> result = new ArrayList<>();
|
||||
for (; ; )
|
||||
{
|
||||
final String line = reader.readLine();
|
||||
if (line == null)
|
||||
{
|
||||
break;
|
||||
result.add(line.replaceAll("[\n\r]+",""));
|
||||
}
|
||||
result.add(line.replaceAll("[\n\r]+", ""));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
catch (IOException e)
|
||||
catch (final IOException e)
|
||||
{
|
||||
CarpetScriptServer.LOG.warn("IOException when reading text file", e);
|
||||
throw new ThrowStatement("Failed to read text file "+filePath.toString(), Throwables.IO_EXCEPTION);
|
||||
throw new ThrowStatement("Failed to read text file " + filePath, 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 {
|
||||
public JsonElement readJsonFile(final Module module)
|
||||
{
|
||||
try
|
||||
{
|
||||
synchronized (writeIOSync)
|
||||
{
|
||||
final Path dataFile = toPath(module);
|
||||
if (dataFile == null || !Files.exists(dataFile))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return readJsonContent(dataFile);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
public static JsonElement readJsonContent(Path filePath)
|
||||
public static JsonElement readJsonContent(final Path filePath)
|
||||
{
|
||||
try (BufferedReader reader = Files.newBufferedReader(filePath, StandardCharsets.UTF_8))
|
||||
try (final BufferedReader reader = Files.newBufferedReader(filePath, StandardCharsets.UTF_8))
|
||||
{
|
||||
return JsonParser.parseReader(reader);
|
||||
}
|
||||
catch (JsonParseException e)
|
||||
catch (final JsonParseException e)
|
||||
{
|
||||
Throwable exc = e;
|
||||
if(e.getCause() != null)
|
||||
if (e.getCause() != null)
|
||||
{
|
||||
exc = e.getCause();
|
||||
}
|
||||
throw new ThrowStatement(MapValue.wrap(Map.of(
|
||||
StringValue.of("error"), StringValue.of(exc.getMessage()),
|
||||
StringValue.of("path"), StringValue.of(filePath.toString())
|
||||
)), Throwables.JSON_ERROR);
|
||||
}
|
||||
catch (IOException e)
|
||||
catch (final IOException e)
|
||||
{
|
||||
CarpetScriptServer.LOG.warn("IOException when reading JSON file", e);
|
||||
throw new ThrowStatement("Failed to read json file content "+filePath.toString(), Throwables.IO_EXCEPTION);
|
||||
throw new ThrowStatement("Failed to read json file content " + filePath, Throwables.IO_EXCEPTION);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ public class FunctionArgument extends Argument
|
||||
public FunctionValue function;
|
||||
public List<Value> args;
|
||||
|
||||
private FunctionArgument(FunctionValue function, int offset, List<Value> args)
|
||||
private FunctionArgument(final FunctionValue function, final int offset, final List<Value> args)
|
||||
{
|
||||
super(offset);
|
||||
this.function = function;
|
||||
@@ -27,10 +27,10 @@ public class FunctionArgument extends Argument
|
||||
}
|
||||
|
||||
/**
|
||||
* @param c context
|
||||
* @param module module
|
||||
* @param params list of params
|
||||
* @param offset offset where to start looking for functional argument
|
||||
* @param c context
|
||||
* @param module module
|
||||
* @param params list of params
|
||||
* @param offset offset where to start looking for functional argument
|
||||
* @param allowNone none indicates no function present, otherwise it will croak
|
||||
* @param checkArgs whether the caller expects trailing parameters to fully resolve function argument list
|
||||
* if not - argument count check will not be performed and its up to the caller to verify
|
||||
@@ -38,63 +38,85 @@ public class FunctionArgument extends Argument
|
||||
* @return argument data
|
||||
*/
|
||||
public static FunctionArgument findIn(
|
||||
Context c,
|
||||
Module module,
|
||||
List<Value> params,
|
||||
int offset,
|
||||
boolean allowNone,
|
||||
boolean checkArgs)
|
||||
final Context c,
|
||||
final Module module,
|
||||
final List<Value> params,
|
||||
final int offset,
|
||||
final boolean allowNone,
|
||||
final boolean checkArgs)
|
||||
{
|
||||
Value functionValue = params.get(offset);
|
||||
if (functionValue.isNull())
|
||||
{
|
||||
if (allowNone) return new FunctionArgument(null, offset+1, Collections.emptyList());
|
||||
if (allowNone)
|
||||
{
|
||||
return new FunctionArgument(null, offset + 1, Collections.emptyList());
|
||||
}
|
||||
throw new InternalExpressionException("function argument cannot be null");
|
||||
}
|
||||
if (!(functionValue instanceof FunctionValue))
|
||||
{
|
||||
String name = functionValue.getString();
|
||||
final String name = functionValue.getString();
|
||||
functionValue = c.host.getAssertFunction(module, name);
|
||||
}
|
||||
FunctionValue fun = (FunctionValue)functionValue;
|
||||
int argsize = fun.getArguments().size();
|
||||
final FunctionValue fun = (FunctionValue) functionValue;
|
||||
final int argsize = fun.getArguments().size();
|
||||
if (checkArgs)
|
||||
{
|
||||
int extraargs = params.size() - argsize - offset - 1;
|
||||
final int extraargs = params.size() - argsize - offset - 1;
|
||||
if (extraargs < 0)
|
||||
{
|
||||
throw new InternalExpressionException("Function " + fun.getPrettyString() + " requires at least " + fun.getArguments().size() + " arguments");
|
||||
}
|
||||
if (extraargs > 0 && fun.getVarArgs() == null)
|
||||
{
|
||||
throw new InternalExpressionException("Function " + fun.getPrettyString() + " requires " + fun.getArguments().size() + " arguments");
|
||||
}
|
||||
}
|
||||
final List<Value> lvargs = new ArrayList<>();
|
||||
for (int i = offset + 1, mx = params.size(); i < mx; i++)
|
||||
{
|
||||
lvargs.add(params.get(i));
|
||||
}
|
||||
List<Value> lvargs = new ArrayList<>();
|
||||
for (int i = offset+1, mx = params.size(); i < mx; i++) lvargs.add(params.get(i));
|
||||
return new FunctionArgument(fun, offset + 1 + argsize, lvargs);
|
||||
}
|
||||
|
||||
public static FunctionArgument fromCommandSpec(ScriptHost host, Value funSpec) throws CommandSyntaxException {
|
||||
FunctionValue function;
|
||||
public static FunctionArgument fromCommandSpec(final ScriptHost host, Value funSpec) throws CommandSyntaxException
|
||||
{
|
||||
final FunctionValue function;
|
||||
List<Value> args = Collections.emptyList();
|
||||
if (!(funSpec instanceof ListValue))
|
||||
{
|
||||
funSpec = ListValue.of(funSpec);
|
||||
List<Value> params = ((ListValue) funSpec).getItems();
|
||||
if (params.isEmpty()) throw CommandArgument.error("Function has empty spec");
|
||||
Value first = params.get(0);
|
||||
}
|
||||
final List<Value> params = ((ListValue) funSpec).getItems();
|
||||
if (params.isEmpty())
|
||||
{
|
||||
throw CommandArgument.error("Function has empty spec");
|
||||
}
|
||||
final Value first = params.get(0);
|
||||
if (first instanceof FunctionValue)
|
||||
{
|
||||
function = (FunctionValue)first;
|
||||
function = (FunctionValue) first;
|
||||
}
|
||||
else
|
||||
{
|
||||
String name = first.getString();
|
||||
final String name = first.getString();
|
||||
function = host.getFunction(name);
|
||||
if (function == null) throw CommandArgument.error("Function "+name+" is not defined yet");
|
||||
if (function == null)
|
||||
{
|
||||
throw CommandArgument.error("Function " + name + " is not defined yet");
|
||||
}
|
||||
}
|
||||
if (params.size() > 1)
|
||||
{
|
||||
args = params.subList(1, params.size());
|
||||
}
|
||||
if (params.size() > 1) args = params.subList(1,params.size());
|
||||
|
||||
return new FunctionArgument(function, 0, args);
|
||||
}
|
||||
|
||||
public List<Value> checkedArgs() {
|
||||
public List<Value> checkedArgs()
|
||||
{
|
||||
function.checkArgs(args.size());
|
||||
return args;
|
||||
}
|
||||
|
||||
@@ -6,9 +6,11 @@ import carpet.script.value.EntityValue;
|
||||
import carpet.script.value.ListValue;
|
||||
import carpet.script.value.NumericValue;
|
||||
import carpet.script.value.Value;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
@@ -19,14 +21,16 @@ public class Vector3Argument extends Argument
|
||||
public final double pitch;
|
||||
public boolean fromBlock = false;
|
||||
public Entity entity = null;
|
||||
private Vector3Argument(Vec3 v, int o)
|
||||
|
||||
private Vector3Argument(final Vec3 v, final int o)
|
||||
{
|
||||
super(o);
|
||||
this.vec = v;
|
||||
this.yaw = 0.0D;
|
||||
this.pitch = 0.0D;
|
||||
}
|
||||
private Vector3Argument(Vec3 v, int o, double y, double p)
|
||||
|
||||
private Vector3Argument(final Vec3 v, final int o, final double y, final double p)
|
||||
{
|
||||
super(o);
|
||||
this.vec = v;
|
||||
@@ -40,52 +44,53 @@ public class Vector3Argument extends Argument
|
||||
return this;
|
||||
}
|
||||
|
||||
private Vector3Argument withEntity(Entity e)
|
||||
private Vector3Argument withEntity(final Entity e)
|
||||
{
|
||||
entity = e;
|
||||
return this;
|
||||
}
|
||||
|
||||
public static Vector3Argument findIn(List<Value> params, int offset)
|
||||
public static Vector3Argument findIn(final List<Value> params, final int offset)
|
||||
{
|
||||
return findIn(params, offset, false, false);
|
||||
}
|
||||
|
||||
public static Vector3Argument findIn(List<Value> params, int offset, boolean optionalDirection, boolean optionalEntity) {
|
||||
public static Vector3Argument findIn(final List<Value> params, final int offset, final boolean optionalDirection, final boolean optionalEntity)
|
||||
{
|
||||
return findIn(params.listIterator(offset), offset, optionalDirection, optionalEntity);
|
||||
}
|
||||
|
||||
public static Vector3Argument findIn(Iterator<Value> params, int offset, boolean optionalDirection, boolean optionalEntity)
|
||||
public static Vector3Argument findIn(final Iterator<Value> params, final int offset, final boolean optionalDirection, final boolean optionalEntity)
|
||||
{
|
||||
try
|
||||
{
|
||||
Value v1 = params.next();
|
||||
final Value v1 = params.next();
|
||||
if (v1 instanceof BlockValue)
|
||||
{
|
||||
return (new Vector3Argument(Vec3.atCenterOf(((BlockValue) v1).getPos()), 1+offset)).fromBlock();
|
||||
return (new Vector3Argument(Vec3.atCenterOf(((BlockValue) v1).getPos()), 1 + offset)).fromBlock();
|
||||
}
|
||||
if (optionalEntity && v1 instanceof EntityValue)
|
||||
{
|
||||
Entity e = ((EntityValue) v1).getEntity();
|
||||
return new Vector3Argument(e.position(), 1+offset).withEntity(e);
|
||||
final Entity e = ((EntityValue) v1).getEntity();
|
||||
return new Vector3Argument(e.position(), 1 + offset).withEntity(e);
|
||||
}
|
||||
if (v1 instanceof ListValue)
|
||||
{
|
||||
List<Value> args = ((ListValue) v1).getItems();
|
||||
Vec3 pos = new Vec3(
|
||||
final List<Value> args = ((ListValue) v1).getItems();
|
||||
final Vec3 pos = new Vec3(
|
||||
NumericValue.asNumber(args.get(0)).getDouble(),
|
||||
NumericValue.asNumber(args.get(1)).getDouble(),
|
||||
NumericValue.asNumber(args.get(2)).getDouble());
|
||||
double yaw = 0.0D;
|
||||
double pitch = 0.0D;
|
||||
if (args.size()>3 && optionalDirection)
|
||||
if (args.size() > 3 && optionalDirection)
|
||||
{
|
||||
yaw = NumericValue.asNumber(args.get(3)).getDouble();
|
||||
pitch = NumericValue.asNumber(args.get(4)).getDouble();
|
||||
}
|
||||
return new Vector3Argument(pos,offset+1, yaw, pitch);
|
||||
return new Vector3Argument(pos, offset + 1, yaw, pitch);
|
||||
}
|
||||
Vec3 pos = new Vec3(
|
||||
final Vec3 pos = new Vec3(
|
||||
NumericValue.asNumber(v1).getDouble(),
|
||||
NumericValue.asNumber(params.next()).getDouble(),
|
||||
NumericValue.asNumber(params.next()).getDouble());
|
||||
@@ -99,9 +104,9 @@ public class Vector3Argument extends Argument
|
||||
eatenLength = 5;
|
||||
}
|
||||
|
||||
return new Vector3Argument(pos, offset+eatenLength, yaw, pitch);
|
||||
return new Vector3Argument(pos, offset + eatenLength, yaw, pitch);
|
||||
}
|
||||
catch (IndexOutOfBoundsException | NoSuchElementException e)
|
||||
catch (final IndexOutOfBoundsException | NoSuchElementException e)
|
||||
{
|
||||
throw new InternalExpressionException("Position argument should be defined either by three coordinates (a triple or by three arguments), or a positioned block value");
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -5,11 +5,13 @@ import carpet.script.value.FunctionValue;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.mojang.brigadier.builder.ArgumentBuilder;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
|
||||
import static net.minecraft.commands.Commands.literal;
|
||||
@@ -20,41 +22,46 @@ public class CommandToken implements Comparable<CommandToken>
|
||||
public boolean isArgument;
|
||||
public CommandArgument type;
|
||||
|
||||
private CommandToken(String surface, CommandArgument type )
|
||||
private CommandToken(final String surface, final CommandArgument type)
|
||||
{
|
||||
this.surface = surface;
|
||||
this.type = type;
|
||||
isArgument = type != null;
|
||||
}
|
||||
|
||||
public static CommandToken getToken(String source, CarpetScriptHost host)
|
||||
public static CommandToken getToken(String source, final CarpetScriptHost host)
|
||||
{
|
||||
// todo add more type checking and return null
|
||||
if (!source.startsWith("<"))
|
||||
{
|
||||
if (!source.matches("[_a-zA-Z]+")) return null;
|
||||
return new CommandToken(source, null);
|
||||
return source.matches("[_a-zA-Z]+") ? new CommandToken(source, null) : null;
|
||||
}
|
||||
source = source.substring(1, source.length()-1);
|
||||
if (!source.matches("[_a-zA-Z]+")) return null;
|
||||
CommandArgument arg = CommandArgument.getTypeForArgument(source, host);
|
||||
return new CommandToken(source, arg);
|
||||
source = source.substring(1, source.length() - 1);
|
||||
return source.matches("[_a-zA-Z]+") ? new CommandToken(source, CommandArgument.getTypeForArgument(source, host)) : null;
|
||||
}
|
||||
|
||||
public static List<CommandToken> parseSpec(String spec, CarpetScriptHost host) throws CommandSyntaxException
|
||||
public static List<CommandToken> parseSpec(String spec, final CarpetScriptHost host) throws CommandSyntaxException
|
||||
{
|
||||
spec = spec.trim();
|
||||
if (spec.isEmpty()) return Collections.emptyList();
|
||||
List<CommandToken> elements = new ArrayList<>();
|
||||
HashSet<String> seenArgs = new HashSet<>();
|
||||
for (String el: spec.split("\\s+"))
|
||||
if (spec.isEmpty())
|
||||
{
|
||||
CommandToken tok = CommandToken.getToken(el, host);
|
||||
if (tok == null) throw CommandArgument.error("Unrecognized command token: "+ el);
|
||||
return Collections.emptyList();
|
||||
}
|
||||
final List<CommandToken> elements = new ArrayList<>();
|
||||
final HashSet<String> seenArgs = new HashSet<>();
|
||||
for (final String el : spec.split("\\s+"))
|
||||
{
|
||||
final CommandToken tok = CommandToken.getToken(el, host);
|
||||
if (tok == null)
|
||||
{
|
||||
throw CommandArgument.error("Unrecognized command token: " + el);
|
||||
}
|
||||
if (tok.isArgument)
|
||||
{
|
||||
if (seenArgs.contains(tok.surface))
|
||||
throw CommandArgument.error("Repeated command argument: "+tok.surface+", for '"+spec+"'. Argument names have to be unique");
|
||||
{
|
||||
throw CommandArgument.error("Repeated command argument: " + tok.surface + ", for '" + spec + "'. Argument names have to be unique");
|
||||
}
|
||||
seenArgs.add(tok.surface);
|
||||
}
|
||||
elements.add(tok);
|
||||
@@ -62,28 +69,33 @@ public class CommandToken implements Comparable<CommandToken>
|
||||
return elements;
|
||||
}
|
||||
|
||||
public static String specFromSignature(FunctionValue function)
|
||||
public static String specFromSignature(final FunctionValue function)
|
||||
{
|
||||
List<String> tokens = Lists.newArrayList(function.getString());
|
||||
for (String arg : function.getArguments()) tokens.add("<"+arg+">");
|
||||
final List<String> tokens = Lists.newArrayList(function.getString());
|
||||
for (final String arg : function.getArguments())
|
||||
{
|
||||
tokens.add("<" + arg + ">");
|
||||
}
|
||||
return String.join(" ", tokens);
|
||||
}
|
||||
|
||||
|
||||
public ArgumentBuilder<CommandSourceStack, ?> getCommandNode(CarpetScriptHost host) throws CommandSyntaxException
|
||||
public ArgumentBuilder<CommandSourceStack, ?> getCommandNode(final CarpetScriptHost host) throws CommandSyntaxException
|
||||
{
|
||||
if (isArgument)
|
||||
return CommandArgument.argumentNode(surface, host);
|
||||
return literal(surface);
|
||||
|
||||
return isArgument ? CommandArgument.argumentNode(surface, host) : literal(surface);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o)
|
||||
public boolean equals(final Object o)
|
||||
{
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
CommandToken that = (CommandToken) o;
|
||||
if (this == o)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
final CommandToken that = (CommandToken) o;
|
||||
return surface.equals(that.surface) &&
|
||||
Objects.equals(type, that.type);
|
||||
}
|
||||
@@ -95,10 +107,16 @@ public class CommandToken implements Comparable<CommandToken>
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(CommandToken o)
|
||||
public int compareTo(final CommandToken o)
|
||||
{
|
||||
if (isArgument && !o.isArgument) return 1;
|
||||
if (!isArgument && o.isArgument) return -1;
|
||||
if (isArgument && !o.isArgument)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
if (!isArgument && o.isArgument)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
return surface.compareTo(o.surface);
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import carpet.script.value.Value;
|
||||
|
||||
public class BreakStatement extends ExitStatement
|
||||
{
|
||||
public BreakStatement(Value value)
|
||||
public BreakStatement(final Value value)
|
||||
{
|
||||
super(value);
|
||||
}
|
||||
|
||||
@@ -2,25 +2,28 @@ package carpet.script.exception;
|
||||
|
||||
import carpet.script.value.FunctionValue;
|
||||
import carpet.utils.Messenger;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
|
||||
public class CarpetExpressionException extends StacklessRuntimeException implements ResolvedException
|
||||
{
|
||||
public final List<FunctionValue> stack;
|
||||
|
||||
public CarpetExpressionException(String message, List<FunctionValue> stack)
|
||||
public CarpetExpressionException(final String message, final List<FunctionValue> stack)
|
||||
{
|
||||
super(message);
|
||||
this.stack = stack;
|
||||
}
|
||||
public void printStack(CommandSourceStack source)
|
||||
|
||||
public void printStack(final CommandSourceStack source)
|
||||
{
|
||||
if (stack != null && !stack.isEmpty())
|
||||
{
|
||||
for (FunctionValue fun : stack)
|
||||
for (final FunctionValue fun : stack)
|
||||
{
|
||||
Messenger.m(source, "e ... in "+fun.fullName(), "e /"+(fun.getToken().lineno+1)+":"+(fun.getToken().linepos+1));
|
||||
Messenger.m(source, "e ... in " + fun.fullName(), "e /" + (fun.getToken().lineno + 1) + ":" + (fun.getToken().linepos + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import carpet.script.value.Value;
|
||||
|
||||
public class ContinueStatement extends ExitStatement
|
||||
{
|
||||
public ContinueStatement(Value value)
|
||||
public ContinueStatement(final Value value)
|
||||
{
|
||||
super(value);
|
||||
}
|
||||
|
||||
@@ -6,7 +6,8 @@ import carpet.script.value.Value;
|
||||
public class ExitStatement extends StacklessRuntimeException
|
||||
{
|
||||
public final Value retval;
|
||||
public ExitStatement(Value value)
|
||||
|
||||
public ExitStatement(final Value value)
|
||||
{
|
||||
retval = value;
|
||||
}
|
||||
|
||||
@@ -19,20 +19,23 @@ public class ExpressionException extends StacklessRuntimeException implements Re
|
||||
public final List<FunctionValue> stack = new ArrayList<>();
|
||||
private final Supplier<String> lazyMessage;
|
||||
private String cachedMessage = null;
|
||||
public static void prepareForDoom(){
|
||||
|
||||
public static void prepareForDoom()
|
||||
{
|
||||
Messenger.c("foo bar");
|
||||
}
|
||||
|
||||
public ExpressionException(Context c, Expression e, String message)
|
||||
public ExpressionException(final Context c, final Expression e, final String message)
|
||||
{
|
||||
this(c, e, Tokenizer.Token.NONE, message);
|
||||
}
|
||||
|
||||
public ExpressionException(Context c, Expression e, Tokenizer.Token t, String message)
|
||||
public ExpressionException(final Context c, final Expression e, final Tokenizer.Token t, final String message)
|
||||
{
|
||||
this(c, e, t, message, Collections.emptyList());
|
||||
}
|
||||
public ExpressionException(Context c, Expression e, Tokenizer.Token t, String message, List<FunctionValue> stack)
|
||||
|
||||
public ExpressionException(final Context c, final Expression e, final Tokenizer.Token t, final String message, final List<FunctionValue> stack)
|
||||
{
|
||||
super("Error");
|
||||
this.stack.addAll(stack);
|
||||
@@ -41,7 +44,7 @@ public class ExpressionException extends StacklessRuntimeException implements Re
|
||||
context = c;
|
||||
}
|
||||
|
||||
public ExpressionException(Context c, Expression e, Tokenizer.Token t, Supplier<String> messageSupplier, List<FunctionValue> stack)
|
||||
public ExpressionException(final Context c, final Expression e, final Tokenizer.Token t, final Supplier<String> messageSupplier, final List<FunctionValue> stack)
|
||||
{
|
||||
super("Error");
|
||||
this.stack.addAll(stack);
|
||||
@@ -50,13 +53,13 @@ public class ExpressionException extends StacklessRuntimeException implements Re
|
||||
context = c;
|
||||
}
|
||||
|
||||
private static List<String> makeError(Expression expr, /*Nullable*/Tokenizer.Token token, String errmessage)
|
||||
private static List<String> makeError(final Expression expr, /*Nullable*/final Tokenizer.Token token, String errmessage)
|
||||
{
|
||||
List<String> errMsg = new ArrayList<>();
|
||||
errmessage += expr.getModuleName() == null?"":(" in "+expr.getModuleName());
|
||||
final List<String> errMsg = new ArrayList<>();
|
||||
errmessage += expr.getModuleName() == null ? "" : (" in " + expr.getModuleName());
|
||||
if (token != null)
|
||||
{
|
||||
List<String> snippet = expr.getExpressionSnippet(token);
|
||||
final List<String> snippet = expr.getExpressionSnippet(token);
|
||||
errMsg.addAll(snippet);
|
||||
|
||||
if (snippet.size() != 1)
|
||||
@@ -72,11 +75,11 @@ public class ExpressionException extends StacklessRuntimeException implements Re
|
||||
return errMsg;
|
||||
}
|
||||
|
||||
synchronized static String makeMessage(Context c, Expression e, Tokenizer.Token t, String message) throws ExpressionException
|
||||
static synchronized String makeMessage(final Context c, final Expression e, final Tokenizer.Token t, final String message) throws ExpressionException
|
||||
{
|
||||
if (c.getErrorSnooper() != null)
|
||||
{
|
||||
List<String> alternative = c.getErrorSnooper().apply(e, t, c, message);
|
||||
final List<String> alternative = c.getErrorSnooper().apply(e, t, c, message);
|
||||
if (alternative != null)
|
||||
{
|
||||
return String.join("\n", alternative);
|
||||
@@ -84,12 +87,13 @@ public class ExpressionException extends StacklessRuntimeException implements Re
|
||||
}
|
||||
return String.join("\n", makeError(e, t, message));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
public String getMessage()
|
||||
{
|
||||
if (cachedMessage == null)
|
||||
{
|
||||
cachedMessage = lazyMessage.get();
|
||||
cachedMessage = lazyMessage.get();
|
||||
}
|
||||
return cachedMessage;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package carpet.script.exception;
|
||||
|
||||
public class IntegrityException extends RuntimeException
|
||||
{
|
||||
public IntegrityException(String message)
|
||||
public IntegrityException(final String message)
|
||||
{
|
||||
super(message);
|
||||
}
|
||||
|
||||
@@ -12,23 +12,25 @@ import java.util.List;
|
||||
public class InternalExpressionException extends StacklessRuntimeException
|
||||
{
|
||||
public List<FunctionValue> stack = new ArrayList<>();
|
||||
public InternalExpressionException(String message)
|
||||
|
||||
public InternalExpressionException(final String message)
|
||||
{
|
||||
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
|
||||
* {@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.
|
||||
* depending on the implementation.
|
||||
*/
|
||||
public ExpressionException promote(Context c, Expression e, Tokenizer.Token token)
|
||||
public ExpressionException promote(final Context c, final Expression e, final Tokenizer.Token token)
|
||||
{
|
||||
return new ExpressionException(c, e, token, getMessage(), stack);
|
||||
}
|
||||
|
||||
@@ -2,18 +2,18 @@ package carpet.script.exception;
|
||||
|
||||
/**
|
||||
* A Scarpet exception that indicates that load of the app has failed.
|
||||
*
|
||||
* <p>
|
||||
* Goes up the stack to the point of app load and gets caught there, preventing the app from loading with
|
||||
* the given message.
|
||||
*
|
||||
*/
|
||||
public class LoadException extends RuntimeException implements ResolvedException {
|
||||
|
||||
public LoadException() {
|
||||
public class LoadException extends RuntimeException implements ResolvedException
|
||||
{
|
||||
public LoadException()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
public LoadException(String message) {
|
||||
public LoadException(final String message)
|
||||
{
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
@@ -8,12 +8,14 @@ import carpet.script.Tokenizer.Token;
|
||||
import carpet.script.value.FunctionValue;
|
||||
import carpet.script.value.Value;
|
||||
|
||||
public class ProcessedThrowStatement extends ExpressionException {
|
||||
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);
|
||||
|
||||
public ProcessedThrowStatement(final Context c, final Expression e, final Token token, final List<FunctionValue> stack, final Throwables thrownExceptionType, final Value data)
|
||||
{
|
||||
super(c, e, token, () -> "Unhandled " + thrownExceptionType.getId() + " exception: " + data.getString(), stack);
|
||||
this.thrownExceptionType = thrownExceptionType;
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
@@ -4,8 +4,7 @@ import carpet.script.value.Value;
|
||||
|
||||
public class ReturnStatement extends ExitStatement
|
||||
{
|
||||
|
||||
public ReturnStatement(Value value)
|
||||
public ReturnStatement(final Value value)
|
||||
{
|
||||
super(value);
|
||||
}
|
||||
|
||||
@@ -3,17 +3,21 @@ package carpet.script.exception;
|
||||
/**
|
||||
* A type of {@link RuntimeException} that doesn't spend time producing and filling a stacktrace
|
||||
*/
|
||||
abstract class StacklessRuntimeException extends RuntimeException {
|
||||
public StacklessRuntimeException() {
|
||||
abstract class StacklessRuntimeException extends RuntimeException
|
||||
{
|
||||
public StacklessRuntimeException()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
public StacklessRuntimeException(String message) {
|
||||
public StacklessRuntimeException(final String message)
|
||||
{
|
||||
super(message);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Throwable fillInStackTrace() {
|
||||
public Throwable fillInStackTrace()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -10,21 +10,22 @@ public class ThrowStatement extends InternalExpressionException
|
||||
{
|
||||
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)
|
||||
public ThrowStatement(final Value data, final Throwables type)
|
||||
{
|
||||
super(type.getId());
|
||||
this.data = data;
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public ThrowStatement(Value data, Throwables parent, String subtype)
|
||||
public ThrowStatement(final Value data, final Throwables parent, final String subtype)
|
||||
{
|
||||
super(subtype);
|
||||
this.data = data;
|
||||
@@ -35,20 +36,22 @@ public class ThrowStatement extends InternalExpressionException
|
||||
* 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.
|
||||
* @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)
|
||||
public ThrowStatement(final String message, final Throwables type)
|
||||
{
|
||||
super(type.getId());
|
||||
this.data = StringValue.of(message);
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public ExpressionException promote(Context c, Expression e, Token token) {
|
||||
public ExpressionException promote(final Context c, final Expression e, final Token token)
|
||||
{
|
||||
return new ProcessedThrowStatement(c, e, token, stack, type, data);
|
||||
}
|
||||
}
|
||||
@@ -7,41 +7,43 @@ 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 {
|
||||
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 UNKNOWN_SCREEN = register("unknown_screen", 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 B64_ERROR = register("b64_error", IO_EXCEPTION);
|
||||
public static final Throwables USER_DEFINED = register("user_exception", THROWN_EXCEPTION_TYPE);
|
||||
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 UNKNOWN_SCREEN = register("unknown_screen", 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 B64_ERROR = register("b64_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 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)
|
||||
public static Throwables register(final String id, final Throwables parent)
|
||||
{
|
||||
Throwables exc = new Throwables(id, parent);
|
||||
final Throwables exc = new Throwables(id, parent);
|
||||
byId.put(id, exc);
|
||||
return exc;
|
||||
}
|
||||
@@ -50,28 +52,33 @@ public class Throwables {
|
||||
* 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)
|
||||
public Throwables(final String id, final Throwables parent)
|
||||
{
|
||||
this.id = id;
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public static Throwables getTypeForException(String type)
|
||||
public static Throwables getTypeForException(final String type)
|
||||
{
|
||||
Throwables properType = byId.get(type);
|
||||
if (properType == null) throw new InternalExpressionException("Unknown exception type: "+type);
|
||||
final 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)
|
||||
public boolean isRelevantFor(final String filter)
|
||||
{
|
||||
return (id.equals(filter) || (parent != null && parent.isRelevantFor(filter)));
|
||||
}
|
||||
@@ -83,9 +90,11 @@ public class Throwables {
|
||||
|
||||
/**
|
||||
* Returns the throwable type
|
||||
*
|
||||
* @return The type of this exception
|
||||
*/
|
||||
public String getId() {
|
||||
public String getId()
|
||||
{
|
||||
return id;
|
||||
}
|
||||
}
|
||||
@@ -8,16 +8,17 @@ import carpet.script.value.ListValue;
|
||||
import carpet.script.value.NumericValue;
|
||||
import carpet.script.value.Value;
|
||||
|
||||
public class Arithmetic {
|
||||
public class Arithmetic
|
||||
{
|
||||
public static final Value PI = new NumericValue(Math.PI);
|
||||
public static final Value euler = new NumericValue(Math.E);
|
||||
|
||||
public static void apply(Expression expression)
|
||||
public static void apply(final Expression expression)
|
||||
{
|
||||
expression.addTypedContextFunction("not", 1, Context.Type.BOOLEAN, (c, t, lv) -> BooleanValue.of(lv.get(0).getBoolean()) );
|
||||
expression.addTypedContextFunction("not", 1, Context.Type.BOOLEAN, (c, t, lv) -> BooleanValue.of(lv.get(0).getBoolean()));
|
||||
expression.addUnaryFunction("fact", (v) ->
|
||||
{
|
||||
long number = NumericValue.asNumber(v).getLong();
|
||||
final long number = NumericValue.asNumber(v).getLong();
|
||||
if (number < 21)
|
||||
{
|
||||
long factorial = 1;
|
||||
@@ -40,72 +41,81 @@ public class Arithmetic {
|
||||
return new NumericValue(factorial);
|
||||
|
||||
});
|
||||
expression.addMathematicalUnaryFunction("sin", (d) -> Math.sin(Math.toRadians(d)));
|
||||
expression.addMathematicalUnaryFunction("cos", (d) -> Math.cos(Math.toRadians(d)));
|
||||
expression.addMathematicalUnaryFunction("tan", (d) -> Math.tan(Math.toRadians(d)));
|
||||
expression.addMathematicalUnaryFunction("asin", (d) -> Math.toDegrees(Math.asin(d)));
|
||||
expression.addMathematicalUnaryFunction("acos", (d) -> Math.toDegrees(Math.acos(d)));
|
||||
expression.addMathematicalUnaryFunction("atan", (d) -> Math.toDegrees(Math.atan(d)));
|
||||
expression.addMathematicalBinaryFunction("atan2", (d, d2) -> Math.toDegrees(Math.atan2(d, d2)) );
|
||||
expression.addMathematicalUnaryFunction("sinh", Math::sinh );
|
||||
expression.addMathematicalUnaryFunction("cosh", Math::cosh );
|
||||
expression.addMathematicalUnaryFunction("tanh", Math::tanh );
|
||||
expression.addMathematicalUnaryFunction("sec", (d) -> 1.0 / Math.cos(Math.toRadians(d)) ); // Formula: sec(x) = 1 / cos(x)
|
||||
expression.addMathematicalUnaryFunction("csc", (d) -> 1.0 / Math.sin(Math.toRadians(d)) ); // Formula: csc(x) = 1 / sin(x)
|
||||
expression.addMathematicalUnaryFunction("sech", (d) -> 1.0 / Math.cosh(d) ); // Formula: sech(x) = 1 / cosh(x)
|
||||
expression.addMathematicalUnaryFunction("csch", (d) -> 1.0 / Math.sinh(d) ); // Formula: csch(x) = 1 / sinh(x)
|
||||
expression.addMathematicalUnaryFunction("cot", (d) -> 1.0 / Math.tan(Math.toRadians(d)) ); // Formula: cot(x) = cos(x) / sin(x) = 1 / tan(x)
|
||||
expression.addMathematicalUnaryFunction("acot", (d) -> Math.toDegrees(Math.atan(1.0 / d)) );// Formula: acot(x) = atan(1/x)
|
||||
expression.addMathematicalUnaryFunction("coth", (d) -> 1.0 / Math.tanh(d) ); // Formula: coth(x) = 1 / tanh(x)
|
||||
expression.addMathematicalUnaryFunction("asinh", (d) -> Math.log(d + (Math.sqrt(Math.pow(d, 2) + 1)))); // Formula: asinh(x) = ln(x + sqrt(x^2 + 1))
|
||||
expression.addMathematicalUnaryFunction("acosh", (d) -> Math.log(d + (Math.sqrt(Math.pow(d, 2) - 1)))); // Formula: acosh(x) = ln(x + sqrt(x^2 - 1))
|
||||
expression.addMathematicalUnaryFunction("atanh", (d) -> // Formula: atanh(x) = 0.5*ln((1 + x)/(1 - x))
|
||||
expression.addMathematicalUnaryFunction("sin", d -> Math.sin(Math.toRadians(d)));
|
||||
expression.addMathematicalUnaryFunction("cos", d -> Math.cos(Math.toRadians(d)));
|
||||
expression.addMathematicalUnaryFunction("tan", d -> Math.tan(Math.toRadians(d)));
|
||||
expression.addMathematicalUnaryFunction("asin", d -> Math.toDegrees(Math.asin(d)));
|
||||
expression.addMathematicalUnaryFunction("acos", d -> Math.toDegrees(Math.acos(d)));
|
||||
expression.addMathematicalUnaryFunction("atan", d -> Math.toDegrees(Math.atan(d)));
|
||||
expression.addMathematicalBinaryFunction("atan2", (d, d2) -> Math.toDegrees(Math.atan2(d, d2)));
|
||||
expression.addMathematicalUnaryFunction("sinh", Math::sinh);
|
||||
expression.addMathematicalUnaryFunction("cosh", Math::cosh);
|
||||
expression.addMathematicalUnaryFunction("tanh", Math::tanh);
|
||||
expression.addMathematicalUnaryFunction("sec", d -> 1.0 / Math.cos(Math.toRadians(d))); // Formula: sec(x) = 1 / cos(x)
|
||||
expression.addMathematicalUnaryFunction("csc", d -> 1.0 / Math.sin(Math.toRadians(d))); // Formula: csc(x) = 1 / sin(x)
|
||||
expression.addMathematicalUnaryFunction("sech", d -> 1.0 / Math.cosh(d)); // Formula: sech(x) = 1 / cosh(x)
|
||||
expression.addMathematicalUnaryFunction("csch", d -> 1.0 / Math.sinh(d)); // Formula: csch(x) = 1 / sinh(x)
|
||||
expression.addMathematicalUnaryFunction("cot", d -> 1.0 / Math.tan(Math.toRadians(d))); // Formula: cot(x) = cos(x) / sin(x) = 1 / tan(x)
|
||||
expression.addMathematicalUnaryFunction("acot", d -> Math.toDegrees(Math.atan(1.0 / d)));// Formula: acot(x) = atan(1/x)
|
||||
expression.addMathematicalUnaryFunction("coth", d -> 1.0 / Math.tanh(d)); // Formula: coth(x) = 1 / tanh(x)
|
||||
expression.addMathematicalUnaryFunction("asinh", d -> Math.log(d + (Math.sqrt(Math.pow(d, 2) + 1)))); // Formula: asinh(x) = ln(x + sqrt(x^2 + 1))
|
||||
expression.addMathematicalUnaryFunction("acosh", d -> Math.log(d + (Math.sqrt(Math.pow(d, 2) - 1)))); // Formula: acosh(x) = ln(x + sqrt(x^2 - 1))
|
||||
expression.addMathematicalUnaryFunction("atanh", d -> // Formula: atanh(x) = 0.5*ln((1 + x)/(1 - x))
|
||||
{
|
||||
if (Math.abs(d) > 1 || Math.abs(d) == 1)
|
||||
{
|
||||
throw new InternalExpressionException("Number must be |x| < 1");
|
||||
}
|
||||
return 0.5 * Math.log((1 + d) / (1 - d));
|
||||
});
|
||||
expression.addMathematicalUnaryFunction("rad", Math::toRadians);
|
||||
expression.addMathematicalUnaryFunction("rad", Math::toRadians);
|
||||
expression.addMathematicalUnaryFunction("deg", Math::toDegrees);
|
||||
expression.addMathematicalUnaryFunction("ln", Math::log);
|
||||
expression.addMathematicalUnaryFunction("ln1p", Math::log1p);
|
||||
expression.addMathematicalUnaryFunction("log10", Math::log10);
|
||||
expression.addMathematicalUnaryFunction("log", a -> Math.log(a)/Math.log(2));
|
||||
expression.addMathematicalUnaryFunction("log1p", x -> Math.log1p(x)/Math.log(2));
|
||||
expression.addMathematicalUnaryFunction("log", a -> Math.log(a) / Math.log(2));
|
||||
expression.addMathematicalUnaryFunction("log1p", x -> Math.log1p(x) / Math.log(2));
|
||||
expression.addMathematicalUnaryFunction("sqrt", Math::sqrt);
|
||||
expression.addMathematicalUnaryFunction("abs", Math::abs);
|
||||
expression.addMathematicalUnaryIntFunction("round", Math::round);
|
||||
expression.addMathematicalUnaryIntFunction("floor", n -> (long)Math.floor(n));
|
||||
expression.addMathematicalUnaryIntFunction("ceil", n -> (long)Math.ceil(n));
|
||||
expression.addMathematicalUnaryIntFunction("floor", n -> (long) Math.floor(n));
|
||||
expression.addMathematicalUnaryIntFunction("ceil", n -> (long) Math.ceil(n));
|
||||
|
||||
expression.addContextFunction("mandelbrot", 3, (c, t, lv) -> {
|
||||
double a0 = NumericValue.asNumber(lv.get(0)).getDouble();
|
||||
double b0 = NumericValue.asNumber(lv.get(1)).getDouble();
|
||||
long maxiter = NumericValue.asNumber(lv.get(2)).getLong();
|
||||
final double a0 = NumericValue.asNumber(lv.get(0)).getDouble();
|
||||
final double b0 = NumericValue.asNumber(lv.get(1)).getDouble();
|
||||
final long maxiter = NumericValue.asNumber(lv.get(2)).getLong();
|
||||
double a = 0.0D;
|
||||
double b = 0.0D;
|
||||
long iter = 0;
|
||||
while(a*a+b*b<4 && iter < maxiter)
|
||||
while (a * a + b * b < 4 && iter < maxiter)
|
||||
{
|
||||
double temp = a*a-b*b+a0;
|
||||
b = 2*a*b+b0;
|
||||
final double temp = a * a - b * b + a0;
|
||||
b = 2 * a * b + b0;
|
||||
a = temp;
|
||||
iter++;
|
||||
}
|
||||
long iFinal = iter;
|
||||
final long iFinal = iter;
|
||||
return new NumericValue(iFinal);
|
||||
});
|
||||
|
||||
expression.addFunction("max", (lv) ->
|
||||
{
|
||||
if (lv.size() == 0)
|
||||
throw new InternalExpressionException("'max' requires at least one parameter");
|
||||
Value max = null;
|
||||
if (lv.size()==1 && lv.get(0) instanceof ListValue)
|
||||
lv = ((ListValue) lv.get(0)).getItems();
|
||||
for (Value parameter : lv)
|
||||
{
|
||||
if (max == null || parameter.compareTo(max) > 0) max = parameter;
|
||||
throw new InternalExpressionException("'max' requires at least one parameter");
|
||||
}
|
||||
Value max = null;
|
||||
if (lv.size() == 1 && lv.get(0) instanceof ListValue)
|
||||
{
|
||||
lv = ((ListValue) lv.get(0)).getItems();
|
||||
}
|
||||
for (final Value parameter : lv)
|
||||
{
|
||||
if (max == null || parameter.compareTo(max) > 0)
|
||||
{
|
||||
max = parameter;
|
||||
}
|
||||
}
|
||||
return max;
|
||||
});
|
||||
@@ -113,17 +123,24 @@ public class Arithmetic {
|
||||
expression.addFunction("min", (lv) ->
|
||||
{
|
||||
if (lv.size() == 0)
|
||||
throw new InternalExpressionException("'min' requires at least one parameter");
|
||||
Value min = null;
|
||||
if (lv.size()==1 && lv.get(0) instanceof ListValue)
|
||||
lv = ((ListValue) lv.get(0)).getItems();
|
||||
for (Value parameter : lv)
|
||||
{
|
||||
if (min == null || parameter.compareTo(min) < 0) min = parameter;
|
||||
throw new InternalExpressionException("'min' requires at least one parameter");
|
||||
}
|
||||
Value min = null;
|
||||
if (lv.size() == 1 && lv.get(0) instanceof ListValue)
|
||||
{
|
||||
lv = ((ListValue) lv.get(0)).getItems();
|
||||
}
|
||||
for (final Value parameter : lv)
|
||||
{
|
||||
if (min == null || parameter.compareTo(min) < 0)
|
||||
{
|
||||
min = parameter;
|
||||
}
|
||||
}
|
||||
return min;
|
||||
});
|
||||
|
||||
expression.addUnaryFunction("relu", (v) -> v.compareTo(Value.ZERO) < 0 ? Value.ZERO : v);
|
||||
expression.addUnaryFunction("relu", v -> v.compareTo(Value.ZERO) < 0 ? Value.ZERO : v);
|
||||
}
|
||||
}
|
||||
@@ -17,21 +17,25 @@ import carpet.script.value.Value;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class ControlFlow {
|
||||
public static void apply(Expression expression) // public just to get the javadoc right
|
||||
public class ControlFlow
|
||||
{
|
||||
public static void apply(final Expression expression) // public just to get the javadoc right
|
||||
{
|
||||
// needs to be lazy cause of custom contextualization
|
||||
expression.addLazyBinaryOperator(";", Operators.precedence.get("nextop;"), true, true, t -> Context.Type.VOID, (c, t, lv1, lv2) ->
|
||||
{
|
||||
lv1.evalValue(c, Context.VOID);
|
||||
Value v2 = lv2.evalValue(c, t);
|
||||
final Value v2 = lv2.evalValue(c, t);
|
||||
return (cc, tt) -> v2;
|
||||
});
|
||||
|
||||
expression.addPureLazyFunction("then", -1, t -> Context.Type.VOID, (c, t, lv) -> {
|
||||
int imax = lv.size()-1;
|
||||
for (int i = 0; i < imax; i++) lv.get(i).evalValue(c, Context.VOID);
|
||||
Value v = lv.get(imax).evalValue(c, t);
|
||||
final int imax = lv.size() - 1;
|
||||
for (int i = 0; i < imax; i++)
|
||||
{
|
||||
lv.get(i).evalValue(c, Context.VOID);
|
||||
}
|
||||
final Value v = lv.get(imax).evalValue(c, t);
|
||||
return (cc, tt) -> v;
|
||||
});
|
||||
expression.addFunctionalEquivalence(";", "then");
|
||||
@@ -40,78 +44,80 @@ public class ControlFlow {
|
||||
// obvious lazy due to conditional evaluation of arguments
|
||||
expression.addLazyFunction("if", (c, t, lv) ->
|
||||
{
|
||||
if ( lv.size() < 2 )
|
||||
if (lv.size() < 2)
|
||||
{
|
||||
throw new InternalExpressionException("'if' statement needs to have at least one condition and one case");
|
||||
for (int i=0; i<lv.size()-1; i+=2)
|
||||
}
|
||||
for (int i = 0; i < lv.size() - 1; i += 2)
|
||||
{
|
||||
if (lv.get(i).evalValue(c, Context.BOOLEAN).getBoolean())
|
||||
{
|
||||
//int iFinal = i;
|
||||
Value ret = lv.get(i+1).evalValue(c, t);
|
||||
final Value ret = lv.get(i + 1).evalValue(c, t);
|
||||
return (cc, tt) -> ret;
|
||||
}
|
||||
}
|
||||
if (lv.size()%2 == 1)
|
||||
if (lv.size() % 2 == 1)
|
||||
{
|
||||
Value ret = lv.get(lv.size() - 1).evalValue(c, t);
|
||||
final Value ret = lv.get(lv.size() - 1).evalValue(c, t);
|
||||
return (cc, tt) -> ret;
|
||||
}
|
||||
return (cc, tt) -> Value.NULL;
|
||||
});
|
||||
|
||||
expression.addImpureFunction("exit", (lv) -> { throw new ExitStatement(lv.size()==0?Value.NULL:lv.get(0)); });
|
||||
expression.addImpureFunction("exit", (lv) -> {
|
||||
throw new ExitStatement(lv.size() == 0 ? Value.NULL : lv.get(0));
|
||||
});
|
||||
|
||||
expression.addImpureFunction("throw", lv->
|
||||
expression.addImpureFunction("throw", lv ->
|
||||
{
|
||||
switch (lv.size())
|
||||
switch (lv.size())
|
||||
{
|
||||
case 0:
|
||||
throw new ThrowStatement(Value.NULL, Throwables.USER_DEFINED);
|
||||
case 1:
|
||||
throw new ThrowStatement(lv.get(0), Throwables.USER_DEFINED );
|
||||
case 2:
|
||||
throw new ThrowStatement(lv.get(1), Throwables.getTypeForException(lv.get(0).getString()));
|
||||
case 3:
|
||||
throw new ThrowStatement(lv.get(2), Throwables.getTypeForException(lv.get(1).getString()), lv.get(0).getString());
|
||||
default:
|
||||
throw new InternalExpressionException("throw() can't accept more than 3 parameters");
|
||||
case 0 -> throw new ThrowStatement(Value.NULL, Throwables.USER_DEFINED);
|
||||
case 1 -> throw new ThrowStatement(lv.get(0), Throwables.USER_DEFINED);
|
||||
case 2 -> throw new ThrowStatement(lv.get(1), Throwables.getTypeForException(lv.get(0).getString()));
|
||||
case 3 -> throw new ThrowStatement(lv.get(2), Throwables.getTypeForException(lv.get(1).getString()), lv.get(0).getString());
|
||||
default -> throw new InternalExpressionException("throw() can't accept more than 3 parameters");
|
||||
}
|
||||
});
|
||||
|
||||
// needs to be lazy since execution of parameters but first one are conditional
|
||||
expression.addLazyFunction("try", (c, t, lv) ->
|
||||
{
|
||||
if (lv.size()==0)
|
||||
if (lv.size() == 0)
|
||||
{
|
||||
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);
|
||||
final Value retval = lv.get(0).evalValue(c, t);
|
||||
return (c_, t_) -> retval;
|
||||
}
|
||||
catch (ProcessedThrowStatement ret)
|
||||
catch (final 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("_");
|
||||
|
||||
final LazyValue __ = c.getVariable("_");
|
||||
c.setVariable("_", (__c, __t) -> ret.data.reboundedTo("_"));
|
||||
LazyValue _trace = c.getVariable("_trace");
|
||||
final LazyValue _trace = c.getVariable("_trace");
|
||||
c.setVariable("_trace", (__c, __t) -> MapValue.wrap(Map.of(
|
||||
StringValue.of("stack"), ListValue.wrap(ret.stack.stream().map(f -> ListValue.of(
|
||||
StringValue.of(f.getModule().name()),
|
||||
StringValue.of(f.getString()),
|
||||
NumericValue.of(f.getToken().lineno+1),
|
||||
NumericValue.of(f.getToken().linepos+1)
|
||||
NumericValue.of(f.getToken().lineno + 1),
|
||||
NumericValue.of(f.getToken().linepos + 1)
|
||||
))),
|
||||
|
||||
StringValue.of("locals"), MapValue.wrap(ret.context.variables.entrySet().stream().filter(e -> !e.getKey().equals("_trace")).collect(Collectors.toMap(
|
||||
@@ -120,20 +126,22 @@ public class ControlFlow {
|
||||
))),
|
||||
StringValue.of("token"), ListValue.of(
|
||||
StringValue.of(ret.token.surface),
|
||||
NumericValue.of(ret.token.lineno+1),
|
||||
NumericValue.of(ret.token.linepos+1)
|
||||
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)
|
||||
while (pointer < lv.size() - 1)
|
||||
{
|
||||
if (ret.thrownExceptionType.isRelevantFor(lv.get(pointer).evalValue(c).getString()))
|
||||
{
|
||||
@@ -145,14 +153,18 @@ public class ControlFlow {
|
||||
}
|
||||
c.setVariable("_", __);
|
||||
if (_trace != null)
|
||||
{
|
||||
c.setVariable("_trace", _trace);
|
||||
}
|
||||
else
|
||||
{
|
||||
c.delVariable("_trace");
|
||||
}
|
||||
if (val == null) // not handled
|
||||
{
|
||||
throw ret;
|
||||
}
|
||||
Value retval = val;
|
||||
final Value retval = val;
|
||||
return (c_, t_) -> retval;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -25,42 +25,42 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class DataStructures {
|
||||
public static void apply(Expression expression)
|
||||
public class DataStructures
|
||||
{
|
||||
public static void apply(final Expression expression)
|
||||
{
|
||||
expression.addFunction("l", lv ->
|
||||
{
|
||||
if (lv.size() == 1 && lv.get(0) instanceof LazyListValue)
|
||||
return ListValue.wrap(((LazyListValue) lv.get(0)).unroll());
|
||||
return new ListValue.ListConstructorValue(lv);
|
||||
});
|
||||
lv.size() == 1 && lv.get(0) instanceof final LazyListValue llv
|
||||
? ListValue.wrap(llv.unroll())
|
||||
: new ListValue.ListConstructorValue(lv));
|
||||
|
||||
expression.addFunction("join", (lv) ->
|
||||
{
|
||||
if (lv.size() < 2)
|
||||
{
|
||||
throw new InternalExpressionException("'join' takes at least 2 arguments");
|
||||
String delimiter = lv.get(0).getString();
|
||||
List<Value> toJoin;
|
||||
if (lv.size()==2 && lv.get(1) instanceof LazyListValue)
|
||||
{
|
||||
toJoin = ((LazyListValue) lv.get(1)).unroll();
|
||||
|
||||
}
|
||||
else if (lv.size() == 2 && lv.get(1) instanceof ListValue)
|
||||
final String delimiter = lv.get(0).getString();
|
||||
final List<Value> toJoin;
|
||||
if (lv.size() == 2 && lv.get(1) instanceof final LazyListValue llv)
|
||||
{
|
||||
toJoin = new ArrayList<>(((ListValue)lv.get(1)).getItems());
|
||||
toJoin = llv.unroll();
|
||||
}
|
||||
else if (lv.size() == 2 && lv.get(1) instanceof final ListValue llv)
|
||||
{
|
||||
toJoin = new ArrayList<>(llv.getItems());
|
||||
}
|
||||
else
|
||||
{
|
||||
toJoin = lv.subList(1,lv.size());
|
||||
toJoin = lv.subList(1, lv.size());
|
||||
}
|
||||
return new StringValue(toJoin.stream().map(Value::getString).collect(Collectors.joining(delimiter)));
|
||||
});
|
||||
|
||||
expression.addFunction("split", (lv) ->
|
||||
{
|
||||
Value delimiter;
|
||||
Value hwat;
|
||||
final Value delimiter;
|
||||
final Value hwat;
|
||||
if (lv.size() == 1)
|
||||
{
|
||||
hwat = lv.get(0);
|
||||
@@ -80,23 +80,26 @@ public class DataStructures {
|
||||
|
||||
expression.addFunction("slice", (lv) ->
|
||||
{
|
||||
|
||||
if (lv.size() != 2 && lv.size() != 3)
|
||||
{
|
||||
throw new InternalExpressionException("'slice' takes 2 or 3 arguments");
|
||||
Value hwat = lv.get(0);
|
||||
long from = NumericValue.asNumber(lv.get(1)).getLong();
|
||||
}
|
||||
final Value hwat = lv.get(0);
|
||||
final long from = NumericValue.asNumber(lv.get(1)).getLong();
|
||||
Long to = null;
|
||||
if (lv.size()== 3)
|
||||
if (lv.size() == 3)
|
||||
{
|
||||
to = NumericValue.asNumber(lv.get(2)).getLong();
|
||||
}
|
||||
return hwat.slice(from, to);
|
||||
});
|
||||
|
||||
expression.addFunction("sort", (lv) ->
|
||||
{
|
||||
List<Value> toSort = lv;
|
||||
if (lv.size()==1 && lv.get(0) instanceof ListValue)
|
||||
if (lv.size() == 1 && lv.get(0) instanceof final ListValue llv)
|
||||
{
|
||||
toSort = new ArrayList<>(((ListValue)lv.get(0)).getItems());
|
||||
toSort = new ArrayList<>(llv.getItems());
|
||||
}
|
||||
Collections.sort(toSort);
|
||||
return ListValue.wrap(toSort);
|
||||
@@ -106,30 +109,34 @@ public class DataStructures {
|
||||
expression.addLazyFunction("sort_key", (c, t, lv) -> //get working with iterators
|
||||
{
|
||||
if (lv.size() == 0)
|
||||
{
|
||||
throw new InternalExpressionException("First argument for 'sort_key' should be a List");
|
||||
Value v = lv.get(0).evalValue(c);
|
||||
if (!(v instanceof ListValue))
|
||||
}
|
||||
final Value v = lv.get(0).evalValue(c);
|
||||
if (!(v instanceof final ListValue list))
|
||||
{
|
||||
throw new InternalExpressionException("First argument for 'sort_key' should be a List");
|
||||
List<Value> toSort = new ArrayList<>(((ListValue) v).getItems());
|
||||
if (lv.size()==1)
|
||||
}
|
||||
final List<Value> toSort = new ArrayList<>(list.getItems());
|
||||
if (lv.size() == 1)
|
||||
{
|
||||
Collections.shuffle(toSort);
|
||||
Value ret = ListValue.wrap(toSort);
|
||||
final Value ret = ListValue.wrap(toSort);
|
||||
return (_c, _t) -> ret;
|
||||
}
|
||||
LazyValue sortKey = lv.get(1);
|
||||
final LazyValue sortKey = lv.get(1);
|
||||
//scoping
|
||||
LazyValue __ = c.getVariable("_");
|
||||
Collections.sort(toSort,(v1, v2) -> {
|
||||
c.setVariable("_",(cc, tt) -> v1);
|
||||
Value ev1 = sortKey.evalValue(c);
|
||||
c.setVariable("_",(cc, tt) -> v2);
|
||||
Value ev2 = sortKey.evalValue(c);
|
||||
final LazyValue __ = c.getVariable("_");
|
||||
toSort.sort((v1, v2) -> {
|
||||
c.setVariable("_", (cc, tt) -> v1);
|
||||
final Value ev1 = sortKey.evalValue(c);
|
||||
c.setVariable("_", (cc, tt) -> v2);
|
||||
final Value ev2 = sortKey.evalValue(c);
|
||||
return ev1.compareTo(ev2);
|
||||
});
|
||||
//revering scope
|
||||
c.setVariable("_", __);
|
||||
Value ret = ListValue.wrap(toSort);
|
||||
final Value ret = ListValue.wrap(toSort);
|
||||
return (cc, tt) -> ret;
|
||||
});
|
||||
|
||||
@@ -138,9 +145,11 @@ public class DataStructures {
|
||||
NumericValue from = Value.ZERO;
|
||||
NumericValue to;
|
||||
NumericValue step = Value.ONE;
|
||||
int argsize = lv.size();
|
||||
final int argsize = lv.size();
|
||||
if (argsize == 0 || argsize > 3)
|
||||
throw new InternalExpressionException("'range' accepts from 1 to 3 arguments, not "+argsize);
|
||||
{
|
||||
throw new InternalExpressionException("'range' accepts from 1 to 3 arguments, not " + argsize);
|
||||
}
|
||||
to = NumericValue.asNumber(lv.get(0));
|
||||
if (lv.size() > 1)
|
||||
{
|
||||
@@ -157,79 +166,84 @@ public class DataStructures {
|
||||
});
|
||||
|
||||
expression.addTypedContextFunction("m", -1, Context.MAPDEF, (c, t, lv) ->
|
||||
{
|
||||
Value ret;
|
||||
if (lv.size() == 1 && lv.get(0) instanceof LazyListValue)
|
||||
ret = new MapValue(((LazyListValue) lv.get(0)).unroll());
|
||||
else
|
||||
ret = new MapValue(lv);
|
||||
return ret;
|
||||
});
|
||||
lv.size() == 1 && lv.get(0) instanceof final LazyListValue llv
|
||||
? new MapValue(llv.unroll())
|
||||
: new MapValue(lv)
|
||||
);
|
||||
|
||||
expression.addUnaryFunction("keys", v ->
|
||||
{
|
||||
if (v instanceof MapValue)
|
||||
return new ListValue(((MapValue) v).getMap().keySet());
|
||||
return Value.NULL;
|
||||
});
|
||||
v instanceof final MapValue map
|
||||
? new ListValue(map.getMap().keySet())
|
||||
: Value.NULL
|
||||
);
|
||||
|
||||
expression.addUnaryFunction("values", v ->
|
||||
{
|
||||
if (v instanceof MapValue)
|
||||
return new ListValue(((MapValue) v).getMap().values());
|
||||
return Value.NULL;
|
||||
});
|
||||
v instanceof final MapValue map
|
||||
? new ListValue(map.getMap().values())
|
||||
: Value.NULL
|
||||
);
|
||||
|
||||
expression.addUnaryFunction("pairs", v ->
|
||||
{
|
||||
if (v instanceof MapValue)
|
||||
return ListValue.wrap(((MapValue) v).getMap().entrySet().stream().map(
|
||||
(p) -> ListValue.of(p.getKey(), p.getValue())
|
||||
));
|
||||
return Value.NULL;
|
||||
});
|
||||
v instanceof final MapValue map
|
||||
? ListValue.wrap(map.getMap().entrySet().stream().map(p -> ListValue.of(p.getKey(), p.getValue())))
|
||||
: Value.NULL);
|
||||
|
||||
expression.addBinaryContextOperator(":", Operators.precedence.get("attribute~:"),true, true, false, (ctx, t, container, address) ->
|
||||
expression.addBinaryContextOperator(":", Operators.precedence.get("attribute~:"), true, true, false, (ctx, t, container, address) ->
|
||||
{
|
||||
if (container instanceof LContainerValue)
|
||||
if (container instanceof final LContainerValue lcv)
|
||||
{
|
||||
ContainerValueInterface outerContainer = ((LContainerValue) container).getContainer();
|
||||
if (outerContainer == null) return LContainerValue.NULL_CONTAINER;
|
||||
Value innerContainer = outerContainer.get(((LContainerValue) container).getAddress());
|
||||
if (!(innerContainer instanceof ContainerValueInterface)) return LContainerValue.NULL_CONTAINER;
|
||||
return new LContainerValue((ContainerValueInterface) innerContainer, address);
|
||||
final ContainerValueInterface outerContainer = lcv.container();
|
||||
if (outerContainer == null)
|
||||
{
|
||||
return LContainerValue.NULL_CONTAINER;
|
||||
}
|
||||
return outerContainer.get(lcv.address()) instanceof final ContainerValueInterface cvi
|
||||
? new LContainerValue(cvi, address)
|
||||
: LContainerValue.NULL_CONTAINER;
|
||||
}
|
||||
if (!(container instanceof ContainerValueInterface))
|
||||
if (!(container instanceof final ContainerValueInterface cvi))
|
||||
{
|
||||
return t == Context.LVALUE ? LContainerValue.NULL_CONTAINER : Value.NULL;
|
||||
if (t != Context.LVALUE) return ((ContainerValueInterface) container).get(address);
|
||||
return new LContainerValue((ContainerValueInterface) container, address);
|
||||
}
|
||||
return t != Context.LVALUE ? cvi.get(address) : new LContainerValue(cvi, address);
|
||||
});
|
||||
|
||||
// lazy cause conditional typing - questionable
|
||||
expression.addLazyFunction("get", (c, t, lv) ->
|
||||
{
|
||||
if (lv.size() == 0)
|
||||
{
|
||||
throw new InternalExpressionException("'get' requires parameters");
|
||||
}
|
||||
if (lv.size() == 1)
|
||||
{
|
||||
Value v = lv.get(0).evalValue(c, Context.LVALUE);
|
||||
if (!(v instanceof LContainerValue))
|
||||
final Value v = lv.get(0).evalValue(c, Context.LVALUE);
|
||||
if (!(v instanceof final LContainerValue lcv))
|
||||
{
|
||||
return LazyValue.NULL;
|
||||
ContainerValueInterface container = ((LContainerValue) v).getContainer();
|
||||
}
|
||||
final ContainerValueInterface container = lcv.container();
|
||||
if (container == null)
|
||||
{
|
||||
return LazyValue.NULL;
|
||||
Value ret = container.get(((LContainerValue) v).getAddress());
|
||||
}
|
||||
final Value ret = container.get(lcv.address());
|
||||
return (cc, tt) -> ret;
|
||||
}
|
||||
Value container = lv.get(0).evalValue(c);
|
||||
for (int i = 1; i < lv.size(); i++)
|
||||
{
|
||||
if (!(container instanceof ContainerValueInterface)) return (cc, tt) -> Value.NULL;
|
||||
container = ((ContainerValueInterface) container).get(lv.get(i).evalValue(c));
|
||||
if (!(container instanceof final ContainerValueInterface cvi))
|
||||
{
|
||||
return (cc, tt) -> Value.NULL;
|
||||
}
|
||||
container = cvi.get(lv.get(i).evalValue(c));
|
||||
}
|
||||
if (container == null)
|
||||
{
|
||||
return (cc, tt) -> Value.NULL;
|
||||
Value finalContainer = container;
|
||||
}
|
||||
final Value finalContainer = container;
|
||||
return (cc, tt) -> finalContainer;
|
||||
});
|
||||
|
||||
@@ -237,66 +251,77 @@ public class DataStructures {
|
||||
expression.addLazyFunction("has", (c, t, lv) ->
|
||||
{
|
||||
if (lv.size() == 0)
|
||||
{
|
||||
throw new InternalExpressionException("'has' requires parameters");
|
||||
}
|
||||
if (lv.size() == 1)
|
||||
{
|
||||
Value v = lv.get(0).evalValue(c, Context.LVALUE);
|
||||
if (!(v instanceof LContainerValue))
|
||||
final Value v = lv.get(0).evalValue(c, Context.LVALUE);
|
||||
if (!(v instanceof final LContainerValue lcv))
|
||||
{
|
||||
return LazyValue.NULL;
|
||||
ContainerValueInterface container = ((LContainerValue) v).getContainer();
|
||||
}
|
||||
final ContainerValueInterface container = lcv.container();
|
||||
if (container == null)
|
||||
{
|
||||
return LazyValue.NULL;
|
||||
Value ret = BooleanValue.of(container.has(((LContainerValue) v).getAddress()));
|
||||
}
|
||||
final Value ret = BooleanValue.of(container.has(lcv.address()));
|
||||
return (cc, tt) -> ret;
|
||||
}
|
||||
Value container = lv.get(0).evalValue(c);
|
||||
for (int i = 1; i < lv.size()-1; i++)
|
||||
for (int i = 1; i < lv.size() - 1; i++)
|
||||
{
|
||||
if (!(container instanceof ContainerValueInterface)) return LazyValue.NULL;
|
||||
container = ((ContainerValueInterface) container).get(lv.get(i).evalValue(c));
|
||||
if (!(container instanceof final ContainerValueInterface cvi))
|
||||
{
|
||||
return LazyValue.NULL;
|
||||
}
|
||||
container = cvi.get(lv.get(i).evalValue(c));
|
||||
}
|
||||
if (!(container instanceof ContainerValueInterface))
|
||||
if (!(container instanceof final ContainerValueInterface cvi))
|
||||
{
|
||||
return LazyValue.NULL;
|
||||
Value ret = BooleanValue.of(((ContainerValueInterface) container).has(lv.get(lv.size()-1).evalValue(c)));
|
||||
}
|
||||
final Value ret = BooleanValue.of(cvi.has(lv.get(lv.size() - 1).evalValue(c)));
|
||||
return (cc, tt) -> ret;
|
||||
});
|
||||
|
||||
// same as `get`
|
||||
expression.addLazyFunction("put", (c, t, lv) ->
|
||||
{
|
||||
if(lv.size()<2)
|
||||
if (lv.size() < 2)
|
||||
{
|
||||
throw new InternalExpressionException("'put' takes at least three arguments, a container, address, and values to insert at that index");
|
||||
}
|
||||
Value container = lv.get(0).evalValue(c, Context.LVALUE);
|
||||
if (container instanceof LContainerValue)
|
||||
final Value container = lv.get(0).evalValue(c, Context.LVALUE);
|
||||
if (container instanceof final LContainerValue lcv)
|
||||
{
|
||||
ContainerValueInterface internalContainer = ((LContainerValue) container).getContainer();
|
||||
final ContainerValueInterface internalContainer = lcv.container();
|
||||
if (internalContainer == null)
|
||||
{
|
||||
return LazyValue.NULL;
|
||||
}
|
||||
Value address = ((LContainerValue) container).getAddress();
|
||||
Value what = lv.get(1).evalValue(c);
|
||||
Value retVal = BooleanValue.of((lv.size() > 2)
|
||||
final Value address = lcv.address();
|
||||
final Value what = lv.get(1).evalValue(c);
|
||||
final Value retVal = BooleanValue.of((lv.size() > 2)
|
||||
? internalContainer.put(address, what, lv.get(2).evalValue(c))
|
||||
: internalContainer.put(address, what));
|
||||
return (cc, tt) -> retVal;
|
||||
|
||||
}
|
||||
if(lv.size()<3)
|
||||
if (lv.size() < 3)
|
||||
{
|
||||
throw new InternalExpressionException("'put' takes at least three arguments, a container, address, and values to insert at that index");
|
||||
}
|
||||
if (!(container instanceof ContainerValueInterface))
|
||||
if (!(container instanceof final ContainerValueInterface cvi))
|
||||
{
|
||||
return LazyValue.NULL;
|
||||
}
|
||||
Value where = lv.get(1).evalValue(c);
|
||||
Value what = lv.get(2).evalValue(c);
|
||||
Value retVal = BooleanValue.of((lv.size()>3)
|
||||
? ((ContainerValueInterface) container).put(where, what, lv.get(3).evalValue(c))
|
||||
: ((ContainerValueInterface) container).put(where, what));
|
||||
final Value where = lv.get(1).evalValue(c);
|
||||
final Value what = lv.get(2).evalValue(c);
|
||||
final Value retVal = BooleanValue.of((lv.size() > 3)
|
||||
? cvi.put(where, what, lv.get(3).evalValue(c))
|
||||
: cvi.put(where, what));
|
||||
return (cc, tt) -> retVal;
|
||||
});
|
||||
|
||||
@@ -304,44 +329,61 @@ public class DataStructures {
|
||||
expression.addLazyFunction("delete", (c, t, lv) ->
|
||||
{
|
||||
if (lv.size() == 0)
|
||||
{
|
||||
throw new InternalExpressionException("'delete' requires parameters");
|
||||
}
|
||||
if (lv.size() == 1)
|
||||
{
|
||||
Value v = lv.get(0).evalValue(c, Context.LVALUE);
|
||||
if (!(v instanceof LContainerValue))
|
||||
final Value v = lv.get(0).evalValue(c, Context.LVALUE);
|
||||
if (!(v instanceof final LContainerValue lcv))
|
||||
{
|
||||
return LazyValue.NULL;
|
||||
ContainerValueInterface container = ((LContainerValue) v).getContainer();
|
||||
}
|
||||
final ContainerValueInterface container = lcv.container();
|
||||
if (container == null)
|
||||
{
|
||||
return LazyValue.NULL;
|
||||
Value ret = BooleanValue.of(container.delete(((LContainerValue) v).getAddress()));
|
||||
}
|
||||
final Value ret = BooleanValue.of(container.delete(lcv.address()));
|
||||
return (cc, tt) -> ret;
|
||||
}
|
||||
Value container = lv.get(0).evalValue(c);
|
||||
for (int i = 1; i < lv.size()-1; i++)
|
||||
for (int i = 1; i < lv.size() - 1; i++)
|
||||
{
|
||||
if (!(container instanceof ContainerValueInterface)) return LazyValue.NULL;
|
||||
container = ((ContainerValueInterface) container).get(lv.get(i).evalValue(c));
|
||||
if (!(container instanceof final ContainerValueInterface cvi))
|
||||
{
|
||||
return LazyValue.NULL;
|
||||
}
|
||||
container = cvi.get(lv.get(i).evalValue(c));
|
||||
}
|
||||
if (!(container instanceof ContainerValueInterface))
|
||||
if (!(container instanceof final ContainerValueInterface cvi))
|
||||
{
|
||||
return LazyValue.NULL;
|
||||
Value ret = BooleanValue.of(((ContainerValueInterface) container).delete(lv.get(lv.size()-1).evalValue(c)));
|
||||
}
|
||||
final Value ret = BooleanValue.of(cvi.delete(lv.get(lv.size() - 1).evalValue(c)));
|
||||
return (cc, tt) -> ret;
|
||||
});
|
||||
|
||||
expression.addUnaryFunction("encode_b64", v -> StringValue.of(Base64.getEncoder().encodeToString(v.getString().getBytes(StandardCharsets.UTF_8))));
|
||||
expression.addUnaryFunction("decode_b64", v -> {
|
||||
try {
|
||||
try
|
||||
{
|
||||
return StringValue.of(new String(Base64.getDecoder().decode(v.getString()), StandardCharsets.UTF_8));
|
||||
} catch (IllegalArgumentException iae){
|
||||
}
|
||||
catch (final IllegalArgumentException iae)
|
||||
{
|
||||
throw new ThrowStatement("Invalid b64 string: " + v.getString(), Throwables.B64_ERROR);
|
||||
}
|
||||
});
|
||||
|
||||
expression.addUnaryFunction("encode_json", v -> StringValue.of(v.toJson().toString()));
|
||||
expression.addUnaryFunction("decode_json", v -> {
|
||||
try {
|
||||
try
|
||||
{
|
||||
return Auxiliary.GSON.fromJson(v.getString(), Value.class);
|
||||
} catch (JsonParseException jpe){
|
||||
}
|
||||
catch (final JsonParseException jpe)
|
||||
{
|
||||
throw new ThrowStatement("Invalid json string: " + v.getString(), Throwables.JSON_ERROR);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -19,61 +19,70 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
public class Functions {
|
||||
public static void apply(Expression expression) // public just to get the javadoc right
|
||||
public class Functions
|
||||
{
|
||||
public static void apply(final Expression expression) // public just to get the javadoc right
|
||||
{
|
||||
// artificial construct to handle user defined functions and function definitions
|
||||
expression.addContextFunction("import", -1, (c, t, lv) ->
|
||||
{
|
||||
if (lv.size() < 1) throw new InternalExpressionException("'import' needs at least a module name to import, and list of values to import");
|
||||
if (lv.size() < 1)
|
||||
{
|
||||
throw new InternalExpressionException("'import' needs at least a module name to import, and list of values to import");
|
||||
}
|
||||
String moduleName = lv.get(0).getString();
|
||||
c.host.importModule(c, moduleName);
|
||||
moduleName = moduleName.toLowerCase(Locale.ROOT);
|
||||
if (lv.size() > 1)
|
||||
{
|
||||
c.host.importNames(c, expression.module, moduleName, lv.subList(1, lv.size()).stream().map(Value::getString).toList());
|
||||
if (t == Context.VOID)
|
||||
return Value.NULL;
|
||||
return ListValue.wrap(c.host.availableImports(moduleName).map(StringValue::new));
|
||||
}
|
||||
return t == Context.VOID ? Value.NULL : ListValue.wrap(c.host.availableImports(moduleName).map(StringValue::new));
|
||||
});
|
||||
|
||||
|
||||
// needs to be lazy because of custom context of execution of arguments as a signature
|
||||
expression.addCustomFunction("call", new Fluff.AbstractLazyFunction(-1, "call") {
|
||||
expression.addCustomFunction("call", new Fluff.AbstractLazyFunction(-1, "call")
|
||||
{
|
||||
@Override
|
||||
public LazyValue lazyEval(Context c, Context.Type t, Expression expr, Tokenizer.Token tok, List<LazyValue> lv)
|
||||
public LazyValue lazyEval(final Context c, final Context.Type t, final Expression expr, final Tokenizer.Token tok, final List<LazyValue> lv)
|
||||
{
|
||||
if (lv.size() == 0)
|
||||
{
|
||||
throw new InternalExpressionException("'call' expects at least function name to call");
|
||||
}
|
||||
//lv.remove(lv.size()-1); // aint gonna cut it // maybe it will because of the eager eval changes
|
||||
if (t != Context.SIGNATURE) // just call the function
|
||||
{
|
||||
List<Value> args = Fluff.AbstractFunction.unpackLazy(lv, c, Context.NONE);
|
||||
FunctionArgument functionArgument = FunctionArgument.findIn(c, expression.module, args, 0, false, true);
|
||||
FunctionValue fun = functionArgument.function;
|
||||
final List<Value> args = Fluff.AbstractFunction.unpackLazy(lv, c, Context.NONE);
|
||||
final FunctionArgument functionArgument = FunctionArgument.findIn(c, expression.module, args, 0, false, true);
|
||||
final FunctionValue fun = functionArgument.function;
|
||||
return fun.callInContext(c, t, functionArgument.args);
|
||||
}
|
||||
// gimme signature
|
||||
String name = lv.get(0).evalValue(c, Context.NONE).getString();
|
||||
List<String> args = new ArrayList<>();
|
||||
List<String> globals = new ArrayList<>();
|
||||
final String name = lv.get(0).evalValue(c, Context.NONE).getString();
|
||||
final List<String> args = new ArrayList<>();
|
||||
final List<String> globals = new ArrayList<>();
|
||||
String varArgs = null;
|
||||
for (int i = 1; i < lv.size(); i++)
|
||||
{
|
||||
Value v = lv.get(i).evalValue(c, Context.LOCALIZATION);
|
||||
final Value v = lv.get(i).evalValue(c, Context.LOCALIZATION);
|
||||
if (!v.isBound())
|
||||
{
|
||||
throw new InternalExpressionException("Only variables can be used in function signature, not " + v.getString());
|
||||
}
|
||||
if (v instanceof FunctionAnnotationValue)
|
||||
if (v instanceof final FunctionAnnotationValue fav)
|
||||
{
|
||||
if (((FunctionAnnotationValue) v).type == FunctionAnnotationValue.Type.GLOBAL)
|
||||
if (fav.type == FunctionAnnotationValue.Type.GLOBAL)
|
||||
{
|
||||
globals.add(v.boundVariable);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (varArgs != null)
|
||||
throw new InternalExpressionException("Variable argument identifier is already defined as "+varArgs+", trying to overwrite with "+v.boundVariable);
|
||||
{
|
||||
throw new InternalExpressionException("Variable argument identifier is already defined as " + varArgs + ", trying to overwrite with " + v.boundVariable);
|
||||
}
|
||||
varArgs = v.boundVariable;
|
||||
}
|
||||
}
|
||||
@@ -82,7 +91,7 @@ public class Functions {
|
||||
args.add(v.boundVariable);
|
||||
}
|
||||
}
|
||||
Value retval = new FunctionSignatureValue(name, args, varArgs, globals);
|
||||
final Value retval = new FunctionSignatureValue(name, args, varArgs, globals);
|
||||
return (cc, tt) -> retval;
|
||||
}
|
||||
|
||||
@@ -93,14 +102,15 @@ public class Functions {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean transitive() {
|
||||
public boolean transitive()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Context.Type staticType(Context.Type outerType)
|
||||
public Context.Type staticType(final Context.Type outerType)
|
||||
{
|
||||
return outerType==Context.SIGNATURE?Context.LOCALIZATION:Context.NONE;
|
||||
return outerType == Context.SIGNATURE ? Context.LOCALIZATION : Context.NONE;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -108,7 +118,9 @@ public class Functions {
|
||||
expression.addContextFunction("outer", 1, (c, t, lv) ->
|
||||
{
|
||||
if (t != Context.LOCALIZATION)
|
||||
{
|
||||
throw new InternalExpressionException("Outer scoping of variables is only possible in function signatures.");
|
||||
}
|
||||
return new FunctionAnnotationValue(lv.get(0), FunctionAnnotationValue.Type.GLOBAL);
|
||||
});
|
||||
|
||||
@@ -118,16 +130,20 @@ public class Functions {
|
||||
{
|
||||
if (type == Context.MAPDEF)
|
||||
{
|
||||
Value result = ListValue.of(lv1.evalValue(c), lv2.evalValue(c));
|
||||
final Value result = ListValue.of(lv1.evalValue(c), lv2.evalValue(c));
|
||||
return (cc, tt) -> result;
|
||||
}
|
||||
Value v1 = lv1.evalValue(c, Context.SIGNATURE);
|
||||
if (!(v1 instanceof FunctionSignatureValue sign))
|
||||
final Value v1 = lv1.evalValue(c, Context.SIGNATURE);
|
||||
if (!(v1 instanceof final FunctionSignatureValue sign))
|
||||
{
|
||||
throw new InternalExpressionException("'->' operator requires a function signature on the LHS");
|
||||
Value result = expression.createUserDefinedFunction(c, sign.getName(), e, t, sign.getArgs(), sign.getVarArgs(), sign.getGlobals(), lv2);
|
||||
}
|
||||
final Value result = expression.createUserDefinedFunction(c, sign.identifier(), e, t, sign.arguments(), sign.varArgs(), sign.globals(), lv2);
|
||||
return (cc, tt) -> result;
|
||||
});
|
||||
|
||||
expression.addImpureFunction("return", (lv) -> { throw new ReturnStatement(lv.size()==0?Value.NULL:lv.get(0));} );
|
||||
expression.addImpureFunction("return", (lv) -> {
|
||||
throw new ReturnStatement(lv.size() == 0 ? Value.NULL : lv.get(0));
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -15,55 +15,74 @@ import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
public class Loops {
|
||||
public static void apply(Expression expression)
|
||||
public class Loops
|
||||
{
|
||||
public static void apply(final Expression expression)
|
||||
{
|
||||
// condition and expression will get a bound '_i'
|
||||
// returns last successful expression or false
|
||||
// while(cond, limit, expr) => ??
|
||||
expression.addImpureFunction("break", lv ->
|
||||
{
|
||||
if (lv.size()==0) throw new BreakStatement(null);
|
||||
if (lv.size()==1) throw new BreakStatement(lv.get(0));
|
||||
if (lv.size() == 0)
|
||||
{
|
||||
throw new BreakStatement(null);
|
||||
}
|
||||
if (lv.size() == 1)
|
||||
{
|
||||
throw new BreakStatement(lv.get(0));
|
||||
}
|
||||
throw new InternalExpressionException("'break' can only be called with zero or one argument");
|
||||
});
|
||||
|
||||
expression.addImpureFunction("continue", lv ->
|
||||
{
|
||||
if (lv.size()==0) throw new ContinueStatement(null);
|
||||
if (lv.size()==1) throw new ContinueStatement(lv.get(0));
|
||||
if (lv.size() == 0)
|
||||
{
|
||||
throw new ContinueStatement(null);
|
||||
}
|
||||
if (lv.size() == 1)
|
||||
{
|
||||
throw new ContinueStatement(lv.get(0));
|
||||
}
|
||||
throw new InternalExpressionException("'continue' can only be called with zero or one argument");
|
||||
});
|
||||
|
||||
// lazy
|
||||
expression.addLazyFunction("while", 3, (c, t, lv) ->
|
||||
{
|
||||
long limit = NumericValue.asNumber(lv.get(1).evalValue(c)).getLong();
|
||||
LazyValue condition = lv.get(0);
|
||||
LazyValue expr = lv.get(2);
|
||||
final long limit = NumericValue.asNumber(lv.get(1).evalValue(c)).getLong();
|
||||
final LazyValue condition = lv.get(0);
|
||||
final LazyValue expr = lv.get(2);
|
||||
long i = 0;
|
||||
Value lastOne = Value.NULL;
|
||||
//scoping
|
||||
LazyValue _val = c.getVariable("_");
|
||||
c.setVariable("_",(cc, tt) -> new NumericValue(0).bindTo("_"));
|
||||
while (i<limit && condition.evalValue(c, Context.BOOLEAN).getBoolean() )
|
||||
final LazyValue _val = c.getVariable("_");
|
||||
c.setVariable("_", (cc, tt) -> new NumericValue(0).bindTo("_"));
|
||||
while (i < limit && condition.evalValue(c, Context.BOOLEAN).getBoolean())
|
||||
{
|
||||
try
|
||||
{
|
||||
lastOne = expr.evalValue(c, t);
|
||||
}
|
||||
catch (BreakStatement | ContinueStatement stmt)
|
||||
catch (final BreakStatement | ContinueStatement stmt)
|
||||
{
|
||||
if (stmt.retval != null) lastOne = stmt.retval;
|
||||
if (stmt instanceof BreakStatement) break;
|
||||
if (stmt.retval != null)
|
||||
{
|
||||
lastOne = stmt.retval;
|
||||
}
|
||||
if (stmt instanceof BreakStatement)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
i++;
|
||||
long seriously = i;
|
||||
final long seriously = i;
|
||||
c.setVariable("_", (cc, tt) -> new NumericValue(seriously).bindTo("_"));
|
||||
}
|
||||
//revering scope
|
||||
c.setVariable("_", _val);
|
||||
Value lastValueNoKidding = lastOne;
|
||||
final Value lastValueNoKidding = lastOne;
|
||||
return (cc, tt) -> lastValueNoKidding;
|
||||
});
|
||||
|
||||
@@ -71,28 +90,34 @@ public class Loops {
|
||||
// expr receives bounded variable '_' indicating iteration
|
||||
expression.addLazyFunction("loop", 2, (c, t, lv) ->
|
||||
{
|
||||
long limit = NumericValue.asNumber(lv.get(0).evalValue(c, Context.NONE)).getLong();
|
||||
final long limit = NumericValue.asNumber(lv.get(0).evalValue(c, Context.NONE)).getLong();
|
||||
Value lastOne = Value.NULL;
|
||||
LazyValue expr = lv.get(1);
|
||||
final LazyValue expr = lv.get(1);
|
||||
//scoping
|
||||
LazyValue _val = c.getVariable("_");
|
||||
for (long i=0; i < limit; i++)
|
||||
final LazyValue _val = c.getVariable("_");
|
||||
for (long i = 0; i < limit; i++)
|
||||
{
|
||||
long whyYouAsk = i;
|
||||
final long whyYouAsk = i;
|
||||
c.setVariable("_", (cc, tt) -> new NumericValue(whyYouAsk).bindTo("_"));
|
||||
try
|
||||
{
|
||||
lastOne = expr.evalValue(c, t);
|
||||
}
|
||||
catch (BreakStatement | ContinueStatement stmt)
|
||||
catch (final BreakStatement | ContinueStatement stmt)
|
||||
{
|
||||
if (stmt.retval != null) lastOne = stmt.retval;
|
||||
if (stmt instanceof BreakStatement) break;
|
||||
if (stmt.retval != null)
|
||||
{
|
||||
lastOne = stmt.retval;
|
||||
}
|
||||
if (stmt instanceof BreakStatement)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
//revering scope
|
||||
c.setVariable("_", _val);
|
||||
Value trulyLastOne = lastOne;
|
||||
final Value trulyLastOne = lastOne;
|
||||
return (cc, tt) -> trulyLastOne;
|
||||
});
|
||||
|
||||
@@ -100,31 +125,39 @@ public class Loops {
|
||||
// receives bounded variable '_' with the expression
|
||||
expression.addLazyFunction("map", 2, (c, t, lv) ->
|
||||
{
|
||||
Value rval= lv.get(0).evalValue(c, Context.NONE);
|
||||
if (rval.isNull()) return ListValue.lazyEmpty();
|
||||
if (!(rval instanceof AbstractListValue))
|
||||
throw new InternalExpressionException("First argument of 'map' function should be a list or iterator");
|
||||
Iterator<Value> iterator = ((AbstractListValue) rval).iterator();
|
||||
LazyValue expr = lv.get(1);
|
||||
//scoping
|
||||
LazyValue _val = c.getVariable("_");
|
||||
LazyValue _iter = c.getVariable("_i");
|
||||
List<Value> result = new ArrayList<>();
|
||||
for (int i=0; iterator.hasNext(); i++)
|
||||
final Value rval = lv.get(0).evalValue(c, Context.NONE);
|
||||
if (rval.isNull())
|
||||
{
|
||||
Value next = iterator.next();
|
||||
String var = next.boundVariable;
|
||||
return ListValue.lazyEmpty();
|
||||
}
|
||||
if (!(rval instanceof final AbstractListValue alv))
|
||||
{
|
||||
throw new InternalExpressionException("First argument of 'map' function should be a list or iterator");
|
||||
}
|
||||
final Iterator<Value> iterator = alv.iterator();
|
||||
final LazyValue expr = lv.get(1);
|
||||
//scoping
|
||||
final LazyValue _val = c.getVariable("_");
|
||||
final LazyValue _iter = c.getVariable("_i");
|
||||
final List<Value> result = new ArrayList<>();
|
||||
for (int i = 0; iterator.hasNext(); i++)
|
||||
{
|
||||
final Value next = iterator.next();
|
||||
final String var = next.boundVariable;
|
||||
next.bindTo("_");
|
||||
int doYouReally = i;
|
||||
final int doYouReally = i;
|
||||
c.setVariable("_", (cc, tt) -> next);
|
||||
c.setVariable("_i", (cc, tt) -> new NumericValue(doYouReally).bindTo("_i"));
|
||||
try
|
||||
{
|
||||
result.add(expr.evalValue(c, t));
|
||||
}
|
||||
catch (BreakStatement | ContinueStatement stmt)
|
||||
catch (final BreakStatement | ContinueStatement stmt)
|
||||
{
|
||||
if (stmt.retval != null) result.add(stmt.retval);
|
||||
if (stmt.retval != null)
|
||||
{
|
||||
result.add(stmt.retval);
|
||||
}
|
||||
if (stmt instanceof BreakStatement)
|
||||
{
|
||||
next.boundVariable = var;
|
||||
@@ -134,11 +167,11 @@ public class Loops {
|
||||
next.boundVariable = var;
|
||||
}
|
||||
((AbstractListValue) rval).fatality();
|
||||
Value ret = ListValue.wrap(result);
|
||||
final Value ret = ListValue.wrap(result);
|
||||
//revering scope
|
||||
c.setVariable("_", _val);
|
||||
c.setVariable("_i", _iter);
|
||||
return (cc, tt) -> ret;
|
||||
return (cc, tt) -> ret;
|
||||
});
|
||||
|
||||
// grep(list or num, expr) => list
|
||||
@@ -146,32 +179,42 @@ public class Loops {
|
||||
// produces list of values for which the expression is true
|
||||
expression.addLazyFunction("filter", 2, (c, t, lv) ->
|
||||
{
|
||||
Value rval= lv.get(0).evalValue(c, Context.NONE);
|
||||
if (rval.isNull()) return ListValue.lazyEmpty();
|
||||
if (!(rval instanceof AbstractListValue))
|
||||
throw new InternalExpressionException("First argument of 'filter' function should be a list or iterator");
|
||||
Iterator<Value> iterator = ((AbstractListValue) rval).iterator();
|
||||
LazyValue expr = lv.get(1);
|
||||
//scoping
|
||||
LazyValue _val = c.getVariable("_");
|
||||
LazyValue _iter = c.getVariable("_i");
|
||||
List<Value> result = new ArrayList<>();
|
||||
for (int i=0; iterator.hasNext(); i++)
|
||||
final Value rval = lv.get(0).evalValue(c, Context.NONE);
|
||||
if (rval.isNull())
|
||||
{
|
||||
Value next = iterator.next();
|
||||
String var = next.boundVariable;
|
||||
return ListValue.lazyEmpty();
|
||||
}
|
||||
if (!(rval instanceof final AbstractListValue alv))
|
||||
{
|
||||
throw new InternalExpressionException("First argument of 'filter' function should be a list or iterator");
|
||||
}
|
||||
final Iterator<Value> iterator = alv.iterator();
|
||||
final LazyValue expr = lv.get(1);
|
||||
//scoping
|
||||
final LazyValue _val = c.getVariable("_");
|
||||
final LazyValue _iter = c.getVariable("_i");
|
||||
final List<Value> result = new ArrayList<>();
|
||||
for (int i = 0; iterator.hasNext(); i++)
|
||||
{
|
||||
final Value next = iterator.next();
|
||||
final String var = next.boundVariable;
|
||||
next.bindTo("_");
|
||||
int seriously = i;
|
||||
final int seriously = i;
|
||||
c.setVariable("_", (cc, tt) -> next);
|
||||
c.setVariable("_i", (cc, tt) -> new NumericValue(seriously).bindTo("_i"));
|
||||
try
|
||||
{
|
||||
if(expr.evalValue(c, Context.BOOLEAN).getBoolean())
|
||||
if (expr.evalValue(c, Context.BOOLEAN).getBoolean())
|
||||
{
|
||||
result.add(next);
|
||||
}
|
||||
}
|
||||
catch (BreakStatement | ContinueStatement stmt)
|
||||
catch (final BreakStatement | ContinueStatement stmt)
|
||||
{
|
||||
if (stmt.retval != null && stmt.retval.getBoolean()) result.add(next);
|
||||
if (stmt.retval != null && stmt.retval.getBoolean())
|
||||
{
|
||||
result.add(next);
|
||||
}
|
||||
if (stmt instanceof BreakStatement)
|
||||
{
|
||||
next.boundVariable = var;
|
||||
@@ -181,7 +224,7 @@ public class Loops {
|
||||
next.boundVariable = var;
|
||||
}
|
||||
((AbstractListValue) rval).fatality();
|
||||
Value ret = ListValue.wrap(result);
|
||||
final Value ret = ListValue.wrap(result);
|
||||
//revering scope
|
||||
c.setVariable("_", _val);
|
||||
c.setVariable("_i", _iter);
|
||||
@@ -193,41 +236,45 @@ public class Loops {
|
||||
// returns first element on the list for which the expr is true
|
||||
expression.addLazyFunction("first", 2, (c, t, lv) ->
|
||||
{
|
||||
|
||||
Value rval= lv.get(0).evalValue(c, Context.NONE);
|
||||
if (rval.isNull()) return LazyValue.NULL;
|
||||
if (!(rval instanceof AbstractListValue))
|
||||
throw new InternalExpressionException("First argument of 'first' function should be a list or iterator");
|
||||
Iterator<Value> iterator = ((AbstractListValue) rval).iterator();
|
||||
LazyValue expr = lv.get(1);
|
||||
//scoping
|
||||
LazyValue _val = c.getVariable("_");
|
||||
LazyValue _iter = c.getVariable("_i");
|
||||
Value result = Value.NULL;
|
||||
for (int i=0; iterator.hasNext(); i++)
|
||||
final Value rval = lv.get(0).evalValue(c, Context.NONE);
|
||||
if (rval.isNull())
|
||||
{
|
||||
Value next = iterator.next();
|
||||
String var = next.boundVariable;
|
||||
return LazyValue.NULL;
|
||||
}
|
||||
if (!(rval instanceof final AbstractListValue alv))
|
||||
{
|
||||
throw new InternalExpressionException("First argument of 'first' function should be a list or iterator");
|
||||
}
|
||||
final Iterator<Value> iterator = alv.iterator();
|
||||
final LazyValue expr = lv.get(1);
|
||||
//scoping
|
||||
final LazyValue _val = c.getVariable("_");
|
||||
final LazyValue _iter = c.getVariable("_i");
|
||||
Value result = Value.NULL;
|
||||
for (int i = 0; iterator.hasNext(); i++)
|
||||
{
|
||||
final Value next = iterator.next();
|
||||
final String var = next.boundVariable;
|
||||
next.bindTo("_");
|
||||
int seriously = i;
|
||||
final int seriously = i;
|
||||
c.setVariable("_", (cc, tt) -> next);
|
||||
c.setVariable("_i", (cc, tt) -> new NumericValue(seriously).bindTo("_i"));
|
||||
try
|
||||
{
|
||||
if(expr.evalValue(c, Context.BOOLEAN).getBoolean())
|
||||
if (expr.evalValue(c, Context.BOOLEAN).getBoolean())
|
||||
{
|
||||
result = next;
|
||||
next.boundVariable = var;
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (BreakStatement stmt)
|
||||
catch (final BreakStatement stmt)
|
||||
{
|
||||
result = stmt.retval == null? next : stmt.retval;
|
||||
result = stmt.retval == null ? next : stmt.retval;
|
||||
next.boundVariable = var;
|
||||
break;
|
||||
}
|
||||
catch (ContinueStatement ignored)
|
||||
catch (final ContinueStatement ignored)
|
||||
{
|
||||
throw new InternalExpressionException("'continue' inside 'first' function has no sense");
|
||||
}
|
||||
@@ -235,7 +282,7 @@ public class Loops {
|
||||
}
|
||||
//revering scope
|
||||
((AbstractListValue) rval).fatality();
|
||||
Value whyWontYouTrustMeJava = result;
|
||||
final Value whyWontYouTrustMeJava = result;
|
||||
c.setVariable("_", _val);
|
||||
c.setVariable("_i", _iter);
|
||||
return (cc, tt) -> whyWontYouTrustMeJava;
|
||||
@@ -246,25 +293,30 @@ public class Loops {
|
||||
// returns true if expr is true for all items
|
||||
expression.addLazyFunction("all", 2, (c, t, lv) ->
|
||||
{
|
||||
Value rval= lv.get(0).evalValue(c, Context.NONE);
|
||||
if (rval.isNull()) return LazyValue.TRUE;
|
||||
if (!(rval instanceof AbstractListValue))
|
||||
throw new InternalExpressionException("First argument of 'all' function should be a list or iterator");
|
||||
Iterator<Value> iterator = ((AbstractListValue) rval).iterator();
|
||||
LazyValue expr = lv.get(1);
|
||||
//scoping
|
||||
LazyValue _val = c.getVariable("_");
|
||||
LazyValue _iter = c.getVariable("_i");
|
||||
LazyValue result = LazyValue.TRUE;
|
||||
for (int i=0; iterator.hasNext(); i++)
|
||||
final Value rval = lv.get(0).evalValue(c, Context.NONE);
|
||||
if (rval.isNull())
|
||||
{
|
||||
Value next = iterator.next();
|
||||
String var = next.boundVariable;
|
||||
return LazyValue.TRUE;
|
||||
}
|
||||
if (!(rval instanceof final AbstractListValue alv))
|
||||
{
|
||||
throw new InternalExpressionException("First argument of 'all' function should be a list or iterator");
|
||||
}
|
||||
final Iterator<Value> iterator = alv.iterator();
|
||||
final LazyValue expr = lv.get(1);
|
||||
//scoping
|
||||
final LazyValue _val = c.getVariable("_");
|
||||
final LazyValue _iter = c.getVariable("_i");
|
||||
LazyValue result = LazyValue.TRUE;
|
||||
for (int i = 0; iterator.hasNext(); i++)
|
||||
{
|
||||
final Value next = iterator.next();
|
||||
final String var = next.boundVariable;
|
||||
next.bindTo("_");
|
||||
int seriously = i;
|
||||
final int seriously = i;
|
||||
c.setVariable("_", (cc, tt) -> next);
|
||||
c.setVariable("_i", (cc, tt) -> new NumericValue(seriously).bindTo("_i"));
|
||||
if(!expr.evalValue(c, Context.BOOLEAN).getBoolean())
|
||||
if (!expr.evalValue(c, Context.BOOLEAN).getBoolean())
|
||||
{
|
||||
result = LazyValue.FALSE;
|
||||
next.boundVariable = var;
|
||||
@@ -282,10 +334,10 @@ public class Loops {
|
||||
// runs traditional for(init, condition, increment, body) tri-argument for loop with body in between
|
||||
expression.addLazyFunction("c_for", 4, (c, t, lv) ->
|
||||
{
|
||||
LazyValue initial = lv.get(0);
|
||||
LazyValue condition = lv.get(1);
|
||||
LazyValue increment = lv.get(2);
|
||||
LazyValue body = lv.get(3);
|
||||
final LazyValue initial = lv.get(0);
|
||||
final LazyValue condition = lv.get(1);
|
||||
final LazyValue increment = lv.get(2);
|
||||
final LazyValue body = lv.get(3);
|
||||
int iterations = 0;
|
||||
for (initial.evalValue(c, Context.VOID); condition.evalValue(c, Context.BOOLEAN).getBoolean(); increment.evalValue(c, Context.VOID))
|
||||
{
|
||||
@@ -293,16 +345,16 @@ public class Loops {
|
||||
{
|
||||
body.evalValue(c, Context.VOID);
|
||||
}
|
||||
catch (BreakStatement stmt)
|
||||
catch (final BreakStatement stmt)
|
||||
{
|
||||
break;
|
||||
}
|
||||
catch (ContinueStatement ignored)
|
||||
catch (final ContinueStatement ignored)
|
||||
{
|
||||
}
|
||||
iterations++;
|
||||
}
|
||||
int finalIterations = iterations;
|
||||
final int finalIterations = iterations;
|
||||
return (cc, tt) -> new NumericValue(finalIterations);
|
||||
});
|
||||
|
||||
@@ -311,22 +363,27 @@ public class Loops {
|
||||
// can be substituted for first and all, but first is more efficient and all doesn't require knowing list size
|
||||
expression.addLazyFunction("for", 2, (c, t, lv) ->
|
||||
{
|
||||
Value rval= lv.get(0).evalValue(c, Context.NONE);
|
||||
if (rval.isNull()) return LazyValue.ZERO;
|
||||
if (!(rval instanceof AbstractListValue))
|
||||
throw new InternalExpressionException("First argument of 'for' function should be a list or iterator");
|
||||
Iterator<Value> iterator = ((AbstractListValue) rval).iterator();
|
||||
LazyValue expr = lv.get(1);
|
||||
//scoping
|
||||
LazyValue _val = c.getVariable("_");
|
||||
LazyValue _ite = c.getVariable("_i");
|
||||
int successCount = 0;
|
||||
for (int i=0; iterator.hasNext(); i++)
|
||||
final Value rval = lv.get(0).evalValue(c, Context.NONE);
|
||||
if (rval.isNull())
|
||||
{
|
||||
Value next = iterator.next();
|
||||
String var = next.boundVariable;
|
||||
return LazyValue.ZERO;
|
||||
}
|
||||
if (!(rval instanceof final AbstractListValue alv))
|
||||
{
|
||||
throw new InternalExpressionException("First argument of 'for' function should be a list or iterator");
|
||||
}
|
||||
final Iterator<Value> iterator = alv.iterator();
|
||||
final LazyValue expr = lv.get(1);
|
||||
//scoping
|
||||
final LazyValue _val = c.getVariable("_");
|
||||
final LazyValue _ite = c.getVariable("_i");
|
||||
int successCount = 0;
|
||||
for (int i = 0; iterator.hasNext(); i++)
|
||||
{
|
||||
final Value next = iterator.next();
|
||||
final String var = next.boundVariable;
|
||||
next.bindTo("_");
|
||||
int seriously = i;
|
||||
final int seriously = i;
|
||||
c.setVariable("_", (cc, tt) -> next);
|
||||
c.setVariable("_i", (cc, tt) -> new NumericValue(seriously).bindTo("_i"));
|
||||
Value result = Value.FALSE;
|
||||
@@ -334,24 +391,29 @@ public class Loops {
|
||||
{
|
||||
result = expr.evalValue(c, t);
|
||||
}
|
||||
catch (BreakStatement | ContinueStatement stmt)
|
||||
catch (final BreakStatement | ContinueStatement stmt)
|
||||
{
|
||||
if (stmt.retval != null) result = stmt.retval;
|
||||
if (stmt.retval != null)
|
||||
{
|
||||
result = stmt.retval;
|
||||
}
|
||||
if (stmt instanceof BreakStatement)
|
||||
{
|
||||
next.boundVariable = var;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(t != Context.VOID && result.getBoolean())
|
||||
if (t != Context.VOID && result.getBoolean())
|
||||
{
|
||||
successCount++;
|
||||
}
|
||||
next.boundVariable = var;
|
||||
}
|
||||
//revering scope
|
||||
((AbstractListValue) rval).fatality();
|
||||
c.setVariable("_", _val);
|
||||
c.setVariable("_i", _ite);
|
||||
long promiseWontChange = successCount;
|
||||
final long promiseWontChange = successCount;
|
||||
return (cc, tt) -> new NumericValue(promiseWontChange);
|
||||
});
|
||||
|
||||
@@ -363,32 +425,37 @@ public class Loops {
|
||||
expression.addLazyFunction("reduce", 3, (c, t, lv) ->
|
||||
{
|
||||
|
||||
Value rval= lv.get(0).evalValue(c, Context.NONE);
|
||||
if (rval.isNull()) return ListValue.lazyEmpty();
|
||||
if (!(rval instanceof AbstractListValue))
|
||||
final Value rval = lv.get(0).evalValue(c, Context.NONE);
|
||||
if (rval.isNull())
|
||||
{
|
||||
return ListValue.lazyEmpty();
|
||||
}
|
||||
if (!(rval instanceof final AbstractListValue alv))
|
||||
{
|
||||
throw new InternalExpressionException("First argument of 'reduce' should be a list or iterator");
|
||||
LazyValue expr = lv.get(1);
|
||||
}
|
||||
final LazyValue expr = lv.get(1);
|
||||
Value acc = lv.get(2).evalValue(c, Context.NONE);
|
||||
Iterator<Value> iterator = ((AbstractListValue) rval).iterator();
|
||||
final Iterator<Value> iterator = alv.iterator();
|
||||
|
||||
if (!iterator.hasNext())
|
||||
{
|
||||
Value seriouslyWontChange = acc;
|
||||
final Value seriouslyWontChange = acc;
|
||||
return (cc, tt) -> seriouslyWontChange;
|
||||
}
|
||||
|
||||
//scoping
|
||||
LazyValue _val = c.getVariable("_");
|
||||
LazyValue _acc = c.getVariable("_a");
|
||||
LazyValue _ite = c.getVariable("_i");
|
||||
final LazyValue _val = c.getVariable("_");
|
||||
final LazyValue _acc = c.getVariable("_a");
|
||||
final LazyValue _ite = c.getVariable("_i");
|
||||
|
||||
for (int i=0; iterator.hasNext(); i++)
|
||||
for (int i = 0; iterator.hasNext(); i++)
|
||||
{
|
||||
Value next = iterator.next();
|
||||
String var = next.boundVariable;
|
||||
final Value next = iterator.next();
|
||||
final String var = next.boundVariable;
|
||||
next.bindTo("_");
|
||||
Value promiseWontChangeYou = acc;
|
||||
int seriously = i;
|
||||
final Value promiseWontChangeYou = acc;
|
||||
final int seriously = i;
|
||||
c.setVariable("_a", (cc, tt) -> promiseWontChangeYou.bindTo("_a"));
|
||||
c.setVariable("_", (cc, tt) -> next);
|
||||
c.setVariable("_i", (cc, tt) -> new NumericValue(seriously).bindTo("_i"));
|
||||
@@ -396,9 +463,12 @@ public class Loops {
|
||||
{
|
||||
acc = expr.evalValue(c, t);
|
||||
}
|
||||
catch (BreakStatement | ContinueStatement stmt)
|
||||
catch (final BreakStatement | ContinueStatement stmt)
|
||||
{
|
||||
if (stmt.retval != null) acc = stmt.retval;
|
||||
if (stmt.retval != null)
|
||||
{
|
||||
acc = stmt.retval;
|
||||
}
|
||||
if (stmt instanceof BreakStatement)
|
||||
{
|
||||
next.boundVariable = var;
|
||||
@@ -413,7 +483,7 @@ public class Loops {
|
||||
c.setVariable("_", _val);
|
||||
c.setVariable("_i", _ite);
|
||||
|
||||
Value hopeItsEnoughPromise = acc;
|
||||
final Value hopeItsEnoughPromise = acc;
|
||||
return (cc, tt) -> hopeItsEnoughPromise;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -21,8 +21,10 @@ import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class Operators {
|
||||
public static final Map<String, Integer> precedence = new HashMap<String, Integer>() {{
|
||||
public class Operators
|
||||
{
|
||||
public static final Map<String, Integer> precedence = new HashMap<>()
|
||||
{{
|
||||
put("attribute~:", 80);
|
||||
put("unary+-!...", 60);
|
||||
put("exponent^", 40);
|
||||
@@ -37,44 +39,68 @@ public class Operators {
|
||||
put("nextop;", 1);
|
||||
}};
|
||||
|
||||
public static void apply(Expression expression)
|
||||
public static void apply(final Expression expression)
|
||||
{
|
||||
expression.addBinaryOperator("+", precedence.get("addition+-"), true, Value::add);
|
||||
expression.addFunction("sum", lv -> {
|
||||
int size = lv.size();
|
||||
if (size == 0) return Value.NULL;
|
||||
final int size = lv.size();
|
||||
if (size == 0)
|
||||
{
|
||||
return Value.NULL;
|
||||
}
|
||||
Value accumulator = lv.get(0);
|
||||
for (Value v: lv.subList(1, size)) accumulator = accumulator.add(v);
|
||||
for (final Value v : lv.subList(1, size))
|
||||
{
|
||||
accumulator = accumulator.add(v);
|
||||
}
|
||||
return accumulator;
|
||||
});
|
||||
expression.addFunctionalEquivalence("+", "sum");
|
||||
|
||||
expression.addBinaryOperator("-", precedence.get("addition+-"), true, Value::subtract);
|
||||
expression.addFunction("difference", lv -> {
|
||||
int size = lv.size();
|
||||
if (size == 0) return Value.NULL;
|
||||
final int size = lv.size();
|
||||
if (size == 0)
|
||||
{
|
||||
return Value.NULL;
|
||||
}
|
||||
Value accumulator = lv.get(0);
|
||||
for (Value v: lv.subList(1, size)) accumulator = accumulator.subtract(v);
|
||||
for (final Value v : lv.subList(1, size))
|
||||
{
|
||||
accumulator = accumulator.subtract(v);
|
||||
}
|
||||
return accumulator;
|
||||
});
|
||||
expression.addFunctionalEquivalence("-", "difference");
|
||||
|
||||
expression.addBinaryOperator("*", precedence.get("multiplication*/%"), true, Value::multiply);
|
||||
expression.addFunction("product", lv -> {
|
||||
int size = lv.size();
|
||||
if (size == 0) return Value.NULL;
|
||||
final int size = lv.size();
|
||||
if (size == 0)
|
||||
{
|
||||
return Value.NULL;
|
||||
}
|
||||
Value accumulator = lv.get(0);
|
||||
for (Value v: lv.subList(1, size)) accumulator = accumulator.multiply(v);
|
||||
for (final Value v : lv.subList(1, size))
|
||||
{
|
||||
accumulator = accumulator.multiply(v);
|
||||
}
|
||||
return accumulator;
|
||||
});
|
||||
expression.addFunctionalEquivalence("*", "product");
|
||||
|
||||
expression.addBinaryOperator("/", precedence.get("multiplication*/%"), true, Value::divide);
|
||||
expression.addFunction("quotient", lv -> {
|
||||
int size = lv.size();
|
||||
if (size == 0) return Value.NULL;
|
||||
final int size = lv.size();
|
||||
if (size == 0)
|
||||
{
|
||||
return Value.NULL;
|
||||
}
|
||||
Value accumulator = lv.get(0);
|
||||
for (Value v: lv.subList(1, size)) accumulator = accumulator.divide(v);
|
||||
for (final Value v : lv.subList(1, size))
|
||||
{
|
||||
accumulator = accumulator.divide(v);
|
||||
}
|
||||
return accumulator;
|
||||
});
|
||||
expression.addFunctionalEquivalence("/", "quotient");
|
||||
@@ -85,51 +111,79 @@ public class Operators {
|
||||
new NumericValue(java.lang.Math.pow(NumericValue.asNumber(v1).getDouble(), NumericValue.asNumber(v2).getDouble())));
|
||||
|
||||
expression.addFunction("bitwise_and", lv -> {
|
||||
int size = lv.size();
|
||||
if (size == 0) return Value.NULL;
|
||||
final int size = lv.size();
|
||||
if (size == 0)
|
||||
{
|
||||
return Value.NULL;
|
||||
}
|
||||
long accumulator = NumericValue.asNumber(lv.get(0)).getLong();
|
||||
for (Value v: lv.subList(1, size)) accumulator = accumulator & NumericValue.asNumber(v).getLong();
|
||||
for (final Value v : lv.subList(1, size))
|
||||
{
|
||||
accumulator = accumulator & NumericValue.asNumber(v).getLong();
|
||||
}
|
||||
return new NumericValue(accumulator);
|
||||
});
|
||||
|
||||
expression.addFunction("bitwise_xor", lv -> {
|
||||
int size = lv.size();
|
||||
if (size == 0) return Value.NULL;
|
||||
final int size = lv.size();
|
||||
if (size == 0)
|
||||
{
|
||||
return Value.NULL;
|
||||
}
|
||||
long accumulator = NumericValue.asNumber(lv.get(0)).getLong();
|
||||
for (Value v: lv.subList(1, size)) accumulator = accumulator ^ NumericValue.asNumber(v).getLong();
|
||||
for (final Value v : lv.subList(1, size))
|
||||
{
|
||||
accumulator = accumulator ^ NumericValue.asNumber(v).getLong();
|
||||
}
|
||||
return new NumericValue(accumulator);
|
||||
});
|
||||
|
||||
expression.addFunction("bitwise_or", lv -> {
|
||||
int size = lv.size();
|
||||
if (size == 0) return Value.NULL;
|
||||
final int size = lv.size();
|
||||
if (size == 0)
|
||||
{
|
||||
return Value.NULL;
|
||||
}
|
||||
long accumulator = NumericValue.asNumber(lv.get(0)).getLong();
|
||||
for (Value v: lv.subList(1, size)) accumulator = accumulator | NumericValue.asNumber(v).getLong();
|
||||
for (final Value v : lv.subList(1, size))
|
||||
{
|
||||
accumulator = accumulator | NumericValue.asNumber(v).getLong();
|
||||
}
|
||||
return new NumericValue(accumulator);
|
||||
});
|
||||
|
||||
// lazy cause RHS is only conditional
|
||||
expression.addLazyBinaryOperator("&&", precedence.get("and&&"), false, true, t -> Context.Type.BOOLEAN, (c, t, lv1, lv2) ->
|
||||
{ // todo check how is optimizations going
|
||||
Value v1 = lv1.evalValue(c, Context.BOOLEAN);
|
||||
if (!v1.getBoolean()) return (cc, tt) -> v1;
|
||||
return lv2;
|
||||
final Value v1 = lv1.evalValue(c, Context.BOOLEAN);
|
||||
return v1.getBoolean() ? lv2 : ((cc, tt) -> v1);
|
||||
});
|
||||
|
||||
expression.addPureLazyFunction("and", -1, t -> Context.Type.BOOLEAN, (c, t, lv) -> {
|
||||
int last = lv.size()-1;
|
||||
if (last == -1) return LazyValue.TRUE;
|
||||
for (LazyValue l: lv.subList(0, last))
|
||||
final int last = lv.size() - 1;
|
||||
if (last == -1)
|
||||
{
|
||||
Value val = l.evalValue(c, Context.Type.BOOLEAN);
|
||||
if (val instanceof FunctionUnpackedArgumentsValue)
|
||||
return LazyValue.TRUE;
|
||||
}
|
||||
for (final LazyValue l : lv.subList(0, last))
|
||||
{
|
||||
final Value val = l.evalValue(c, Context.Type.BOOLEAN);
|
||||
if (val instanceof final FunctionUnpackedArgumentsValue fuav)
|
||||
{
|
||||
for (Value it : (FunctionUnpackedArgumentsValue) val)
|
||||
if (!it.getBoolean()) return (cc, tt) -> it;
|
||||
for (final Value it : fuav)
|
||||
{
|
||||
if (!it.getBoolean())
|
||||
{
|
||||
return (cc, tt) -> it;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!val.getBoolean()) return (cc, tt) -> val;
|
||||
if (!val.getBoolean())
|
||||
{
|
||||
return (cc, tt) -> val;
|
||||
}
|
||||
}
|
||||
}
|
||||
return lv.get(last);
|
||||
@@ -139,25 +193,35 @@ public class Operators {
|
||||
// lazy cause RHS is only conditional
|
||||
expression.addLazyBinaryOperator("||", precedence.get("or||"), false, true, t -> Context.Type.BOOLEAN, (c, t, lv1, lv2) ->
|
||||
{
|
||||
Value v1 = lv1.evalValue(c, Context.BOOLEAN);
|
||||
if (v1.getBoolean()) return (cc, tt) -> v1;
|
||||
return lv2;
|
||||
final Value v1 = lv1.evalValue(c, Context.BOOLEAN);
|
||||
return v1.getBoolean() ? ((cc, tt) -> v1) : lv2;
|
||||
});
|
||||
|
||||
expression.addPureLazyFunction("or", -1, t -> Context.Type.BOOLEAN, (c, t, lv) -> {
|
||||
int last = lv.size()-1;
|
||||
if (last == -1) return LazyValue.FALSE;
|
||||
for (LazyValue l: lv.subList(0, last))
|
||||
final int last = lv.size() - 1;
|
||||
if (last == -1)
|
||||
{
|
||||
Value val = l.evalValue(c, Context.Type.BOOLEAN);
|
||||
if (val instanceof FunctionUnpackedArgumentsValue)
|
||||
return LazyValue.FALSE;
|
||||
}
|
||||
for (final LazyValue l : lv.subList(0, last))
|
||||
{
|
||||
final Value val = l.evalValue(c, Context.Type.BOOLEAN);
|
||||
if (val instanceof final FunctionUnpackedArgumentsValue fuav)
|
||||
{
|
||||
for (Value it : (FunctionUnpackedArgumentsValue) val)
|
||||
if (it.getBoolean()) return (cc, tt) -> it;
|
||||
for (final Value it : fuav)
|
||||
{
|
||||
if (it.getBoolean())
|
||||
{
|
||||
return (cc, tt) -> it;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (val.getBoolean()) return (cc, tt) -> val;
|
||||
if (val.getBoolean())
|
||||
{
|
||||
return (cc, tt) -> val;
|
||||
}
|
||||
}
|
||||
}
|
||||
return lv.get(last);
|
||||
@@ -169,12 +233,18 @@ public class Operators {
|
||||
expression.addBinaryOperator(">", precedence.get("compare>=><=<"), false, (v1, v2) ->
|
||||
BooleanValue.of(v1.compareTo(v2) > 0));
|
||||
expression.addFunction("decreasing", lv -> {
|
||||
int size = lv.size();
|
||||
if (size < 2) return Value.TRUE;
|
||||
Value prev = lv.get(0);
|
||||
for (Value next: lv.subList(1, size))
|
||||
final int size = lv.size();
|
||||
if (size < 2)
|
||||
{
|
||||
if (prev.compareTo(next) <= 0) return Value.FALSE;
|
||||
return Value.TRUE;
|
||||
}
|
||||
Value prev = lv.get(0);
|
||||
for (final Value next : lv.subList(1, size))
|
||||
{
|
||||
if (prev.compareTo(next) <= 0)
|
||||
{
|
||||
return Value.FALSE;
|
||||
}
|
||||
prev = next;
|
||||
}
|
||||
return Value.TRUE;
|
||||
@@ -184,12 +254,18 @@ public class Operators {
|
||||
expression.addBinaryOperator(">=", precedence.get("compare>=><=<"), false, (v1, v2) ->
|
||||
BooleanValue.of(v1.compareTo(v2) >= 0));
|
||||
expression.addFunction("nonincreasing", lv -> {
|
||||
int size = lv.size();
|
||||
if (size < 2) return Value.TRUE;
|
||||
Value prev = lv.get(0);
|
||||
for (Value next: lv.subList(1, size))
|
||||
final int size = lv.size();
|
||||
if (size < 2)
|
||||
{
|
||||
if (prev.compareTo(next) < 0) return Value.FALSE;
|
||||
return Value.TRUE;
|
||||
}
|
||||
Value prev = lv.get(0);
|
||||
for (final Value next : lv.subList(1, size))
|
||||
{
|
||||
if (prev.compareTo(next) < 0)
|
||||
{
|
||||
return Value.FALSE;
|
||||
}
|
||||
prev = next;
|
||||
}
|
||||
return Value.TRUE;
|
||||
@@ -199,12 +275,18 @@ public class Operators {
|
||||
expression.addBinaryOperator("<", precedence.get("compare>=><=<"), false, (v1, v2) ->
|
||||
BooleanValue.of(v1.compareTo(v2) < 0));
|
||||
expression.addFunction("increasing", lv -> {
|
||||
int size = lv.size();
|
||||
if (size < 2) return Value.TRUE;
|
||||
Value prev = lv.get(0);
|
||||
for (Value next: lv.subList(1, size))
|
||||
final int size = lv.size();
|
||||
if (size < 2)
|
||||
{
|
||||
if (prev.compareTo(next) >= 0) return Value.FALSE;
|
||||
return Value.TRUE;
|
||||
}
|
||||
Value prev = lv.get(0);
|
||||
for (final Value next : lv.subList(1, size))
|
||||
{
|
||||
if (prev.compareTo(next) >= 0)
|
||||
{
|
||||
return Value.FALSE;
|
||||
}
|
||||
prev = next;
|
||||
}
|
||||
return Value.TRUE;
|
||||
@@ -214,79 +296,84 @@ public class Operators {
|
||||
expression.addBinaryOperator("<=", precedence.get("compare>=><=<"), false, (v1, v2) ->
|
||||
BooleanValue.of(v1.compareTo(v2) <= 0));
|
||||
expression.addFunction("nondecreasing", lv -> {
|
||||
int size = lv.size();
|
||||
if (size < 2) return Value.TRUE;
|
||||
Value prev = lv.get(0);
|
||||
for (Value next: lv.subList(1, size))
|
||||
final int size = lv.size();
|
||||
if (size < 2)
|
||||
{
|
||||
if (prev.compareTo(next) > 0) return Value.FALSE;
|
||||
return Value.TRUE;
|
||||
}
|
||||
Value prev = lv.get(0);
|
||||
for (final Value next : lv.subList(1, size))
|
||||
{
|
||||
if (prev.compareTo(next) > 0)
|
||||
{
|
||||
return Value.FALSE;
|
||||
}
|
||||
prev = next;
|
||||
}
|
||||
return Value.TRUE;
|
||||
});
|
||||
expression.addFunctionalEquivalence("<=", "nondecreasing");
|
||||
|
||||
expression.addMathematicalBinaryIntFunction("bitwise_shift_left", (num, amount) -> {
|
||||
return num << amount;
|
||||
});
|
||||
expression.addMathematicalBinaryIntFunction("bitwise_shift_right", (num, amount) -> {
|
||||
return num >> amount;
|
||||
});
|
||||
expression.addMathematicalBinaryIntFunction("bitwise_shift_left", (num, amount) -> num << amount);
|
||||
expression.addMathematicalBinaryIntFunction("bitwise_shift_right", (num, amount) -> num >> amount);
|
||||
expression.addMathematicalBinaryIntFunction("bitwise_roll_left", (num, num2) -> {
|
||||
long amount = num2 % 64;
|
||||
|
||||
long amountToRoll = 64 - amount;
|
||||
long rolledBits = ((-1L) >> amountToRoll) << amountToRoll;
|
||||
long rolledAmount = (num & rolledBits) >> amountToRoll;
|
||||
final long amount = num2 % 64;
|
||||
final long amountToRoll = 64 - amount;
|
||||
final long rolledBits = ((-1L) >> amountToRoll) << amountToRoll;
|
||||
final long rolledAmount = (num & rolledBits) >> amountToRoll;
|
||||
return num << amount | rolledAmount;
|
||||
});
|
||||
expression.addMathematicalBinaryIntFunction("bitwise_roll_right", (num, num2) -> {
|
||||
long amount = num2 % 64;
|
||||
|
||||
long amountToRoll = 64 - amount;
|
||||
long rolledBits = ((-1L) << amountToRoll) >> amountToRoll;
|
||||
long rolledAmount = (num & rolledBits) << amountToRoll;
|
||||
final long amount = num2 % 64;
|
||||
final long amountToRoll = 64 - amount;
|
||||
final long rolledBits = ((-1L) << amountToRoll) >> amountToRoll;
|
||||
final long rolledAmount = (num & rolledBits) << amountToRoll;
|
||||
return num >> amount | rolledAmount;
|
||||
});
|
||||
expression.addMathematicalUnaryIntFunction("bitwise_not", d -> {
|
||||
long num = d.longValue();
|
||||
return num ^ (-1L);
|
||||
final long num = (long) d;
|
||||
return ~num;
|
||||
});
|
||||
expression.addMathematicalUnaryIntFunction("bitwise_popcount", d -> Long.valueOf(Long.bitCount(d.longValue())));
|
||||
|
||||
expression.addMathematicalUnaryIntFunction("bitwise_popcount", d -> (long) Long.bitCount((long)d));
|
||||
expression.addMathematicalUnaryIntFunction("double_to_long_bits", Double::doubleToLongBits);
|
||||
expression.addUnaryFunction("long_to_double_bits", v -> {
|
||||
return new NumericValue(Double.longBitsToDouble(NumericValue.asNumber(v).getLong()));
|
||||
});
|
||||
|
||||
|
||||
expression.addUnaryFunction("long_to_double_bits", v ->
|
||||
new NumericValue(Double.longBitsToDouble(NumericValue.asNumber(v).getLong())));
|
||||
expression.addBinaryOperator("==", precedence.get("equal==!="), false, (v1, v2) ->
|
||||
v1.equals(v2) ? Value.TRUE : Value.FALSE);
|
||||
expression.addFunction("equal", lv -> {
|
||||
int size = lv.size();
|
||||
if (size < 2) return Value.TRUE;
|
||||
Value prev = lv.get(0);
|
||||
for (Value next: lv.subList(1, size))
|
||||
final int size = lv.size();
|
||||
if (size < 2)
|
||||
{
|
||||
if (!prev.equals(next)) return Value.FALSE;
|
||||
return Value.TRUE;
|
||||
}
|
||||
Value prev = lv.get(0);
|
||||
for (final Value next : lv.subList(1, size))
|
||||
{
|
||||
if (!prev.equals(next))
|
||||
{
|
||||
return Value.FALSE;
|
||||
}
|
||||
prev = next;
|
||||
}
|
||||
return Value.TRUE;
|
||||
});
|
||||
expression.addFunctionalEquivalence("==", "equal");
|
||||
|
||||
|
||||
expression.addBinaryOperator("!=", precedence.get("equal==!="), false, (v1, v2) ->
|
||||
v1.equals(v2) ? Value.FALSE : Value.TRUE);
|
||||
expression.addFunction("unique", lv -> {
|
||||
int size = lv.size();
|
||||
if (size < 2) return Value.TRUE;
|
||||
final int size = lv.size();
|
||||
if (size < 2)
|
||||
{
|
||||
return Value.TRUE;
|
||||
}
|
||||
// need to order them so same obejects will be next to each other.
|
||||
lv.sort(Comparator.comparingInt(Value::hashCode));
|
||||
Value prev = lv.get(0);
|
||||
for (Value next: lv.subList(1, size))
|
||||
for (final Value next : lv.subList(1, size))
|
||||
{
|
||||
if (prev.equals(next)) return Value.FALSE;
|
||||
if (prev.equals(next))
|
||||
{
|
||||
return Value.FALSE;
|
||||
}
|
||||
prev = next;
|
||||
}
|
||||
return Value.TRUE;
|
||||
@@ -296,38 +383,52 @@ public class Operators {
|
||||
// lazy cause of assignment which is non-trivial
|
||||
expression.addLazyBinaryOperator("=", precedence.get("assign=<>"), false, false, t -> Context.Type.LVALUE, (c, t, lv1, lv2) ->
|
||||
{
|
||||
Value v1 = lv1.evalValue(c, Context.LVALUE);
|
||||
Value v2 = lv2.evalValue(c);
|
||||
if (v1 instanceof ListValue.ListConstructorValue && v2 instanceof ListValue)
|
||||
final Value v1 = lv1.evalValue(c, Context.LVALUE);
|
||||
final Value v2 = lv2.evalValue(c);
|
||||
if (v1 instanceof final ListValue.ListConstructorValue lcv && v2 instanceof final ListValue list)
|
||||
{
|
||||
List<Value> ll = ((ListValue)v1).getItems();
|
||||
List<Value> rl = ((ListValue)v2).getItems();
|
||||
if (ll.size() < rl.size()) throw new InternalExpressionException("Too many values to unpack");
|
||||
if (ll.size() > rl.size()) throw new InternalExpressionException("Too few values to unpack");
|
||||
for (Value v: ll) v.assertAssignable();
|
||||
Iterator<Value> li = ll.iterator();
|
||||
Iterator<Value> ri = rl.iterator();
|
||||
while(li.hasNext())
|
||||
final List<Value> ll = lcv.getItems();
|
||||
final List<Value> rl = list.getItems();
|
||||
if (ll.size() < rl.size())
|
||||
{
|
||||
String lname = li.next().getVariable();
|
||||
Value vval = ri.next().reboundedTo(lname);
|
||||
throw new InternalExpressionException("Too many values to unpack");
|
||||
}
|
||||
if (ll.size() > rl.size())
|
||||
{
|
||||
throw new InternalExpressionException("Too few values to unpack");
|
||||
}
|
||||
for (final Value v : ll)
|
||||
{
|
||||
v.assertAssignable();
|
||||
}
|
||||
final Iterator<Value> li = ll.iterator();
|
||||
final Iterator<Value> ri = rl.iterator();
|
||||
while (li.hasNext())
|
||||
{
|
||||
final String lname = li.next().getVariable();
|
||||
final Value vval = ri.next().reboundedTo(lname);
|
||||
expression.setAnyVariable(c, lname, (cc, tt) -> vval);
|
||||
}
|
||||
return (cc, tt) -> Value.TRUE;
|
||||
}
|
||||
if (v1 instanceof LContainerValue)
|
||||
if (v1 instanceof final LContainerValue lcv)
|
||||
{
|
||||
ContainerValueInterface container = ((LContainerValue) v1).getContainer();
|
||||
final ContainerValueInterface container = lcv.container();
|
||||
if (container == null)
|
||||
{
|
||||
return (cc, tt) -> Value.NULL;
|
||||
Value address = ((LContainerValue) v1).getAddress();
|
||||
if (!(container.put(address, v2))) return (cc, tt) -> Value.NULL;
|
||||
}
|
||||
final Value address = lcv.address();
|
||||
if (!(container.put(address, v2)))
|
||||
{
|
||||
return (cc, tt) -> Value.NULL;
|
||||
}
|
||||
return (cc, tt) -> v2;
|
||||
}
|
||||
v1.assertAssignable();
|
||||
String varname = v1.getVariable();
|
||||
Value copy = v2.reboundedTo(varname);
|
||||
LazyValue boundedLHS = (cc, tt) -> copy;
|
||||
final String varname = v1.getVariable();
|
||||
final Value copy = v2.reboundedTo(varname);
|
||||
final LazyValue boundedLHS = (cc, tt) -> copy;
|
||||
expression.setAnyVariable(c, varname, boundedLHS);
|
||||
return boundedLHS;
|
||||
});
|
||||
@@ -335,35 +436,44 @@ public class Operators {
|
||||
// lazy due to assignment
|
||||
expression.addLazyBinaryOperator("+=", precedence.get("assign=<>"), false, false, t -> Context.Type.LVALUE, (c, t, lv1, lv2) ->
|
||||
{
|
||||
Value v1 = lv1.evalValue(c, Context.LVALUE);
|
||||
Value v2 = lv2.evalValue(c);
|
||||
if (v1 instanceof ListValue.ListConstructorValue && v2 instanceof ListValue)
|
||||
final Value v1 = lv1.evalValue(c, Context.LVALUE);
|
||||
final Value v2 = lv2.evalValue(c);
|
||||
if (v1 instanceof final ListValue.ListConstructorValue lcv && v2 instanceof final ListValue list)
|
||||
{
|
||||
List<Value> ll = ((ListValue)v1).getItems();
|
||||
List<Value> rl = ((ListValue)v2).getItems();
|
||||
if (ll.size() < rl.size()) throw new InternalExpressionException("Too many values to unpack");
|
||||
if (ll.size() > rl.size()) throw new InternalExpressionException("Too few values to unpack");
|
||||
for (Value v: ll) v.assertAssignable();
|
||||
Iterator<Value> li = ll.iterator();
|
||||
Iterator<Value> ri = rl.iterator();
|
||||
while(li.hasNext())
|
||||
final List<Value> ll = lcv.getItems();
|
||||
final List<Value> rl = list.getItems();
|
||||
if (ll.size() < rl.size())
|
||||
{
|
||||
Value lval = li.next();
|
||||
String lname = lval.getVariable();
|
||||
Value result = lval.add(ri.next()).bindTo(lname);
|
||||
throw new InternalExpressionException("Too many values to unpack");
|
||||
}
|
||||
if (ll.size() > rl.size())
|
||||
{
|
||||
throw new InternalExpressionException("Too few values to unpack");
|
||||
}
|
||||
for (final Value v : ll)
|
||||
{
|
||||
v.assertAssignable();
|
||||
}
|
||||
final Iterator<Value> li = ll.iterator();
|
||||
final Iterator<Value> ri = rl.iterator();
|
||||
while (li.hasNext())
|
||||
{
|
||||
final Value lval = li.next();
|
||||
final String lname = lval.getVariable();
|
||||
final Value result = lval.add(ri.next()).bindTo(lname);
|
||||
expression.setAnyVariable(c, lname, (cc, tt) -> result);
|
||||
}
|
||||
return (cc, tt) -> Value.TRUE;
|
||||
}
|
||||
if (v1 instanceof LContainerValue)
|
||||
if (v1 instanceof final LContainerValue lcv)
|
||||
{
|
||||
ContainerValueInterface cvi = ((LContainerValue) v1).getContainer();
|
||||
final ContainerValueInterface cvi = lcv.container();
|
||||
if (cvi == null)
|
||||
{
|
||||
throw new InternalExpressionException("Failed to resolve left hand side of the += operation");
|
||||
}
|
||||
Value key = ((LContainerValue) v1).getAddress();
|
||||
Value value = cvi.get(key);
|
||||
final Value key = lcv.address();
|
||||
final Value value = cvi.get(key);
|
||||
if (value instanceof ListValue || value instanceof MapValue)
|
||||
{
|
||||
((AbstractListValue) value).append(v2);
|
||||
@@ -371,22 +481,22 @@ public class Operators {
|
||||
}
|
||||
else
|
||||
{
|
||||
Value res = value.add(v2);
|
||||
final Value res = value.add(v2);
|
||||
cvi.put(key, res);
|
||||
return (cc, tt) -> res;
|
||||
}
|
||||
}
|
||||
v1.assertAssignable();
|
||||
String varname = v1.getVariable();
|
||||
LazyValue boundedLHS;
|
||||
final String varname = v1.getVariable();
|
||||
final LazyValue boundedLHS;
|
||||
if (v1 instanceof ListValue || v1 instanceof MapValue)
|
||||
{
|
||||
((AbstractListValue) v1).append(v2);
|
||||
boundedLHS = (cc, tt)-> v1;
|
||||
boundedLHS = (cc, tt) -> v1;
|
||||
}
|
||||
else
|
||||
{
|
||||
Value result = v1.add(v2).bindTo(varname);
|
||||
final Value result = v1.add(v2).bindTo(varname);
|
||||
boundedLHS = (cc, tt) -> result;
|
||||
}
|
||||
expression.setAnyVariable(c, varname, boundedLHS);
|
||||
@@ -395,22 +505,34 @@ public class Operators {
|
||||
|
||||
expression.addBinaryContextOperator("<>", precedence.get("assign=<>"), false, false, false, (c, t, v1, v2) ->
|
||||
{
|
||||
if (v1 instanceof ListValue.ListConstructorValue && v2 instanceof ListValue.ListConstructorValue)
|
||||
if (v1 instanceof final ListValue.ListConstructorValue lcv1 && v2 instanceof final ListValue.ListConstructorValue lcv2)
|
||||
{
|
||||
List<Value> ll = ((ListValue)v1).getItems();
|
||||
List<Value> rl = ((ListValue)v2).getItems();
|
||||
if (ll.size() < rl.size()) throw new InternalExpressionException("Too many values to unpack");
|
||||
if (ll.size() > rl.size()) throw new InternalExpressionException("Too few values to unpack");
|
||||
for (Value v: ll) v.assertAssignable();
|
||||
for (Value v: rl) v.assertAssignable();
|
||||
Iterator<Value> li = ll.iterator();
|
||||
Iterator<Value> ri = rl.iterator();
|
||||
while(li.hasNext())
|
||||
final List<Value> ll = lcv1.getItems();
|
||||
final List<Value> rl = lcv2.getItems();
|
||||
if (ll.size() < rl.size())
|
||||
{
|
||||
Value lval = li.next();
|
||||
Value rval = ri.next();
|
||||
String lname = lval.getVariable();
|
||||
String rname = rval.getVariable();
|
||||
throw new InternalExpressionException("Too many values to unpack");
|
||||
}
|
||||
if (ll.size() > rl.size())
|
||||
{
|
||||
throw new InternalExpressionException("Too few values to unpack");
|
||||
}
|
||||
for (final Value v : ll)
|
||||
{
|
||||
v.assertAssignable();
|
||||
}
|
||||
for (final Value v : rl)
|
||||
{
|
||||
v.assertAssignable();
|
||||
}
|
||||
final Iterator<Value> li = ll.iterator();
|
||||
final Iterator<Value> ri = rl.iterator();
|
||||
while (li.hasNext())
|
||||
{
|
||||
final Value lval = li.next();
|
||||
final Value rval = ri.next();
|
||||
final String lname = lval.getVariable();
|
||||
final String rname = rval.getVariable();
|
||||
lval.reboundedTo(rname);
|
||||
rval.reboundedTo(lname);
|
||||
expression.setAnyVariable(c, lname, (cc, tt) -> rval);
|
||||
@@ -420,36 +542,37 @@ public class Operators {
|
||||
}
|
||||
v1.assertAssignable();
|
||||
v2.assertAssignable();
|
||||
String lvalvar = v1.getVariable();
|
||||
String rvalvar = v2.getVariable();
|
||||
Value lval = v2.reboundedTo(lvalvar);
|
||||
Value rval = v1.reboundedTo(rvalvar);
|
||||
final String lvalvar = v1.getVariable();
|
||||
final String rvalvar = v2.getVariable();
|
||||
final Value lval = v2.reboundedTo(lvalvar);
|
||||
final Value rval = v1.reboundedTo(rvalvar);
|
||||
expression.setAnyVariable(c, lvalvar, (cc, tt) -> lval);
|
||||
expression.setAnyVariable(c, rvalvar, (cc, tt) -> rval);
|
||||
return lval;
|
||||
});
|
||||
|
||||
expression.addUnaryOperator("-", false, v -> NumericValue.asNumber(v).opposite());
|
||||
expression.addUnaryOperator("-", false, v -> NumericValue.asNumber(v).opposite());
|
||||
|
||||
expression.addUnaryOperator("+", false, NumericValue::asNumber);
|
||||
|
||||
// could be non-lazy, but who cares - its a small one.
|
||||
expression.addLazyUnaryOperator("!", precedence.get("unary+-!..."), false, true, x -> Context.Type.BOOLEAN, (c, t, lv) ->
|
||||
lv.evalValue(c, Context.BOOLEAN).getBoolean() ? (cc, tt)-> Value.FALSE : (cc, tt) -> Value.TRUE
|
||||
lv.evalValue(c, Context.BOOLEAN).getBoolean() ? (cc, tt) -> Value.FALSE : (cc, tt) -> Value.TRUE
|
||||
); // might need context boolean
|
||||
|
||||
// lazy because of typed evaluation of the argument
|
||||
expression.addLazyUnaryOperator("...", Operators.precedence.get("unary+-!..."), false, true, t -> t== Context.Type.LOCALIZATION?Context.NONE:t, (c, t, lv) ->
|
||||
expression.addLazyUnaryOperator("...", Operators.precedence.get("unary+-!..."), false, true, t -> t == Context.Type.LOCALIZATION ? Context.NONE : t, (c, t, lv) ->
|
||||
{
|
||||
if (t == Context.LOCALIZATION)
|
||||
{
|
||||
return (cc, tt) -> new FunctionAnnotationValue(lv.evalValue(c), FunctionAnnotationValue.Type.VARARG);
|
||||
|
||||
Value params = lv.evalValue(c, t);
|
||||
if (!(params instanceof AbstractListValue))
|
||||
}
|
||||
if (!(lv.evalValue(c, t) instanceof final AbstractListValue alv))
|
||||
{
|
||||
throw new InternalExpressionException("Unable to unpack a non-list");
|
||||
FunctionUnpackedArgumentsValue fuaval = new FunctionUnpackedArgumentsValue( ((AbstractListValue) params).unpack());
|
||||
}
|
||||
final FunctionUnpackedArgumentsValue fuaval = new FunctionUnpackedArgumentsValue(alv.unpack());
|
||||
return (cc, tt) -> fuaval;
|
||||
//throw new InternalExpressionException("That functionality has not been implemented yet.");
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package carpet.script.language;
|
||||
import carpet.script.Context;
|
||||
import carpet.script.Expression;
|
||||
import carpet.script.LazyValue;
|
||||
import carpet.script.ScriptHost;
|
||||
import carpet.script.exception.InternalExpressionException;
|
||||
import carpet.script.utils.PerlinNoiseSampler;
|
||||
import carpet.script.utils.SimplexNoiseSampler;
|
||||
@@ -27,12 +26,13 @@ import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.regex.PatternSyntaxException;
|
||||
|
||||
public class Sys {
|
||||
public class Sys
|
||||
{
|
||||
public static final Random randomizer = new Random();
|
||||
// %[argument_index$][flags][width][.precision][t]conversion
|
||||
private static final Pattern formatPattern = Pattern.compile("%(\\d+\\$)?([-#+ 0,(<]*)?(\\d+)?(\\.\\d+)?([tT])?([a-zA-Z%])");
|
||||
|
||||
public static void apply(Expression expression)
|
||||
public static void apply(final Expression expression)
|
||||
{
|
||||
expression.addUnaryFunction("hash_code", v -> new NumericValue(v.hashCode()));
|
||||
|
||||
@@ -40,21 +40,23 @@ public class Sys {
|
||||
|
||||
expression.addTypedContextFunction("bool", 1, Context.BOOLEAN, (c, t, lv) ->
|
||||
{
|
||||
Value v = lv.get(0);
|
||||
final Value v = lv.get(0);
|
||||
if (v instanceof StringValue)
|
||||
{
|
||||
String str = v.getString().toLowerCase(Locale.ROOT);
|
||||
if ("false".equals(str) || "null".equals(str)) return Value.FALSE;
|
||||
final String str = v.getString().toLowerCase(Locale.ROOT);
|
||||
if ("false".equals(str) || "null".equals(str))
|
||||
{
|
||||
return Value.FALSE;
|
||||
}
|
||||
}
|
||||
return BooleanValue.of(v.getBoolean());
|
||||
});
|
||||
|
||||
expression.addUnaryFunction("number", v ->
|
||||
{
|
||||
if (v instanceof NumericValue num)
|
||||
if (v instanceof final NumericValue num)
|
||||
{
|
||||
if (num.isInteger()) return new NumericValue(num.getLong());
|
||||
return new NumericValue(num.getDouble());
|
||||
return new NumericValue(num.isInteger() ? num.getLong() : num.getDouble());
|
||||
}
|
||||
if (v instanceof ListValue || v instanceof MapValue)
|
||||
{
|
||||
@@ -64,7 +66,7 @@ public class Sys {
|
||||
{
|
||||
return new NumericValue(v.getString());
|
||||
}
|
||||
catch (NumberFormatException format)
|
||||
catch (final NumberFormatException format)
|
||||
{
|
||||
return Value.NULL;
|
||||
}
|
||||
@@ -73,51 +75,65 @@ public class Sys {
|
||||
expression.addFunction("str", lv ->
|
||||
{
|
||||
if (lv.size() == 0)
|
||||
throw new InternalExpressionException("'str' requires at least one argument");
|
||||
String format = lv.get(0).getString();
|
||||
if (lv.size() == 1)
|
||||
return new StringValue(format);
|
||||
int argIndex = 1;
|
||||
if (lv.get(1) instanceof ListValue && lv.size() == 2)
|
||||
{
|
||||
lv = ((ListValue) lv.get(1)).getItems();
|
||||
throw new InternalExpressionException("'str' requires at least one argument");
|
||||
}
|
||||
final String format = lv.get(0).getString();
|
||||
if (lv.size() == 1)
|
||||
{
|
||||
return new StringValue(format);
|
||||
}
|
||||
int argIndex = 1;
|
||||
if (lv.get(1) instanceof final ListValue list && lv.size() == 2)
|
||||
{
|
||||
lv = list.getItems();
|
||||
argIndex = 0;
|
||||
}
|
||||
List<Object> args = new ArrayList<>();
|
||||
Matcher m = formatPattern.matcher(format);
|
||||
final List<Object> args = new ArrayList<>();
|
||||
final Matcher m = formatPattern.matcher(format);
|
||||
|
||||
for (int i = 0, len = format.length(); i < len; ) {
|
||||
if (m.find(i)) {
|
||||
for (int i = 0, len = format.length(); i < len; )
|
||||
{
|
||||
if (m.find(i))
|
||||
{
|
||||
// Anything between the start of the string and the beginning
|
||||
// of the format specifier is either fixed text or contains
|
||||
// an invalid format string.
|
||||
// [[scarpet]] but we skip it and let the String.format fail
|
||||
char fmt = m.group(6).toLowerCase().charAt(0);
|
||||
final char fmt = m.group(6).toLowerCase().charAt(0);
|
||||
if (fmt == 's')
|
||||
{
|
||||
if (argIndex >= lv.size())
|
||||
throw new InternalExpressionException("Not enough arguments for "+m.group(0));
|
||||
{
|
||||
throw new InternalExpressionException("Not enough arguments for " + m.group(0));
|
||||
}
|
||||
args.add(lv.get(argIndex).getString());
|
||||
argIndex++;
|
||||
}
|
||||
else if (fmt == 'd' || fmt == 'o' || fmt == 'x')
|
||||
{
|
||||
if (argIndex >= lv.size())
|
||||
throw new InternalExpressionException("Not enough arguments for "+m.group(0));
|
||||
{
|
||||
throw new InternalExpressionException("Not enough arguments for " + m.group(0));
|
||||
}
|
||||
args.add(lv.get(argIndex).readInteger());
|
||||
argIndex++;
|
||||
}
|
||||
else if (fmt == 'a' || fmt == 'e' || fmt == 'f' || fmt == 'g')
|
||||
{
|
||||
if (argIndex >= lv.size())
|
||||
throw new InternalExpressionException("Not enough arguments for "+m.group(0));
|
||||
{
|
||||
throw new InternalExpressionException("Not enough arguments for " + m.group(0));
|
||||
}
|
||||
args.add(lv.get(argIndex).readDoubleNumber());
|
||||
argIndex++;
|
||||
}
|
||||
else if (fmt == 'b')
|
||||
{
|
||||
if (argIndex >= lv.size())
|
||||
throw new InternalExpressionException("Not enough arguments for "+m.group(0));
|
||||
{
|
||||
throw new InternalExpressionException("Not enough arguments for " + m.group(0));
|
||||
}
|
||||
args.add(lv.get(argIndex).getBoolean());
|
||||
argIndex++;
|
||||
}
|
||||
@@ -127,11 +143,13 @@ public class Sys {
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InternalExpressionException("Format not supported: "+m.group(6));
|
||||
throw new InternalExpressionException("Format not supported: " + m.group(6));
|
||||
}
|
||||
|
||||
i = m.end();
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
// No more valid format specifiers. Check for possible invalid
|
||||
// format specifiers.
|
||||
// [[scarpet]] but we skip it and let the String.format fail
|
||||
@@ -142,9 +160,9 @@ public class Sys {
|
||||
{
|
||||
return new StringValue(String.format(Locale.ROOT, format, args.toArray()));
|
||||
}
|
||||
catch (IllegalFormatException ife)
|
||||
catch (final IllegalFormatException ife)
|
||||
{
|
||||
throw new InternalExpressionException("Illegal string format: "+ife.getMessage());
|
||||
throw new InternalExpressionException("Illegal string format: " + ife.getMessage());
|
||||
}
|
||||
});
|
||||
|
||||
@@ -156,32 +174,40 @@ public class Sys {
|
||||
|
||||
expression.addFunction("replace", (lv) ->
|
||||
{
|
||||
if (lv.size() != 3 && lv.size() !=2)
|
||||
if (lv.size() != 3 && lv.size() != 2)
|
||||
{
|
||||
throw new InternalExpressionException("'replace' expects string to read, pattern regex, and optional replacement string");
|
||||
String data = lv.get(0).getString();
|
||||
String regex = lv.get(1).getString();
|
||||
}
|
||||
final String data = lv.get(0).getString();
|
||||
final String regex = lv.get(1).getString();
|
||||
String replacement = "";
|
||||
if (lv.size() == 3)
|
||||
{
|
||||
replacement = lv.get(2).getString();
|
||||
}
|
||||
try
|
||||
{
|
||||
return new StringValue(data.replaceAll(regex, replacement));
|
||||
}
|
||||
catch (PatternSyntaxException pse)
|
||||
catch (final PatternSyntaxException pse)
|
||||
{
|
||||
throw new InternalExpressionException("Incorrect pattern for 'replace': "+pse.getMessage());
|
||||
throw new InternalExpressionException("Incorrect pattern for 'replace': " + pse.getMessage());
|
||||
}
|
||||
});
|
||||
|
||||
expression.addFunction("replace_first", (lv) ->
|
||||
{
|
||||
if (lv.size() != 3 && lv.size() !=2)
|
||||
if (lv.size() != 3 && lv.size() != 2)
|
||||
{
|
||||
throw new InternalExpressionException("'replace_first' expects string to read, pattern regex, and optional replacement string");
|
||||
String data = lv.get(0).getString();
|
||||
String regex = lv.get(1).getString();
|
||||
}
|
||||
final String data = lv.get(0).getString();
|
||||
final String regex = lv.get(1).getString();
|
||||
String replacement = "";
|
||||
if (lv.size() == 3)
|
||||
{
|
||||
replacement = lv.get(2).getString();
|
||||
}
|
||||
return new StringValue(data.replaceFirst(regex, replacement));
|
||||
});
|
||||
|
||||
@@ -189,31 +215,36 @@ public class Sys {
|
||||
expression.addUnaryFunction("length", v -> new NumericValue(v.length()));
|
||||
expression.addContextFunction("rand", -1, (c, t, lv) ->
|
||||
{
|
||||
int argsize = lv.size();
|
||||
final int argsize = lv.size();
|
||||
Random randomizer = Sys.randomizer;
|
||||
if (argsize != 1 && argsize != 2)
|
||||
throw new InternalExpressionException("'rand' takes one (range) or two arguments (range and seed)");
|
||||
if (argsize == 2) randomizer = c.host.getRandom(NumericValue.asNumber(lv.get(1)).getLong());
|
||||
Value argument = lv.get(0);
|
||||
if (argument instanceof ListValue)
|
||||
{
|
||||
List<Value> list = ((ListValue) argument).getItems();
|
||||
throw new InternalExpressionException("'rand' takes one (range) or two arguments (range and seed)");
|
||||
}
|
||||
if (argsize == 2)
|
||||
{
|
||||
randomizer = c.host.getRandom(NumericValue.asNumber(lv.get(1)).getLong());
|
||||
}
|
||||
final Value argument = lv.get(0);
|
||||
if (argument instanceof final ListValue listValue)
|
||||
{
|
||||
final List<Value> list = listValue.getItems();
|
||||
return list.get(randomizer.nextInt(list.size()));
|
||||
}
|
||||
double value = NumericValue.asNumber(argument).getDouble()*randomizer.nextDouble();
|
||||
if (t == Context.BOOLEAN)
|
||||
return BooleanValue.of(value >= 1.0D);
|
||||
return new NumericValue(value);
|
||||
final double value = NumericValue.asNumber(argument).getDouble() * randomizer.nextDouble();
|
||||
return t == Context.BOOLEAN ? BooleanValue.of(value >= 1.0D) : new NumericValue(value);
|
||||
});
|
||||
expression.addContextFunction("reset_seed", 1, (c, t, lv) -> {
|
||||
boolean gotIt = c.host.resetRandom(NumericValue.asNumber(lv.get(0)).getLong());
|
||||
final boolean gotIt = c.host.resetRandom(NumericValue.asNumber(lv.get(0)).getLong());
|
||||
return BooleanValue.of(gotIt);
|
||||
});
|
||||
|
||||
expression.addFunction("perlin", lv ->
|
||||
{
|
||||
PerlinNoiseSampler sampler;
|
||||
Value x, y, z;
|
||||
final PerlinNoiseSampler sampler;
|
||||
final Value x;
|
||||
Value y;
|
||||
Value z;
|
||||
|
||||
if (lv.size() >= 4)
|
||||
{
|
||||
@@ -227,36 +258,45 @@ public class Sys {
|
||||
sampler = PerlinNoiseSampler.instance;
|
||||
y = Value.NULL;
|
||||
z = Value.NULL;
|
||||
if (lv.size() == 0 )
|
||||
if (lv.size() == 0)
|
||||
{
|
||||
throw new InternalExpressionException("'perlin' requires at least one dimension to sample from");
|
||||
}
|
||||
x = NumericValue.asNumber(lv.get(0));
|
||||
if (lv.size() > 1)
|
||||
{
|
||||
y = NumericValue.asNumber(lv.get(1));
|
||||
if (lv.size() > 2)
|
||||
{
|
||||
z = NumericValue.asNumber(lv.get(2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double result;
|
||||
final double result;
|
||||
|
||||
if (z.isNull())
|
||||
if (y.isNull())
|
||||
result = sampler.sample1d(NumericValue.asNumber(x).getDouble());
|
||||
else
|
||||
result = sampler.sample2d(NumericValue.asNumber(x).getDouble(), NumericValue.asNumber(y).getDouble());
|
||||
{
|
||||
result = y.isNull()
|
||||
? sampler.sample1d(NumericValue.asNumber(x).getDouble())
|
||||
: sampler.sample2d(NumericValue.asNumber(x).getDouble(), NumericValue.asNumber(y).getDouble());
|
||||
}
|
||||
else
|
||||
{
|
||||
result = sampler.sample3d(
|
||||
NumericValue.asNumber(x).getDouble(),
|
||||
NumericValue.asNumber(y).getDouble(),
|
||||
NumericValue.asNumber(z).getDouble());
|
||||
}
|
||||
return new NumericValue(result);
|
||||
});
|
||||
|
||||
expression.addFunction("simplex", lv ->
|
||||
{
|
||||
SimplexNoiseSampler sampler;
|
||||
Value x, y, z;
|
||||
final SimplexNoiseSampler sampler;
|
||||
final Value x;
|
||||
final Value y;
|
||||
Value z;
|
||||
|
||||
if (lv.size() >= 4)
|
||||
{
|
||||
@@ -269,22 +309,30 @@ public class Sys {
|
||||
{
|
||||
sampler = SimplexNoiseSampler.instance;
|
||||
z = Value.NULL;
|
||||
if (lv.size() < 2 )
|
||||
if (lv.size() < 2)
|
||||
{
|
||||
throw new InternalExpressionException("'simplex' requires at least two dimensions to sample from");
|
||||
}
|
||||
x = NumericValue.asNumber(lv.get(0));
|
||||
y = NumericValue.asNumber(lv.get(1));
|
||||
if (lv.size() > 2)
|
||||
{
|
||||
z = NumericValue.asNumber(lv.get(2));
|
||||
}
|
||||
}
|
||||
double result;
|
||||
final double result;
|
||||
|
||||
if (z.isNull())
|
||||
{
|
||||
result = sampler.sample2d(NumericValue.asNumber(x).getDouble(), NumericValue.asNumber(y).getDouble());
|
||||
}
|
||||
else
|
||||
{
|
||||
result = sampler.sample3d(
|
||||
NumericValue.asNumber(x).getDouble(),
|
||||
NumericValue.asNumber(y).getDouble(),
|
||||
NumericValue.asNumber(z).getDouble());
|
||||
}
|
||||
return new NumericValue(result);
|
||||
});
|
||||
|
||||
@@ -303,17 +351,23 @@ public class Sys {
|
||||
expression.addFunction("convert_date", lv ->
|
||||
{
|
||||
int argsize = lv.size();
|
||||
if (lv.size() == 0) throw new InternalExpressionException("'convert_date' requires at least one parameter");
|
||||
Value value = lv.get(0);
|
||||
if (lv.size() == 0)
|
||||
{
|
||||
throw new InternalExpressionException("'convert_date' requires at least one parameter");
|
||||
}
|
||||
final Value value = lv.get(0);
|
||||
if (argsize == 1 && !(value instanceof ListValue))
|
||||
{
|
||||
Calendar cal = new GregorianCalendar(Locale.ROOT);
|
||||
final Calendar cal = new GregorianCalendar(Locale.ROOT);
|
||||
cal.setTimeInMillis(NumericValue.asNumber(value, "timestamp").getLong());
|
||||
int weekday = cal.get(Calendar.DAY_OF_WEEK)-1;
|
||||
if (weekday == 0) weekday = 7;
|
||||
int weekday = cal.get(Calendar.DAY_OF_WEEK) - 1;
|
||||
if (weekday == 0)
|
||||
{
|
||||
weekday = 7;
|
||||
}
|
||||
return ListValue.ofNums(
|
||||
cal.get(Calendar.YEAR),
|
||||
cal.get(Calendar.MONTH)+1,
|
||||
cal.get(Calendar.MONTH) + 1,
|
||||
cal.get(Calendar.DAY_OF_MONTH),
|
||||
cal.get(Calendar.HOUR_OF_DAY),
|
||||
cal.get(Calendar.MINUTE),
|
||||
@@ -323,18 +377,18 @@ public class Sys {
|
||||
cal.get(Calendar.WEEK_OF_YEAR)
|
||||
);
|
||||
}
|
||||
else if(value instanceof ListValue)
|
||||
else if (value instanceof final ListValue list)
|
||||
{
|
||||
lv = ((ListValue) value).getItems();
|
||||
lv = list.getItems();
|
||||
argsize = lv.size();
|
||||
}
|
||||
Calendar cal = new GregorianCalendar(0, 0, 0, 0, 0, 0);
|
||||
final Calendar cal = new GregorianCalendar(0, 0, 0, 0, 0, 0);
|
||||
|
||||
if (argsize == 3)
|
||||
{
|
||||
cal.set(
|
||||
NumericValue.asNumber(lv.get(0)).getInt(),
|
||||
NumericValue.asNumber(lv.get(1)).getInt()-1,
|
||||
NumericValue.asNumber(lv.get(1)).getInt() - 1,
|
||||
NumericValue.asNumber(lv.get(2)).getInt()
|
||||
);
|
||||
}
|
||||
@@ -342,29 +396,32 @@ public class Sys {
|
||||
{
|
||||
cal.set(
|
||||
NumericValue.asNumber(lv.get(0)).getInt(),
|
||||
NumericValue.asNumber(lv.get(1)).getInt()-1,
|
||||
NumericValue.asNumber(lv.get(1)).getInt() - 1,
|
||||
NumericValue.asNumber(lv.get(2)).getInt(),
|
||||
NumericValue.asNumber(lv.get(3)).getInt(),
|
||||
NumericValue.asNumber(lv.get(4)).getInt(),
|
||||
NumericValue.asNumber(lv.get(5)).getInt()
|
||||
);
|
||||
}
|
||||
else throw new InternalExpressionException("Date conversion requires 3 arguments for Dates or 6 arguments, for time");
|
||||
else
|
||||
{
|
||||
throw new InternalExpressionException("Date conversion requires 3 arguments for Dates or 6 arguments, for time");
|
||||
}
|
||||
return new NumericValue(cal.getTimeInMillis());
|
||||
});
|
||||
|
||||
// lazy cause evaluates expression multiple times
|
||||
expression.addLazyFunction("profile_expr", 1, (c, t, lv) ->
|
||||
{
|
||||
LazyValue lazy = lv.get(0);
|
||||
long end = System.nanoTime()+50000000L;
|
||||
final LazyValue lazy = lv.get(0);
|
||||
final long end = System.nanoTime() + 50000000L;
|
||||
long it = 0;
|
||||
while (System.nanoTime()<end)
|
||||
while (System.nanoTime() < end)
|
||||
{
|
||||
lazy.evalValue(c);
|
||||
it++;
|
||||
}
|
||||
Value res = new NumericValue(it);
|
||||
final Value res = new NumericValue(it);
|
||||
return (cc, tt) -> res;
|
||||
});
|
||||
|
||||
@@ -373,16 +430,18 @@ public class Sys {
|
||||
|
||||
expression.addContextFunction("undef", 1, (c, t, lv) ->
|
||||
{
|
||||
Value remove = lv.get(0);
|
||||
final Value remove = lv.get(0);
|
||||
if (remove instanceof FunctionValue)
|
||||
{
|
||||
c.host.delFunction(expression.module, remove.getString());
|
||||
return Value.NULL;
|
||||
}
|
||||
String varname = remove.getString();
|
||||
boolean isPrefix = varname.endsWith("*");
|
||||
final boolean isPrefix = varname.endsWith("*");
|
||||
if (isPrefix)
|
||||
{
|
||||
varname = varname.replaceAll("\\*+$", "");
|
||||
}
|
||||
if (isPrefix)
|
||||
{
|
||||
c.host.delFunctionWithPrefix(expression.module, varname);
|
||||
@@ -413,8 +472,8 @@ public class Sys {
|
||||
//deprecate
|
||||
expression.addContextFunction("vars", 1, (c, t, lv) ->
|
||||
{
|
||||
String prefix = lv.get(0).getString();
|
||||
List<Value> values = new ArrayList<>();
|
||||
final String prefix = lv.get(0).getString();
|
||||
final List<Value> values = new ArrayList<>();
|
||||
if (prefix.startsWith("global"))
|
||||
{
|
||||
c.host.globalVariableNames(expression.module, (s) -> s.startsWith(prefix)).forEach(s -> values.add(new StringValue(s)));
|
||||
@@ -429,19 +488,23 @@ public class Sys {
|
||||
// lazy cause default expression may not be executed if not needed
|
||||
expression.addLazyFunction("system_variable_get", (c, t, lv) ->
|
||||
{
|
||||
if (lv.size() == 0) throw new InternalExpressionException("'system_variable_get' expects at least a key to be fetched");
|
||||
Value key = lv.get(0).evalValue(c);
|
||||
if (lv.size() > 1) c.host.scriptServer().systemGlobals.computeIfAbsent(key, k -> lv.get(1).evalValue(c));
|
||||
Value res = c.host.scriptServer().systemGlobals.get(key);
|
||||
if (res!=null) return (cc, tt) -> res;
|
||||
return LazyValue.NULL;
|
||||
if (lv.size() == 0)
|
||||
{
|
||||
throw new InternalExpressionException("'system_variable_get' expects at least a key to be fetched");
|
||||
}
|
||||
final Value key = lv.get(0).evalValue(c);
|
||||
if (lv.size() > 1)
|
||||
{
|
||||
c.host.scriptServer().systemGlobals.computeIfAbsent(key, k -> lv.get(1).evalValue(c));
|
||||
}
|
||||
final Value res = c.host.scriptServer().systemGlobals.get(key);
|
||||
return res == null ? LazyValue.NULL : ((cc, tt) -> res);
|
||||
});
|
||||
|
||||
expression.addContextFunction("system_variable_set", 2, (c, t, lv) ->
|
||||
{
|
||||
Value res = c.host.scriptServer().systemGlobals.put(lv.get(0), lv.get(1));
|
||||
if (res!=null) return res;
|
||||
return Value.NULL;
|
||||
final Value res = c.host.scriptServer().systemGlobals.put(lv.get(0), lv.get(1));
|
||||
return res == null ? Value.NULL : res;
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -11,14 +11,16 @@ import carpet.script.value.Value;
|
||||
|
||||
public class Threading
|
||||
{
|
||||
public static void apply(Expression expression)
|
||||
public static void apply(final Expression expression)
|
||||
{
|
||||
expression.addFunctionWithDelegation("task", -1, false, false, (c, t, expr, tok, lv) ->
|
||||
{
|
||||
if (lv.size() == 0)
|
||||
{
|
||||
throw new InternalExpressionException("'task' requires at least function to call as a parameter");
|
||||
FunctionArgument functionArgument = FunctionArgument.findIn(c, expression.module, lv, 0, false, true);
|
||||
ThreadValue thread = new ThreadValue(Value.NULL, functionArgument.function, expr, tok, c, functionArgument.checkedArgs());
|
||||
}
|
||||
final FunctionArgument functionArgument = FunctionArgument.findIn(c, expression.module, lv, 0, false, true);
|
||||
final ThreadValue thread = new ThreadValue(Value.NULL, functionArgument.function, expr, tok, c, functionArgument.checkedArgs());
|
||||
Thread.yield();
|
||||
return thread;
|
||||
});
|
||||
@@ -26,30 +28,36 @@ public class Threading
|
||||
expression.addFunctionWithDelegation("task_thread", -1, false, false, (c, t, expr, tok, lv) ->
|
||||
{
|
||||
if (lv.size() < 2)
|
||||
{
|
||||
throw new InternalExpressionException("'task' requires at least function to call as a parameter");
|
||||
Value queue = lv.get(0);
|
||||
FunctionArgument functionArgument = FunctionArgument.findIn(c, expression.module, lv, 1, false, true);
|
||||
ThreadValue thread = new ThreadValue(queue, functionArgument.function, expr, tok, c, functionArgument.checkedArgs());
|
||||
}
|
||||
final Value queue = lv.get(0);
|
||||
final FunctionArgument functionArgument = FunctionArgument.findIn(c, expression.module, lv, 1, false, true);
|
||||
final ThreadValue thread = new ThreadValue(queue, functionArgument.function, expr, tok, c, functionArgument.checkedArgs());
|
||||
Thread.yield();
|
||||
return thread;
|
||||
});
|
||||
|
||||
|
||||
expression.addContextFunction("task_count", -1, (c, t, lv) ->
|
||||
(lv.size() > 0)? new NumericValue(c.host.taskCount(lv.get(0))):new NumericValue(c.host.taskCount()));
|
||||
(lv.size() > 0) ? new NumericValue(c.host.taskCount(lv.get(0))) : new NumericValue(c.host.taskCount()));
|
||||
|
||||
expression.addUnaryFunction("task_value", (v) ->
|
||||
{
|
||||
if (!(v instanceof ThreadValue))
|
||||
if (!(v instanceof final ThreadValue tv))
|
||||
{
|
||||
throw new InternalExpressionException("'task_value' could only be used with a task value");
|
||||
return ((ThreadValue) v).getValue();
|
||||
}
|
||||
return tv.getValue();
|
||||
});
|
||||
|
||||
expression.addUnaryFunction("task_join", (v) ->
|
||||
{
|
||||
if (!(v instanceof ThreadValue))
|
||||
if (!(v instanceof final ThreadValue tv))
|
||||
{
|
||||
throw new InternalExpressionException("'task_join' could only be used with a task value");
|
||||
return ((ThreadValue) v).join();
|
||||
}
|
||||
return tv.join();
|
||||
});
|
||||
|
||||
expression.addLazyFunction("task_dock", 1, (c, t, lv) ->
|
||||
@@ -61,15 +69,20 @@ public class Threading
|
||||
|
||||
expression.addUnaryFunction("task_completed", (v) ->
|
||||
{
|
||||
if (!(v instanceof ThreadValue))
|
||||
if (!(v instanceof final ThreadValue tv))
|
||||
{
|
||||
throw new InternalExpressionException("'task_completed' could only be used with a task value");
|
||||
return BooleanValue.of(((ThreadValue) v).isFinished());
|
||||
}
|
||||
return BooleanValue.of(tv.isFinished());
|
||||
});
|
||||
|
||||
// lazy cause expr is evaluated in the same type
|
||||
expression.addLazyFunction("synchronize", (c, t, lv) ->
|
||||
{
|
||||
if (lv.size() == 0) throw new InternalExpressionException("'synchronize' require at least an expression to synchronize");
|
||||
if (lv.size() == 0)
|
||||
{
|
||||
throw new InternalExpressionException("'synchronize' require at least an expression to synchronize");
|
||||
}
|
||||
Value lockValue = Value.NULL;
|
||||
int ind = 0;
|
||||
if (lv.size() == 2)
|
||||
@@ -79,7 +92,7 @@ public class Threading
|
||||
}
|
||||
synchronized (c.host.getLock(lockValue))
|
||||
{
|
||||
Value ret = lv.get(ind).evalValue(c, t);
|
||||
final Value ret = lv.get(ind).evalValue(c, t);
|
||||
return (_c, _t) -> ret;
|
||||
}
|
||||
});
|
||||
@@ -87,15 +100,21 @@ public class Threading
|
||||
// lazy since exception expression is very conditional
|
||||
expression.addLazyFunction("sleep", (c, t, lv) ->
|
||||
{
|
||||
long time = lv.isEmpty()?0L:NumericValue.asNumber(lv.get(0).evalValue(c)).getLong();
|
||||
final long time = lv.isEmpty() ? 0L : NumericValue.asNumber(lv.get(0).evalValue(c)).getLong();
|
||||
boolean interrupted = false;
|
||||
try
|
||||
{
|
||||
if (Thread.interrupted()) interrupted = true;
|
||||
if (time > 0) Thread.sleep(time);
|
||||
if (Thread.interrupted())
|
||||
{
|
||||
interrupted = true;
|
||||
}
|
||||
if (time > 0)
|
||||
{
|
||||
Thread.sleep(time);
|
||||
}
|
||||
Thread.yield();
|
||||
}
|
||||
catch (InterruptedException ignored)
|
||||
catch (final InterruptedException ignored)
|
||||
{
|
||||
interrupted = true;
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import net.fabricmc.loader.api.FabricLoader;
|
||||
import net.minecraft.commands.CommandRuntimeException;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.world.level.storage.LevelResource;
|
||||
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@@ -40,22 +41,28 @@ import org.apache.commons.io.IOUtils;
|
||||
*/
|
||||
public class AppStoreManager
|
||||
{
|
||||
/** A local copy of the scarpet repo's file structure, to avoid multiple queries to github.com while typing out the
|
||||
/**
|
||||
* A local copy of the scarpet repo's file structure, to avoid multiple queries to github.com while typing out the
|
||||
* {@code /script download} command and getting the suggestions.
|
||||
*/
|
||||
private static StoreNode APP_STORE_ROOT = StoreNode.folder(null, "");
|
||||
|
||||
/** This is the base link to the scarpet app repo from the github api.
|
||||
/**
|
||||
* This is the base link to the scarpet app repo from the github api.
|
||||
*/
|
||||
private static String scarpetRepoLink = "https://api.github.com/repos/gnembon/scarpet/contents/programs/";
|
||||
|
||||
private static record AppInfo(String name, String url, StoreNode source) {}
|
||||
private record AppInfo(String name, String url, StoreNode source)
|
||||
{
|
||||
}
|
||||
|
||||
public static class ScarpetAppStoreValidator extends Validator<String>
|
||||
{
|
||||
@Override public String validate(CommandSourceStack source, CarpetRule<String> currentRule, String newValue, String stringInput)
|
||||
@Override
|
||||
public String validate(final CommandSourceStack source, final CarpetRule<String> currentRule, String newValue, final String stringInput)
|
||||
{
|
||||
if (newValue.equals(currentRule.value())) {
|
||||
if (newValue.equals(currentRule.value()))
|
||||
{
|
||||
// Don't refresh the local repo if it's the same (world change), helps preventing hitting rate limits from github when
|
||||
// getting suggestions. Pending is a way to invalidate the cache when it gets old, and investigating api usage further
|
||||
return newValue;
|
||||
@@ -66,12 +73,19 @@ public class AppStoreManager
|
||||
scarpetRepoLink = null;
|
||||
return newValue;
|
||||
}
|
||||
if (newValue.endsWith("/")) newValue = newValue.replaceAll("/$", "");
|
||||
scarpetRepoLink = "https://api.github.com/repos/"+newValue+"/";
|
||||
if (newValue.endsWith("/"))
|
||||
{
|
||||
newValue = newValue.replaceAll("/$", "");
|
||||
}
|
||||
scarpetRepoLink = "https://api.github.com/repos/" + newValue + "/";
|
||||
return newValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() { return "Appstore link should point to a valid github repository";}
|
||||
public String description()
|
||||
{
|
||||
return "Appstore link should point to a valid github repository";
|
||||
}
|
||||
}
|
||||
|
||||
public static class StoreNode
|
||||
@@ -81,18 +95,19 @@ public class AppStoreManager
|
||||
public Map<String, StoreNode> children;
|
||||
public boolean sealed;
|
||||
public String value;
|
||||
public static StoreNode folder(StoreNode parent, String name)
|
||||
|
||||
public static StoreNode folder(final StoreNode parent, final String name)
|
||||
{
|
||||
StoreNode node = new StoreNode(parent, name);
|
||||
final StoreNode node = new StoreNode(parent, name);
|
||||
node.children = new HashMap<>();
|
||||
node.value = null;
|
||||
node.sealed = false;
|
||||
return node;
|
||||
}
|
||||
|
||||
public static StoreNode scriptFile(StoreNode parent, String name, String value)
|
||||
public static StoreNode scriptFile(final StoreNode parent, final String name, final String value)
|
||||
{
|
||||
StoreNode node = new StoreNode(parent, name);
|
||||
final StoreNode node = new StoreNode(parent, name);
|
||||
node.children = null;
|
||||
node.value = value;
|
||||
node.sealed = true;
|
||||
@@ -103,19 +118,23 @@ public class AppStoreManager
|
||||
{
|
||||
return value != null;
|
||||
}
|
||||
|
||||
public String pathElement()
|
||||
{
|
||||
return name+(isLeaf()?"":"/");
|
||||
return name + (isLeaf() ? "" : "/");
|
||||
}
|
||||
|
||||
public String getPath()
|
||||
{
|
||||
return createPrePath().toString();
|
||||
}
|
||||
|
||||
private StringBuilder createPrePath()
|
||||
{
|
||||
return this == APP_STORE_ROOT ? new StringBuilder() : parent.createPrePath().append(pathElement());
|
||||
}
|
||||
private StoreNode(StoreNode parent, String name)
|
||||
|
||||
private StoreNode(final StoreNode parent, final String name)
|
||||
{
|
||||
this.parent = parent;
|
||||
this.name = name;
|
||||
@@ -124,32 +143,38 @@ public class AppStoreManager
|
||||
|
||||
public synchronized void fillChildren() throws IOException
|
||||
{
|
||||
if (sealed) return;
|
||||
if (scarpetRepoLink == null) throw new IOException("Accessing scarpet app repo is disabled");
|
||||
if (sealed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (scarpetRepoLink == null)
|
||||
{
|
||||
throw new IOException("Accessing scarpet app repo is disabled");
|
||||
}
|
||||
|
||||
String queryPath = scarpetRepoLink + getPath();
|
||||
String response;
|
||||
final String queryPath = scarpetRepoLink + getPath();
|
||||
final String response;
|
||||
try
|
||||
{
|
||||
response = IOUtils.toString(new URL(queryPath), StandardCharsets.UTF_8);
|
||||
}
|
||||
catch (IOException e)
|
||||
catch (final IOException e)
|
||||
{
|
||||
// Not sealing to allow retrying
|
||||
throw new IOException("Problems fetching " + queryPath, e);
|
||||
}
|
||||
JsonArray files = JsonParser.parseString(response).getAsJsonArray();
|
||||
for(JsonElement je : files)
|
||||
final JsonArray files = JsonParser.parseString(response).getAsJsonArray();
|
||||
for (final JsonElement je : files)
|
||||
{
|
||||
JsonObject jo = je.getAsJsonObject();
|
||||
String name = jo.get("name").getAsString();
|
||||
final JsonObject jo = je.getAsJsonObject();
|
||||
final String name = jo.get("name").getAsString();
|
||||
if (jo.get("type").getAsString().equals("dir"))
|
||||
{
|
||||
children.put(name, folder(this, name));
|
||||
}
|
||||
else// if (name.matches("(\\w+\\.scl?)"))
|
||||
{
|
||||
String value = jo.get("download_url").getAsString();
|
||||
final String value = jo.get("download_url").getAsString();
|
||||
children.put(name, scriptFile(this, name, value));
|
||||
}
|
||||
}
|
||||
@@ -159,12 +184,16 @@ public class AppStoreManager
|
||||
/**
|
||||
* Returns true if doing down the directory structire cannot continue since the matching element is either a leaf or
|
||||
* a string not matching of any node.
|
||||
*
|
||||
* @param pathElement
|
||||
* @return
|
||||
*/
|
||||
public boolean cannotContinueFor(String pathElement) throws IOException
|
||||
public boolean cannotContinueFor(final String pathElement) throws IOException
|
||||
{
|
||||
if (isLeaf()) return true;
|
||||
if (isLeaf())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
fillChildren();
|
||||
return !children.containsKey(pathElement);
|
||||
}
|
||||
@@ -173,120 +202,140 @@ public class AppStoreManager
|
||||
{
|
||||
if (isLeaf())
|
||||
{
|
||||
if (name.endsWith(".sc"))
|
||||
return Collections.singletonList(getPath());
|
||||
return Collections.emptyList();
|
||||
return name.endsWith(".sc") ? Collections.singletonList(getPath()) : Collections.emptyList();
|
||||
}
|
||||
fillChildren();
|
||||
String prefix = getPath();
|
||||
final String prefix = getPath();
|
||||
return children.values().stream().
|
||||
filter(n -> (!n.isLeaf() || n.name.endsWith(".sc"))).
|
||||
map(s -> prefix+s.pathElement().replaceAll("/$", "")).
|
||||
map(s -> prefix + s.pathElement().replaceAll("/$", "")).
|
||||
collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public StoreNode drillDown(String pathElement) throws IOException
|
||||
public StoreNode drillDown(final String pathElement) throws IOException
|
||||
{
|
||||
if (isLeaf()) throw new IOException(pathElement+" is not a folder");
|
||||
if (isLeaf())
|
||||
{
|
||||
throw new IOException(pathElement + " is not a folder");
|
||||
}
|
||||
fillChildren();
|
||||
if (!children.containsKey(pathElement)) throw new IOException("Folder "+pathElement+" is not present");
|
||||
if (!children.containsKey(pathElement))
|
||||
{
|
||||
throw new IOException("Folder " + pathElement + " is not present");
|
||||
}
|
||||
return children.get(pathElement);
|
||||
}
|
||||
|
||||
public String getValue(String file) throws IOException
|
||||
public String getValue(final String file) throws IOException
|
||||
{
|
||||
StoreNode leaf = drillDown(file);
|
||||
if (!leaf.isLeaf()) throw new IOException(file+" is not a file");
|
||||
final StoreNode leaf = drillDown(file);
|
||||
if (!leaf.isLeaf())
|
||||
{
|
||||
throw new IOException(file + " is not a file");
|
||||
}
|
||||
return leaf.value;
|
||||
}
|
||||
}
|
||||
|
||||
/** This method searches for valid file names from the user-inputted string, e.g if the user has thus far typed
|
||||
/**
|
||||
* This method searches for valid file names from the user-inputted string, e.g if the user has thus far typed
|
||||
* {@code survival/a} then it will return all the files in the {@code survival} directory of the scarpet repo (and
|
||||
* will automatically highlight those starting with a), and the string {@code survival/} as the current most valid path.
|
||||
*
|
||||
* @param currentPath The path down which we want to search for files
|
||||
* @return A pair of the current valid path, as well as the set of all the file/directory names at the end of that path
|
||||
*/
|
||||
public static List<String> suggestionsFromPath(String currentPath) throws IOException
|
||||
public static List<String> suggestionsFromPath(final String currentPath) throws IOException
|
||||
{
|
||||
String[] path = currentPath.split("/");
|
||||
final String[] path = currentPath.split("/");
|
||||
StoreNode appKiosk = APP_STORE_ROOT;
|
||||
for(String pathElement : path)
|
||||
for (final String pathElement : path)
|
||||
{
|
||||
if (appKiosk.cannotContinueFor(pathElement)) break;
|
||||
if (appKiosk.cannotContinueFor(pathElement))
|
||||
{
|
||||
break;
|
||||
}
|
||||
appKiosk = appKiosk.children.get(pathElement);
|
||||
}
|
||||
List<String> filteredSuggestions = appKiosk.createPathSuggestions().stream().filter(s -> s.startsWith(currentPath)).collect(Collectors.toList());
|
||||
if (filteredSuggestions.size() == 1) {
|
||||
final List<String> filteredSuggestions = appKiosk.createPathSuggestions().stream().filter(s -> s.startsWith(currentPath)).collect(Collectors.toList());
|
||||
if (filteredSuggestions.size() == 1)
|
||||
{
|
||||
if (!appKiosk.isLeaf())
|
||||
{
|
||||
return suggestionsFromPath(filteredSuggestions.get(0)); // Start suggesting directory contents
|
||||
}
|
||||
}
|
||||
return filteredSuggestions;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** Downloads script and saves it to appropriate place.
|
||||
/**
|
||||
* Downloads script and saves it to appropriate place.
|
||||
*
|
||||
* @param path The user-inputted path to the script
|
||||
* @return {@code 1} if we succesfully saved the script, {@code 0} otherwise
|
||||
*/
|
||||
|
||||
public static int downloadScript(CommandSourceStack source, String path)
|
||||
public static int downloadScript(final CommandSourceStack source, final String path)
|
||||
{
|
||||
AppInfo nodeInfo = getFileNode(path);
|
||||
final AppInfo nodeInfo = getFileNode(path);
|
||||
return downloadScript(source, path, nodeInfo, false);
|
||||
}
|
||||
|
||||
private static int downloadScript(CommandSourceStack source, String path, AppInfo nodeInfo, boolean useTrash)
|
||||
private static int downloadScript(final CommandSourceStack source, final String path, final AppInfo nodeInfo, final boolean useTrash)
|
||||
{
|
||||
String code;
|
||||
final String code;
|
||||
try
|
||||
{
|
||||
code = IOUtils.toString(new URL(nodeInfo.url()), StandardCharsets.UTF_8);
|
||||
}
|
||||
catch (IOException e)
|
||||
catch (final IOException e)
|
||||
{
|
||||
throw new CommandRuntimeException(Messenger.c("rb Failed to obtain app file content: "+e.getMessage()));
|
||||
throw new CommandRuntimeException(Messenger.c("rb Failed to obtain app file content: " + e.getMessage()));
|
||||
}
|
||||
if (!saveScriptToFile(source, path, nodeInfo.name(), code, false, useTrash)) return 0;
|
||||
boolean success = CarpetServer.scriptServer.addScriptHost(source, nodeInfo.name().replaceFirst("\\.sc$", ""), null, true, false, false, nodeInfo.source());
|
||||
return success?1:0;
|
||||
if (!saveScriptToFile(source, path, nodeInfo.name(), code, false, useTrash))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
final boolean success = CarpetServer.scriptServer.addScriptHost(source, nodeInfo.name().replaceFirst("\\.sc$", ""), null, true, false, false, nodeInfo.source());
|
||||
return success ? 1 : 0;
|
||||
}
|
||||
|
||||
/** Gets the code once the user inputs the command.
|
||||
/**
|
||||
* Gets the code once the user inputs the command.
|
||||
*
|
||||
* @param appPath The user inputted path to the scarpet script
|
||||
* @return Pair of app file name and content
|
||||
*/
|
||||
public static AppInfo getFileNode(String appPath)
|
||||
public static AppInfo getFileNode(final String appPath)
|
||||
{
|
||||
return getFileNodeFrom(APP_STORE_ROOT, appPath);
|
||||
}
|
||||
|
||||
public static AppInfo getFileNodeFrom(StoreNode start, String appPath)
|
||||
public static AppInfo getFileNodeFrom(final StoreNode start, final String appPath)
|
||||
{
|
||||
String[] path = appPath.split("/");
|
||||
final String[] path = appPath.split("/");
|
||||
StoreNode appKiosk = start;
|
||||
try
|
||||
{
|
||||
for(String pathElement : Arrays.copyOfRange(path, 0, path.length-1))
|
||||
for (final String pathElement : Arrays.copyOfRange(path, 0, path.length - 1))
|
||||
{
|
||||
appKiosk = appKiosk.drillDown(pathElement);
|
||||
String appName = path[path.length-1];
|
||||
}
|
||||
final String appName = path[path.length - 1];
|
||||
appKiosk.getValue(appName);
|
||||
return new AppInfo(appName, appKiosk.getValue(appName), appKiosk);
|
||||
}
|
||||
catch (IOException e)
|
||||
catch (final IOException e)
|
||||
{
|
||||
throw new CommandRuntimeException(Messenger.c("rb '"+ appPath + "' is not a valid path to a scarpet app: "+e.getMessage()));
|
||||
throw new CommandRuntimeException(Messenger.c("rb '" + appPath + "' is not a valid path to a scarpet app: " + e.getMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static boolean saveScriptToFile(CommandSourceStack source, String path, String appFileName, String code, boolean globalSavePath, boolean useTrash)
|
||||
public static boolean saveScriptToFile(final CommandSourceStack source, final String path, final String appFileName, final String code, final boolean globalSavePath, final boolean useTrash)
|
||||
{
|
||||
Path scriptLocation;
|
||||
final Path scriptLocation;
|
||||
if (globalSavePath && !source.getServer().isDedicatedServer()) // never happens, this is always called with globalSavePath being false
|
||||
{ //cos config folder only is in clients
|
||||
scriptLocation = FabricLoader.getInstance().getConfigDir().resolve("carpet/scripts/appstore").toAbsolutePath().resolve(path);
|
||||
@@ -307,29 +356,29 @@ public class AppStoreManager
|
||||
int i = 0;
|
||||
while (Files.exists(trashPath))
|
||||
{
|
||||
String[] nameAndExtension = appFileName.split("\\.");
|
||||
String newFileName = String.format(nameAndExtension[0] + "%02d." + nameAndExtension[1],i);
|
||||
final String[] nameAndExtension = appFileName.split("\\.");
|
||||
final String newFileName = String.format(nameAndExtension[0] + "%02d." + nameAndExtension[1], i);
|
||||
trashPath = trashPath.getParent().resolve(newFileName);
|
||||
i++;
|
||||
}
|
||||
Files.move(scriptLocation, trashPath);
|
||||
}
|
||||
Messenger.m(source, String.format("gi Note: replaced existing app '%s'"+(useTrash ? " (old moved to /trash folder)" : ""), appFileName));
|
||||
Messenger.m(source, String.format("gi Note: replaced existing app '%s'" + (useTrash ? " (old moved to /trash folder)" : ""), appFileName));
|
||||
}
|
||||
BufferedWriter writer = Files.newBufferedWriter(scriptLocation);
|
||||
final BufferedWriter writer = Files.newBufferedWriter(scriptLocation);
|
||||
writer.write(code);
|
||||
writer.close();
|
||||
}
|
||||
catch (IOException e)
|
||||
catch (final IOException e)
|
||||
{
|
||||
Messenger.m(source, "r Error while downloading app: " + e.toString());
|
||||
Messenger.m(source, "r Error while downloading app: " + e);
|
||||
CarpetScriptServer.LOG.warn("Error while downloading app", e);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void writeUrlToFile(String url, Path destination) throws IOException
|
||||
public static void writeUrlToFile(final String url, final Path destination) throws IOException
|
||||
{
|
||||
try (final InputStream in = new URL(url).openStream())
|
||||
{
|
||||
@@ -337,88 +386,112 @@ public class AppStoreManager
|
||||
}
|
||||
}
|
||||
|
||||
private static String getFullContentUrl(String original, StoreNode storeSource)
|
||||
private static String getFullContentUrl(final String original, final StoreNode storeSource)
|
||||
{
|
||||
if (original.matches("^https?://.*$")) // We've got a full url here: Just use it
|
||||
{
|
||||
return original;
|
||||
}
|
||||
if (original.charAt(0) == '/') // We've got an absolute path: Use app store root
|
||||
{
|
||||
return getFileNode(original.substring(1)).url();
|
||||
}
|
||||
return getFileNodeFrom(storeSource, original).url(); // Relative path: Use download location
|
||||
}
|
||||
|
||||
public static void addResource(CarpetScriptHost carpetScriptHost, StoreNode storeSource, Value resource)
|
||||
public static void addResource(final CarpetScriptHost carpetScriptHost, final StoreNode storeSource, final Value resource)
|
||||
{
|
||||
if (!(resource instanceof MapValue))
|
||||
throw new InternalExpressionException("This is not a valid resource map: "+resource.getString());
|
||||
Map<String, Value> resourceMap = ((MapValue) resource).getMap().entrySet().stream().collect(Collectors.toMap(e -> e.getKey().getString(), Map.Entry::getValue));
|
||||
if (!resourceMap.containsKey("source")) throw new InternalExpressionException("Missing 'source' field in resource descriptor: "+resource.getString());
|
||||
String source = resourceMap.get("source").getString();
|
||||
String contentUrl = getFullContentUrl(source, storeSource);
|
||||
String target = resourceMap.computeIfAbsent("target", k -> new StringValue(contentUrl.substring(contentUrl.lastIndexOf('/') + 1))).getString();
|
||||
boolean shared = resourceMap.getOrDefault("shared", Value.FALSE).getBoolean();
|
||||
if (!(resource instanceof final MapValue map))
|
||||
{
|
||||
throw new InternalExpressionException("This is not a valid resource map: " + resource.getString());
|
||||
}
|
||||
final Map<String, Value> resourceMap = map.getMap().entrySet().stream().collect(Collectors.toMap(e -> e.getKey().getString(), Map.Entry::getValue));
|
||||
if (!resourceMap.containsKey("source"))
|
||||
{
|
||||
throw new InternalExpressionException("Missing 'source' field in resource descriptor: " + resource.getString());
|
||||
}
|
||||
final String source = resourceMap.get("source").getString();
|
||||
final String contentUrl = getFullContentUrl(source, storeSource);
|
||||
final String target = resourceMap.computeIfAbsent("target", k -> new StringValue(contentUrl.substring(contentUrl.lastIndexOf('/') + 1))).getString();
|
||||
final boolean shared = resourceMap.getOrDefault("shared", Value.FALSE).getBoolean();
|
||||
|
||||
if (!carpetScriptHost.applyActionForResource(target, shared, p -> {
|
||||
try {
|
||||
try
|
||||
{
|
||||
writeUrlToFile(contentUrl, p);
|
||||
} catch (IOException e) {
|
||||
throw new InternalExpressionException("Unable to write resource "+target+": "+e.toString());
|
||||
}
|
||||
catch (final IOException e)
|
||||
{
|
||||
throw new InternalExpressionException("Unable to write resource " + target + ": " + e);
|
||||
}
|
||||
}))
|
||||
{
|
||||
throw new InternalExpressionException("Unable to write resource "+target);
|
||||
throw new InternalExpressionException("Unable to write resource " + target);
|
||||
}
|
||||
CarpetScriptServer.LOG.info("Downloaded resource "+target+" from "+contentUrl);
|
||||
CarpetScriptServer.LOG.info("Downloaded resource " + target + " from " + contentUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a new StoreNode for an app's dependency with proper relativeness. Will be null if it comes from an external URL
|
||||
*
|
||||
* @param originalSource The StoreNode from the container's app
|
||||
* @param sourceString The string the app specified as source
|
||||
* @param contentUrl The full content URL, from {@link #getFullContentUrl(String, StoreNode)}
|
||||
* @param sourceString The string the app specified as source
|
||||
* @param contentUrl The full content URL, from {@link #getFullContentUrl(String, StoreNode)}
|
||||
* @return A {@link StoreNode} that can be used in an app that came from the provided source
|
||||
*/
|
||||
private static StoreNode getNewStoreNode(StoreNode originalSource, String sourceString, String contentUrl)
|
||||
private static StoreNode getNewStoreNode(final StoreNode originalSource, String sourceString, final String contentUrl)
|
||||
{
|
||||
StoreNode next = originalSource;
|
||||
if (sourceString == contentUrl) // External URL (check getFullUrlContent)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (sourceString.charAt(0) == '/') // Absolute URL
|
||||
{
|
||||
next = APP_STORE_ROOT;
|
||||
sourceString = sourceString.substring(1);
|
||||
}
|
||||
String[] dirs = sourceString.split("/");
|
||||
final String[] dirs = sourceString.split("/");
|
||||
try
|
||||
{
|
||||
for (int i = 0; i < dirs.length - 1; i++)
|
||||
{
|
||||
next = next.drillDown(dirs[i]);
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
catch (final IOException e)
|
||||
{
|
||||
return null; // Should never happen, but let's not give a potentially incorrect node just in case
|
||||
}
|
||||
return next;
|
||||
}
|
||||
|
||||
public static void addLibrary(CarpetScriptHost carpetScriptHost, StoreNode storeSource, Value library) {
|
||||
if (!(library instanceof MapValue))
|
||||
throw new InternalExpressionException("This is not a valid library map: "+library.getString());
|
||||
Map<String, String> libraryMap = ((MapValue) library).getMap().entrySet().stream().collect(Collectors.toMap(e -> e.getKey().getString(), e -> e.getValue().getString()));
|
||||
String source = libraryMap.get("source");
|
||||
String contentUrl = getFullContentUrl(source, storeSource);
|
||||
String target = libraryMap.computeIfAbsent("target", k -> contentUrl.substring(contentUrl.lastIndexOf('/') + 1));
|
||||
public static void addLibrary(final CarpetScriptHost carpetScriptHost, final StoreNode storeSource, final Value library)
|
||||
{
|
||||
if (!(library instanceof final MapValue map))
|
||||
{
|
||||
throw new InternalExpressionException("This is not a valid library map: " + library.getString());
|
||||
}
|
||||
final Map<String, String> libraryMap = map.getMap().entrySet().stream().collect(Collectors.toMap(e -> e.getKey().getString(), e -> e.getValue().getString()));
|
||||
final String source = libraryMap.get("source");
|
||||
final String contentUrl = getFullContentUrl(source, storeSource);
|
||||
final String target = libraryMap.computeIfAbsent("target", k -> contentUrl.substring(contentUrl.lastIndexOf('/') + 1));
|
||||
if (!(contentUrl.endsWith(".sc") || contentUrl.endsWith(".scl")))
|
||||
{
|
||||
throw new InternalExpressionException("App resource type must download a scarpet app or library.");
|
||||
}
|
||||
if (target.indexOf('/') != -1)
|
||||
{
|
||||
throw new InternalExpressionException("App resource tried to leave script reserved space");
|
||||
}
|
||||
try
|
||||
{
|
||||
downloadScript(carpetScriptHost.responsibleSource, target, new AppInfo(target, contentUrl, getNewStoreNode(storeSource, source, contentUrl)), true);
|
||||
}
|
||||
catch (CommandRuntimeException e)
|
||||
catch (final CommandRuntimeException e)
|
||||
{
|
||||
throw new InternalExpressionException("Error when installing app dependencies: " + e.toString());
|
||||
throw new InternalExpressionException("Error when installing app dependencies: " + e);
|
||||
}
|
||||
CarpetScriptServer.LOG.info("Downloaded app "+target+" from "+contentUrl);
|
||||
CarpetScriptServer.LOG.info("Downloaded app " + target + " from " + contentUrl);
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import carpet.script.value.NumericValue;
|
||||
import carpet.script.value.StringValue;
|
||||
import carpet.script.value.Value;
|
||||
import carpet.script.value.ValueConversions;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
@@ -20,12 +21,9 @@ import net.minecraft.world.level.levelgen.feature.ConfiguredFeature;
|
||||
|
||||
public class BiomeInfo
|
||||
{
|
||||
public final static Map<String, BiFunction<ServerLevel, Biome, Value>> biomeFeatures = new HashMap<String, BiFunction<ServerLevel, Biome, Value>>(){{
|
||||
//put("top_material", (w, b) -> new BlockValue( b.getGenerationSettings(). getSurfaceConfig().getTopMaterial(), null, null));
|
||||
//put("under_material", (w, b) -> new BlockValue( b.getGenerationSettings().getSurfaceConfig().getUnderMaterial(), null, null));
|
||||
//put("category", (w, b) -> StringValue.of(Biome.getBiomeCategory(Holder.direct(b)).getName()));
|
||||
public static final Map<String, BiFunction<ServerLevel, Biome, Value>> biomeFeatures = new HashMap<>()
|
||||
{{
|
||||
put("tags", (w, b) -> ListValue.wrap(w.registryAccess().registryOrThrow(Registries.BIOME).getTags().filter(p -> p.getSecond().stream().anyMatch(h -> h.value() == b)).map(p -> p.getFirst().location()).map(ValueConversions::of)));
|
||||
|
||||
put("temperature", (w, b) -> NumericValue.of(b.getBaseTemperature()));
|
||||
put("fog_color", (w, b) -> ValueConversions.ofRGB(b.getSpecialEffects().getFogColor()));
|
||||
put("foliage_color", (w, b) -> ValueConversions.ofRGB(b.getSpecialEffects().getFoliageColorOverride().orElse(4764952))); // client Biome.getDefaultFoliageColor
|
||||
@@ -34,11 +32,8 @@ public class BiomeInfo
|
||||
put("water_fog_color", (w, b) -> ValueConversions.ofRGB(b.getSpecialEffects().getWaterFogColor()));
|
||||
put("humidity", (w, b) -> NumericValue.of(((BiomeInterface) (Object) b).getClimateSettings().downfall()));
|
||||
put("precipitation", (w, b) -> StringValue.of(b.getPrecipitationAt(new BlockPos(0, w.getSeaLevel(), 0)).name().toLowerCase(Locale.ROOT)));
|
||||
//put("depth", (w, b) -> NumericValue.of(b.getDepth()));
|
||||
//put("scale", (w, b) -> NumericValue.of(b.getScale()));
|
||||
put("features", (w, b) -> {
|
||||
|
||||
Registry<ConfiguredFeature<?,?>> registry = w.registryAccess().registryOrThrow(Registries.CONFIGURED_FEATURE);
|
||||
final Registry<ConfiguredFeature<?, ?>> registry = w.registryAccess().registryOrThrow(Registries.CONFIGURED_FEATURE);
|
||||
return ListValue.wrap(
|
||||
b.getGenerationSettings().features().stream().map(step ->
|
||||
ListValue.wrap(step.stream().map(cfp ->
|
||||
@@ -47,9 +42,5 @@ public class BiomeInfo
|
||||
)
|
||||
);
|
||||
});
|
||||
//put("structures", (w, b) -> {
|
||||
// Registry<ConfiguredStructureFeature<?,?>> registry = w.getRegistryManager().get(Registry.CONFIGURED_STRUCTURE_FEATURE_KEY);
|
||||
// return ListValue.wrap(b.getGenerationSettings().getStructureFeatures().stream().map(str -> ValueConversions.of(registry.getId(str.get()))));
|
||||
//});
|
||||
}};
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package carpet.script.utils;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import net.minecraft.world.Container;
|
||||
import net.minecraft.world.entity.EquipmentSlot;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
@@ -17,7 +18,8 @@ public class EquipmentInventory implements Container
|
||||
);
|
||||
|
||||
LivingEntity mob;
|
||||
public EquipmentInventory(LivingEntity mob)
|
||||
|
||||
public EquipmentInventory(final LivingEntity mob)
|
||||
{
|
||||
this.mob = mob;
|
||||
}
|
||||
@@ -31,20 +33,25 @@ public class EquipmentInventory implements Container
|
||||
@Override
|
||||
public boolean isEmpty()
|
||||
{
|
||||
for (EquipmentSlot slot: slotToSlot)
|
||||
if (!mob.getItemBySlot(slot).isEmpty()) return false;
|
||||
for (final EquipmentSlot slot : slotToSlot)
|
||||
{
|
||||
if (!mob.getItemBySlot(slot).isEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getItem(int slot)
|
||||
public ItemStack getItem(final int slot)
|
||||
{
|
||||
EquipmentSlot slotSlot;
|
||||
final EquipmentSlot slotSlot;
|
||||
try
|
||||
{
|
||||
slotSlot = slotToSlot.get(slot);
|
||||
}
|
||||
catch (IndexOutOfBoundsException ignored)
|
||||
catch (final IndexOutOfBoundsException ignored)
|
||||
{
|
||||
//going out of the index should be really exceptional
|
||||
return ItemStack.EMPTY;
|
||||
@@ -53,14 +60,14 @@ public class EquipmentInventory implements Container
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removeItem(int slot, int amount)
|
||||
public ItemStack removeItem(final int slot, final int amount)
|
||||
{
|
||||
EquipmentSlot slotSlot;
|
||||
final EquipmentSlot slotSlot;
|
||||
try
|
||||
{
|
||||
slotSlot = slotToSlot.get(slot);
|
||||
}
|
||||
catch (IndexOutOfBoundsException ignored)
|
||||
catch (final IndexOutOfBoundsException ignored)
|
||||
{
|
||||
//going out of the index should be really exceptional
|
||||
return ItemStack.EMPTY;
|
||||
@@ -69,32 +76,32 @@ public class EquipmentInventory implements Container
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removeItemNoUpdate(int slot)
|
||||
public ItemStack removeItemNoUpdate(final int slot)
|
||||
{
|
||||
EquipmentSlot slotSlot;
|
||||
final EquipmentSlot slotSlot;
|
||||
try
|
||||
{
|
||||
slotSlot = slotToSlot.get(slot);
|
||||
}
|
||||
catch (IndexOutOfBoundsException ignored)
|
||||
catch (final IndexOutOfBoundsException ignored)
|
||||
{
|
||||
//going out of the index should be really exceptional
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
ItemStack previous = mob.getItemBySlot(slotSlot);
|
||||
final ItemStack previous = mob.getItemBySlot(slotSlot);
|
||||
mob.setItemSlot(slotSlot, ItemStack.EMPTY);
|
||||
return previous;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem(int slot, ItemStack stack)
|
||||
public void setItem(final int slot, final ItemStack stack)
|
||||
{
|
||||
EquipmentSlot slotSlot;
|
||||
final EquipmentSlot slotSlot;
|
||||
try
|
||||
{
|
||||
slotSlot = slotToSlot.get(slot);
|
||||
}
|
||||
catch (IndexOutOfBoundsException ignored)
|
||||
catch (final IndexOutOfBoundsException ignored)
|
||||
{
|
||||
//going out of the index should be really exceptional
|
||||
return;
|
||||
@@ -109,7 +116,7 @@ public class EquipmentInventory implements Container
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean stillValid(Player player)
|
||||
public boolean stillValid(final Player player)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -117,7 +124,9 @@ public class EquipmentInventory implements Container
|
||||
@Override
|
||||
public void clearContent()
|
||||
{
|
||||
for (EquipmentSlot slot: slotToSlot)
|
||||
for (final EquipmentSlot slot : slotToSlot)
|
||||
{
|
||||
mob.setItemSlot(slot, ItemStack.EMPTY);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,7 +6,7 @@ public class GlocalFlag extends ThreadLocal<Boolean>
|
||||
{
|
||||
private final boolean initial;
|
||||
|
||||
public GlocalFlag(boolean initial)
|
||||
public GlocalFlag(final boolean initial)
|
||||
{
|
||||
this.initial = initial;
|
||||
}
|
||||
@@ -19,19 +19,20 @@ public class GlocalFlag extends ThreadLocal<Boolean>
|
||||
|
||||
/**
|
||||
* Allows to thread-safely wrap a call while disabling a global flag and setting it back up right after.
|
||||
*
|
||||
* @param action - callback to invoke when the wrapping is all setup
|
||||
* @param <T> - returned value of that action, whatever that might be
|
||||
* @param <T> - returned value of that action, whatever that might be
|
||||
* @return result of the action
|
||||
*/
|
||||
public <T> T getWhileDisabled(Supplier<T> action)
|
||||
public <T> T getWhileDisabled(final Supplier<T> action)
|
||||
{
|
||||
return whileValueReturn(!initial, action);
|
||||
}
|
||||
|
||||
private <T> T whileValueReturn(boolean what, Supplier<T> action)
|
||||
private <T> T whileValueReturn(final boolean what, final Supplier<T> action)
|
||||
{
|
||||
T result;
|
||||
boolean previous;
|
||||
final boolean previous;
|
||||
synchronized (this)
|
||||
{
|
||||
previous = get();
|
||||
@@ -48,11 +49,14 @@ public class GlocalFlag extends ThreadLocal<Boolean>
|
||||
return result;
|
||||
}
|
||||
|
||||
public <T> T runIfEnabled(Supplier<T> action)
|
||||
public <T> T runIfEnabled(final Supplier<T> action)
|
||||
{
|
||||
synchronized (this)
|
||||
{
|
||||
if (get() != initial) return null;
|
||||
if (get() != initial)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
set(!initial);
|
||||
}
|
||||
T result;
|
||||
|
||||
@@ -3,27 +3,31 @@ package carpet.script.utils;
|
||||
import carpet.script.exception.InternalExpressionException;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import net.minecraft.ResourceLocationException;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
|
||||
public class InputValidator {
|
||||
public static String validateSimpleString(String input, boolean strict)
|
||||
public class InputValidator
|
||||
{
|
||||
public static String validateSimpleString(final String input, final boolean strict)
|
||||
{
|
||||
String simplified = input.toLowerCase(Locale.ROOT).replaceAll("[^A-Za-z0-9+_]", "");
|
||||
final 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;
|
||||
}
|
||||
|
||||
public static ResourceLocation identifierOf(String string)
|
||||
public static ResourceLocation identifierOf(final String string)
|
||||
{
|
||||
try
|
||||
{
|
||||
return new ResourceLocation(string);
|
||||
}
|
||||
catch (ResourceLocationException iie)
|
||||
catch (final ResourceLocationException iie)
|
||||
{
|
||||
throw new InternalExpressionException("Incorrect identifier format '"+string+"': "+iie.getMessage());
|
||||
throw new InternalExpressionException("Incorrect identifier format '" + string + "': " + iie.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,8 @@ import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
||||
// extracted from import net.minecraft.util.math.noise.PerlinNoiseSampler
|
||||
public class PerlinNoiseSampler {
|
||||
public class PerlinNoiseSampler
|
||||
{
|
||||
protected static final int[][] gradients3d = new int[][]{
|
||||
{1, 1, 0}, {-1, 1, 0}, {1, -1, 0}, {-1, -1, 0},
|
||||
{1, 0, 1}, {-1, 0, 1}, {1, 0, -1}, {-1, 0, -1},
|
||||
@@ -23,49 +24,50 @@ public class PerlinNoiseSampler {
|
||||
public static PerlinNoiseSampler instance = new PerlinNoiseSampler(new Random(0));
|
||||
public static Map<Long, PerlinNoiseSampler> samplers = new Long2ObjectOpenHashMap<>();
|
||||
|
||||
public static PerlinNoiseSampler getPerlin(long aLong)
|
||||
public static PerlinNoiseSampler getPerlin(final long aLong)
|
||||
{
|
||||
if (samplers.size() > 256)
|
||||
{
|
||||
samplers.clear();
|
||||
}
|
||||
return samplers.computeIfAbsent(aLong, seed -> new PerlinNoiseSampler(new Random(seed)));
|
||||
}
|
||||
|
||||
public PerlinNoiseSampler(Random random) {
|
||||
public PerlinNoiseSampler(final Random random)
|
||||
{
|
||||
this.originX = random.nextDouble() * 256.0D;
|
||||
this.originY = random.nextDouble() * 256.0D;
|
||||
this.originZ = random.nextDouble() * 256.0D;
|
||||
this.permutations = new byte[256];
|
||||
|
||||
int j;
|
||||
for(j = 0; j < 256; ++j) {
|
||||
this.permutations[j] = (byte)j;
|
||||
for (j = 0; j < 256; ++j)
|
||||
{
|
||||
this.permutations[j] = (byte) j;
|
||||
}
|
||||
|
||||
for(j = 0; j < 256; ++j) {
|
||||
int k = random.nextInt(256 - j);
|
||||
byte b = this.permutations[j];
|
||||
for (j = 0; j < 256; ++j)
|
||||
{
|
||||
final int k = random.nextInt(256 - j);
|
||||
final byte b = this.permutations[j];
|
||||
this.permutations[j] = this.permutations[j + k];
|
||||
this.permutations[j + k] = b;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
//3D
|
||||
public double sample3d(double x, double y, double z) {//, double d, double e) {
|
||||
double f = x + this.originX;
|
||||
double g = y + this.originY;
|
||||
double h = z + this.originZ;
|
||||
int i = floor(f);
|
||||
int j = floor(g);
|
||||
int k = floor(h);
|
||||
double l = f - (double)i;
|
||||
double m = g - (double)j;
|
||||
double n = h - (double)k;
|
||||
double o = perlinFade(l);
|
||||
double p = perlinFade(m);
|
||||
double q = perlinFade(n);
|
||||
public double sample3d(final double x, final double y, final double z)
|
||||
{//, double d, double e) {
|
||||
final double f = x + this.originX;
|
||||
final double g = y + this.originY;
|
||||
final double h = z + this.originZ;
|
||||
final int i = floor(f);
|
||||
final int j = floor(g);
|
||||
final int k = floor(h);
|
||||
final double l = f - (double) i;
|
||||
final double m = g - (double) j;
|
||||
final double n = h - (double) k;
|
||||
final double o = perlinFade(l);
|
||||
final double p = perlinFade(m);
|
||||
final double q = perlinFade(n);
|
||||
//double t;
|
||||
/*
|
||||
if (d != 0.0D) {
|
||||
@@ -75,110 +77,127 @@ public class PerlinNoiseSampler {
|
||||
t = 0.0D;
|
||||
}*/
|
||||
//return this.sample(i, j, k, l, m - t, n, o, p, q);
|
||||
return this.sample3d(i, j, k, l, m, n, o, p, q)/2+0.5;
|
||||
return this.sample3d(i, j, k, l, m, n, o, p, q) / 2 + 0.5;
|
||||
}
|
||||
|
||||
private double sample3d(int sectionX, int sectionY, int sectionZ, double localX, double localY, double localZ, double fadeLocalX, double fadeLocalY, double fadeLocalZ) {
|
||||
int i = this.getGradient(sectionX) + sectionY;
|
||||
int j = this.getGradient(i) + sectionZ;
|
||||
int k = this.getGradient(i + 1) + sectionZ;
|
||||
int l = this.getGradient(sectionX + 1) + sectionY;
|
||||
int m = this.getGradient(l) + sectionZ;
|
||||
int n = this.getGradient(l + 1) + sectionZ;
|
||||
double d = grad3d(this.getGradient(j), localX, localY, localZ);
|
||||
double e = grad3d(this.getGradient(m), localX - 1.0D, localY, localZ);
|
||||
double f = grad3d(this.getGradient(k), localX, localY - 1.0D, localZ);
|
||||
double g = grad3d(this.getGradient(n), localX - 1.0D, localY - 1.0D, localZ);
|
||||
double h = grad3d(this.getGradient(j + 1), localX, localY, localZ - 1.0D);
|
||||
double o = grad3d(this.getGradient(m + 1), localX - 1.0D, localY, localZ - 1.0D);
|
||||
double p = grad3d(this.getGradient(k + 1), localX, localY - 1.0D, localZ - 1.0D);
|
||||
double q = grad3d(this.getGradient(n + 1), localX - 1.0D, localY - 1.0D, localZ - 1.0D);
|
||||
private double sample3d(final int sectionX, final int sectionY, final int sectionZ, final double localX, final double localY, final double localZ, final double fadeLocalX, final double fadeLocalY, final double fadeLocalZ)
|
||||
{
|
||||
final int i = this.getGradient(sectionX) + sectionY;
|
||||
final int j = this.getGradient(i) + sectionZ;
|
||||
final int k = this.getGradient(i + 1) + sectionZ;
|
||||
final int l = this.getGradient(sectionX + 1) + sectionY;
|
||||
final int m = this.getGradient(l) + sectionZ;
|
||||
final int n = this.getGradient(l + 1) + sectionZ;
|
||||
final double d = grad3d(this.getGradient(j), localX, localY, localZ);
|
||||
final double e = grad3d(this.getGradient(m), localX - 1.0D, localY, localZ);
|
||||
final double f = grad3d(this.getGradient(k), localX, localY - 1.0D, localZ);
|
||||
final double g = grad3d(this.getGradient(n), localX - 1.0D, localY - 1.0D, localZ);
|
||||
final double h = grad3d(this.getGradient(j + 1), localX, localY, localZ - 1.0D);
|
||||
final double o = grad3d(this.getGradient(m + 1), localX - 1.0D, localY, localZ - 1.0D);
|
||||
final double p = grad3d(this.getGradient(k + 1), localX, localY - 1.0D, localZ - 1.0D);
|
||||
final double q = grad3d(this.getGradient(n + 1), localX - 1.0D, localY - 1.0D, localZ - 1.0D);
|
||||
return lerp3(fadeLocalX, fadeLocalY, fadeLocalZ, d, e, f, g, h, o, p, q);
|
||||
}
|
||||
|
||||
private static double grad3d(int hash, double x, double y, double z) {
|
||||
int i = hash & 15;
|
||||
private static double grad3d(final int hash, final double x, final double y, final double z)
|
||||
{
|
||||
final int i = hash & 15;
|
||||
return dot3d(gradients3d[i], x, y, z);
|
||||
}
|
||||
|
||||
protected static double dot3d(int[] gArr, double x, double y, double z) {
|
||||
return (double)gArr[0] * x + (double)gArr[1] * y + (double)gArr[2] * z;
|
||||
protected static double dot3d(final int[] gArr, final double x, final double y, final double z)
|
||||
{
|
||||
return gArr[0] * x + gArr[1] * y + gArr[2] * z;
|
||||
}
|
||||
|
||||
public static double lerp3(double deltaX, double deltaY, double deltaZ, double d, double e, double f, double g, double h, double i, double j, double k) {
|
||||
public static double lerp3(final double deltaX, final double deltaY, final double deltaZ, final double d, final double e, final double f, final double g, final double h, final double i, final double j, final double k)
|
||||
{
|
||||
return lerp(deltaZ, lerp2(deltaX, deltaY, d, e, f, g), lerp2(deltaX, deltaY, h, i, j, k));
|
||||
}
|
||||
|
||||
//2D
|
||||
public double sample2d(double x, double y) {//, double d, double e) {
|
||||
double f = x + this.originX;
|
||||
double g = y + this.originY;
|
||||
int i = floor(f);
|
||||
int j = floor(g);
|
||||
double l = f - (double)i;
|
||||
double m = g - (double)j;
|
||||
double o = perlinFade(l);
|
||||
double p = perlinFade(m);
|
||||
return this.sample2d(i, j, l, m, o, p)/2+0.5;
|
||||
public double sample2d(final double x, final double y)
|
||||
{
|
||||
final double f = x + this.originX;
|
||||
final double g = y + this.originY;
|
||||
final int i = floor(f);
|
||||
final int j = floor(g);
|
||||
final double l = f - (double) i;
|
||||
final double m = g - (double) j;
|
||||
final double o = perlinFade(l);
|
||||
final double p = perlinFade(m);
|
||||
return this.sample2d(i, j, l, m, o, p) / 2 + 0.5;
|
||||
}
|
||||
|
||||
private double sample2d(int sectionX, int sectionY, double localX, double localY, double fadeLocalX, double fadeLocalY) {
|
||||
int j = this.getGradient(sectionX) + sectionY;
|
||||
int m = this.getGradient(sectionX + 1) + sectionY;
|
||||
double d = grad2d(this.getGradient(j), localX, localY);
|
||||
double e = grad2d(this.getGradient(m), localX - 1.0D, localY);
|
||||
double f = grad2d(this.getGradient(j+1), localX, localY - 1.0D);
|
||||
double g = grad2d(this.getGradient(m+1), localX - 1.0D, localY - 1.0D);
|
||||
private double sample2d(final int sectionX, final int sectionY, final double localX, final double localY, final double fadeLocalX, final double fadeLocalY)
|
||||
{
|
||||
final int j = this.getGradient(sectionX) + sectionY;
|
||||
final int m = this.getGradient(sectionX + 1) + sectionY;
|
||||
final double d = grad2d(this.getGradient(j), localX, localY);
|
||||
final double e = grad2d(this.getGradient(m), localX - 1.0D, localY);
|
||||
final double f = grad2d(this.getGradient(j + 1), localX, localY - 1.0D);
|
||||
final double g = grad2d(this.getGradient(m + 1), localX - 1.0D, localY - 1.0D);
|
||||
|
||||
return lerp2(fadeLocalX, fadeLocalY, d, e, f, g);
|
||||
}
|
||||
|
||||
private static double grad2d(int hash, double x, double y) {
|
||||
int i = hash & 3;
|
||||
private static double grad2d(final int hash, final double x, final double y)
|
||||
{
|
||||
final int i = hash & 3;
|
||||
return dot2d(gradients2d[i], x, y);
|
||||
}
|
||||
|
||||
protected static double dot2d(int[] gArr, double x, double y) {
|
||||
return (double)gArr[0] * x + (double)gArr[1] * y;
|
||||
protected static double dot2d(final int[] gArr, final double x, final double y)
|
||||
{
|
||||
return gArr[0] * x + gArr[1] * y;
|
||||
}
|
||||
|
||||
public static double lerp2(double deltaX, double deltaY, double d, double e, double f, double g) {
|
||||
public static double lerp2(final double deltaX, final double deltaY, final double d, final double e, final double f, final double g)
|
||||
{
|
||||
return lerp(deltaY, lerp(deltaX, d, e), lerp(deltaX, f, g));
|
||||
}
|
||||
|
||||
// 1D
|
||||
public double sample1d(double x) {//, double d, double e) {
|
||||
double f = x + this.originX;
|
||||
int i = floor(f);
|
||||
double l = f - (double)i;
|
||||
double o = perlinFade(l);
|
||||
return this.sample1d(i, l, o)+0.5;
|
||||
public double sample1d(final double x)
|
||||
{
|
||||
final double f = x + this.originX;
|
||||
final int i = floor(f);
|
||||
final double l = f - i;
|
||||
final double o = perlinFade(l);
|
||||
return this.sample1d(i, l, o) + 0.5;
|
||||
}
|
||||
private double sample1d(int sectionX, double localX, double fadeLocalX) {
|
||||
double d = grad1d(this.getGradient(sectionX), localX);
|
||||
double e = grad1d(this.getGradient(sectionX+1), localX - 1.0D);
|
||||
|
||||
private double sample1d(final int sectionX, final double localX, final double fadeLocalX)
|
||||
{
|
||||
final double d = grad1d(this.getGradient(sectionX), localX);
|
||||
final double e = grad1d(this.getGradient(sectionX + 1), localX - 1.0D);
|
||||
return lerp(fadeLocalX, d, e);
|
||||
}
|
||||
|
||||
private static double grad1d(int hash, double x) {
|
||||
return ((hash & 1) == 0)? x:-x;
|
||||
private static double grad1d(final int hash, final double x)
|
||||
{
|
||||
return ((hash & 1) == 0) ? x : -x;
|
||||
}
|
||||
|
||||
public static double lerp(double delta, double first, double second) {
|
||||
public static double lerp(final double delta, final double first, final double second)
|
||||
{
|
||||
return first + delta * (second - first);
|
||||
}
|
||||
|
||||
// shared
|
||||
public int getGradient(int hash) {
|
||||
public int getGradient(final int hash)
|
||||
{
|
||||
return this.permutations[hash & 255] & 255;
|
||||
}
|
||||
|
||||
public static double perlinFade(double d) {
|
||||
public static double perlinFade(final double d)
|
||||
{
|
||||
return d * d * d * (d * (d * 6.0D - 15.0D) + 10.0D);
|
||||
}
|
||||
|
||||
public static int floor(double d) {
|
||||
int i = (int)d;
|
||||
return d < (double)i ? i - 1 : i;
|
||||
public static int floor(final double d)
|
||||
{
|
||||
final int i = (int) d;
|
||||
return d < i ? i - 1 : i;
|
||||
}
|
||||
}
|
||||
@@ -20,15 +20,17 @@ import carpet.script.value.NumericValue;
|
||||
import carpet.script.value.StringValue;
|
||||
import carpet.script.value.Value;
|
||||
|
||||
public class ScarpetJsonDeserializer implements JsonDeserializer<Value>{
|
||||
public class ScarpetJsonDeserializer implements JsonDeserializer<Value>
|
||||
{
|
||||
|
||||
@Override
|
||||
public Value deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
|
||||
throws JsonParseException {
|
||||
public Value deserialize(final JsonElement json, final Type typeOfT, final JsonDeserializationContext context) throws JsonParseException
|
||||
{
|
||||
return parseElement(json);
|
||||
}
|
||||
|
||||
private Value parseElement(JsonElement element) throws JsonParseException {
|
||||
|
||||
private Value parseElement(final JsonElement element) throws JsonParseException
|
||||
{
|
||||
if (element.isJsonObject())
|
||||
{
|
||||
return parseMap(element.getAsJsonObject());
|
||||
@@ -44,23 +46,22 @@ public class ScarpetJsonDeserializer implements JsonDeserializer<Value>{
|
||||
return Value.NULL;
|
||||
}
|
||||
|
||||
private Value parseMap(JsonObject jsonMap) throws JsonParseException {
|
||||
Map<Value, Value> map = new HashMap<Value, Value>();
|
||||
jsonMap.entrySet().forEach(entry -> {
|
||||
map.put(new StringValue(entry.getKey()), parseElement(entry.getValue()));
|
||||
});
|
||||
private Value parseMap(final JsonObject jsonMap) throws JsonParseException
|
||||
{
|
||||
final Map<Value, Value> map = new HashMap<>();
|
||||
jsonMap.entrySet().forEach(entry -> map.put(new StringValue(entry.getKey()), parseElement(entry.getValue())));
|
||||
return MapValue.wrap(map);
|
||||
}
|
||||
|
||||
private Value parseList(JsonArray jsonList) throws JsonParseException {
|
||||
List<Value> list = new ArrayList<Value>();
|
||||
jsonList.forEach(elem -> {
|
||||
list.add(parseElement(elem));
|
||||
});
|
||||
private Value parseList(final JsonArray jsonList) throws JsonParseException
|
||||
{
|
||||
final List<Value> list = new ArrayList<>();
|
||||
jsonList.forEach(elem -> list.add(parseElement(elem)));
|
||||
return new ListValue(list);
|
||||
}
|
||||
|
||||
private Value parsePrimitive(JsonPrimitive primitive) throws JsonParseException {
|
||||
|
||||
private Value parsePrimitive(final JsonPrimitive primitive) throws JsonParseException
|
||||
{
|
||||
if (primitive.isString())
|
||||
{
|
||||
return new StringValue(primitive.getAsString());
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -6,7 +6,8 @@ import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
||||
// extracted from import net.minecraft.util.math.noise.SimplexNoiseSampler
|
||||
public class SimplexNoiseSampler extends PerlinNoiseSampler {
|
||||
public class SimplexNoiseSampler extends PerlinNoiseSampler
|
||||
{
|
||||
private static final double sqrt3 = Math.sqrt(3.0D);
|
||||
private static final double SKEW_FACTOR_2D;
|
||||
private static final double UNSKEW_FACTOR_2D;
|
||||
@@ -14,23 +15,30 @@ public class SimplexNoiseSampler extends PerlinNoiseSampler {
|
||||
public static SimplexNoiseSampler instance = new SimplexNoiseSampler(new Random(0));
|
||||
public static Map<Long, SimplexNoiseSampler> samplers = new Long2ObjectOpenHashMap<>();
|
||||
|
||||
public static SimplexNoiseSampler getSimplex(long aLong)
|
||||
public static SimplexNoiseSampler getSimplex(final long aLong)
|
||||
{
|
||||
if (samplers.size() > 256)
|
||||
{
|
||||
samplers.clear();
|
||||
}
|
||||
return samplers.computeIfAbsent(aLong, seed -> new SimplexNoiseSampler(new Random(seed)));
|
||||
}
|
||||
|
||||
public SimplexNoiseSampler(Random random) {
|
||||
public SimplexNoiseSampler(final Random random)
|
||||
{
|
||||
super(random);
|
||||
}
|
||||
|
||||
private double grad(int hash, double x, double y, double z, double d) {
|
||||
private double grad(final int hash, final double x, final double y, final double z, final double d)
|
||||
{
|
||||
double e = d - x * x - y * y - z * z;
|
||||
double g;
|
||||
if (e < 0.0D) {
|
||||
final double g;
|
||||
if (e < 0.0D)
|
||||
{
|
||||
g = 0.0D;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
e *= e;
|
||||
g = e * e * PerlinNoiseSampler.dot3d(PerlinNoiseSampler.gradients3d[hash], x, y, z);
|
||||
}
|
||||
@@ -39,83 +47,94 @@ public class SimplexNoiseSampler extends PerlinNoiseSampler {
|
||||
}
|
||||
|
||||
@Override
|
||||
public double sample2d(double x, double y) {
|
||||
x = x/2;
|
||||
y = y/2;
|
||||
double d = (x + y) * SKEW_FACTOR_2D;
|
||||
int i = PerlinNoiseSampler.floor(x + d);
|
||||
int j = PerlinNoiseSampler.floor(y + d);
|
||||
double e = (double)(i + j) * UNSKEW_FACTOR_2D;
|
||||
double f = (double)i - e;
|
||||
double g = (double)j - e;
|
||||
double h = x - f;
|
||||
double k = y - g;
|
||||
byte n;
|
||||
byte o;
|
||||
if (h > k) {
|
||||
public double sample2d(double x, double y)
|
||||
{
|
||||
x = x / 2;
|
||||
y = y / 2;
|
||||
final double d = (x + y) * SKEW_FACTOR_2D;
|
||||
final int i = PerlinNoiseSampler.floor(x + d);
|
||||
final int j = PerlinNoiseSampler.floor(y + d);
|
||||
final double e = (i + j) * UNSKEW_FACTOR_2D;
|
||||
final double f = i - e;
|
||||
final double g = j - e;
|
||||
final double h = x - f;
|
||||
final double k = y - g;
|
||||
final byte n;
|
||||
final byte o;
|
||||
if (h > k)
|
||||
{
|
||||
n = 1;
|
||||
o = 0;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
n = 0;
|
||||
o = 1;
|
||||
}
|
||||
|
||||
double p = h - (double)n + UNSKEW_FACTOR_2D;
|
||||
double q = k - (double)o + UNSKEW_FACTOR_2D;
|
||||
double r = h - 1.0D + 2.0D * UNSKEW_FACTOR_2D;
|
||||
double s = k - 1.0D + 2.0D * UNSKEW_FACTOR_2D;
|
||||
int t = i & 255;
|
||||
int u = j & 255;
|
||||
int v = this.getGradient(t + this.getGradient(u)) % 12;
|
||||
int w = this.getGradient(t + n + this.getGradient(u + o)) % 12;
|
||||
int z = this.getGradient(t + 1 + this.getGradient(u + 1)) % 12;
|
||||
double aa = this.grad(v, h, k, 0.0D, 0.5D);
|
||||
double ab = this.grad(w, p, q, 0.0D, 0.5D);
|
||||
double ac = this.grad(z, r, s, 0.0D, 0.5D);
|
||||
final double p = h - n + UNSKEW_FACTOR_2D;
|
||||
final double q = k - o + UNSKEW_FACTOR_2D;
|
||||
final double r = h - 1.0D + 2.0D * UNSKEW_FACTOR_2D;
|
||||
final double s = k - 1.0D + 2.0D * UNSKEW_FACTOR_2D;
|
||||
final int t = i & 255;
|
||||
final int u = j & 255;
|
||||
final int v = this.getGradient(t + this.getGradient(u)) % 12;
|
||||
final int w = this.getGradient(t + n + this.getGradient(u + o)) % 12;
|
||||
final int z = this.getGradient(t + 1 + this.getGradient(u + 1)) % 12;
|
||||
final double aa = this.grad(v, h, k, 0.0D, 0.5D);
|
||||
final double ab = this.grad(w, p, q, 0.0D, 0.5D);
|
||||
final double ac = this.grad(z, r, s, 0.0D, 0.5D);
|
||||
//return 70.0D * (aa + ab + ac);
|
||||
return 35.0D * (aa + ab + ac)+0.5;
|
||||
return 35.0D * (aa + ab + ac) + 0.5;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double sample3d(double d, double e, double f) {
|
||||
d = d/2;
|
||||
e = e/2;
|
||||
f = f/2;
|
||||
double g = 0.3333333333333333D;
|
||||
double h = (d + e + f) * 0.3333333333333333D;
|
||||
int i = floor(d + h);
|
||||
int j = floor(e + h);
|
||||
int k = floor(f + h);
|
||||
double l = 0.16666666666666666D;
|
||||
double m = (double)(i + j + k) * 0.16666666666666666D;
|
||||
double n = (double)i - m;
|
||||
double o = (double)j - m;
|
||||
double p = (double)k - m;
|
||||
double q = d - n;
|
||||
double r = e - o;
|
||||
double s = f - p;
|
||||
byte z;
|
||||
byte aa;
|
||||
byte ab;
|
||||
byte ac;
|
||||
byte ad;
|
||||
byte bc;
|
||||
if (q >= r) {
|
||||
if (r >= s) {
|
||||
public double sample3d(double d, double e, double f)
|
||||
{
|
||||
d = d / 2;
|
||||
e = e / 2;
|
||||
f = f / 2;
|
||||
//final double g = 0.3333333333333333D;
|
||||
final double h = (d + e + f) * 0.3333333333333333D;
|
||||
final int i = floor(d + h);
|
||||
final int j = floor(e + h);
|
||||
final int k = floor(f + h);
|
||||
//final double l = 0.16666666666666666D;
|
||||
final double m = (i + j + k) * 0.16666666666666666D;
|
||||
final double n = i - m;
|
||||
final double o = j - m;
|
||||
final double p = k - m;
|
||||
final double q = d - n;
|
||||
final double r = e - o;
|
||||
final double s = f - p;
|
||||
final byte z;
|
||||
final byte aa;
|
||||
final byte ab;
|
||||
final byte ac;
|
||||
final byte ad;
|
||||
final byte bc;
|
||||
if (q >= r)
|
||||
{
|
||||
if (r >= s)
|
||||
{
|
||||
z = 1;
|
||||
aa = 0;
|
||||
ab = 0;
|
||||
ac = 1;
|
||||
ad = 1;
|
||||
bc = 0;
|
||||
} else if (q >= s) {
|
||||
}
|
||||
else if (q >= s)
|
||||
{
|
||||
z = 1;
|
||||
aa = 0;
|
||||
ab = 0;
|
||||
ac = 1;
|
||||
ad = 0;
|
||||
bc = 1;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
z = 0;
|
||||
aa = 0;
|
||||
ab = 1;
|
||||
@@ -123,21 +142,27 @@ public class SimplexNoiseSampler extends PerlinNoiseSampler {
|
||||
ad = 0;
|
||||
bc = 1;
|
||||
}
|
||||
} else if (r < s) {
|
||||
}
|
||||
else if (r < s)
|
||||
{
|
||||
z = 0;
|
||||
aa = 0;
|
||||
ab = 1;
|
||||
ac = 0;
|
||||
ad = 1;
|
||||
bc = 1;
|
||||
} else if (q < s) {
|
||||
}
|
||||
else if (q < s)
|
||||
{
|
||||
z = 0;
|
||||
aa = 1;
|
||||
ab = 0;
|
||||
ac = 0;
|
||||
ad = 1;
|
||||
bc = 1;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
z = 0;
|
||||
aa = 1;
|
||||
ab = 0;
|
||||
@@ -146,30 +171,31 @@ public class SimplexNoiseSampler extends PerlinNoiseSampler {
|
||||
bc = 0;
|
||||
}
|
||||
|
||||
double bd = q - (double)z + 0.16666666666666666D;
|
||||
double be = r - (double)aa + 0.16666666666666666D;
|
||||
double bf = s - (double)ab + 0.16666666666666666D;
|
||||
double bg = q - (double)ac + 0.3333333333333333D;
|
||||
double bh = r - (double)ad + 0.3333333333333333D;
|
||||
double bi = s - (double)bc + 0.3333333333333333D;
|
||||
double bj = q - 1.0D + 0.5D;
|
||||
double bk = r - 1.0D + 0.5D;
|
||||
double bl = s - 1.0D + 0.5D;
|
||||
int bm = i & 255;
|
||||
int bn = j & 255;
|
||||
int bo = k & 255;
|
||||
int bp = this.getGradient(bm + this.getGradient(bn + this.getGradient(bo))) % 12;
|
||||
int bq = this.getGradient(bm + z + this.getGradient(bn + aa + this.getGradient(bo + ab))) % 12;
|
||||
int br = this.getGradient(bm + ac + this.getGradient(bn + ad + this.getGradient(bo + bc))) % 12;
|
||||
int bs = this.getGradient(bm + 1 + this.getGradient(bn + 1 + this.getGradient(bo + 1))) % 12;
|
||||
double bt = this.grad(bp, q, r, s, 0.6D);
|
||||
double bu = this.grad(bq, bd, be, bf, 0.6D);
|
||||
double bv = this.grad(br, bg, bh, bi, 0.6D);
|
||||
double bw = this.grad(bs, bj, bk, bl, 0.6D);
|
||||
final double bd = q - z + 0.16666666666666666D;
|
||||
final double be = r - aa + 0.16666666666666666D;
|
||||
final double bf = s - ab + 0.16666666666666666D;
|
||||
final double bg = q - ac + 0.3333333333333333D;
|
||||
final double bh = r - ad + 0.3333333333333333D;
|
||||
final double bi = s - bc + 0.3333333333333333D;
|
||||
final double bj = q - 1.0D + 0.5D;
|
||||
final double bk = r - 1.0D + 0.5D;
|
||||
final double bl = s - 1.0D + 0.5D;
|
||||
final int bm = i & 255;
|
||||
final int bn = j & 255;
|
||||
final int bo = k & 255;
|
||||
final int bp = this.getGradient(bm + this.getGradient(bn + this.getGradient(bo))) % 12;
|
||||
final int bq = this.getGradient(bm + z + this.getGradient(bn + aa + this.getGradient(bo + ab))) % 12;
|
||||
final int br = this.getGradient(bm + ac + this.getGradient(bn + ad + this.getGradient(bo + bc))) % 12;
|
||||
final int bs = this.getGradient(bm + 1 + this.getGradient(bn + 1 + this.getGradient(bo + 1))) % 12;
|
||||
final double bt = this.grad(bp, q, r, s, 0.6D);
|
||||
final double bu = this.grad(bq, bd, be, bf, 0.6D);
|
||||
final double bv = this.grad(br, bg, bh, bi, 0.6D);
|
||||
final double bw = this.grad(bs, bj, bk, bl, 0.6D);
|
||||
return 16.0D * (bt + bu + bv + bw) + 0.5;
|
||||
}
|
||||
|
||||
static {
|
||||
static
|
||||
{
|
||||
SKEW_FACTOR_2D = 0.5D * (sqrt3 - 1.0D);
|
||||
UNSKEW_FACTOR_2D = (3.0D - sqrt3) / 6.0D;
|
||||
}
|
||||
|
||||
@@ -40,11 +40,12 @@ public class SnoopyCommandSource extends CommandSourceStack
|
||||
private final List<Component> chatOutput;
|
||||
private final CommandSigningContext signingContext;
|
||||
|
||||
public SnoopyCommandSource(CommandSourceStack original, Component[] error, List<Component> chatOutput)
|
||||
public SnoopyCommandSource(final CommandSourceStack original, final Component[] error, final List<Component> chatOutput)
|
||||
{
|
||||
super(CommandSource.NULL, original.getPosition(), original.getRotation(), original.getLevel(), CarpetSettings.runPermissionLevel,
|
||||
original.getTextName(), original.getDisplayName(), original.getServer(), original.getEntity(), false,
|
||||
(ctx, succ, res) -> { }, EntityAnchorArgument.Anchor.FEET, CommandSigningContext.ANONYMOUS, TaskChainer.immediate(original.getServer()));
|
||||
(ctx, succ, res) -> {
|
||||
}, EntityAnchorArgument.Anchor.FEET, CommandSigningContext.ANONYMOUS, TaskChainer.immediate(original.getServer()));
|
||||
this.output = CommandSource.NULL;
|
||||
this.position = original.getPosition();
|
||||
this.world = original.getLevel();
|
||||
@@ -53,7 +54,8 @@ public class SnoopyCommandSource extends CommandSourceStack
|
||||
this.name = original.getDisplayName();
|
||||
this.server = original.getServer();
|
||||
this.entity = original.getEntity();
|
||||
this.resultConsumer = (ctx, succ, res) -> { };
|
||||
this.resultConsumer = (ctx, succ, res) -> {
|
||||
};
|
||||
this.entityAnchor = original.getAnchor();
|
||||
this.rotation = original.getRotation();
|
||||
this.error = error;
|
||||
@@ -61,20 +63,22 @@ public class SnoopyCommandSource extends CommandSourceStack
|
||||
this.signingContext = original.getSigningContext();
|
||||
}
|
||||
|
||||
public SnoopyCommandSource(ServerPlayer player, Component[] error, List<Component> output) {
|
||||
public SnoopyCommandSource(final ServerPlayer player, final Component[] error, final List<Component> output)
|
||||
{
|
||||
super(player, player.position(), player.getRotationVector(),
|
||||
player.level instanceof ServerLevel ? (ServerLevel) player.level : null,
|
||||
player.level instanceof final ServerLevel serverLevel ? serverLevel : null,
|
||||
player.server.getProfilePermissions(player.getGameProfile()), player.getName().getString(), player.getDisplayName(),
|
||||
player.level.getServer(), player);
|
||||
this.output = player;
|
||||
this.position = player.position();
|
||||
this.world = player.level instanceof ServerLevel ? (ServerLevel) player.level : null;
|
||||
this.world = player.level instanceof final ServerLevel serverLevel ? serverLevel : null;
|
||||
this.level = player.server.getProfilePermissions(player.getGameProfile());
|
||||
this.simpleName = player.getName().getString();
|
||||
this.name = player.getDisplayName();
|
||||
this.server = player.level.getServer();
|
||||
this.entity = player;
|
||||
this.resultConsumer = (ctx, succ, res) -> { };
|
||||
this.resultConsumer = (ctx, succ, res) -> {
|
||||
};
|
||||
this.entityAnchor = EntityAnchorArgument.Anchor.FEET;
|
||||
this.rotation = player.getRotationVector(); // not a client call really
|
||||
this.error = error;
|
||||
@@ -82,9 +86,10 @@ public class SnoopyCommandSource extends CommandSourceStack
|
||||
this.signingContext = CommandSigningContext.ANONYMOUS;
|
||||
}
|
||||
|
||||
private SnoopyCommandSource(CommandSource output, Vec3 pos, Vec2 rot, ServerLevel world, int level, String simpleName, Component name, MinecraftServer server, @Nullable Entity entity, ResultConsumer<CommandSourceStack> consumer, EntityAnchorArgument.Anchor entityAnchor, CommandSigningContext context,
|
||||
Component[] error, List<Component> chatOutput
|
||||
) {
|
||||
private SnoopyCommandSource(final CommandSource output, final Vec3 pos, final Vec2 rot, final ServerLevel world, final int level, final String simpleName, final Component name, final MinecraftServer server, @Nullable final Entity entity, final ResultConsumer<CommandSourceStack> consumer, final EntityAnchorArgument.Anchor entityAnchor, final CommandSigningContext context,
|
||||
final Component[] error, final List<Component> chatOutput
|
||||
)
|
||||
{
|
||||
super(output, pos, rot, world, level,
|
||||
simpleName, name, server, entity, false,
|
||||
consumer, entityAnchor, context, TaskChainer.immediate(server));
|
||||
@@ -105,33 +110,33 @@ public class SnoopyCommandSource extends CommandSourceStack
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandSourceStack withEntity(Entity entity)
|
||||
public CommandSourceStack withEntity(final Entity entity)
|
||||
{
|
||||
return new SnoopyCommandSource(output, position, rotation, world, level, entity.getName().getString(), entity.getDisplayName(), server, entity, resultConsumer, entityAnchor, signingContext, error, chatOutput);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandSourceStack withPosition(Vec3 position)
|
||||
public CommandSourceStack withPosition(final Vec3 position)
|
||||
{
|
||||
return new SnoopyCommandSource(output, position, rotation, world, level, simpleName, name, server, entity, resultConsumer, entityAnchor, signingContext, error, chatOutput);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandSourceStack withRotation(Vec2 rotation)
|
||||
public CommandSourceStack withRotation(final Vec2 rotation)
|
||||
{
|
||||
return new SnoopyCommandSource(output, position, rotation, world, level, simpleName, name, server, entity, resultConsumer, entityAnchor, signingContext, error, chatOutput);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandSourceStack withCallback(ResultConsumer<CommandSourceStack> consumer)
|
||||
public CommandSourceStack withCallback(final ResultConsumer<CommandSourceStack> consumer)
|
||||
{
|
||||
return new SnoopyCommandSource(output, position, rotation, world, level, simpleName, name, server, entity, consumer, entityAnchor, signingContext, error, chatOutput);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandSourceStack withCallback(ResultConsumer<CommandSourceStack> consumer, BinaryOperator<ResultConsumer<CommandSourceStack>> binaryOperator)
|
||||
public CommandSourceStack withCallback(final ResultConsumer<CommandSourceStack> consumer, final BinaryOperator<ResultConsumer<CommandSourceStack>> binaryOperator)
|
||||
{
|
||||
ResultConsumer<CommandSourceStack> resultConsumer = binaryOperator.apply(this.resultConsumer, consumer);
|
||||
final ResultConsumer<CommandSourceStack> resultConsumer = binaryOperator.apply(this.resultConsumer, consumer);
|
||||
return this.withCallback(resultConsumer);
|
||||
}
|
||||
|
||||
@@ -140,56 +145,58 @@ public class SnoopyCommandSource extends CommandSourceStack
|
||||
//public ServerCommandSource withSilent() { return this; }
|
||||
|
||||
@Override
|
||||
public CommandSourceStack withPermission(int level)
|
||||
public CommandSourceStack withPermission(final int level)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandSourceStack withMaximumPermission(int level)
|
||||
public CommandSourceStack withMaximumPermission(final int level)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandSourceStack withAnchor(EntityAnchorArgument.Anchor anchor)
|
||||
public CommandSourceStack withAnchor(final EntityAnchorArgument.Anchor anchor)
|
||||
{
|
||||
return new SnoopyCommandSource(output, position, rotation, world, level, simpleName, name, server, entity, resultConsumer, anchor, signingContext, error, chatOutput);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandSourceStack withSigningContext(CommandSigningContext commandSigningContext) {
|
||||
public CommandSourceStack withSigningContext(final CommandSigningContext commandSigningContext)
|
||||
{
|
||||
return new SnoopyCommandSource(output, position, rotation, world, level, simpleName, name, server, entity, resultConsumer, entityAnchor, commandSigningContext, error, chatOutput);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandSourceStack withLevel(ServerLevel world)
|
||||
public CommandSourceStack withLevel(final ServerLevel world)
|
||||
{
|
||||
double d = DimensionType.getTeleportationScale(this.world.dimensionType(), world.dimensionType());
|
||||
Vec3 position = new Vec3(this.position.x * d, this.position.y, this.position.z * d);
|
||||
final double d = DimensionType.getTeleportationScale(this.world.dimensionType(), world.dimensionType());
|
||||
final Vec3 position = new Vec3(this.position.x * d, this.position.y, this.position.z * d);
|
||||
return new SnoopyCommandSource(output, position, rotation, world, level, simpleName, name, server, entity, resultConsumer, entityAnchor, signingContext, error, chatOutput);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CommandSourceStack facing(Vec3 position)
|
||||
public CommandSourceStack facing(final Vec3 position)
|
||||
{
|
||||
Vec3 vec3d = this.entityAnchor.apply(this);
|
||||
double d = position.x - vec3d.x;
|
||||
double e = position.y - vec3d.y;
|
||||
double f = position.z - vec3d.z;
|
||||
double g = (double) Math.sqrt(d * d + f * f);
|
||||
float h = Mth.wrapDegrees((float)(-(Mth.atan2(e, g) * 57.2957763671875D)));
|
||||
float i = Mth.wrapDegrees((float)(Mth.atan2(f, d) * 57.2957763671875D) - 90.0F);
|
||||
final Vec3 vec3d = this.entityAnchor.apply(this);
|
||||
final double d = position.x - vec3d.x;
|
||||
final double e = position.y - vec3d.y;
|
||||
final double f = position.z - vec3d.z;
|
||||
final double g = Math.sqrt(d * d + f * f);
|
||||
final float h = Mth.wrapDegrees((float) (-(Mth.atan2(e, g) * 57.2957763671875D)));
|
||||
final float i = Mth.wrapDegrees((float) (Mth.atan2(f, d) * 57.2957763671875D) - 90.0F);
|
||||
return this.withRotation(new Vec2(h, i));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendFailure(Component message)
|
||||
public void sendFailure(final Component message)
|
||||
{
|
||||
error[0] = message;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendSuccess(Component message, boolean broadcastToOps)
|
||||
public void sendSuccess(final Component message, final boolean broadcastToOps)
|
||||
{
|
||||
chatOutput.add(message);
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ import net.minecraft.world.level.border.WorldBorder;
|
||||
import net.minecraft.world.level.storage.LevelData;
|
||||
import net.minecraft.world.level.storage.LevelResource;
|
||||
import net.minecraft.world.phys.Vec2;
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
@@ -36,15 +37,17 @@ import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class SystemInfo {
|
||||
private static final Map<String, Function<CarpetContext,Value>> options = new HashMap<>(){{
|
||||
public class SystemInfo
|
||||
{
|
||||
private static final Map<String, Function<CarpetContext, Value>> options = new HashMap<>()
|
||||
{{
|
||||
put("app_name", c ->
|
||||
{
|
||||
String name = c.host.getName();
|
||||
return name == null?Value.NULL:new StringValue(name);
|
||||
final String name = c.host.getName();
|
||||
return name == null ? Value.NULL : new StringValue(name);
|
||||
});
|
||||
put("app_list", c -> ListValue.wrap(((CarpetScriptHost)c.host).scriptServer().modules.keySet().stream().filter(Objects::nonNull).map(StringValue::new)));
|
||||
put("app_scope", c -> StringValue.of((c.host).isPerUser()?"player":"global"));
|
||||
put("app_list", c -> ListValue.wrap(((CarpetScriptHost) c.host).scriptServer().modules.keySet().stream().filter(Objects::nonNull).map(StringValue::new)));
|
||||
put("app_scope", c -> StringValue.of((c.host).isPerUser() ? "player" : "global"));
|
||||
put("app_players", c -> ListValue.wrap(c.host.getUserList().stream().map(StringValue::new)));
|
||||
|
||||
put("world_name", c -> new StringValue(c.server().getWorldData().getLevelName()));
|
||||
@@ -52,30 +55,33 @@ public class SystemInfo {
|
||||
put("server_motd", c -> StringValue.of(c.server().getMotd()));
|
||||
put("world_path", c -> StringValue.of(c.server().getWorldPath(LevelResource.ROOT).toString()));
|
||||
put("world_folder", c -> {
|
||||
Path serverPath = c.server().getWorldPath(LevelResource.ROOT);
|
||||
int nodeCount = serverPath.getNameCount();
|
||||
if (nodeCount < 2) return Value.NULL;
|
||||
String tlf = serverPath.getName(nodeCount-2).toString();
|
||||
final Path serverPath = c.server().getWorldPath(LevelResource.ROOT);
|
||||
final int nodeCount = serverPath.getNameCount();
|
||||
if (nodeCount < 2)
|
||||
{
|
||||
return Value.NULL;
|
||||
}
|
||||
final String tlf = serverPath.getName(nodeCount - 2).toString();
|
||||
return StringValue.of(tlf);
|
||||
});
|
||||
put("world_dimensions", c -> ListValue.wrap(c.server().levelKeys().stream().map(k -> ValueConversions.of(k.location()))));
|
||||
put("world_spawn_point", c -> {
|
||||
LevelData prop = c.server().overworld().getLevelData();
|
||||
final LevelData prop = c.server().overworld().getLevelData();
|
||||
return ListValue.of(NumericValue.of(prop.getXSpawn()), NumericValue.of(prop.getYSpawn()), NumericValue.of(prop.getZSpawn()));
|
||||
});
|
||||
|
||||
put("world_bottom", c-> new NumericValue(c.level().getMinBuildHeight()));
|
||||
put("world_bottom", c -> new NumericValue(c.level().getMinBuildHeight()));
|
||||
|
||||
put("world_top", c-> new NumericValue(c.level().getMaxBuildHeight()));
|
||||
put("world_top", c -> new NumericValue(c.level().getMaxBuildHeight()));
|
||||
|
||||
put("world_center", c-> {
|
||||
WorldBorder worldBorder = c.level().getWorldBorder();
|
||||
put("world_center", c -> {
|
||||
final WorldBorder worldBorder = c.level().getWorldBorder();
|
||||
return ListValue.fromTriple(worldBorder.getCenterX(), 0, worldBorder.getCenterZ());
|
||||
});
|
||||
|
||||
put("world_size", c -> new NumericValue(c.level().getWorldBorder().getSize() / 2));
|
||||
|
||||
put("world_max_size", c-> new NumericValue( c.level().getWorldBorder().getAbsoluteMaxSize()));
|
||||
put("world_max_size", c -> new NumericValue(c.level().getWorldBorder().getAbsoluteMaxSize()));
|
||||
|
||||
put("world_time", c -> new NumericValue(c.level().getGameTime()));
|
||||
|
||||
@@ -90,133 +96,137 @@ public class SystemInfo {
|
||||
put("game_target", c -> StringValue.of(CarpetSettings.releaseTarget));
|
||||
put("game_protocol", c -> NumericValue.of(SharedConstants.getProtocolVersion()));
|
||||
put("game_major_target", c -> {
|
||||
String [] vers = CarpetSettings.releaseTarget.split("\\.");
|
||||
return NumericValue.of((vers.length > 1)?Integer.parseInt(vers[1]):0);
|
||||
final String[] vers = CarpetSettings.releaseTarget.split("\\.");
|
||||
return NumericValue.of((vers.length > 1) ? Integer.parseInt(vers[1]) : 0);
|
||||
});
|
||||
put("game_minor_target", c -> {
|
||||
String [] vers = CarpetSettings.releaseTarget.split("\\.");
|
||||
return NumericValue.of((vers.length > 2)?Integer.parseInt(vers[2]):0);
|
||||
final String[] vers = CarpetSettings.releaseTarget.split("\\.");
|
||||
return NumericValue.of((vers.length > 2) ? Integer.parseInt(vers[2]) : 0);
|
||||
});
|
||||
put("game_stable", c -> BooleanValue.of(SharedConstants.getCurrentVersion().isStable()));
|
||||
put("game_data_version", c->NumericValue.of(SharedConstants.getCurrentVersion().getDataVersion().getVersion()));
|
||||
put("game_pack_version", c->NumericValue.of(SharedConstants.getCurrentVersion().getPackVersion(PackType.SERVER_DATA)));
|
||||
put("game_data_version", c -> NumericValue.of(SharedConstants.getCurrentVersion().getDataVersion().getVersion()));
|
||||
put("game_pack_version", c -> NumericValue.of(SharedConstants.getCurrentVersion().getPackVersion(PackType.SERVER_DATA)));
|
||||
|
||||
put("server_ip", c -> StringValue.of(c.server().getLocalIp()));
|
||||
put("server_whitelisted", c -> BooleanValue.of(c.server().isEnforceWhitelist()));
|
||||
put("server_whitelist", c -> {
|
||||
MapValue whitelist = new MapValue(Collections.emptyList());
|
||||
for (String s: c.server().getPlayerList().getWhiteListNames())
|
||||
final MapValue whitelist = new MapValue(Collections.emptyList());
|
||||
for (final String s : c.server().getPlayerList().getWhiteListNames())
|
||||
{
|
||||
whitelist.append(StringValue.of(s));
|
||||
}
|
||||
return whitelist;
|
||||
});
|
||||
put("server_banned_players", c -> {
|
||||
MapValue whitelist = new MapValue(Collections.emptyList());
|
||||
for (String s: c.server().getPlayerList().getBans().getUserList())
|
||||
final MapValue whitelist = new MapValue(Collections.emptyList());
|
||||
for (final String s : c.server().getPlayerList().getBans().getUserList())
|
||||
{
|
||||
whitelist.append(StringValue.of(s));
|
||||
}
|
||||
return whitelist;
|
||||
});
|
||||
put("server_banned_ips", c -> {
|
||||
MapValue whitelist = new MapValue(Collections.emptyList());
|
||||
for (String s: c.server().getPlayerList().getIpBans().getUserList())
|
||||
final MapValue whitelist = new MapValue(Collections.emptyList());
|
||||
for (final String s : c.server().getPlayerList().getIpBans().getUserList())
|
||||
{
|
||||
whitelist.append(StringValue.of(s));
|
||||
}
|
||||
return whitelist;
|
||||
});
|
||||
put("server_dev_environment", c-> BooleanValue.of(FabricLoader.getInstance().isDevelopmentEnvironment()));
|
||||
put("server_dev_environment", c -> BooleanValue.of(FabricLoader.getInstance().isDevelopmentEnvironment()));
|
||||
put("server_mods", c -> {
|
||||
Map<Value, Value> ret = new HashMap<>();
|
||||
for (ModContainer mod : FabricLoader.getInstance().getAllMods())
|
||||
final Map<Value, Value> ret = new HashMap<>();
|
||||
for (final ModContainer mod : FabricLoader.getInstance().getAllMods())
|
||||
{
|
||||
ret.put(new StringValue(mod.getMetadata().getId()), new StringValue(mod.getMetadata().getVersion().getFriendlyString()));
|
||||
}
|
||||
return MapValue.wrap(ret);
|
||||
});
|
||||
put("server_last_tick_times", c -> {
|
||||
//assuming we are in the tick world section
|
||||
//assuming we are in the tick world section
|
||||
// might be off one tick when run in the off tasks or asynchronously.
|
||||
int currentReportedTick = c.server().getTickCount()-1;
|
||||
List<Value> ticks = new ArrayList<>(100);
|
||||
final int currentReportedTick = c.server().getTickCount() - 1;
|
||||
final List<Value> ticks = new ArrayList<>(100);
|
||||
final long[] tickArray = c.server().tickTimes;
|
||||
for (int i=currentReportedTick+100; i > currentReportedTick; i--)
|
||||
for (int i = currentReportedTick + 100; i > currentReportedTick; i--)
|
||||
{
|
||||
ticks.add(new NumericValue((tickArray[i % 100])/1000000.0));
|
||||
ticks.add(new NumericValue((tickArray[i % 100]) / 1000000.0));
|
||||
}
|
||||
return ListValue.wrap(ticks);
|
||||
});
|
||||
|
||||
put("java_max_memory", c -> new NumericValue(Runtime.getRuntime().maxMemory()));
|
||||
put("java_allocated_memory", c -> new NumericValue(Runtime.getRuntime().totalMemory()));
|
||||
put("java_used_memory", c -> new NumericValue(Runtime.getRuntime().totalMemory()-Runtime.getRuntime().freeMemory()));
|
||||
put("java_used_memory", c -> new NumericValue(Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()));
|
||||
put("java_cpu_count", c -> new NumericValue(Runtime.getRuntime().availableProcessors()));
|
||||
put("java_version", c -> StringValue.of(System.getProperty("java.version")));
|
||||
put("java_bits", c -> {
|
||||
for (String property : new String[]{"sun.arch.data.model", "com.ibm.vm.bitmode", "os.arch"})
|
||||
for (final String property : new String[]{"sun.arch.data.model", "com.ibm.vm.bitmode", "os.arch"})
|
||||
{
|
||||
String value = System.getProperty(property);
|
||||
final String value = System.getProperty(property);
|
||||
if (value != null && value.contains("64"))
|
||||
{
|
||||
return new NumericValue(64);
|
||||
|
||||
}
|
||||
}
|
||||
return new NumericValue(32);
|
||||
});
|
||||
put("java_system_cpu_load", c -> {
|
||||
OperatingSystemMXBean osBean = ManagementFactory.getPlatformMXBean(
|
||||
final OperatingSystemMXBean osBean = ManagementFactory.getPlatformMXBean(
|
||||
OperatingSystemMXBean.class);
|
||||
return new NumericValue(osBean.getCpuLoad());
|
||||
});
|
||||
put("java_process_cpu_load", c -> {
|
||||
OperatingSystemMXBean osBean = ManagementFactory.getPlatformMXBean(
|
||||
final OperatingSystemMXBean osBean = ManagementFactory.getPlatformMXBean(
|
||||
OperatingSystemMXBean.class);
|
||||
return new NumericValue(osBean.getProcessCpuLoad());
|
||||
});
|
||||
put("world_carpet_rules", c -> {
|
||||
Collection<CarpetRule<?>> rules = CarpetServer.settingsManager.getCarpetRules();
|
||||
MapValue carpetRules = new MapValue(Collections.emptyList());
|
||||
rules.forEach(rule -> {
|
||||
carpetRules.put(new StringValue(rule.name()), new StringValue(RuleHelper.toRuleString(rule.value())));
|
||||
});
|
||||
final Collection<CarpetRule<?>> rules = CarpetServer.settingsManager.getCarpetRules();
|
||||
final MapValue carpetRules = new MapValue(Collections.emptyList());
|
||||
rules.forEach(rule -> carpetRules.put(new StringValue(rule.name()), new StringValue(RuleHelper.toRuleString(rule.value()))));
|
||||
CarpetServer.extensions.forEach(e -> {
|
||||
SettingsManager manager = e.extensionSettingsManager();
|
||||
if (manager == null) return;
|
||||
|
||||
Collection<CarpetRule<?>> extensionRules = manager.getCarpetRules();
|
||||
extensionRules.forEach(rule -> {
|
||||
carpetRules.put(new StringValue(manager.identifier()+":"+rule.name()), new StringValue(RuleHelper.toRuleString(rule.value())));
|
||||
});
|
||||
final SettingsManager manager = e.extensionSettingsManager();
|
||||
if (manager == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
manager.getCarpetRules().forEach(rule -> carpetRules.put(new StringValue(manager.identifier() + ":" + rule.name()), new StringValue(RuleHelper.toRuleString(rule.value()))));
|
||||
});
|
||||
return carpetRules;
|
||||
});
|
||||
put("world_gamerules", c->{
|
||||
Map<Value, Value> rules = new HashMap<>();
|
||||
put("world_gamerules", c -> {
|
||||
final Map<Value, Value> rules = new HashMap<>();
|
||||
final GameRules gameRules = c.level().getGameRules();
|
||||
GameRules.visitGameRuleTypes(new GameRules.GameRuleTypeVisitor() {
|
||||
GameRules.visitGameRuleTypes(new GameRules.GameRuleTypeVisitor()
|
||||
{
|
||||
@Override
|
||||
public <T extends GameRules.Value<T>> void visit(GameRules.Key<T> key, GameRules.Type<T> type) {
|
||||
public <T extends GameRules.Value<T>> void visit(final GameRules.Key<T> key, final GameRules.Type<T> type)
|
||||
{
|
||||
rules.put(StringValue.of(key.getId()), StringValue.of(gameRules.getRule(key).toString()));
|
||||
}
|
||||
});
|
||||
return MapValue.wrap(rules);
|
||||
});
|
||||
put("world_min_spawning_light", c-> NumericValue.of(c.level().dimensionType().monsterSpawnBlockLightLimit()));
|
||||
put("world_min_spawning_light", c -> NumericValue.of(c.level().dimensionType().monsterSpawnBlockLightLimit()));
|
||||
|
||||
put("source_entity", c -> EntityValue.of(c.source().getEntity()));
|
||||
put("source_position", c -> ValueConversions.of(c.source().getPosition()));
|
||||
put("source_dimension", c -> ValueConversions.of(c.level()));
|
||||
put("source_rotation", c -> {
|
||||
Vec2 rotation = c.source().getRotation();
|
||||
final Vec2 rotation = c.source().getRotation();
|
||||
return ListValue.of(new NumericValue(rotation.x), new NumericValue(rotation.y));
|
||||
});
|
||||
|
||||
|
||||
put("scarpet_version", c -> StringValue.of(CarpetSettings.carpetVersion));
|
||||
|
||||
}};
|
||||
public static Value get(String what, CarpetContext cc)
|
||||
|
||||
public static Value get(final String what, final CarpetContext cc)
|
||||
{
|
||||
return options.getOrDefault(what, c -> null).apply(cc);
|
||||
}
|
||||
|
||||
public static Value getAll()
|
||||
{
|
||||
return ListValue.wrap(options.keySet().stream().map(StringValue::of));
|
||||
|
||||
@@ -32,6 +32,7 @@ import net.minecraft.world.level.levelgen.NoiseGeneratorSettings;
|
||||
import net.minecraft.world.level.levelgen.WorldGenSettings;
|
||||
import net.minecraft.world.level.storage.DerivedLevelData;
|
||||
import net.minecraft.world.level.storage.ServerLevelData;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
@@ -42,32 +43,48 @@ import java.util.Set;
|
||||
public class WorldTools
|
||||
{
|
||||
|
||||
public static boolean canHasChunk(ServerLevel world, ChunkPos chpos, Map<String, RegionFile> regionCache, boolean deepcheck)
|
||||
public static boolean canHasChunk(final ServerLevel world, final ChunkPos chpos, final Map<String, RegionFile> regionCache, final boolean deepcheck)
|
||||
{
|
||||
if (world.getChunk(chpos.x, chpos.z, ChunkStatus.STRUCTURE_STARTS, false) != null)
|
||||
{
|
||||
return true;
|
||||
String currentRegionName = "r." + chpos.getRegionX() + "." + chpos.getRegionZ() + ".mca";
|
||||
}
|
||||
final String currentRegionName = "r." + chpos.getRegionX() + "." + chpos.getRegionZ() + ".mca";
|
||||
if (regionCache != null && regionCache.containsKey(currentRegionName))
|
||||
{
|
||||
RegionFile region = regionCache.get(currentRegionName);
|
||||
if (region == null) return false;
|
||||
final RegionFile region = regionCache.get(currentRegionName);
|
||||
if (region == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return region.hasChunk(chpos);
|
||||
}
|
||||
Path regionsFolder = ((MinecraftServerInterface)world.getServer()).getCMSession().getDimensionPath(world.dimension()).resolve("region");
|
||||
Path regionPath = regionsFolder.resolve(currentRegionName);
|
||||
final Path regionsFolder = ((MinecraftServerInterface) world.getServer()).getCMSession().getDimensionPath(world.dimension()).resolve("region");
|
||||
final Path regionPath = regionsFolder.resolve(currentRegionName);
|
||||
if (!regionPath.toFile().exists())
|
||||
{
|
||||
if (regionCache != null) regionCache.put(currentRegionName, null);
|
||||
if (regionCache != null)
|
||||
{
|
||||
regionCache.put(currentRegionName, null);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (!deepcheck) return true; // not using cache in this case.
|
||||
if (!deepcheck)
|
||||
{
|
||||
return true; // not using cache in this case.
|
||||
}
|
||||
try
|
||||
{
|
||||
RegionFile region = new RegionFile(regionPath, regionsFolder, true);
|
||||
if (regionCache != null) regionCache.put(currentRegionName, region);
|
||||
final RegionFile region = new RegionFile(regionPath, regionsFolder, true);
|
||||
if (regionCache != null)
|
||||
{
|
||||
regionCache.put(currentRegionName, region);
|
||||
}
|
||||
return region.hasChunk(chpos);
|
||||
}
|
||||
catch (IOException ignored) { }
|
||||
catch (final IOException ignored)
|
||||
{
|
||||
}
|
||||
return true;
|
||||
}
|
||||
/*
|
||||
@@ -137,16 +154,16 @@ public class WorldTools
|
||||
return true;
|
||||
}*/
|
||||
|
||||
public static void forceChunkUpdate(BlockPos pos, ServerLevel world)
|
||||
public static void forceChunkUpdate(final BlockPos pos, final ServerLevel world)
|
||||
{
|
||||
ChunkPos chunkPos = new ChunkPos(pos);
|
||||
LevelChunk worldChunk = world.getChunkSource().getChunk(chunkPos.x, chunkPos.z, false);
|
||||
final ChunkPos chunkPos = new ChunkPos(pos);
|
||||
final LevelChunk worldChunk = world.getChunkSource().getChunk(chunkPos.x, chunkPos.z, false);
|
||||
if (worldChunk != null)
|
||||
{
|
||||
List<ServerPlayer> players = world.getChunkSource().chunkMap.getPlayers(chunkPos, false);
|
||||
final List<ServerPlayer> players = world.getChunkSource().chunkMap.getPlayers(chunkPos, false);
|
||||
if (!players.isEmpty())
|
||||
{
|
||||
ClientboundLevelChunkWithLightPacket packet = new ClientboundLevelChunkWithLightPacket(worldChunk, world.getLightEngine(), null, null, false); // false seems to update neighbours as well.
|
||||
final ClientboundLevelChunkWithLightPacket packet = new ClientboundLevelChunkWithLightPacket(worldChunk, world.getLightEngine(), null, null, false); // false seems to update neighbours as well.
|
||||
players.forEach(p -> p.connection.send(packet));
|
||||
}
|
||||
}
|
||||
@@ -155,11 +172,26 @@ public class WorldTools
|
||||
|
||||
private static class NoopWorldGenerationProgressListener implements ChunkProgressListener
|
||||
{
|
||||
@Override public void updateSpawnPos(ChunkPos spawnPos) { }
|
||||
@Override public void onStatusChange(ChunkPos pos, ChunkStatus status) { }
|
||||
@Override
|
||||
public void updateSpawnPos(final ChunkPos spawnPos)
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStatusChange(final ChunkPos pos, final ChunkStatus status)
|
||||
{
|
||||
}
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
@Override public void start() { }
|
||||
@Override public void stop() { }
|
||||
@Override
|
||||
public void start()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public static final ChunkProgressListener NOOP_LISTENER = new NoopWorldGenerationProgressListener();
|
||||
|
||||
@@ -7,7 +7,8 @@ import net.minecraft.world.phys.Vec3;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
public enum ShapeDirection {
|
||||
public enum ShapeDirection
|
||||
{
|
||||
NORTH,
|
||||
SOUTH,
|
||||
EAST,
|
||||
@@ -17,8 +18,10 @@ public enum ShapeDirection {
|
||||
CAMERA,
|
||||
PLAYER;
|
||||
|
||||
public static ShapeDirection fromString(String direction) {
|
||||
return switch (direction.toLowerCase(Locale.ROOT)) {
|
||||
public static ShapeDirection fromString(final String direction)
|
||||
{
|
||||
return switch (direction.toLowerCase(Locale.ROOT))
|
||||
{
|
||||
case "north" -> NORTH;
|
||||
case "south" -> SOUTH;
|
||||
case "east" -> EAST;
|
||||
@@ -31,8 +34,10 @@ public enum ShapeDirection {
|
||||
};
|
||||
}
|
||||
|
||||
public static void rotatePoseStackByShapeDirection(PoseStack poseStack, ShapeDirection shapeDirection, Camera camera, Vec3 objectPos) {
|
||||
switch (shapeDirection) {
|
||||
public static void rotatePoseStackByShapeDirection(final PoseStack poseStack, final ShapeDirection shapeDirection, final Camera camera, final Vec3 objectPos)
|
||||
{
|
||||
switch (shapeDirection)
|
||||
{
|
||||
case NORTH -> {}
|
||||
case SOUTH -> poseStack.mulPose(Axis.YP.rotationDegrees(180));
|
||||
case EAST -> poseStack.mulPose(Axis.YP.rotationDegrees(270));
|
||||
|
||||
@@ -8,21 +8,25 @@ import java.util.List;
|
||||
|
||||
public abstract class AbstractListValue extends Value implements Iterable<Value>
|
||||
{
|
||||
@Override public abstract Iterator<Value> iterator();
|
||||
public List<Value> unpack()
|
||||
{
|
||||
List<Value> retVal = Lists.newArrayList(iterator());
|
||||
final List<Value> retVal = Lists.newArrayList(iterator());
|
||||
fatality();
|
||||
return retVal;
|
||||
}
|
||||
public void fatality() { }
|
||||
public void append(Value v)
|
||||
|
||||
public void fatality()
|
||||
{
|
||||
}
|
||||
|
||||
public void append(final Value v)
|
||||
{
|
||||
throw new InternalExpressionException("Cannot append a value to an abstract list");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value fromConstant() {
|
||||
public Value fromConstant()
|
||||
{
|
||||
return this.deepcopy();
|
||||
}
|
||||
}
|
||||
@@ -7,12 +7,14 @@ import carpet.script.exception.Throwables;
|
||||
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import net.minecraft.commands.arguments.blocks.BlockStateParser;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
@@ -44,42 +46,51 @@ public class BlockValue extends Value
|
||||
{
|
||||
private BlockState blockState;
|
||||
private final BlockPos pos;
|
||||
@NotNull private final Level world;
|
||||
@NotNull
|
||||
private final Level world;
|
||||
private CompoundTag data;
|
||||
|
||||
public static BlockValue fromCoords(CarpetContext c, int x, int y, int z)
|
||||
public static BlockValue fromCoords(final CarpetContext c, final int x, final int y, final int z)
|
||||
{
|
||||
BlockPos pos = locateBlockPos(c, x,y,z);
|
||||
final BlockPos pos = locateBlockPos(c, x, y, z);
|
||||
return new BlockValue(null, c.level(), pos);
|
||||
}
|
||||
|
||||
private static final Map<String, BlockValue> bvCache= new HashMap<>();
|
||||
public static BlockValue fromString(String str, Level level)
|
||||
private static final Map<String, BlockValue> bvCache = new HashMap<>();
|
||||
|
||||
public static BlockValue fromString(final String str, final Level level)
|
||||
{
|
||||
try
|
||||
{
|
||||
BlockValue bv = bvCache.get(str); // [SCARY SHIT] persistent caches over server reloads
|
||||
if (bv != null) return bv;
|
||||
BlockStateParser.BlockResult foo = BlockStateParser.parseForBlock(level.registryAccess().lookupOrThrow(Registries.BLOCK), new StringReader(str), true );
|
||||
if (bv != null)
|
||||
{
|
||||
return bv;
|
||||
}
|
||||
final BlockStateParser.BlockResult foo = BlockStateParser.parseForBlock(level.registryAccess().lookupOrThrow(Registries.BLOCK), new StringReader(str), true);
|
||||
if (foo.blockState() != null)
|
||||
{
|
||||
CompoundTag bd = foo.nbt();
|
||||
if (bd == null)
|
||||
{
|
||||
bd = new CompoundTag();
|
||||
bv = new BlockValue(foo.blockState(), level, null, bd );
|
||||
if (bvCache.size()>10000)
|
||||
}
|
||||
bv = new BlockValue(foo.blockState(), level, null, bd);
|
||||
if (bvCache.size() > 10000)
|
||||
{
|
||||
bvCache.clear();
|
||||
}
|
||||
bvCache.put(str, bv);
|
||||
return bv;
|
||||
}
|
||||
}
|
||||
catch (CommandSyntaxException ignored)
|
||||
catch (final CommandSyntaxException ignored)
|
||||
{
|
||||
}
|
||||
throw new ThrowStatement(str, Throwables.UNKNOWN_BLOCK);
|
||||
}
|
||||
|
||||
public static BlockPos locateBlockPos(CarpetContext c, int xpos, int ypos, int zpos)
|
||||
public static BlockPos locateBlockPos(final CarpetContext c, final int xpos, final int ypos, final int zpos)
|
||||
{
|
||||
final BlockPos pos = c.origin();
|
||||
return new BlockPos(pos.getX() + xpos, pos.getY() + ypos, pos.getZ() + zpos);
|
||||
@@ -99,13 +110,13 @@ public class BlockValue extends Value
|
||||
throw new InternalExpressionException("Attempted to fetch block state without world or stored block state");
|
||||
}
|
||||
|
||||
public static BlockEntity getBlockEntity(Level level, BlockPos pos)
|
||||
public static BlockEntity getBlockEntity(final Level level, final BlockPos pos)
|
||||
{
|
||||
if (level instanceof ServerLevel serverLevel) {
|
||||
if (serverLevel.getServer().isSameThread())
|
||||
return serverLevel.getBlockEntity(pos);
|
||||
else
|
||||
return serverLevel.getChunkAt(pos).getBlockEntity(pos, LevelChunk.EntityCreationType.IMMEDIATE);
|
||||
if (level instanceof final ServerLevel serverLevel)
|
||||
{
|
||||
return serverLevel.getServer().isSameThread()
|
||||
? serverLevel.getBlockEntity(pos)
|
||||
: serverLevel.getChunkAt(pos).getBlockEntity(pos, LevelChunk.EntityCreationType.IMMEDIATE);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -115,13 +126,11 @@ public class BlockValue extends Value
|
||||
{
|
||||
if (data != null)
|
||||
{
|
||||
if (data.isEmpty())
|
||||
return null;
|
||||
return data;
|
||||
return data.isEmpty() ? null : data;
|
||||
}
|
||||
if (pos != null)
|
||||
{
|
||||
BlockEntity be = getBlockEntity(world, pos);
|
||||
final BlockEntity be = getBlockEntity(world, pos);
|
||||
if (be == null)
|
||||
{
|
||||
data = new CompoundTag();
|
||||
@@ -134,7 +143,7 @@ public class BlockValue extends Value
|
||||
}
|
||||
|
||||
|
||||
public BlockValue(BlockState state, @NotNull Level world, BlockPos position)
|
||||
public BlockValue(final BlockState state, @NotNull final Level world, final BlockPos position)
|
||||
{
|
||||
this.world = world;
|
||||
blockState = state;
|
||||
@@ -142,7 +151,7 @@ public class BlockValue extends Value
|
||||
data = null;
|
||||
}
|
||||
|
||||
public BlockValue(BlockState state, @NotNull Level world, BlockPos position, CompoundTag nbt)
|
||||
public BlockValue(final BlockState state, @NotNull final Level world, final BlockPos position, final CompoundTag nbt)
|
||||
{
|
||||
this.world = world;
|
||||
blockState = state;
|
||||
@@ -154,7 +163,7 @@ public class BlockValue extends Value
|
||||
@Override
|
||||
public String getString()
|
||||
{
|
||||
Registry<Block> blockRegistry = world.registryAccess().registryOrThrow(Registries.BLOCK);
|
||||
final Registry<Block> blockRegistry = world.registryAccess().registryOrThrow(Registries.BLOCK);
|
||||
return nameFromRegistryId(blockRegistry.getKey(getBlockState().getBlock()));
|
||||
}
|
||||
|
||||
@@ -179,9 +188,9 @@ public class BlockValue extends Value
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
if (pos != null )
|
||||
return GlobalPos.of(world.dimension() , pos).hashCode(); //getDimension().getType()
|
||||
return ("b"+getString()).hashCode();
|
||||
return pos != null
|
||||
? GlobalPos.of(world.dimension(), pos).hashCode()
|
||||
: ("b" + getString()).hashCode();
|
||||
}
|
||||
|
||||
public BlockPos getPos()
|
||||
@@ -189,29 +198,35 @@ public class BlockValue extends Value
|
||||
return pos;
|
||||
}
|
||||
|
||||
public Level getWorld() { return world;}
|
||||
public Level getWorld()
|
||||
{
|
||||
return world;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tag toTag(boolean force)
|
||||
public Tag toTag(final boolean force)
|
||||
{
|
||||
if (!force) throw new NBTSerializableValue.IncompatibleTypeException(this);
|
||||
if (!force)
|
||||
{
|
||||
throw new NBTSerializableValue.IncompatibleTypeException(this);
|
||||
}
|
||||
// follows falling block convertion
|
||||
CompoundTag tag = new CompoundTag();
|
||||
CompoundTag state = new CompoundTag();
|
||||
BlockState s = getBlockState();
|
||||
final CompoundTag tag = new CompoundTag();
|
||||
final CompoundTag state = new CompoundTag();
|
||||
final BlockState s = getBlockState();
|
||||
state.put("Name", StringTag.valueOf(world.registryAccess().registryOrThrow(Registries.BLOCK).getKey(s.getBlock()).toString()));
|
||||
Collection<Property<?>> properties = s.getProperties();
|
||||
final Collection<Property<?>> properties = s.getProperties();
|
||||
if (!properties.isEmpty())
|
||||
{
|
||||
CompoundTag props = new CompoundTag();
|
||||
for (Property<?> p: properties)
|
||||
final CompoundTag props = new CompoundTag();
|
||||
for (final Property<?> p : properties)
|
||||
{
|
||||
props.put(p.getName(), StringTag.valueOf(s.getValue(p).toString().toLowerCase(Locale.ROOT)));
|
||||
}
|
||||
state.put("Properties", props);
|
||||
}
|
||||
tag.put("BlockState", state);
|
||||
CompoundTag dataTag = getData();
|
||||
final CompoundTag dataTag = getData();
|
||||
if (dataTag != null)
|
||||
{
|
||||
tag.put("TileEntityData", dataTag);
|
||||
@@ -219,29 +234,30 @@ public class BlockValue extends Value
|
||||
return tag;
|
||||
}
|
||||
|
||||
public enum SpecificDirection {
|
||||
UP("up",0.5, 0.0, 0.5, Direction.UP),
|
||||
public enum SpecificDirection
|
||||
{
|
||||
UP("up", 0.5, 0.0, 0.5, Direction.UP),
|
||||
|
||||
UPNORTH ("up-north", 0.5, 0.0, 0.4, Direction.UP),
|
||||
UPSOUTH ("up-south", 0.5, 0.0, 0.6, Direction.UP),
|
||||
UPNORTH("up-north", 0.5, 0.0, 0.4, Direction.UP),
|
||||
UPSOUTH("up-south", 0.5, 0.0, 0.6, Direction.UP),
|
||||
UPEAST("up-east", 0.6, 0.0, 0.5, Direction.UP),
|
||||
UPWEST("up-west", 0.4, 0.0, 0.5, Direction.UP),
|
||||
|
||||
DOWN("down", 0.5, 1.0, 0.5, Direction.DOWN),
|
||||
|
||||
DOWNNORTH ("down-north", 0.5, 1.0, 0.4, Direction.DOWN),
|
||||
DOWNSOUTH ("down-south", 0.5, 1.0, 0.6, Direction.DOWN),
|
||||
DOWNNORTH("down-north", 0.5, 1.0, 0.4, Direction.DOWN),
|
||||
DOWNSOUTH("down-south", 0.5, 1.0, 0.6, Direction.DOWN),
|
||||
DOWNEAST("down-east", 0.6, 1.0, 0.5, Direction.DOWN),
|
||||
DOWNWEST("down-west", 0.4, 1.0, 0.5, Direction.DOWN),
|
||||
|
||||
|
||||
NORTH ("north", 0.5, 0.4, 1.0, Direction.NORTH),
|
||||
SOUTH ("south", 0.5, 0.4, 0.0, Direction.SOUTH),
|
||||
NORTH("north", 0.5, 0.4, 1.0, Direction.NORTH),
|
||||
SOUTH("south", 0.5, 0.4, 0.0, Direction.SOUTH),
|
||||
EAST("east", 0.0, 0.4, 0.5, Direction.EAST),
|
||||
WEST("west", 1.0, 0.4, 0.5, Direction.WEST),
|
||||
|
||||
NORTHUP ("north-up", 0.5, 0.6, 1.0, Direction.NORTH),
|
||||
SOUTHUP ("south-up", 0.5, 0.6, 0.0, Direction.SOUTH),
|
||||
NORTHUP("north-up", 0.5, 0.6, 1.0, Direction.NORTH),
|
||||
SOUTHUP("south-up", 0.5, 0.6, 0.0, Direction.SOUTH),
|
||||
EASTUP("east-up", 0.0, 0.6, 0.5, Direction.EAST),
|
||||
WESTUP("west-up", 1.0, 0.6, 0.5, Direction.WEST);
|
||||
|
||||
@@ -252,87 +268,94 @@ public class BlockValue extends Value
|
||||
private static final Map<String, SpecificDirection> DIRECTION_MAP = Arrays.stream(values()).collect(Collectors.toMap(SpecificDirection::getName, d -> d));
|
||||
|
||||
|
||||
SpecificDirection(String name, double hitx, double hity, double hitz, Direction blockFacing)
|
||||
SpecificDirection(final String name, final double hitx, final double hity, final double hitz, final Direction blockFacing)
|
||||
{
|
||||
this.name = name;
|
||||
this.hitOffset = new Vec3(hitx, hity, hitz);
|
||||
this.facing = blockFacing;
|
||||
}
|
||||
|
||||
private String getName()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
}
|
||||
|
||||
public static class PlacementContext extends BlockPlaceContext {
|
||||
public static class PlacementContext extends BlockPlaceContext
|
||||
{
|
||||
private final Direction facing;
|
||||
private final boolean sneakPlace;
|
||||
|
||||
public static PlacementContext from(Level world, BlockPos pos, String direction, boolean sneakPlace, ItemStack itemStack)
|
||||
public static PlacementContext from(final Level world, final BlockPos pos, final String direction, final boolean sneakPlace, final ItemStack itemStack)
|
||||
{
|
||||
SpecificDirection dir = SpecificDirection.DIRECTION_MAP.get(direction);
|
||||
final SpecificDirection dir = SpecificDirection.DIRECTION_MAP.get(direction);
|
||||
if (dir == null)
|
||||
throw new InternalExpressionException("unknown block placement direction: "+direction);
|
||||
BlockHitResult hitres = new BlockHitResult(Vec3.atLowerCornerOf(pos).add(dir.hitOffset), dir.facing, pos, false);
|
||||
{
|
||||
throw new InternalExpressionException("unknown block placement direction: " + direction);
|
||||
}
|
||||
final BlockHitResult hitres = new BlockHitResult(Vec3.atLowerCornerOf(pos).add(dir.hitOffset), dir.facing, pos, false);
|
||||
return new PlacementContext(world, dir.facing, sneakPlace, itemStack, hitres);
|
||||
}
|
||||
private PlacementContext(Level world_1, Direction direction_1, boolean sneakPlace, ItemStack itemStack_1, BlockHitResult hitres) {
|
||||
|
||||
private PlacementContext(final Level world_1, final Direction direction_1, final boolean sneakPlace, final ItemStack itemStack_1, final BlockHitResult hitres)
|
||||
{
|
||||
super(world_1, null, InteractionHand.MAIN_HAND, itemStack_1, hitres);
|
||||
this.facing = direction_1;
|
||||
this.sneakPlace = sneakPlace;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockPos getClickedPos() {
|
||||
boolean prevcanReplaceExisting = replaceClicked;
|
||||
public BlockPos getClickedPos()
|
||||
{
|
||||
final boolean prevcanReplaceExisting = replaceClicked;
|
||||
replaceClicked = true;
|
||||
BlockPos ret = super.getClickedPos();
|
||||
final BlockPos ret = super.getClickedPos();
|
||||
replaceClicked = prevcanReplaceExisting;
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Direction getNearestLookingDirection() {
|
||||
public Direction getNearestLookingDirection()
|
||||
{
|
||||
return facing.getOpposite();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Direction[] getNearestLookingDirections() {
|
||||
switch(this.facing) {
|
||||
case DOWN:
|
||||
default:
|
||||
return new Direction[]{Direction.DOWN, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP};
|
||||
case UP:
|
||||
return new Direction[]{Direction.DOWN, Direction.UP, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST};
|
||||
case NORTH:
|
||||
return new Direction[]{Direction.DOWN, Direction.NORTH, Direction.EAST, Direction.WEST, Direction.UP, Direction.SOUTH};
|
||||
case SOUTH:
|
||||
return new Direction[]{Direction.DOWN, Direction.SOUTH, Direction.EAST, Direction.WEST, Direction.UP, Direction.NORTH};
|
||||
case WEST:
|
||||
return new Direction[]{Direction.DOWN, Direction.WEST, Direction.SOUTH, Direction.UP, Direction.NORTH, Direction.EAST};
|
||||
case EAST:
|
||||
return new Direction[]{Direction.DOWN, Direction.EAST, Direction.SOUTH, Direction.UP, Direction.NORTH, Direction.WEST};
|
||||
}
|
||||
public Direction[] getNearestLookingDirections()
|
||||
{
|
||||
return switch (this.facing)
|
||||
{
|
||||
case DOWN -> new Direction[]{Direction.DOWN, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP};
|
||||
case UP -> new Direction[]{Direction.DOWN, Direction.UP, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST};
|
||||
case NORTH -> new Direction[]{Direction.DOWN, Direction.NORTH, Direction.EAST, Direction.WEST, Direction.UP, Direction.SOUTH};
|
||||
case SOUTH -> new Direction[]{Direction.DOWN, Direction.SOUTH, Direction.EAST, Direction.WEST, Direction.UP, Direction.NORTH};
|
||||
case WEST -> new Direction[]{Direction.DOWN, Direction.WEST, Direction.SOUTH, Direction.UP, Direction.NORTH, Direction.EAST};
|
||||
case EAST -> new Direction[]{Direction.DOWN, Direction.EAST, Direction.SOUTH, Direction.UP, Direction.NORTH, Direction.WEST};
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Direction getHorizontalDirection() {
|
||||
public Direction getHorizontalDirection()
|
||||
{
|
||||
return this.facing.getAxis() == Direction.Axis.Y ? Direction.NORTH : this.facing;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Direction getNearestLookingVerticalDirection() {
|
||||
public Direction getNearestLookingVerticalDirection()
|
||||
{
|
||||
return facing.getAxis() == Axis.Y ? facing : Direction.UP;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSecondaryUseActive() {
|
||||
public boolean isSecondaryUseActive()
|
||||
{
|
||||
return sneakPlace;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getRotation() {
|
||||
return (float)(this.facing.get2DDataValue() * 90);
|
||||
public float getRotation()
|
||||
{
|
||||
return (float) (this.facing.get2DDataValue() * 90);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,53 +11,63 @@ public class BooleanValue extends NumericValue
|
||||
public static final BooleanValue TRUE = new BooleanValue(true);
|
||||
|
||||
boolean boolValue;
|
||||
private BooleanValue(boolean boolval) {
|
||||
|
||||
private BooleanValue(final boolean boolval)
|
||||
{
|
||||
super(boolval ? 1L : 0L);
|
||||
boolValue = boolval;
|
||||
}
|
||||
|
||||
public static BooleanValue of(boolean value)
|
||||
public static BooleanValue of(final boolean value)
|
||||
{
|
||||
return value ? TRUE : FALSE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString() {
|
||||
return boolValue?"true":"false";
|
||||
public String getString()
|
||||
{
|
||||
return boolValue ? "true" : "false";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrettyString() {
|
||||
public String getPrettyString()
|
||||
{
|
||||
return getString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getTypeString() {
|
||||
public String getTypeString()
|
||||
{
|
||||
return "bool";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value clone() {
|
||||
public Value clone()
|
||||
{
|
||||
return new BooleanValue(boolValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
public int hashCode()
|
||||
{
|
||||
return Boolean.hashCode(boolValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tag toTag(boolean force) {
|
||||
public Tag toTag(final boolean force)
|
||||
{
|
||||
return ByteTag.valueOf(boolValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonElement toJson() {
|
||||
public JsonElement toJson()
|
||||
{
|
||||
return new JsonPrimitive(boolValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInteger() {
|
||||
public boolean isInteger()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -3,11 +3,15 @@ package carpet.script.value;
|
||||
public interface ContainerValueInterface
|
||||
{
|
||||
boolean put(Value where, Value value);
|
||||
default boolean put(Value where, Value value, Value conditions)
|
||||
|
||||
default boolean put(final Value where, final Value value, final Value conditions)
|
||||
{
|
||||
return put(where, value);
|
||||
}
|
||||
|
||||
Value get(Value where);
|
||||
|
||||
boolean has(Value where);
|
||||
|
||||
boolean delete(Value where);
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -9,51 +9,56 @@ import net.minecraft.network.chat.MutableComponent;
|
||||
public class FormattedTextValue extends StringValue
|
||||
{
|
||||
Component text;
|
||||
public FormattedTextValue(Component text)
|
||||
|
||||
public FormattedTextValue(final Component text)
|
||||
{
|
||||
super(null);
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
public static Value combine(Value left, Value right) {
|
||||
MutableComponent text;
|
||||
if (left instanceof FormattedTextValue)
|
||||
public static Value combine(final Value left, final Value right)
|
||||
{
|
||||
final MutableComponent text;
|
||||
if (left instanceof final FormattedTextValue ftv)
|
||||
{
|
||||
text = ((FormattedTextValue) left).getText().copy();
|
||||
text = ftv.getText().copy();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (left.isNull())
|
||||
{
|
||||
return right;
|
||||
}
|
||||
text = Component.literal(left.getString());
|
||||
}
|
||||
|
||||
if (right instanceof FormattedTextValue)
|
||||
{
|
||||
text.append(((FormattedTextValue) right).getText().copy());
|
||||
return new FormattedTextValue(text);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (right.isNull())
|
||||
return left;
|
||||
text.append(right.getString());
|
||||
return new FormattedTextValue(text);
|
||||
}
|
||||
}
|
||||
|
||||
public static Value of(Component text) {
|
||||
if (text == null) return Value.NULL;
|
||||
if (right instanceof final FormattedTextValue ftv)
|
||||
{
|
||||
text.append(ftv.getText().copy());
|
||||
return new FormattedTextValue(text);
|
||||
}
|
||||
if (right.isNull())
|
||||
{
|
||||
return left;
|
||||
}
|
||||
text.append(right.getString());
|
||||
return new FormattedTextValue(text);
|
||||
}
|
||||
|
||||
public static Value of(final Component text)
|
||||
{
|
||||
return text == null ? Value.NULL : new FormattedTextValue(text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString() {
|
||||
public String getString()
|
||||
{
|
||||
return text.getString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getBoolean() {
|
||||
public boolean getBoolean()
|
||||
{
|
||||
return !text.getString().isEmpty();
|
||||
}
|
||||
|
||||
@@ -75,19 +80,24 @@ public class FormattedTextValue extends StringValue
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tag toTag(boolean force)
|
||||
public Tag toTag(final boolean force)
|
||||
{
|
||||
if (!force) throw new NBTSerializableValue.IncompatibleTypeException(this);
|
||||
if (!force)
|
||||
{
|
||||
throw new NBTSerializableValue.IncompatibleTypeException(this);
|
||||
}
|
||||
return StringTag.valueOf(Component.Serializer.toJson(text));
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonElement toJson() {
|
||||
public JsonElement toJson()
|
||||
{
|
||||
return Component.Serializer.toJsonTree(text);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value add(Value o) {
|
||||
public Value add(final Value o)
|
||||
{
|
||||
return combine(this, o);
|
||||
}
|
||||
|
||||
@@ -96,13 +106,14 @@ public class FormattedTextValue extends StringValue
|
||||
return Component.Serializer.toJson(text);
|
||||
}
|
||||
|
||||
public static FormattedTextValue deserialize(String serialized)
|
||||
public static FormattedTextValue deserialize(final String serialized)
|
||||
{
|
||||
return new FormattedTextValue(Component.Serializer.fromJson(serialized));
|
||||
}
|
||||
|
||||
public static Component getTextByValue(Value value) {
|
||||
return (value instanceof FormattedTextValue) ? ((FormattedTextValue) value).getText() : Component.literal(value.getString());
|
||||
public static Component getTextByValue(final Value value)
|
||||
{
|
||||
return (value instanceof final FormattedTextValue ftv) ? ftv.getText() : Component.literal(value.getString());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -29,7 +29,7 @@ public abstract class FrameworkValue extends Value
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tag toTag(boolean force)
|
||||
public Tag toTag(final boolean force)
|
||||
{
|
||||
throw new UnsupportedOperationException("Scarpet language component cannot be serialized to the tag");
|
||||
}
|
||||
|
||||
@@ -9,11 +9,15 @@ public class FunctionAnnotationValue extends Value
|
||||
{
|
||||
GLOBAL, VARARG
|
||||
}
|
||||
|
||||
public Type type;
|
||||
public FunctionAnnotationValue(Value variable, Type type)
|
||||
|
||||
public FunctionAnnotationValue(final Value variable, final Type type)
|
||||
{
|
||||
if (variable.boundVariable == null)
|
||||
{
|
||||
throw new InternalExpressionException("You can only borrow variables from the outer scope");
|
||||
}
|
||||
this.boundVariable = variable.boundVariable;
|
||||
this.type = type;
|
||||
}
|
||||
@@ -37,7 +41,7 @@ public class FunctionAnnotationValue extends Value
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tag toTag(boolean force)
|
||||
public Tag toTag(final boolean force)
|
||||
{
|
||||
throw new UnsupportedOperationException("Global value cannot be serialized to the tag");
|
||||
}
|
||||
|
||||
@@ -9,23 +9,21 @@ public class FunctionSignatureValue extends FrameworkValue
|
||||
private List<String> globals;
|
||||
private String varArgs;
|
||||
|
||||
public FunctionSignatureValue(String name, List<String> args, String varArgs, List<String> globals)
|
||||
public FunctionSignatureValue(final String name, final List<String> args, final String varArgs, final List<String> globals)
|
||||
{
|
||||
this.identifier = name;
|
||||
this.arguments = args;
|
||||
this.varArgs = varArgs;
|
||||
this.globals = globals;
|
||||
}
|
||||
public String getName()
|
||||
public String identifier()
|
||||
{
|
||||
return identifier;
|
||||
}
|
||||
public List<String> getArgs()
|
||||
public List<String> arguments()
|
||||
{
|
||||
return arguments;
|
||||
}
|
||||
public List<String> getGlobals() {return globals;}
|
||||
|
||||
|
||||
public String getVarArgs() { return varArgs;}
|
||||
public List<String> globals() {return globals;}
|
||||
public String varArgs() { return varArgs;}
|
||||
}
|
||||
@@ -4,7 +4,8 @@ import java.util.List;
|
||||
|
||||
public class FunctionUnpackedArgumentsValue extends ListValue
|
||||
{
|
||||
public FunctionUnpackedArgumentsValue(List<Value> list) {
|
||||
public FunctionUnpackedArgumentsValue(final List<Value> list)
|
||||
{
|
||||
super(list);
|
||||
}
|
||||
|
||||
@@ -17,7 +18,6 @@ public class FunctionUnpackedArgumentsValue extends ListValue
|
||||
@Override
|
||||
public Value deepcopy()
|
||||
{
|
||||
ListValue copy = (ListValue)super.deepcopy();
|
||||
return new FunctionUnpackedArgumentsValue(copy.items);
|
||||
return new FunctionUnpackedArgumentsValue(((ListValue) super.deepcopy()).items);
|
||||
}
|
||||
}
|
||||
@@ -12,12 +12,14 @@ import carpet.script.exception.ContinueStatement;
|
||||
import carpet.script.exception.ExpressionException;
|
||||
import carpet.script.exception.InternalExpressionException;
|
||||
import carpet.script.exception.ReturnStatement;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import net.minecraft.nbt.StringTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
|
||||
@@ -33,7 +35,7 @@ public class FunctionValue extends Value implements Fluff.ILazyFunction
|
||||
private static long variantCounter = 1;
|
||||
private long variant;
|
||||
|
||||
private FunctionValue(Expression expression, Tokenizer.Token token, String name, LazyValue body, List<String> args, String varArgs)
|
||||
private FunctionValue(final Expression expression, final Tokenizer.Token token, final String name, final LazyValue body, final List<String> args, final String varArgs)
|
||||
{
|
||||
this.expression = expression;
|
||||
this.token = token;
|
||||
@@ -45,7 +47,7 @@ public class FunctionValue extends Value implements Fluff.ILazyFunction
|
||||
variant = 0L;
|
||||
}
|
||||
|
||||
public FunctionValue(Expression expression, Tokenizer.Token token, String name, LazyValue body, List<String> args, String varArgs, Map<String, LazyValue> outerState)
|
||||
public FunctionValue(final Expression expression, final Tokenizer.Token token, final String name, final LazyValue body, final List<String> args, final String varArgs, final Map<String, LazyValue> outerState)
|
||||
{
|
||||
this.expression = expression;
|
||||
this.token = token;
|
||||
@@ -63,19 +65,27 @@ public class FunctionValue extends Value implements Fluff.ILazyFunction
|
||||
return name;
|
||||
}
|
||||
|
||||
public Module getModule() {return expression.module;}
|
||||
public Module getModule()
|
||||
{
|
||||
return expression.module;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrettyString()
|
||||
{
|
||||
List<String> stringArgs= new ArrayList<>(args);
|
||||
final List<String> stringArgs = new ArrayList<>(args);
|
||||
if (outerState != null)
|
||||
{
|
||||
stringArgs.addAll(outerState.entrySet().stream().map(e ->
|
||||
"outer("+e.getKey()+") = "+e.getValue().evalValue(null).getPrettyString()).collect(Collectors.toList()));
|
||||
return (name.equals("_")?"<lambda>":name) +"("+String.join(", ",stringArgs)+")";
|
||||
"outer(" + e.getKey() + ") = " + e.getValue().evalValue(null).getPrettyString()).toList());
|
||||
}
|
||||
return (name.equals("_") ? "<lambda>" : name) + "(" + String.join(", ", stringArgs) + ")";
|
||||
}
|
||||
|
||||
public String fullName() {return (name.equals("_")?"<lambda>":name)+(expression.module == null?"":"["+expression.module.name()+"]");}
|
||||
public String fullName()
|
||||
{
|
||||
return (name.equals("_") ? "<lambda>" : name) + (expression.module == null ? "" : "[" + expression.module.name() + "]");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getBoolean()
|
||||
@@ -86,7 +96,7 @@ public class FunctionValue extends Value implements Fluff.ILazyFunction
|
||||
@Override
|
||||
protected Value clone()
|
||||
{
|
||||
FunctionValue ret = new FunctionValue(expression, token, name, body, args, varArgs);
|
||||
final FunctionValue ret = new FunctionValue(expression, token, name, body, args, varArgs);
|
||||
ret.outerState = this.outerState;
|
||||
ret.variant = this.variant;
|
||||
return ret;
|
||||
@@ -95,26 +105,22 @@ public class FunctionValue extends Value implements Fluff.ILazyFunction
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return name.hashCode()+(int)variant;
|
||||
return name.hashCode() + (int) variant;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o)
|
||||
public boolean equals(final Object o)
|
||||
{
|
||||
if (o instanceof FunctionValue)
|
||||
return name.equals(((FunctionValue) o).name) && variant == ((FunctionValue) o).variant;
|
||||
return false;
|
||||
return o instanceof final FunctionValue fv && name.equals(fv.name) && variant == fv.variant;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(final Value o)
|
||||
{
|
||||
if (o instanceof FunctionValue)
|
||||
if (o instanceof final FunctionValue fv)
|
||||
{
|
||||
int nameSame = this.name.compareTo(((FunctionValue) o).name);
|
||||
if (nameSame != 0)
|
||||
return nameSame;
|
||||
return (int) (variant-((FunctionValue) o).variant);
|
||||
final int nameSame = this.name.compareTo(fv.name);
|
||||
return nameSame != 0 ? nameSame : (int) (variant - fv.variant);
|
||||
}
|
||||
return getString().compareTo(o.getString());
|
||||
}
|
||||
@@ -132,7 +138,7 @@ public class FunctionValue extends Value implements Fluff.ILazyFunction
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value slice(long from, Long to)
|
||||
public Value slice(final long from, final Long to)
|
||||
{
|
||||
throw new InternalExpressionException("Cannot slice a function");
|
||||
}
|
||||
@@ -149,46 +155,49 @@ public class FunctionValue extends Value implements Fluff.ILazyFunction
|
||||
return varArgs != null;
|
||||
}
|
||||
|
||||
public LazyValue callInContext(Context c, Context.Type type, List<Value> params)
|
||||
public LazyValue callInContext(final Context c, final Context.Type type, final List<Value> params)
|
||||
{
|
||||
try
|
||||
{
|
||||
return execute(c, type, expression, token, params);
|
||||
}
|
||||
catch (ExpressionException exc)
|
||||
catch (final ExpressionException exc)
|
||||
{
|
||||
exc.stack.add(this);
|
||||
throw exc;
|
||||
}
|
||||
catch (InternalExpressionException exc)
|
||||
catch (final InternalExpressionException exc)
|
||||
{
|
||||
exc.stack.add(this);
|
||||
throw new ExpressionException(c, expression, token, exc.getMessage(), exc.stack);
|
||||
}
|
||||
|
||||
catch (ArithmeticException exc)
|
||||
catch (final ArithmeticException exc)
|
||||
{
|
||||
throw new ExpressionException(c, expression, token, "Your math is wrong, "+exc.getMessage(), Collections.singletonList(this));
|
||||
throw new ExpressionException(c, expression, token, "Your math is wrong, " + exc.getMessage(), Collections.singletonList(this));
|
||||
}
|
||||
}
|
||||
|
||||
public void checkArgs(int candidates)
|
||||
public void checkArgs(final int candidates)
|
||||
{
|
||||
int actual = getArguments().size();
|
||||
final int actual = getArguments().size();
|
||||
|
||||
if (candidates < actual)
|
||||
{
|
||||
throw new InternalExpressionException("Function " + getPrettyString() + " requires at least " + actual + " arguments");
|
||||
}
|
||||
if (candidates > actual && getVarArgs() == null)
|
||||
{
|
||||
throw new InternalExpressionException("Function " + getPrettyString() + " requires " + actual + " arguments");
|
||||
}
|
||||
}
|
||||
|
||||
public static List<Value> unpackArgs(List<LazyValue> lazyParams, Context c)
|
||||
public static List<Value> unpackArgs(final List<LazyValue> lazyParams, final Context c)
|
||||
{
|
||||
// TODO we shoudn't need that if all fuctions are not lazy really
|
||||
List<Value> params = new ArrayList<>();
|
||||
for (LazyValue lv : lazyParams)
|
||||
final List<Value> params = new ArrayList<>();
|
||||
for (final LazyValue lv : lazyParams)
|
||||
{
|
||||
Value param = lv.evalValue(c, Context.NONE);
|
||||
final Value param = lv.evalValue(c, Context.NONE);
|
||||
if (param instanceof FunctionUnpackedArgumentsValue)
|
||||
{
|
||||
CarpetSettings.LOG.error("How did we get here?");
|
||||
@@ -203,48 +212,51 @@ public class FunctionValue extends Value implements Fluff.ILazyFunction
|
||||
}
|
||||
|
||||
@Override
|
||||
public LazyValue lazyEval(Context c, Context.Type type, Expression e, Tokenizer.Token t, List<LazyValue> lazyParams) {
|
||||
List<Value> resolvedParams = unpackArgs(lazyParams, c);
|
||||
public LazyValue lazyEval(final Context c, final Context.Type type, final Expression e, final Tokenizer.Token t, final List<LazyValue> lazyParams)
|
||||
{
|
||||
final List<Value> resolvedParams = unpackArgs(lazyParams, c);
|
||||
return execute(c, type, e, t, resolvedParams);
|
||||
}
|
||||
|
||||
public LazyValue execute(Context c, Context.Type type, Expression e, Tokenizer.Token t, List<Value> params)
|
||||
public LazyValue execute(final Context c, final Context.Type type, final Expression e, final Tokenizer.Token t, final List<Value> params)
|
||||
{
|
||||
assertArgsOk(params, (fixedArgs) ->{
|
||||
assertArgsOk(params, fixedArgs -> {
|
||||
if (fixedArgs) // wrong number of args for fixed args
|
||||
{
|
||||
throw new ExpressionException(c, e, t,
|
||||
"Incorrect number of arguments for function "+name+
|
||||
". Should be "+args.size()+", not "+params.size()+" like "+args
|
||||
"Incorrect number of arguments for function " + name +
|
||||
". Should be " + args.size() + ", not " + params.size() + " like " + args
|
||||
);
|
||||
}
|
||||
else // too few args for varargs
|
||||
{
|
||||
List<String> argList = new ArrayList<>(args);
|
||||
argList.add("... "+varArgs);
|
||||
throw new ExpressionException(c, e, t,
|
||||
"Incorrect number of arguments for function "+name+
|
||||
". Should be at least "+args.size()+", not "+params.size()+" like "+argList
|
||||
);
|
||||
}
|
||||
});
|
||||
Context newFrame = c.recreate();
|
||||
// too few args for varargs
|
||||
|
||||
if (outerState != null) outerState.forEach(newFrame::setVariable);
|
||||
for (int i=0; i<args.size(); i++)
|
||||
final List<String> argList = new ArrayList<>(args);
|
||||
argList.add("... " + varArgs);
|
||||
throw new ExpressionException(c, e, t,
|
||||
"Incorrect number of arguments for function " + name +
|
||||
". Should be at least " + args.size() + ", not " + params.size() + " like " + argList
|
||||
);
|
||||
});
|
||||
final Context newFrame = c.recreate();
|
||||
|
||||
if (outerState != null)
|
||||
{
|
||||
String arg = args.get(i);
|
||||
Value val = params.get(i).reboundedTo(arg); // todo check if we need to copy that
|
||||
outerState.forEach(newFrame::setVariable);
|
||||
}
|
||||
for (int i = 0; i < args.size(); i++)
|
||||
{
|
||||
final String arg = args.get(i);
|
||||
final Value val = params.get(i).reboundedTo(arg); // todo check if we need to copy that
|
||||
newFrame.setVariable(arg, (cc, tt) -> val);
|
||||
}
|
||||
if (varArgs != null)
|
||||
{
|
||||
List<Value> extraParams = new ArrayList<>();
|
||||
final List<Value> extraParams = new ArrayList<>();
|
||||
for (int i = args.size(), mx = params.size(); i < mx; i++)
|
||||
{
|
||||
extraParams.add(params.get(i).reboundedTo(null)); // copy by value I guess
|
||||
}
|
||||
Value rest = ListValue.wrap(extraParams).bindTo(varArgs); // didn't we just copied that?
|
||||
final Value rest = ListValue.wrap(extraParams).bindTo(varArgs); // didn't we just copied that?
|
||||
newFrame.setVariable(varArgs, (cc, tt) -> rest);
|
||||
|
||||
}
|
||||
@@ -253,15 +265,15 @@ public class FunctionValue extends Value implements Fluff.ILazyFunction
|
||||
{
|
||||
retVal = body.evalValue(newFrame, type); // todo not sure if we need to propagete type / consider boolean context in defined functions - answer seems ye
|
||||
}
|
||||
catch (BreakStatement | ContinueStatement exc)
|
||||
catch (final BreakStatement | ContinueStatement exc)
|
||||
{
|
||||
throw new ExpressionException(c, e, t, "'continue' and 'break' can only be called inside loop function bodies");
|
||||
}
|
||||
catch (ReturnStatement returnStatement)
|
||||
catch (final ReturnStatement returnStatement)
|
||||
{
|
||||
retVal = returnStatement.retval;
|
||||
}
|
||||
Value otherRetVal = retVal;
|
||||
final Value otherRetVal = retVal;
|
||||
return (cc, tt) -> otherRetVal;
|
||||
}
|
||||
|
||||
@@ -269,30 +281,38 @@ public class FunctionValue extends Value implements Fluff.ILazyFunction
|
||||
{
|
||||
return expression;
|
||||
}
|
||||
|
||||
public Tokenizer.Token getToken()
|
||||
{
|
||||
return token;
|
||||
}
|
||||
|
||||
public List<String> getArguments()
|
||||
{
|
||||
return args;
|
||||
}
|
||||
public String getVarArgs() {return varArgs; }
|
||||
|
||||
public String getVarArgs()
|
||||
{
|
||||
return varArgs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tag toTag(boolean force)
|
||||
public Tag toTag(final boolean force)
|
||||
{
|
||||
if (!force) throw new NBTSerializableValue.IncompatibleTypeException(this);
|
||||
if (!force)
|
||||
{
|
||||
throw new NBTSerializableValue.IncompatibleTypeException(this);
|
||||
}
|
||||
return StringTag.valueOf(getString());
|
||||
}
|
||||
|
||||
public void assertArgsOk(List<?> list, Consumer<Boolean> feedback)
|
||||
public void assertArgsOk(final List<?> list, final Consumer<Boolean> feedback)
|
||||
{
|
||||
int size = list.size();
|
||||
final int size = list.size();
|
||||
if (varArgs == null && args.size() != size) // wrong number of args for fixed args
|
||||
{
|
||||
feedback.accept(true);
|
||||
|
||||
}
|
||||
else if (varArgs != null && args.size() > size) // too few args for varargs
|
||||
{
|
||||
@@ -301,12 +321,14 @@ public class FunctionValue extends Value implements Fluff.ILazyFunction
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean pure() {
|
||||
public boolean pure()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean transitive() {
|
||||
public boolean transitive()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -2,16 +2,23 @@ package carpet.script.value;
|
||||
|
||||
public class LContainerValue extends FrameworkValue
|
||||
{
|
||||
private ContainerValueInterface container;
|
||||
private Value address;
|
||||
private final ContainerValueInterface container;
|
||||
private final Value address;
|
||||
public static final LContainerValue NULL_CONTAINER = new LContainerValue(null, null);
|
||||
|
||||
public LContainerValue(ContainerValueInterface c, Value v)
|
||||
public LContainerValue(final ContainerValueInterface c, final Value v)
|
||||
{
|
||||
container = c;
|
||||
address = v;
|
||||
}
|
||||
|
||||
public ContainerValueInterface getContainer() {return container; }
|
||||
public Value getAddress() {return address; }
|
||||
public ContainerValueInterface container()
|
||||
{
|
||||
return container;
|
||||
}
|
||||
|
||||
public Value address()
|
||||
{
|
||||
return address;
|
||||
}
|
||||
}
|
||||
@@ -1,35 +1,41 @@
|
||||
package carpet.script.value;
|
||||
|
||||
import carpet.script.exception.InternalExpressionException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import net.minecraft.nbt.StringTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
|
||||
public abstract class LazyListValue extends AbstractListValue implements Iterator<Value>
|
||||
{
|
||||
public static LazyListValue rangeDouble(double from, double to, double step)
|
||||
public static LazyListValue rangeDouble(final double from, final double to, final double step)
|
||||
{
|
||||
return new LazyListValue()
|
||||
{
|
||||
{
|
||||
if (step == 0)
|
||||
{
|
||||
throw new InternalExpressionException("Range will never end with a zero step");
|
||||
}
|
||||
this.start = from;
|
||||
this.current = this.start;
|
||||
this.limit = to;
|
||||
this.stepp = step;
|
||||
}
|
||||
|
||||
private final double start;
|
||||
private double current;
|
||||
private final double limit;
|
||||
private final double stepp;
|
||||
|
||||
@Override
|
||||
public Value next()
|
||||
{
|
||||
Value val = new NumericValue(current);
|
||||
final Value val = new NumericValue(current);
|
||||
current += stepp;
|
||||
return val;
|
||||
}
|
||||
@@ -43,36 +49,41 @@ public abstract class LazyListValue extends AbstractListValue implements Iterato
|
||||
@Override
|
||||
public boolean hasNext()
|
||||
{
|
||||
return stepp > 0?(current < limit):(current > limit);
|
||||
return stepp > 0 ? (current < limit) : (current > limit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString()
|
||||
{
|
||||
return String.format(Locale.ROOT, "[%s, %s, ..., %s)",NumericValue.of(start).getString(), NumericValue.of(start+stepp).getString(), NumericValue.of( limit).getString());
|
||||
return String.format(Locale.ROOT, "[%s, %s, ..., %s)", NumericValue.of(start).getString(), NumericValue.of(start + stepp).getString(), NumericValue.of(limit).getString());
|
||||
}
|
||||
};
|
||||
}
|
||||
public static LazyListValue rangeLong(long from, long to, long step)
|
||||
|
||||
public static LazyListValue rangeLong(final long from, final long to, final long step)
|
||||
{
|
||||
return new LazyListValue()
|
||||
{
|
||||
{
|
||||
if (step == 0)
|
||||
{
|
||||
throw new InternalExpressionException("Range will never end with a zero step");
|
||||
}
|
||||
this.start = from;
|
||||
this.current = this.start;
|
||||
this.limit = to;
|
||||
this.stepp = step;
|
||||
}
|
||||
|
||||
private final long start;
|
||||
private long current;
|
||||
private final long limit;
|
||||
private final long stepp;
|
||||
|
||||
@Override
|
||||
public Value next()
|
||||
{
|
||||
Value val = new NumericValue(current);
|
||||
final Value val = new NumericValue(current);
|
||||
current += stepp;
|
||||
return val;
|
||||
}
|
||||
@@ -86,13 +97,13 @@ public abstract class LazyListValue extends AbstractListValue implements Iterato
|
||||
@Override
|
||||
public boolean hasNext()
|
||||
{
|
||||
return stepp > 0?(current < limit):(current > limit);
|
||||
return stepp > 0 ? (current < limit) : (current > limit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString()
|
||||
{
|
||||
return String.format(Locale.ROOT, "[%s, %s, ..., %s)",NumericValue.of(start).getString(), NumericValue.of(start+stepp).getString(), NumericValue.of( limit).getString());
|
||||
return String.format(Locale.ROOT, "[%s, %s, ..., %s)", NumericValue.of(start).getString(), NumericValue.of(start + stepp).getString(), NumericValue.of(limit).getString());
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -108,20 +119,24 @@ public abstract class LazyListValue extends AbstractListValue implements Iterato
|
||||
{
|
||||
return hasNext();
|
||||
}
|
||||
@Override public abstract boolean hasNext();
|
||||
|
||||
@Override public abstract Value next();
|
||||
|
||||
@Override
|
||||
public void fatality() {reset();}
|
||||
public void fatality()
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
public abstract void reset();
|
||||
|
||||
@Override
|
||||
public Iterator<Value> iterator() {return this;}
|
||||
public Iterator<Value> iterator()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
public List<Value> unroll()
|
||||
{
|
||||
List<Value> result = new ArrayList<>();
|
||||
final List<Value> result = new ArrayList<>();
|
||||
this.forEachRemaining(result::add);
|
||||
fatality();
|
||||
return result;
|
||||
@@ -130,26 +145,38 @@ public abstract class LazyListValue extends AbstractListValue implements Iterato
|
||||
@Override
|
||||
public Value slice(long from, Long to)
|
||||
{
|
||||
if (to == null || to < 0) to = (long) Integer.MAX_VALUE;
|
||||
if (from < 0) from = 0;
|
||||
if (to == null || to < 0)
|
||||
{
|
||||
to = (long) Integer.MAX_VALUE;
|
||||
}
|
||||
if (from < 0)
|
||||
{
|
||||
from = 0;
|
||||
}
|
||||
if (from > to)
|
||||
{
|
||||
return ListValue.of();
|
||||
List<Value> result = new ArrayList<>();
|
||||
}
|
||||
final List<Value> result = new ArrayList<>();
|
||||
int i;
|
||||
for (i = 0; i < from; i++)
|
||||
{
|
||||
if (hasNext())
|
||||
{
|
||||
next();
|
||||
}
|
||||
else
|
||||
{
|
||||
fatality();
|
||||
return ListValue.wrap(result);
|
||||
}
|
||||
}
|
||||
for (i = (int)from; i < to; i++)
|
||||
for (i = (int) from; i < to; i++)
|
||||
{
|
||||
if (hasNext())
|
||||
{
|
||||
result.add(next());
|
||||
}
|
||||
else
|
||||
{
|
||||
fatality();
|
||||
@@ -158,8 +185,9 @@ public abstract class LazyListValue extends AbstractListValue implements Iterato
|
||||
}
|
||||
return ListValue.wrap(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value add(Value other)
|
||||
public Value add(final Value other)
|
||||
{
|
||||
throw new InternalExpressionException("Cannot add to iterators");
|
||||
}
|
||||
@@ -175,37 +203,42 @@ public abstract class LazyListValue extends AbstractListValue implements Iterato
|
||||
{
|
||||
return "iterator";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object clone()
|
||||
{
|
||||
Object copy;
|
||||
final Object copy;
|
||||
try
|
||||
{
|
||||
copy = super.clone();
|
||||
}
|
||||
catch (CloneNotSupportedException e)
|
||||
catch (final CloneNotSupportedException e)
|
||||
{
|
||||
throw new InternalExpressionException("Cannot copy iterators");
|
||||
}
|
||||
((LazyListValue)copy).reset();
|
||||
((LazyListValue) copy).reset();
|
||||
return copy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value fromConstant() {
|
||||
return (Value)clone();
|
||||
public Value fromConstant()
|
||||
{
|
||||
return (Value) clone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return ("i"+getString()).hashCode();
|
||||
return ("i" + getString()).hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tag toTag(boolean force)
|
||||
public Tag toTag(final boolean force)
|
||||
{
|
||||
if (!force) throw new NBTSerializableValue.IncompatibleTypeException(this);
|
||||
if (!force)
|
||||
{
|
||||
throw new NBTSerializableValue.IncompatibleTypeException(this);
|
||||
}
|
||||
return StringTag.valueOf(getString());
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import carpet.script.LazyValue;
|
||||
import carpet.script.exception.InternalExpressionException;
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
@@ -14,6 +15,7 @@ import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.DoubleTag;
|
||||
import net.minecraft.nbt.IntTag;
|
||||
@@ -28,23 +30,24 @@ import static java.lang.Math.abs;
|
||||
public class ListValue extends AbstractListValue implements ContainerValueInterface
|
||||
{
|
||||
protected final List<Value> items;
|
||||
|
||||
@Override
|
||||
public String getString()
|
||||
{
|
||||
return "["+items.stream().map(Value::getString).collect(Collectors.joining(", "))+"]";
|
||||
return "[" + items.stream().map(Value::getString).collect(Collectors.joining(", ")) + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrettyString()
|
||||
{
|
||||
if (items.size()<8)
|
||||
return "["+items.stream().map(Value::getPrettyString).collect(Collectors.joining(", "))+"]";
|
||||
return "["+items.get(0).getPrettyString()+", "+items.get(1).getPrettyString()+", ..., "+
|
||||
items.get(items.size()-2).getPrettyString()+", "+items.get(items.size()-1).getPrettyString()+"]";
|
||||
return items.size() < 8
|
||||
? "[" + items.stream().map(Value::getPrettyString).collect(Collectors.joining(", ")) + "]"
|
||||
: "[" + items.get(0).getPrettyString() + ", " + items.get(1).getPrettyString() + ", ..., " + items.get(items.size() - 2).getPrettyString() + ", " + items.get(items.size() - 1).getPrettyString() + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getBoolean() {
|
||||
public boolean getBoolean()
|
||||
{
|
||||
return !items.isEmpty();
|
||||
}
|
||||
|
||||
@@ -57,56 +60,63 @@ public class ListValue extends AbstractListValue implements ContainerValueInterf
|
||||
@Override
|
||||
public Value deepcopy()
|
||||
{
|
||||
List<Value> copyItems = new ArrayList<>(items.size());
|
||||
for (Value entry: items) copyItems.add(entry.deepcopy());
|
||||
final List<Value> copyItems = new ArrayList<>(items.size());
|
||||
for (final Value entry : items)
|
||||
{
|
||||
copyItems.add(entry.deepcopy());
|
||||
}
|
||||
return new ListValue(copyItems);
|
||||
}
|
||||
|
||||
public ListValue(Collection<? extends Value> list)
|
||||
public ListValue(final Collection<? extends Value> list)
|
||||
{
|
||||
items = new ArrayList<>(list);
|
||||
}
|
||||
|
||||
protected ListValue(List<Value> list)
|
||||
protected ListValue(final List<Value> list)
|
||||
{
|
||||
items = list;
|
||||
}
|
||||
|
||||
public static Value fromTriple(double a, double b, double c)
|
||||
public static Value fromTriple(final double a, final double b, final double c)
|
||||
{
|
||||
return ListValue.of(new NumericValue(a), new NumericValue(b), new NumericValue(c));
|
||||
}
|
||||
|
||||
public static Value fromTriple(int a, int b, int c)
|
||||
public static Value fromTriple(final int a, final int b, final int c)
|
||||
{
|
||||
return fromTriple((double) a, b, c);
|
||||
}
|
||||
|
||||
|
||||
public static ListValue wrap(Stream<Value> stream)
|
||||
public static ListValue wrap(final Stream<Value> stream)
|
||||
{
|
||||
return wrap(stream.collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
public static ListValue wrap(List<Value> list)
|
||||
public static ListValue wrap(final List<Value> list)
|
||||
{
|
||||
return new ListValue(list);
|
||||
}
|
||||
public static ListValue of(Value ... list)
|
||||
|
||||
public static ListValue of(final Value... list)
|
||||
{
|
||||
return new ListValue(new ArrayList<>(Arrays.asList(list)));
|
||||
}
|
||||
public static ListValue ofNums(Number ... list)
|
||||
|
||||
public static ListValue ofNums(final Number... list)
|
||||
{
|
||||
List<Value> valList = new ArrayList<>();
|
||||
for (Number i : list)
|
||||
final List<Value> valList = new ArrayList<>();
|
||||
for (final Number i : list)
|
||||
{
|
||||
valList.add(new NumericValue(i.doubleValue()));
|
||||
}
|
||||
return new ListValue(valList);
|
||||
}
|
||||
|
||||
public static LazyValue lazyEmpty()
|
||||
{
|
||||
Value ret = new ListValue();
|
||||
final Value ret = new ListValue();
|
||||
return (c, t) -> ret;
|
||||
}
|
||||
|
||||
@@ -116,16 +126,17 @@ public class ListValue extends AbstractListValue implements ContainerValueInterf
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value add(Value other) {
|
||||
ListValue output = new ListValue();
|
||||
if (other instanceof ListValue)
|
||||
public Value add(final Value other)
|
||||
{
|
||||
final ListValue output = new ListValue();
|
||||
if (other instanceof final ListValue list)
|
||||
{
|
||||
List<Value> other_list = ((ListValue) other).items;
|
||||
if (other_list.size() == items.size())
|
||||
final List<Value> otherItems = list.items;
|
||||
if (otherItems.size() == items.size())
|
||||
{
|
||||
for(int i = 0, size = items.size(); i < size; i++)
|
||||
for (int i = 0, size = items.size(); i < size; i++)
|
||||
{
|
||||
output.items.add(items.get(i).add(other_list.get(i)));
|
||||
output.items.add(items.get(i).add(otherItems.get(i)));
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -135,31 +146,32 @@ public class ListValue extends AbstractListValue implements ContainerValueInterf
|
||||
}
|
||||
else
|
||||
{
|
||||
for (Value v : items)
|
||||
for (final Value v : items)
|
||||
{
|
||||
output.items.add(v.add(other));
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void append(Value v)
|
||||
public void append(final Value v)
|
||||
{
|
||||
items.add(v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value subtract(Value other)
|
||||
public Value subtract(final Value other)
|
||||
{
|
||||
ListValue output = new ListValue();
|
||||
if (other instanceof ListValue)
|
||||
final ListValue output = new ListValue();
|
||||
if (other instanceof final ListValue list)
|
||||
{
|
||||
List<Value> other_list = ((ListValue) other).items;
|
||||
if (other_list.size() == items.size())
|
||||
final List<Value> otherItems = list.items;
|
||||
if (otherItems.size() == items.size())
|
||||
{
|
||||
for(int i = 0, size = items.size(); i < size; i++)
|
||||
for (int i = 0, size = items.size(); i < size; i++)
|
||||
{
|
||||
output.items.add(items.get(i).subtract(other_list.get(i)));
|
||||
output.items.add(items.get(i).subtract(otherItems.get(i)));
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -169,31 +181,32 @@ public class ListValue extends AbstractListValue implements ContainerValueInterf
|
||||
}
|
||||
else
|
||||
{
|
||||
for (Value v : items)
|
||||
for (final Value v : items)
|
||||
{
|
||||
output.items.add(v.subtract(other));
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
public void subtractFrom(Value v) // if I ever do -= then it wouod remove items
|
||||
|
||||
public void subtractFrom(final Value v) // if I ever do -= then it wouod remove items
|
||||
{
|
||||
throw new UnsupportedOperationException(); // TODO
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Value multiply(Value other)
|
||||
public Value multiply(final Value other)
|
||||
{
|
||||
ListValue output = new ListValue();
|
||||
if (other instanceof ListValue)
|
||||
final ListValue output = new ListValue();
|
||||
if (other instanceof final ListValue list)
|
||||
{
|
||||
List<Value> other_list = ((ListValue) other).items;
|
||||
if (other_list.size() == items.size())
|
||||
final List<Value> otherItems = list.items;
|
||||
if (otherItems.size() == items.size())
|
||||
{
|
||||
for(int i = 0, size = items.size(); i < size; i++)
|
||||
for (int i = 0, size = items.size(); i < size; i++)
|
||||
{
|
||||
output.items.add(items.get(i).multiply(other_list.get(i)));
|
||||
output.items.add(items.get(i).multiply(otherItems.get(i)));
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -203,7 +216,7 @@ public class ListValue extends AbstractListValue implements ContainerValueInterf
|
||||
}
|
||||
else
|
||||
{
|
||||
for (Value v : items)
|
||||
for (final Value v : items)
|
||||
{
|
||||
output.items.add(v.multiply(other));
|
||||
}
|
||||
@@ -212,17 +225,17 @@ public class ListValue extends AbstractListValue implements ContainerValueInterf
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value divide(Value other)
|
||||
public Value divide(final Value other)
|
||||
{
|
||||
ListValue output = new ListValue();
|
||||
if (other instanceof ListValue)
|
||||
final ListValue output = new ListValue();
|
||||
if (other instanceof final ListValue list)
|
||||
{
|
||||
List<Value> other_list = ((ListValue) other).items;
|
||||
if (other_list.size() == items.size())
|
||||
final List<Value> otherItems = list.items;
|
||||
if (otherItems.size() == items.size())
|
||||
{
|
||||
for(int i = 0, size = items.size(); i < size; i++)
|
||||
for (int i = 0, size = items.size(); i < size; i++)
|
||||
{
|
||||
output.items.add(items.get(i).divide(other_list.get(i)));
|
||||
output.items.add(items.get(i).divide(otherItems.get(i)));
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -232,7 +245,7 @@ public class ListValue extends AbstractListValue implements ContainerValueInterf
|
||||
}
|
||||
else
|
||||
{
|
||||
for (Value v : items)
|
||||
for (final Value v : items)
|
||||
{
|
||||
output.items.add(v.divide(other));
|
||||
}
|
||||
@@ -241,19 +254,27 @@ public class ListValue extends AbstractListValue implements ContainerValueInterf
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Value o)
|
||||
public int compareTo(final Value o)
|
||||
{
|
||||
if (o instanceof ListValue)
|
||||
if (o instanceof final ListValue ol)
|
||||
{
|
||||
ListValue ol = (ListValue)o;
|
||||
int this_size = this.getItems().size();
|
||||
int o_size = ol.getItems().size();
|
||||
if (this_size != o_size) return this_size - o_size;
|
||||
if (this_size == 0) return 0;
|
||||
for (int i = 0; i < this_size; i++)
|
||||
final int size = this.getItems().size();
|
||||
final int otherSize = ol.getItems().size();
|
||||
if (size != otherSize)
|
||||
{
|
||||
int res = this.items.get(i).compareTo(ol.items.get(i));
|
||||
if (res != 0) return res;
|
||||
return size - otherSize;
|
||||
}
|
||||
if (size == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
final int res = this.items.get(i).compareTo(ol.items.get(i));
|
||||
if (res != 0)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -263,11 +284,7 @@ public class ListValue extends AbstractListValue implements ContainerValueInterf
|
||||
@Override
|
||||
public boolean equals(final Object o)
|
||||
{
|
||||
if (o instanceof ListValue)
|
||||
{
|
||||
return getItems().equals(((ListValue) o).getItems());
|
||||
}
|
||||
return false;
|
||||
return o instanceof final ListValue list && getItems().equals(list.getItems());
|
||||
}
|
||||
|
||||
public List<Value> getItems()
|
||||
@@ -276,7 +293,10 @@ public class ListValue extends AbstractListValue implements ContainerValueInterf
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Value> iterator() { return new ArrayList<>(items).iterator(); } // should be thread safe
|
||||
public Iterator<Value> iterator()
|
||||
{
|
||||
return new ArrayList<>(items).iterator();
|
||||
} // should be thread safe
|
||||
|
||||
@Override
|
||||
public List<Value> unpack()
|
||||
@@ -284,7 +304,7 @@ public class ListValue extends AbstractListValue implements ContainerValueInterf
|
||||
return new ArrayList<>(items);
|
||||
}
|
||||
|
||||
public void extend(List<Value> subList)
|
||||
public void extend(final List<Value> subList)
|
||||
{
|
||||
items.addAll(subList);
|
||||
}
|
||||
@@ -296,18 +316,21 @@ public class ListValue extends AbstractListValue implements ContainerValueInterf
|
||||
* @param len
|
||||
* @return
|
||||
*/
|
||||
public static int normalizeIndex(long idx, int len)
|
||||
public static int normalizeIndex(long idx, final int len)
|
||||
{
|
||||
if (idx >=0 && idx < len) return (int)idx;
|
||||
long range = abs(idx)/len;
|
||||
idx += (range+2)*len;
|
||||
if (idx >= 0 && idx < len)
|
||||
{
|
||||
return (int) idx;
|
||||
}
|
||||
final long range = abs(idx) / len;
|
||||
idx += (range + 2) * len;
|
||||
idx = idx % len;
|
||||
return (int)idx;
|
||||
return (int) idx;
|
||||
}
|
||||
|
||||
public static class ListConstructorValue extends ListValue
|
||||
{
|
||||
public ListConstructorValue(Collection<? extends Value> list)
|
||||
public ListConstructorValue(final Collection<? extends Value> list)
|
||||
{
|
||||
super(list);
|
||||
}
|
||||
@@ -320,11 +343,11 @@ public class ListValue extends AbstractListValue implements ContainerValueInterf
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value in(Value value1)
|
||||
public Value in(final Value value1)
|
||||
{
|
||||
for (int i = 0; i < items.size(); i++)
|
||||
{
|
||||
Value v = items.get(i);
|
||||
final Value v = items.get(i);
|
||||
if (v.equals(value1))
|
||||
{
|
||||
return new NumericValue(i);
|
||||
@@ -334,22 +357,27 @@ public class ListValue extends AbstractListValue implements ContainerValueInterf
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value slice(long fromDesc, Long toDesc)
|
||||
public Value slice(final long fromDesc, final Long toDesc)
|
||||
{
|
||||
List<Value> items = getItems();
|
||||
int size = items.size();
|
||||
int from = normalizeIndex(fromDesc, size);
|
||||
final List<Value> items = getItems();
|
||||
final int size = items.size();
|
||||
final int from = normalizeIndex(fromDesc, size);
|
||||
if (toDesc == null)
|
||||
{
|
||||
return new ListValue(new ArrayList<>(getItems().subList(from, size)));
|
||||
int to = normalizeIndex(toDesc, size+1);
|
||||
}
|
||||
final int to = normalizeIndex(toDesc, size + 1);
|
||||
if (from > to)
|
||||
{
|
||||
return ListValue.of();
|
||||
}
|
||||
return new ListValue(new ArrayList<>(getItems().subList(from, to)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value split(Value delimiter) {
|
||||
ListValue result = new ListValue();
|
||||
public Value split(final Value delimiter)
|
||||
{
|
||||
final ListValue result = new ListValue();
|
||||
if (delimiter == null)
|
||||
{
|
||||
this.forEach(item -> result.items.add(of(item)));
|
||||
@@ -357,12 +385,12 @@ public class ListValue extends AbstractListValue implements ContainerValueInterf
|
||||
}
|
||||
int startIndex = 0;
|
||||
int index = 0;
|
||||
for (Value val : this.items)
|
||||
for (final Value val : this.items)
|
||||
{
|
||||
index++;
|
||||
if (val.equals(delimiter))
|
||||
{
|
||||
result.items.add(new ListValue(new ArrayList<>(this.items.subList(startIndex, index-1))));
|
||||
result.items.add(new ListValue(new ArrayList<>(this.items.subList(startIndex, index - 1))));
|
||||
startIndex = index;
|
||||
}
|
||||
}
|
||||
@@ -373,35 +401,41 @@ public class ListValue extends AbstractListValue implements ContainerValueInterf
|
||||
@Override
|
||||
public double readDoubleNumber()
|
||||
{
|
||||
return (double)items.size();
|
||||
return items.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean put(Value where, Value value, Value conditionValue)
|
||||
public boolean put(final Value where, final Value value, final Value conditionValue)
|
||||
{
|
||||
String condition = conditionValue.getString();
|
||||
final String condition = conditionValue.getString();
|
||||
if (condition.equalsIgnoreCase("insert"))
|
||||
{
|
||||
return put(where, value, false, false);
|
||||
}
|
||||
if (condition.equalsIgnoreCase("extend"))
|
||||
{
|
||||
return put(where, value, false, true);
|
||||
}
|
||||
if (condition.equalsIgnoreCase("replace"))
|
||||
{
|
||||
return put(where, value, true, false);
|
||||
throw new InternalExpressionException("List 'put' modifier could be either 'insert', 'replace', or extend");
|
||||
}
|
||||
throw new InternalExpressionException("List 'put' modifier could be either 'insert', 'replace', or extend");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean put(Value ind, Value value)
|
||||
public boolean put(final Value ind, final Value value)
|
||||
{
|
||||
return put(ind, value, true, false);
|
||||
}
|
||||
|
||||
private boolean put(Value ind, Value value, boolean replace, boolean extend)
|
||||
private boolean put(final Value ind, final Value value, final boolean replace, final boolean extend)
|
||||
{
|
||||
if (ind.isNull())
|
||||
{
|
||||
if (extend && value instanceof AbstractListValue)
|
||||
{
|
||||
((AbstractListValue) value).iterator().forEachRemaining((v)-> items.add(v));
|
||||
((AbstractListValue) value).iterator().forEachRemaining(items::add);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -410,27 +444,35 @@ public class ListValue extends AbstractListValue implements ContainerValueInterf
|
||||
}
|
||||
else
|
||||
{
|
||||
int numitems = items.size();
|
||||
final int numitems = items.size();
|
||||
if (!(ind instanceof NumericValue))
|
||||
{
|
||||
return false;
|
||||
int index = (int)((NumericValue) ind).getLong();
|
||||
}
|
||||
int index = (int) ((NumericValue) ind).getLong();
|
||||
if (index < 0)
|
||||
{// only for values < 0
|
||||
index = normalizeIndex(index, numitems);
|
||||
}
|
||||
if (replace)
|
||||
{
|
||||
while (index >= items.size()) items.add(Value.NULL);
|
||||
while (index >= items.size())
|
||||
{
|
||||
items.add(Value.NULL);
|
||||
}
|
||||
items.set(index, value);
|
||||
return true;
|
||||
}
|
||||
while (index > items.size()) items.add(Value.NULL);
|
||||
while (index > items.size())
|
||||
{
|
||||
items.add(Value.NULL);
|
||||
}
|
||||
|
||||
if (extend && value instanceof AbstractListValue)
|
||||
{
|
||||
Iterable<Value> iterable = ((AbstractListValue) value)::iterator;
|
||||
List<Value> appendix = StreamSupport.stream( iterable.spliterator(), false).collect(Collectors.toList());
|
||||
items.addAll(index, appendix );
|
||||
final Iterable<Value> iterable = ((AbstractListValue) value)::iterator;
|
||||
final List<Value> appendix = StreamSupport.stream(iterable.spliterator(), false).collect(Collectors.toList());
|
||||
items.addAll(index, appendix);
|
||||
return true;
|
||||
}
|
||||
items.add(index, value);
|
||||
@@ -439,25 +481,27 @@ public class ListValue extends AbstractListValue implements ContainerValueInterf
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value get(Value value)
|
||||
public Value get(final Value value)
|
||||
{
|
||||
int size = items.size();
|
||||
if (size == 0) return Value.NULL;
|
||||
return items.get(normalizeIndex(NumericValue.asNumber(value, "'address' to a list index").getLong(), size));
|
||||
final int size = items.size();
|
||||
return size == 0 ? Value.NULL : items.get(normalizeIndex(NumericValue.asNumber(value, "'address' to a list index").getLong(), size));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean has(Value where)
|
||||
public boolean has(final Value where)
|
||||
{
|
||||
long index = NumericValue.asNumber(where, "'address' to a list index").getLong();
|
||||
final long index = NumericValue.asNumber(where, "'address' to a list index").getLong();
|
||||
return index >= 0 && index < items.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean delete(Value where)
|
||||
public boolean delete(final Value where)
|
||||
{
|
||||
if (!(where instanceof NumericValue) || items.isEmpty()) return false;
|
||||
long index = ((NumericValue) where).getLong();
|
||||
if (!(where instanceof NumericValue) || items.isEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
final long index = ((NumericValue) where).getLong();
|
||||
items.remove(normalizeIndex(index, items.size()));
|
||||
return true;
|
||||
}
|
||||
@@ -482,35 +526,54 @@ public class ListValue extends AbstractListValue implements ContainerValueInterf
|
||||
LIST,
|
||||
MAP,
|
||||
STRING;
|
||||
private static TagTypeCompat getType(Tag tag)
|
||||
|
||||
private static TagTypeCompat getType(final Tag tag)
|
||||
{
|
||||
if (tag instanceof IntTag) return INT;
|
||||
if (tag instanceof LongTag) return LONG;
|
||||
if (tag instanceof DoubleTag) return DBL;
|
||||
if (tag instanceof ListTag) return LIST;
|
||||
if (tag instanceof CompoundTag) return MAP;
|
||||
if (tag instanceof IntTag)
|
||||
{
|
||||
return INT;
|
||||
}
|
||||
if (tag instanceof LongTag)
|
||||
{
|
||||
return LONG;
|
||||
}
|
||||
if (tag instanceof DoubleTag)
|
||||
{
|
||||
return DBL;
|
||||
}
|
||||
if (tag instanceof ListTag)
|
||||
{
|
||||
return LIST;
|
||||
}
|
||||
if (tag instanceof CompoundTag)
|
||||
{
|
||||
return MAP;
|
||||
}
|
||||
return STRING;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Tag toTag(boolean force)
|
||||
public Tag toTag(final boolean force)
|
||||
{
|
||||
int argSize = items.size();
|
||||
if (argSize == 0) return new ListTag();
|
||||
ListTag tag = new ListTag();
|
||||
if (argSize ==1)
|
||||
final int argSize = items.size();
|
||||
if (argSize == 0)
|
||||
{
|
||||
return new ListTag();
|
||||
}
|
||||
final ListTag tag = new ListTag();
|
||||
if (argSize == 1)
|
||||
{
|
||||
tag.add(items.get(0).toTag(force));
|
||||
return tag;
|
||||
}
|
||||
// figuring out the types
|
||||
List<Tag> tags= new ArrayList<>();
|
||||
final List<Tag> tags = new ArrayList<>();
|
||||
items.forEach(v -> tags.add(v.toTag(force)));
|
||||
Set<TagTypeCompat> cases = EnumSet.noneOf(TagTypeCompat.class);
|
||||
final Set<TagTypeCompat> cases = EnumSet.noneOf(TagTypeCompat.class);
|
||||
tags.forEach(t -> cases.add(TagTypeCompat.getType(t)));
|
||||
if (cases.size()==1) // well, one type of items
|
||||
if (cases.size() == 1) // well, one type of items
|
||||
{
|
||||
tag.addAll(tags);
|
||||
return tag;
|
||||
@@ -519,28 +582,28 @@ public class ListValue extends AbstractListValue implements ContainerValueInterf
|
||||
|| cases.contains(TagTypeCompat.MAP)
|
||||
|| cases.contains(TagTypeCompat.STRING)) // incompatible types
|
||||
{
|
||||
if (!force) throw new NBTSerializableValue.IncompatibleTypeException(this);
|
||||
if (!force)
|
||||
{
|
||||
throw new NBTSerializableValue.IncompatibleTypeException(this);
|
||||
}
|
||||
tags.forEach(t -> tag.add(StringTag.valueOf(t.getAsString())));
|
||||
return tag;
|
||||
}
|
||||
// only numbers / mixed types
|
||||
if (cases.contains(TagTypeCompat.DBL))
|
||||
{
|
||||
tags.forEach(t -> tag.add(DoubleTag.valueOf(((NumericTag)t).getAsDouble())));
|
||||
}
|
||||
else
|
||||
{
|
||||
tags.forEach(t -> tag.add(LongTag.valueOf(((NumericTag)t).getAsLong())));
|
||||
}
|
||||
tags.forEach(cases.contains(TagTypeCompat.DBL)
|
||||
? (t -> tag.add(DoubleTag.valueOf(((NumericTag) t).getAsDouble())))
|
||||
: (t -> tag.add(LongTag.valueOf(((NumericTag) t).getAsLong()))));
|
||||
return tag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JsonElement toJson()
|
||||
{
|
||||
JsonArray array = new JsonArray();
|
||||
for (Value el: items)
|
||||
final JsonArray array = new JsonArray();
|
||||
for (final Value el : items)
|
||||
{
|
||||
array.add(el.toJson());
|
||||
}
|
||||
return array;
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ package carpet.script.value;
|
||||
import carpet.script.exception.InternalExpressionException;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
@@ -11,6 +12,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
|
||||
@@ -23,22 +25,16 @@ public class MapValue extends AbstractListValue implements ContainerValueInterfa
|
||||
map = new HashMap<>();
|
||||
}
|
||||
|
||||
public MapValue(List<Value> kvPairs)
|
||||
public MapValue(final List<Value> kvPairs)
|
||||
{
|
||||
this();
|
||||
for (Value v : kvPairs)
|
||||
{
|
||||
put(v);
|
||||
}
|
||||
kvPairs.forEach(this::put);
|
||||
}
|
||||
|
||||
public MapValue(Set<Value> keySet)
|
||||
public MapValue(final Set<Value> keySet)
|
||||
{
|
||||
this();
|
||||
for (Value v : keySet)
|
||||
{
|
||||
map.put(v, Value.NULL);
|
||||
}
|
||||
keySet.forEach(v -> map.put(v, Value.NULL));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -56,20 +52,22 @@ public class MapValue extends AbstractListValue implements ContainerValueInterfa
|
||||
@Override
|
||||
public String getString()
|
||||
{
|
||||
return "{"+map.entrySet().stream().map((p) -> p.getKey().getString()+": "+p.getValue().getString()).collect(Collectors.joining(", "))+"}";
|
||||
return "{" + map.entrySet().stream().map(p -> p.getKey().getString() + ": " + p.getValue().getString()).collect(Collectors.joining(", ")) + "}";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrettyString()
|
||||
{
|
||||
if (map.size()<6)
|
||||
return "{"+map.entrySet().stream().map((p) -> p.getKey().getPrettyString()+": "+p.getValue().getPrettyString()).collect(Collectors.joining(", "))+"}";
|
||||
List<Value> keys = new ArrayList<>(map.keySet());
|
||||
int max = keys.size();
|
||||
return "{"+keys.get(0).getPrettyString()+": "+map.get(keys.get(0)).getPrettyString()+", "+
|
||||
keys.get(1).getPrettyString()+": "+map.get(keys.get(1)).getPrettyString()+", ..., "+
|
||||
keys.get(max-2).getPrettyString()+": "+map.get(keys.get(max-2)).getPrettyString()+", "+
|
||||
keys.get(max-1).getPrettyString()+": "+map.get(keys.get(max-1)).getPrettyString()+"}";
|
||||
if (map.size() < 6)
|
||||
{
|
||||
return "{" + map.entrySet().stream().map(p -> p.getKey().getPrettyString() + ": " + p.getValue().getPrettyString()).collect(Collectors.joining(", ")) + "}";
|
||||
}
|
||||
final List<Value> keys = new ArrayList<>(map.keySet());
|
||||
final int max = keys.size();
|
||||
return "{" + keys.get(0).getPrettyString() + ": " + map.get(keys.get(0)).getPrettyString() + ", " +
|
||||
keys.get(1).getPrettyString() + ": " + map.get(keys.get(1)).getPrettyString() + ", ..., " +
|
||||
keys.get(max - 2).getPrettyString() + ": " + map.get(keys.get(max - 2)).getPrettyString() + ", " +
|
||||
keys.get(max - 1).getPrettyString() + ": " + map.get(keys.get(max - 1)).getPrettyString() + "}";
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -87,35 +85,34 @@ public class MapValue extends AbstractListValue implements ContainerValueInterfa
|
||||
@Override
|
||||
public Value deepcopy()
|
||||
{
|
||||
Map<Value, Value> copyMap = new HashMap<>();
|
||||
final Map<Value, Value> copyMap = new HashMap<>();
|
||||
map.forEach((key, value) -> copyMap.put(key.deepcopy(), value.deepcopy()));
|
||||
return new MapValue(copyMap);
|
||||
}
|
||||
|
||||
private MapValue(Map<Value,Value> other)
|
||||
private MapValue(final Map<Value, Value> other)
|
||||
{
|
||||
map = other;
|
||||
}
|
||||
|
||||
public static MapValue wrap(Map<Value,Value> other)
|
||||
public static MapValue wrap(final Map<Value, Value> other)
|
||||
{
|
||||
return new MapValue(other);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value add(Value o)
|
||||
public Value add(final Value o)
|
||||
{
|
||||
Map<Value, Value> newItems = new HashMap<>(map);
|
||||
if (o instanceof MapValue)
|
||||
final Map<Value, Value> newItems = new HashMap<>(map);
|
||||
if (o instanceof final MapValue mapValue)
|
||||
{
|
||||
newItems.putAll(((MapValue) o).map);
|
||||
newItems.putAll(mapValue.map);
|
||||
}
|
||||
else if (o instanceof AbstractListValue)
|
||||
else if (o instanceof final AbstractListValue alv)
|
||||
{
|
||||
Iterator<Value> it = ((AbstractListValue) o).iterator();
|
||||
while (it.hasNext())
|
||||
for (final Value value : alv)
|
||||
{
|
||||
newItems.put(it.next(), Value.NULL);
|
||||
newItems.put(value, Value.NULL);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -126,31 +123,30 @@ public class MapValue extends AbstractListValue implements ContainerValueInterfa
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value subtract(Value v)
|
||||
public Value subtract(final Value v)
|
||||
{
|
||||
throw new InternalExpressionException("Cannot subtract from a map value");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value multiply(Value v)
|
||||
public Value multiply(final Value v)
|
||||
{
|
||||
throw new InternalExpressionException("Cannot multiply with a map value");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value divide(Value v)
|
||||
public Value divide(final Value v)
|
||||
{
|
||||
throw new InternalExpressionException("Cannot divide a map value");
|
||||
}
|
||||
|
||||
public void put(Value v)
|
||||
public void put(final Value v)
|
||||
{
|
||||
if (!(v instanceof ListValue))
|
||||
if (!(v instanceof final ListValue pair))
|
||||
{
|
||||
map.put(v, Value.NULL);
|
||||
return;
|
||||
}
|
||||
ListValue pair = (ListValue)v;
|
||||
if (pair.getItems().size() != 2)
|
||||
{
|
||||
throw new InternalExpressionException("Map constructor requires elements that have two items");
|
||||
@@ -159,13 +155,13 @@ public class MapValue extends AbstractListValue implements ContainerValueInterfa
|
||||
}
|
||||
|
||||
@Override
|
||||
public void append(Value v)
|
||||
public void append(final Value v)
|
||||
{
|
||||
map.put(v, Value.NULL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Value o)
|
||||
public int compareTo(final Value o)
|
||||
{
|
||||
throw new InternalExpressionException("Cannot compare with a map value");
|
||||
}
|
||||
@@ -173,11 +169,7 @@ public class MapValue extends AbstractListValue implements ContainerValueInterfa
|
||||
@Override
|
||||
public boolean equals(final Object o)
|
||||
{
|
||||
if (o instanceof MapValue)
|
||||
{
|
||||
return map.equals(((MapValue) o).map);
|
||||
}
|
||||
return false;
|
||||
return o instanceof final MapValue mapValue && map.equals(mapValue.map);
|
||||
}
|
||||
|
||||
public Map<Value, Value> getMap()
|
||||
@@ -185,9 +177,9 @@ public class MapValue extends AbstractListValue implements ContainerValueInterfa
|
||||
return map;
|
||||
}
|
||||
|
||||
public void extend(List<Value> subList)
|
||||
public void extend(final List<Value> subList)
|
||||
{
|
||||
for (Value v: subList) put(v);
|
||||
subList.forEach(this::put);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -197,53 +189,51 @@ public class MapValue extends AbstractListValue implements ContainerValueInterfa
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value in(Value value1)
|
||||
public Value in(final Value value)
|
||||
{
|
||||
if (map.containsKey(value1)) return value1;
|
||||
return Value.NULL;
|
||||
return map.containsKey(value) ? value : Value.NULL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value slice(long from, Long to)
|
||||
public Value slice(final long from, final Long to)
|
||||
{
|
||||
throw new InternalExpressionException("Cannot slice a map value");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value split(Value delimiter) {
|
||||
throw new InternalExpressionException("Cannot split a map value");
|
||||
public Value split(final Value delimiter)
|
||||
{
|
||||
throw new InternalExpressionException("Cannot split a map value");
|
||||
}
|
||||
|
||||
@Override
|
||||
public double readDoubleNumber()
|
||||
{
|
||||
return (double)map.size();
|
||||
return map.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value get(Value v2)
|
||||
public Value get(final Value v2)
|
||||
{
|
||||
return map.getOrDefault(v2, Value.NULL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean has(Value where)
|
||||
public boolean has(final Value where)
|
||||
{
|
||||
return map.containsKey(where);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean delete(Value where)
|
||||
public boolean delete(final Value where)
|
||||
{
|
||||
Value ret = map.remove(where);
|
||||
return ret != null;
|
||||
return map.remove(where) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean put(Value key, Value value)
|
||||
public boolean put(final Value key, final Value value)
|
||||
{
|
||||
Value ret = map.put(key, value);
|
||||
return ret != null;
|
||||
return map.put(key, value) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -259,12 +249,15 @@ public class MapValue extends AbstractListValue implements ContainerValueInterfa
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tag toTag(boolean force)
|
||||
public Tag toTag(final boolean force)
|
||||
{
|
||||
CompoundTag tag = new CompoundTag() ;
|
||||
map.forEach( (k, v) ->
|
||||
final CompoundTag tag = new CompoundTag();
|
||||
map.forEach((k, v) ->
|
||||
{
|
||||
if (!force && !(k instanceof StringValue)) throw new NBTSerializableValue.IncompatibleTypeException(k);
|
||||
if (!force && !(k instanceof StringValue))
|
||||
{
|
||||
throw new NBTSerializableValue.IncompatibleTypeException(k);
|
||||
}
|
||||
tag.put(k.getString(), v.toTag(force));
|
||||
});
|
||||
return tag;
|
||||
@@ -273,10 +266,10 @@ public class MapValue extends AbstractListValue implements ContainerValueInterfa
|
||||
@Override
|
||||
public JsonElement toJson()
|
||||
{
|
||||
JsonObject jsonMap = new JsonObject();
|
||||
List<Value> keys = new ArrayList<>(map.keySet());
|
||||
final JsonObject jsonMap = new JsonObject();
|
||||
final List<Value> keys = new ArrayList<>(map.keySet());
|
||||
Collections.sort(keys);
|
||||
keys.forEach( k -> jsonMap.add(k.getString(), map.get(k).toJson()));
|
||||
keys.forEach(k -> jsonMap.add(k.getString(), map.get(k).toJson()));
|
||||
return jsonMap;
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ import carpet.script.exception.Throwables;
|
||||
import carpet.script.utils.EquipmentInventory;
|
||||
import com.mojang.brigadier.StringReader;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
@@ -17,6 +18,7 @@ import java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import net.minecraft.commands.arguments.NbtPathArgument;
|
||||
import net.minecraft.commands.arguments.item.ItemInput;
|
||||
import net.minecraft.commands.arguments.item.ItemParser;
|
||||
@@ -59,9 +61,11 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa
|
||||
private Supplier<Tag> nbtSupplier = null;
|
||||
private boolean owned = false;
|
||||
|
||||
private NBTSerializableValue() {}
|
||||
private NBTSerializableValue()
|
||||
{
|
||||
}
|
||||
|
||||
public NBTSerializableValue(String nbtString)
|
||||
public NBTSerializableValue(final String nbtString)
|
||||
{
|
||||
nbtSupplier = () ->
|
||||
{
|
||||
@@ -69,64 +73,70 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa
|
||||
{
|
||||
return (new TagParser(new StringReader(nbtString))).readValue();
|
||||
}
|
||||
catch (CommandSyntaxException e)
|
||||
catch (final CommandSyntaxException e)
|
||||
{
|
||||
throw new InternalExpressionException("Incorrect NBT data: "+nbtString);
|
||||
throw new InternalExpressionException("Incorrect NBT data: " + nbtString);
|
||||
}
|
||||
};
|
||||
owned = true;
|
||||
}
|
||||
|
||||
public NBTSerializableValue(Tag tag)
|
||||
public NBTSerializableValue(final Tag tag)
|
||||
{
|
||||
nbtTag = tag;
|
||||
owned = true;
|
||||
}
|
||||
|
||||
public static Value of(Tag tag)
|
||||
public static Value of(final Tag tag)
|
||||
{
|
||||
if (tag == null) return Value.NULL;
|
||||
if (tag == null)
|
||||
{
|
||||
return Value.NULL;
|
||||
}
|
||||
return new NBTSerializableValue(tag);
|
||||
}
|
||||
|
||||
public NBTSerializableValue(Supplier<Tag> tagSupplier)
|
||||
public NBTSerializableValue(final Supplier<Tag> tagSupplier)
|
||||
{
|
||||
nbtSupplier = tagSupplier;
|
||||
}
|
||||
|
||||
public static Value fromStack(ItemStack stack)
|
||||
public static Value fromStack(final ItemStack stack)
|
||||
{
|
||||
if (stack.hasTag())
|
||||
{
|
||||
NBTSerializableValue value = new NBTSerializableValue();
|
||||
final NBTSerializableValue value = new NBTSerializableValue();
|
||||
value.nbtSupplier = stack::getTag;
|
||||
return value;
|
||||
}
|
||||
return Value.NULL;
|
||||
}
|
||||
|
||||
public static String nameFromRegistryId(ResourceLocation id)
|
||||
public static String nameFromRegistryId(final ResourceLocation id)
|
||||
{
|
||||
if (id == null) // should be Value.NULL
|
||||
{
|
||||
return "";
|
||||
if (id.getNamespace().equals("minecraft"))
|
||||
return id.getPath();
|
||||
return id.toString();
|
||||
}
|
||||
return id.getNamespace().equals("minecraft") ? id.getPath() : id.toString();
|
||||
}
|
||||
|
||||
public static NBTSerializableValue parseString(String nbtString, boolean fail)
|
||||
public static NBTSerializableValue parseString(final String nbtString, final boolean fail)
|
||||
{
|
||||
Tag tag;
|
||||
final Tag tag;
|
||||
try
|
||||
{
|
||||
tag = (new TagParser(new StringReader(nbtString))).readValue();
|
||||
}
|
||||
catch (CommandSyntaxException e)
|
||||
catch (final CommandSyntaxException e)
|
||||
{
|
||||
if (fail) throw new InternalExpressionException("Incorrect NBT tag: "+ nbtString);
|
||||
if (fail)
|
||||
{
|
||||
throw new InternalExpressionException("Incorrect NBT tag: " + nbtString);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
NBTSerializableValue value = new NBTSerializableValue(tag);
|
||||
final NBTSerializableValue value = new NBTSerializableValue(tag);
|
||||
value.nbtString = null;
|
||||
return value;
|
||||
}
|
||||
@@ -136,7 +146,7 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa
|
||||
public Value clone()
|
||||
{
|
||||
// sets only nbttag, even if emtpy;
|
||||
NBTSerializableValue copy = new NBTSerializableValue(nbtTag);
|
||||
final NBTSerializableValue copy = new NBTSerializableValue(nbtTag);
|
||||
copy.nbtSupplier = this.nbtSupplier;
|
||||
copy.nbtString = this.nbtString;
|
||||
copy.owned = this.owned;
|
||||
@@ -146,59 +156,67 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa
|
||||
@Override
|
||||
public Value deepcopy()
|
||||
{
|
||||
NBTSerializableValue copy = (NBTSerializableValue) clone();
|
||||
final NBTSerializableValue copy = (NBTSerializableValue) clone();
|
||||
copy.owned = false;
|
||||
ensureOwnership();
|
||||
return copy;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value fromConstant() {
|
||||
public Value fromConstant()
|
||||
{
|
||||
return deepcopy();
|
||||
}
|
||||
|
||||
// stolen from HopperBlockEntity, adjusted for threaded operation
|
||||
public static Container getInventoryAt(ServerLevel world, BlockPos blockPos)
|
||||
public static Container getInventoryAt(final ServerLevel world, final BlockPos blockPos)
|
||||
{
|
||||
Container inventory = null;
|
||||
BlockState blockState = world.getBlockState(blockPos);
|
||||
Block block = blockState.getBlock();
|
||||
if (block instanceof WorldlyContainerHolder) {
|
||||
inventory = ((WorldlyContainerHolder)block).getContainer(blockState, world, blockPos);
|
||||
} else if (blockState.hasBlockEntity()) {
|
||||
BlockEntity blockEntity = BlockValue.getBlockEntity(world, blockPos);
|
||||
if (blockEntity instanceof Container) {
|
||||
inventory = (Container)blockEntity;
|
||||
if (inventory instanceof ChestBlockEntity && block instanceof ChestBlock) {
|
||||
inventory = ChestBlock.getContainer((ChestBlock)block, blockState, world, blockPos, true);
|
||||
final BlockState blockState = world.getBlockState(blockPos);
|
||||
final Block block = blockState.getBlock();
|
||||
if (block instanceof final WorldlyContainerHolder containerHolder)
|
||||
{
|
||||
inventory = containerHolder.getContainer(blockState, world, blockPos);
|
||||
}
|
||||
else if (blockState.hasBlockEntity())
|
||||
{
|
||||
final BlockEntity blockEntity = BlockValue.getBlockEntity(world, blockPos);
|
||||
if (blockEntity instanceof final Container inventoryHolder)
|
||||
{
|
||||
inventory = inventoryHolder;
|
||||
if (inventory instanceof ChestBlockEntity && block instanceof final ChestBlock chestBlock)
|
||||
{
|
||||
inventory = ChestBlock.getContainer(chestBlock, blockState, world, blockPos, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (inventory == null) {
|
||||
List<Entity> list = world.getEntities(
|
||||
(Entity)null, //TODO check this matches the correct method
|
||||
if (inventory == null)
|
||||
{
|
||||
final List<Entity> list = world.getEntities(
|
||||
(Entity) null, //TODO check this matches the correct method
|
||||
new AABB(
|
||||
blockPos.getX() - 0.5D, blockPos.getY() - 0.5D, blockPos.getZ() - 0.5D,
|
||||
blockPos.getX() + 0.5D, blockPos.getY() + 0.5D, blockPos.getZ() + 0.5D),
|
||||
EntitySelector.CONTAINER_ENTITY_SELECTOR
|
||||
);
|
||||
if (!list.isEmpty()) {
|
||||
inventory = (Container)list.get(world.random.nextInt(list.size()));
|
||||
if (!list.isEmpty())
|
||||
{
|
||||
inventory = (Container) list.get(world.random.nextInt(list.size()));
|
||||
}
|
||||
}
|
||||
|
||||
return inventory;
|
||||
}
|
||||
|
||||
public static InventoryLocator locateInventory(CarpetContext c, List<Value> params, int offset)
|
||||
public static InventoryLocator locateInventory(final CarpetContext c, final List<Value> params, int offset)
|
||||
{
|
||||
try
|
||||
{
|
||||
Value v1 = params.get(offset);
|
||||
if (v1.isNull())
|
||||
{
|
||||
offset ++;
|
||||
offset++;
|
||||
v1 = params.get(offset);
|
||||
}
|
||||
else if (v1 instanceof StringValue)
|
||||
@@ -206,23 +224,38 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa
|
||||
String strVal = v1.getString().toLowerCase(Locale.ROOT);
|
||||
if (strVal.equals("enderchest"))
|
||||
{
|
||||
Value v2 = params.get(1 + offset);
|
||||
ServerPlayer player = EntityValue.getPlayerByValue(c.server(), v2);
|
||||
if (player == null) throw new InternalExpressionException("enderchest inventory requires player argument");
|
||||
final Value v2 = params.get(1 + offset);
|
||||
final ServerPlayer player = EntityValue.getPlayerByValue(c.server(), v2);
|
||||
if (player == null)
|
||||
{
|
||||
throw new InternalExpressionException("enderchest inventory requires player argument");
|
||||
}
|
||||
return new InventoryLocator(player, player.blockPosition(), player.getEnderChestInventory(), offset + 2, true);
|
||||
}
|
||||
if (strVal.equals("equipment"))
|
||||
{
|
||||
Value v2 = params.get(1 + offset);
|
||||
if (!(v2 instanceof EntityValue)) throw new InternalExpressionException("Equipment inventory requires a living entity argument");
|
||||
Entity e = ((EntityValue) v2).getEntity();
|
||||
if (!(e instanceof LivingEntity)) throw new InternalExpressionException("Equipment inventory requires a living entity argument");
|
||||
return new InventoryLocator(e, e.blockPosition(), new EquipmentInventory((LivingEntity) e), offset + 2);
|
||||
final Value v2 = params.get(1 + offset);
|
||||
if (!(v2 instanceof final EntityValue ev))
|
||||
{
|
||||
throw new InternalExpressionException("Equipment inventory requires a living entity argument");
|
||||
}
|
||||
final Entity e = ev.getEntity();
|
||||
if (!(e instanceof final LivingEntity le))
|
||||
{
|
||||
throw new InternalExpressionException("Equipment inventory requires a living entity argument");
|
||||
}
|
||||
return new InventoryLocator(e, e.blockPosition(), new EquipmentInventory(le), offset + 2);
|
||||
}
|
||||
final boolean isEnder = strVal.startsWith("enderchest_");
|
||||
if (isEnder)
|
||||
{
|
||||
strVal = strVal.substring(11); // len("enderchest_")
|
||||
}
|
||||
final ServerPlayer player = c.server().getPlayerList().getPlayerByName(strVal);
|
||||
if (player == null)
|
||||
{
|
||||
throw new InternalExpressionException("String description of an inventory should either denote a player or player's enderchest");
|
||||
}
|
||||
boolean isEnder = strVal.startsWith("enderchest_");
|
||||
if (isEnder) strVal = strVal.substring(11); // len("enderchest_")
|
||||
ServerPlayer player = c.server().getPlayerList().getPlayerByName(strVal);
|
||||
if (player == null) throw new InternalExpressionException("String description of an inventory should either denote a player or player's enderchest");
|
||||
return new InventoryLocator(
|
||||
player,
|
||||
player.blockPosition(),
|
||||
@@ -231,149 +264,150 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa
|
||||
isEnder
|
||||
);
|
||||
}
|
||||
if (v1 instanceof EntityValue)
|
||||
if (v1 instanceof final EntityValue ev)
|
||||
{
|
||||
Container inv = null;
|
||||
Entity e = ((EntityValue) v1).getEntity();
|
||||
if (e instanceof Player pe) inv = pe.getInventory();
|
||||
else if (e instanceof Container) inv = (Container) e;
|
||||
else if (e instanceof InventoryCarrier io) inv = io.getInventory();
|
||||
else if (e instanceof InventoryBearerInterface ibi) inv = ibi.getCMInventory(); // horse only
|
||||
else if (e instanceof LivingEntity le) return new InventoryLocator(e, e.blockPosition(), new EquipmentInventory(le), offset+1);
|
||||
if (inv == null)
|
||||
return null;
|
||||
final Entity e = ev.getEntity();
|
||||
if (e instanceof final Player pe)
|
||||
{
|
||||
inv = pe.getInventory();
|
||||
}
|
||||
else if (e instanceof final Container container)
|
||||
{
|
||||
inv = container;
|
||||
}
|
||||
else if (e instanceof final InventoryCarrier io)
|
||||
{
|
||||
inv = io.getInventory();
|
||||
}
|
||||
else if (e instanceof final InventoryBearerInterface ibi)
|
||||
{
|
||||
inv = ibi.getCMInventory(); // horse only
|
||||
}
|
||||
else if (e instanceof final LivingEntity le)
|
||||
{
|
||||
return new InventoryLocator(e, e.blockPosition(), new EquipmentInventory(le), offset + 1);
|
||||
}
|
||||
return inv == null ? null : new InventoryLocator(e, e.blockPosition(), inv, offset + 1);
|
||||
|
||||
return new InventoryLocator(e, e.blockPosition(), inv, offset+1);
|
||||
}
|
||||
if (v1 instanceof BlockValue)
|
||||
if (v1 instanceof final BlockValue bv)
|
||||
{
|
||||
BlockPos pos = ((BlockValue) v1).getPos();
|
||||
final BlockPos pos = bv.getPos();
|
||||
if (pos == null)
|
||||
{
|
||||
throw new InternalExpressionException("Block to access inventory needs to be positioned in the world");
|
||||
Container inv = getInventoryAt(c.level(), pos);
|
||||
if (inv == null)
|
||||
return null;
|
||||
return new InventoryLocator(pos, pos, inv, offset+1);
|
||||
}
|
||||
final Container inv = getInventoryAt(c.level(), pos);
|
||||
return inv == null ? null : new InventoryLocator(pos, pos, inv, offset + 1);
|
||||
}
|
||||
if (v1 instanceof ListValue)
|
||||
if (v1 instanceof final ListValue lv)
|
||||
{
|
||||
List<Value> args = ((ListValue) v1).getItems();
|
||||
BlockPos pos = new BlockPos(
|
||||
final List<Value> args = lv.getItems();
|
||||
final BlockPos pos = new BlockPos(
|
||||
NumericValue.asNumber(args.get(0)).getDouble(),
|
||||
NumericValue.asNumber(args.get(1)).getDouble(),
|
||||
NumericValue.asNumber(args.get(2)).getDouble());
|
||||
Container inv = getInventoryAt(c.level(), pos);
|
||||
if (inv == null)
|
||||
return null;
|
||||
return new InventoryLocator(pos, pos, inv, offset+1);
|
||||
final Container inv = getInventoryAt(c.level(), pos);
|
||||
return inv == null ? null : new InventoryLocator(pos, pos, inv, offset + 1);
|
||||
}
|
||||
if (v1 instanceof ScreenValue screenValue)
|
||||
if (v1 instanceof final ScreenValue screenValue)
|
||||
{
|
||||
if(!screenValue.isOpen()) return null;
|
||||
return new InventoryLocator(screenValue.getPlayer(), screenValue.getPlayer().blockPosition(), screenValue.getInventory(), offset+1);
|
||||
return !screenValue.isOpen() ? null : new InventoryLocator(screenValue.getPlayer(), screenValue.getPlayer().blockPosition(), screenValue.getInventory(), offset + 1);
|
||||
}
|
||||
BlockPos pos = new BlockPos(
|
||||
final BlockPos pos = new BlockPos(
|
||||
NumericValue.asNumber(v1).getDouble(),
|
||||
NumericValue.asNumber(params.get(1 + offset)).getDouble(),
|
||||
NumericValue.asNumber(params.get(2 + offset)).getDouble());
|
||||
Container inv = getInventoryAt(c.level(), pos);
|
||||
if (inv == null)
|
||||
return null;
|
||||
return new InventoryLocator(pos, pos, inv, offset + 3);
|
||||
final Container inv = getInventoryAt(c.level(), pos);
|
||||
return inv == null ? null : new InventoryLocator(pos, pos, inv, offset + 3);
|
||||
}
|
||||
catch (IndexOutOfBoundsException e)
|
||||
catch (final IndexOutOfBoundsException e)
|
||||
{
|
||||
throw new InternalExpressionException("Inventory should be defined either by three coordinates, a block value, an entity, or a screen");
|
||||
}
|
||||
}
|
||||
|
||||
private static final Map<String,ItemInput> itemCache = new HashMap<>();
|
||||
private static final Map<String, ItemInput> itemCache = new HashMap<>();
|
||||
|
||||
public static ItemInput parseItem(String itemString, RegistryAccess regs)
|
||||
public static ItemInput parseItem(final String itemString, final RegistryAccess regs)
|
||||
{
|
||||
return parseItem(itemString, null, regs);
|
||||
}
|
||||
|
||||
public static ItemInput parseItem(String itemString, CompoundTag customTag, RegistryAccess regs)
|
||||
public static ItemInput parseItem(final String itemString, final CompoundTag customTag, final RegistryAccess regs)
|
||||
{
|
||||
try
|
||||
{
|
||||
ItemInput res = itemCache.get(itemString); // [SCARY SHIT] persistent caches over server reloads
|
||||
if (res != null)
|
||||
if (customTag == null)
|
||||
return res;
|
||||
else
|
||||
return new ItemInput(Holder.direct(res.getItem()), customTag);
|
||||
ItemParser.ItemResult parser = ItemParser.parseForItem(regs.lookupOrThrow(Registries.ITEM), new StringReader(itemString));
|
||||
{
|
||||
return customTag == null ? res : new ItemInput(Holder.direct(res.getItem()), customTag);
|
||||
}
|
||||
final ItemParser.ItemResult parser = ItemParser.parseForItem(regs.lookupOrThrow(Registries.ITEM), new StringReader(itemString));
|
||||
res = new ItemInput(parser.item(), parser.nbt());
|
||||
|
||||
itemCache.put(itemString, res);
|
||||
if (itemCache.size()>64000)
|
||||
if (itemCache.size() > 64000)
|
||||
{
|
||||
itemCache.clear();
|
||||
if (customTag == null)
|
||||
return res;
|
||||
else
|
||||
return new ItemInput(Holder.direct(res.getItem()), customTag);
|
||||
}
|
||||
return customTag == null ? res : new ItemInput(Holder.direct(res.getItem()), customTag);
|
||||
}
|
||||
catch (CommandSyntaxException e)
|
||||
catch (final CommandSyntaxException e)
|
||||
{
|
||||
throw new ThrowStatement(itemString, Throwables.UNKNOWN_ITEM);
|
||||
}
|
||||
}
|
||||
|
||||
public static int validateSlot(int slot, Container inv)
|
||||
public static int validateSlot(int slot, final Container inv)
|
||||
{
|
||||
int invSize = inv.getContainerSize();
|
||||
final int invSize = inv.getContainerSize();
|
||||
if (slot < 0)
|
||||
{
|
||||
slot = invSize + slot;
|
||||
if (slot < 0 || slot >= invSize)
|
||||
return inv.getContainerSize(); // outside of inventory
|
||||
return slot;
|
||||
}
|
||||
return slot < 0 || slot >= invSize ? inv.getContainerSize() : slot; // outside of inventory
|
||||
}
|
||||
|
||||
private static Value decodeSimpleTag(Tag t)
|
||||
private static Value decodeSimpleTag(final Tag t)
|
||||
{
|
||||
if (t instanceof NumericTag)
|
||||
if (t instanceof final NumericTag number)
|
||||
{
|
||||
if (t instanceof LongTag || t instanceof IntTag) // short and byte will never exceed float's precision, even int won't
|
||||
{
|
||||
return NumericValue.of(((NumericTag) t).getAsLong());
|
||||
}
|
||||
return NumericValue.of(((NumericTag) t).getAsNumber());
|
||||
// short and byte will never exceed float's precision, even int won't
|
||||
return t instanceof LongTag || t instanceof IntTag ? NumericValue.of(number.getAsLong()) : NumericValue.of(number.getAsNumber());
|
||||
}
|
||||
if (t instanceof StringTag)
|
||||
return StringValue.of(t.getAsString());
|
||||
if (t instanceof EndTag)
|
||||
return Value.NULL;
|
||||
|
||||
throw new InternalExpressionException("How did we get here: Unknown nbt element class: "+t.getType().getName());
|
||||
|
||||
}
|
||||
|
||||
private static Value decodeTag(Tag t)
|
||||
{
|
||||
if (t instanceof CompoundTag || t instanceof CollectionTag)
|
||||
return new NBTSerializableValue(() -> t);
|
||||
return decodeSimpleTag(t);
|
||||
}
|
||||
|
||||
private static Value decodeTagDeep(Tag t)
|
||||
{
|
||||
if (t instanceof CompoundTag)
|
||||
{
|
||||
Map<Value, Value> pairs = new HashMap<>();
|
||||
CompoundTag ctag = (CompoundTag)t;
|
||||
for (String key: ctag.getAllKeys())
|
||||
return StringValue.of(t.getAsString());
|
||||
}
|
||||
if (t instanceof EndTag)
|
||||
{
|
||||
return Value.NULL;
|
||||
}
|
||||
throw new InternalExpressionException("How did we get here: Unknown nbt element class: " + t.getType().getName());
|
||||
}
|
||||
|
||||
private static Value decodeTag(final Tag t)
|
||||
{
|
||||
return t instanceof CompoundTag || t instanceof CollectionTag ? new NBTSerializableValue(() -> t) : decodeSimpleTag(t);
|
||||
}
|
||||
|
||||
private static Value decodeTagDeep(final Tag t)
|
||||
{
|
||||
if (t instanceof final CompoundTag ctag)
|
||||
{
|
||||
final Map<Value, Value> pairs = new HashMap<>();
|
||||
for (final String key : ctag.getAllKeys())
|
||||
{
|
||||
pairs.put(new StringValue(key), decodeTagDeep(ctag.get(key)));
|
||||
}
|
||||
return MapValue.wrap(pairs);
|
||||
}
|
||||
if (t instanceof CollectionTag)
|
||||
if (t instanceof final CollectionTag<?> ltag)
|
||||
{
|
||||
List<Value> elems = new ArrayList<>();
|
||||
CollectionTag<?> ltag = (CollectionTag<?>)t;
|
||||
for (Tag elem: ltag)
|
||||
final List<Value> elems = new ArrayList<>();
|
||||
for (final Tag elem : ltag)
|
||||
{
|
||||
elems.add(decodeTagDeep(elem));
|
||||
}
|
||||
@@ -387,50 +421,64 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa
|
||||
return decodeTagDeep(this.getTag());
|
||||
}
|
||||
|
||||
public static Value fromValue(Value v)
|
||||
public static Value fromValue(final Value v)
|
||||
{
|
||||
if (v instanceof NBTSerializableValue)
|
||||
{
|
||||
return v;
|
||||
}
|
||||
if (v.isNull())
|
||||
{
|
||||
return Value.NULL;
|
||||
}
|
||||
return NBTSerializableValue.parseString(v.getString(), true);
|
||||
}
|
||||
|
||||
public Tag getTag()
|
||||
{
|
||||
if (nbtTag == null)
|
||||
{
|
||||
nbtTag = nbtSupplier.get();
|
||||
}
|
||||
return nbtTag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o)
|
||||
{
|
||||
if (o instanceof NBTSerializableValue)
|
||||
return getTag().equals(((NBTSerializableValue) o).getTag());
|
||||
return super.equals(o);
|
||||
return o instanceof final NBTSerializableValue nbtsv ? getTag().equals(nbtsv.getTag()) : super.equals(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString()
|
||||
{
|
||||
if (nbtString == null)
|
||||
{
|
||||
nbtString = getTag().toString();
|
||||
}
|
||||
return nbtString;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getBoolean()
|
||||
{
|
||||
Tag tag = getTag();
|
||||
if (tag instanceof CompoundTag)
|
||||
return !((CompoundTag) tag).isEmpty();
|
||||
if (tag instanceof CollectionTag)
|
||||
return !((CollectionTag<?>) tag).isEmpty();
|
||||
if (tag instanceof NumericTag)
|
||||
return ((NumericTag) tag).getAsDouble()!=0.0;
|
||||
final Tag tag = getTag();
|
||||
if (tag instanceof final CompoundTag ctag)
|
||||
{
|
||||
return !(ctag).isEmpty();
|
||||
}
|
||||
if (tag instanceof final CollectionTag<?> ltag)
|
||||
{
|
||||
return !(ltag).isEmpty();
|
||||
}
|
||||
if (tag instanceof final NumericTag number)
|
||||
{
|
||||
return number.getAsDouble() != 0.0;
|
||||
}
|
||||
if (tag instanceof StringTag)
|
||||
{
|
||||
return !tag.getAsString().isEmpty();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -441,86 +489,89 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa
|
||||
ensureOwnership();
|
||||
return (CompoundTag) getTag();
|
||||
}
|
||||
catch (ClassCastException e)
|
||||
catch (final ClassCastException e)
|
||||
{
|
||||
throw new InternalExpressionException(getString()+" is not a valid compound tag");
|
||||
throw new InternalExpressionException(getString() + " is not a valid compound tag");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean put(Value where, Value value)
|
||||
public boolean put(final Value where, final Value value)
|
||||
{
|
||||
return put(where, value, new StringValue("replace"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean put(Value where, Value value, Value conditions)
|
||||
public boolean put(final Value where, final Value value, final Value conditions)
|
||||
{
|
||||
/// WIP
|
||||
ensureOwnership();
|
||||
NbtPathArgument.NbtPath path = cachePath(where.getString());
|
||||
Tag tagToInsert = value instanceof NBTSerializableValue ?
|
||||
((NBTSerializableValue) value).getTag() :
|
||||
new NBTSerializableValue(value.getString()).getTag();
|
||||
boolean modifiedTag;
|
||||
if (conditions instanceof NumericValue)
|
||||
final NbtPathArgument.NbtPath path = cachePath(where.getString());
|
||||
final Tag tagToInsert = value instanceof final NBTSerializableValue nbtsv
|
||||
? nbtsv.getTag()
|
||||
: new NBTSerializableValue(value.getString()).getTag();
|
||||
final boolean modifiedTag;
|
||||
if (conditions instanceof final NumericValue number)
|
||||
{
|
||||
modifiedTag = modify_insert((int)((NumericValue) conditions).getLong(), path, tagToInsert);
|
||||
modifiedTag = modifyInsert((int) number.getLong(), path, tagToInsert);
|
||||
}
|
||||
else
|
||||
{
|
||||
String ops = conditions.getString();
|
||||
final String ops = conditions.getString();
|
||||
if (ops.equalsIgnoreCase("merge"))
|
||||
{
|
||||
modifiedTag = modify_merge(path, tagToInsert);
|
||||
modifiedTag = modifyMerge(path, tagToInsert);
|
||||
}
|
||||
else if (ops.equalsIgnoreCase("replace"))
|
||||
{
|
||||
modifiedTag = modify_replace(path, tagToInsert);
|
||||
modifiedTag = modifyReplace(path, tagToInsert);
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (modifiedTag) dirty();
|
||||
if (modifiedTag)
|
||||
{
|
||||
dirty();
|
||||
}
|
||||
return modifiedTag;
|
||||
}
|
||||
|
||||
|
||||
|
||||
private boolean modify_insert(int index, NbtPathArgument.NbtPath nbtPath, Tag newElement)
|
||||
private boolean modifyInsert(final int index, final NbtPathArgument.NbtPath nbtPath, final Tag newElement)
|
||||
{
|
||||
return modify_insert(index, nbtPath, newElement, this.getTag());
|
||||
return modifyInsert(index, nbtPath, newElement, this.getTag());
|
||||
}
|
||||
|
||||
private boolean modify_insert(int index, NbtPathArgument.NbtPath nbtPath, Tag newElement, Tag currentTag)
|
||||
private boolean modifyInsert(final int index, final NbtPathArgument.NbtPath nbtPath, final Tag newElement, final Tag currentTag)
|
||||
{
|
||||
Collection<Tag> targets;
|
||||
final Collection<Tag> targets;
|
||||
try
|
||||
{
|
||||
targets = nbtPath.getOrCreate(currentTag, ListTag::new);
|
||||
}
|
||||
catch (CommandSyntaxException e)
|
||||
catch (final CommandSyntaxException e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean modified = false;
|
||||
for (Tag target : targets)
|
||||
for (final Tag target : targets)
|
||||
{
|
||||
if (!(target instanceof CollectionTag))
|
||||
if (!(target instanceof final CollectionTag<?> targetList))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
try
|
||||
{
|
||||
CollectionTag<?> targetList = (CollectionTag<?>) target;
|
||||
if (!targetList.addTag(index < 0 ? targetList.size() + index + 1 : index, newElement.copy()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
modified = true;
|
||||
}
|
||||
catch (IndexOutOfBoundsException ignored)
|
||||
catch (final IndexOutOfBoundsException ignored)
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -528,68 +579,72 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa
|
||||
}
|
||||
|
||||
|
||||
private boolean modify_merge(NbtPathArgument.NbtPath nbtPath, Tag replacement) //nbtPathArgumentType$NbtPath_1, list_1)
|
||||
private boolean modifyMerge(final NbtPathArgument.NbtPath nbtPath, final Tag replacement) //nbtPathArgumentType$NbtPath_1, list_1)
|
||||
{
|
||||
if (!(replacement instanceof CompoundTag))
|
||||
if (!(replacement instanceof final CompoundTag replacementCompound))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Tag ownTag = getTag();
|
||||
final Tag ownTag = getTag();
|
||||
try
|
||||
{
|
||||
for (Tag target : nbtPath.getOrCreate(ownTag, CompoundTag::new))
|
||||
for (final Tag target : nbtPath.getOrCreate(ownTag, CompoundTag::new))
|
||||
{
|
||||
if (!(target instanceof CompoundTag))
|
||||
if (!(target instanceof final CompoundTag targetCompound))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
((CompoundTag) target).merge((CompoundTag) replacement);
|
||||
targetCompound.merge(replacementCompound);
|
||||
}
|
||||
}
|
||||
catch (CommandSyntaxException ignored)
|
||||
catch (final CommandSyntaxException ignored)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean modify_replace(NbtPathArgument.NbtPath nbtPath, Tag replacement) //nbtPathArgumentType$NbtPath_1, list_1)
|
||||
private boolean modifyReplace(final NbtPathArgument.NbtPath nbtPath, final Tag replacement) //nbtPathArgumentType$NbtPath_1, list_1)
|
||||
{
|
||||
Tag tag = getTag();
|
||||
String pathText = nbtPath.toString();
|
||||
final Tag tag = getTag();
|
||||
final String pathText = nbtPath.toString();
|
||||
if (pathText.endsWith("]")) // workaround for array replacement or item in the array replacement
|
||||
{
|
||||
if (nbtPath.remove(tag)==0)
|
||||
if (nbtPath.remove(tag) == 0)
|
||||
{
|
||||
return false;
|
||||
Pattern pattern = Pattern.compile("\\[[^\\[]*]$");
|
||||
Matcher matcher = pattern.matcher(pathText);
|
||||
}
|
||||
final Pattern pattern = Pattern.compile("\\[[^\\[]*]$");
|
||||
final Matcher matcher = pattern.matcher(pathText);
|
||||
if (!matcher.find()) // malformed path
|
||||
{
|
||||
return false;
|
||||
}
|
||||
String arrAccess = matcher.group();
|
||||
int pos;
|
||||
if (arrAccess.length()==2) // we just removed entire array
|
||||
final String arrAccess = matcher.group();
|
||||
final int pos;
|
||||
if (arrAccess.length() == 2) // we just removed entire array
|
||||
{
|
||||
pos = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
pos = Integer.parseInt(arrAccess.substring(1, arrAccess.length() - 1));
|
||||
}
|
||||
catch (NumberFormatException e)
|
||||
catch (final NumberFormatException e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
NbtPathArgument.NbtPath newPath = cachePath(pathText.substring(0, pathText.length()-arrAccess.length()));
|
||||
return modify_insert(pos,newPath,replacement, tag);
|
||||
final NbtPathArgument.NbtPath newPath = cachePath(pathText.substring(0, pathText.length() - arrAccess.length()));
|
||||
return modifyInsert(pos, newPath, replacement, tag);
|
||||
}
|
||||
try
|
||||
{
|
||||
nbtPath.set(tag, replacement);
|
||||
}
|
||||
catch (CommandSyntaxException e)
|
||||
catch (final CommandSyntaxException e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -597,25 +652,31 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value get(Value value)
|
||||
public Value get(final Value value)
|
||||
{
|
||||
String valString = value.getString();
|
||||
NbtPathArgument.NbtPath path = cachePath(valString);
|
||||
final String valString = value.getString();
|
||||
final NbtPathArgument.NbtPath path = cachePath(valString);
|
||||
try
|
||||
{
|
||||
List<Tag> tags = path.get(getTag());
|
||||
if (tags.size()==0)
|
||||
final List<Tag> tags = path.get(getTag());
|
||||
if (tags.isEmpty())
|
||||
{
|
||||
return Value.NULL;
|
||||
if (tags.size()==1 && !valString.endsWith("[]"))
|
||||
}
|
||||
if (tags.size() == 1 && !valString.endsWith("[]"))
|
||||
{
|
||||
return NBTSerializableValue.decodeTag(tags.get(0));
|
||||
}
|
||||
return ListValue.wrap(tags.stream().map(NBTSerializableValue::decodeTag));
|
||||
}
|
||||
catch (CommandSyntaxException ignored) { }
|
||||
catch (final CommandSyntaxException ignored)
|
||||
{
|
||||
}
|
||||
return Value.NULL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean has(Value where)
|
||||
public boolean has(final Value where)
|
||||
{
|
||||
return cachePath(where.getString()).countMatching(getTag()) > 0;
|
||||
}
|
||||
@@ -637,11 +698,11 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean delete(Value where)
|
||||
public boolean delete(final Value where)
|
||||
{
|
||||
NbtPathArgument.NbtPath path = cachePath(where.getString());
|
||||
final NbtPathArgument.NbtPath path = cachePath(where.getString());
|
||||
ensureOwnership();
|
||||
int removed = path.remove(getTag());
|
||||
final int removed = path.remove(getTag());
|
||||
if (removed > 0)
|
||||
{
|
||||
dirty();
|
||||
@@ -650,30 +711,36 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa
|
||||
return false;
|
||||
}
|
||||
|
||||
public static record InventoryLocator(Object owner, BlockPos position, Container inventory, int offset, boolean isEnder)
|
||||
public record InventoryLocator(Object owner, BlockPos position, Container inventory, int offset,
|
||||
boolean isEnder)
|
||||
{
|
||||
InventoryLocator(Object owner, BlockPos pos, Container i, int o)
|
||||
InventoryLocator(final Object owner, final BlockPos pos, final Container i, final int o)
|
||||
{
|
||||
this(owner, pos, i, o, false);
|
||||
}
|
||||
}
|
||||
|
||||
private static Map<String, NbtPathArgument.NbtPath> pathCache = new HashMap<>();
|
||||
private static NbtPathArgument.NbtPath cachePath(String arg)
|
||||
private static final Map<String, NbtPathArgument.NbtPath> pathCache = new HashMap<>();
|
||||
|
||||
private static NbtPathArgument.NbtPath cachePath(final String arg)
|
||||
{
|
||||
NbtPathArgument.NbtPath res = pathCache.get(arg);
|
||||
if (res != null)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
try
|
||||
{
|
||||
res = NbtPathArgument.nbtPath().parse(new StringReader(arg));
|
||||
}
|
||||
catch (CommandSyntaxException exc)
|
||||
catch (final CommandSyntaxException exc)
|
||||
{
|
||||
throw new InternalExpressionException("Incorrect nbt path: "+arg);
|
||||
throw new InternalExpressionException("Incorrect nbt path: " + arg);
|
||||
}
|
||||
if (pathCache.size() > 1024)
|
||||
{
|
||||
pathCache.clear();
|
||||
}
|
||||
pathCache.put(arg, res);
|
||||
return res;
|
||||
}
|
||||
@@ -686,9 +753,12 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa
|
||||
|
||||
|
||||
@Override
|
||||
public Tag toTag(boolean force)
|
||||
public Tag toTag(final boolean force)
|
||||
{
|
||||
if (!force) throw new NBTSerializableValue.IncompatibleTypeException(this);
|
||||
if (!force)
|
||||
{
|
||||
throw new NBTSerializableValue.IncompatibleTypeException(this);
|
||||
}
|
||||
ensureOwnership();
|
||||
return getTag();
|
||||
}
|
||||
@@ -696,9 +766,11 @@ public class NBTSerializableValue extends Value implements ContainerValueInterfa
|
||||
public static class IncompatibleTypeException extends RuntimeException
|
||||
{
|
||||
public final Value val;
|
||||
public IncompatibleTypeException(Value val)
|
||||
|
||||
public IncompatibleTypeException(final Value val)
|
||||
{
|
||||
this.val = val;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package carpet.script.value;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import net.minecraft.nbt.StringTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import com.google.gson.JsonElement;
|
||||
@@ -9,6 +10,7 @@ import com.google.gson.JsonNull;
|
||||
public class NullValue extends NumericValue // TODO check nonsingleton code
|
||||
{
|
||||
public static final NullValue NULL = new NullValue();
|
||||
|
||||
@Override
|
||||
public String getString()
|
||||
{
|
||||
@@ -32,41 +34,45 @@ public class NullValue extends NumericValue // TODO check nonsingleton code
|
||||
{
|
||||
return new NullValue();
|
||||
}
|
||||
protected NullValue() {super(0);}
|
||||
|
||||
protected NullValue()
|
||||
{
|
||||
super(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o)
|
||||
{
|
||||
if (o instanceof Value value)
|
||||
{
|
||||
return value.isNull();
|
||||
}
|
||||
return false;
|
||||
return o instanceof Value value && value.isNull();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value slice(long fromDesc, Long toDesc) {
|
||||
public Value slice(final long fromDesc, final Long toDesc)
|
||||
{
|
||||
return Value.NULL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NumericValue opposite() {
|
||||
public NumericValue opposite()
|
||||
{
|
||||
return Value.NULL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int length() {
|
||||
public int length()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Value o)
|
||||
public int compareTo(final Value o)
|
||||
{
|
||||
return o.isNull() ? 0 : -1;
|
||||
return o.isNull() ? 0 : -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value in(Value value) {
|
||||
public Value in(final Value value)
|
||||
{
|
||||
return Value.NULL;
|
||||
}
|
||||
|
||||
@@ -83,15 +89,19 @@ public class NullValue extends NumericValue // TODO check nonsingleton code
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tag toTag(boolean force)
|
||||
public Tag toTag(final boolean force)
|
||||
{
|
||||
if (!force) throw new NBTSerializableValue.IncompatibleTypeException(this);
|
||||
if (!force)
|
||||
{
|
||||
throw new NBTSerializableValue.IncompatibleTypeException(this);
|
||||
}
|
||||
return StringTag.valueOf("null");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value split(Value delimiter) {
|
||||
return ListValue.wrap(new ArrayList<Value>());
|
||||
public Value split(final Value delimiter)
|
||||
{
|
||||
return ListValue.wrap(new ArrayList<>());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -101,7 +111,8 @@ public class NullValue extends NumericValue // TODO check nonsingleton code
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNull() {
|
||||
public boolean isNull()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import java.math.BigDecimal;
|
||||
import java.math.MathContext;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.Locale;
|
||||
|
||||
import net.minecraft.nbt.DoubleTag;
|
||||
import net.minecraft.nbt.IntTag;
|
||||
import net.minecraft.nbt.LongTag;
|
||||
@@ -21,28 +22,41 @@ public class NumericValue extends Value
|
||||
{
|
||||
private final double value;
|
||||
private Long longValue;
|
||||
private final static double epsilon = abs(32*((7*0.1)*10-7));
|
||||
private final static MathContext displayRounding = new MathContext(12, RoundingMode.HALF_EVEN);
|
||||
private static final double epsilon = abs(32 * ((7 * 0.1) * 10 - 7));
|
||||
private static final MathContext displayRounding = new MathContext(12, RoundingMode.HALF_EVEN);
|
||||
|
||||
public static NumericValue asNumber(Value v1, String id)
|
||||
public static NumericValue asNumber(final Value v1, final String id)
|
||||
{
|
||||
if (!(v1 instanceof NumericValue))
|
||||
throw new InternalExpressionException("Argument "+id+" has to be of a numeric type");
|
||||
return ((NumericValue) v1);
|
||||
if (v1 instanceof final NumericValue nv)
|
||||
{
|
||||
return nv;
|
||||
}
|
||||
throw new InternalExpressionException("Argument " + id + " has to be of a numeric type");
|
||||
}
|
||||
|
||||
public static NumericValue asNumber(Value v1)
|
||||
public static NumericValue asNumber(final Value v1)
|
||||
{
|
||||
if (!(v1 instanceof NumericValue))
|
||||
throw new InternalExpressionException("Operand has to be of a numeric type");
|
||||
return ((NumericValue) v1);
|
||||
if (v1 instanceof final NumericValue nv)
|
||||
{
|
||||
return nv;
|
||||
}
|
||||
throw new InternalExpressionException("Operand has to be of a numeric type");
|
||||
}
|
||||
|
||||
public static <T extends Number> Value of(T value)
|
||||
public static <T extends Number> Value of(final T value)
|
||||
{
|
||||
if (value == null) return Value.NULL;
|
||||
if (value.doubleValue() == value.longValue()) return new NumericValue(value.longValue());
|
||||
if (value instanceof Float) return new NumericValue(0.000_001D * Math.round(1_000_000.0D*value.doubleValue()));
|
||||
if (value == null)
|
||||
{
|
||||
return Value.NULL;
|
||||
}
|
||||
if (value.doubleValue() == value.longValue())
|
||||
{
|
||||
return new NumericValue(value.longValue());
|
||||
}
|
||||
if (value instanceof Float)
|
||||
{
|
||||
return new NumericValue(0.000_001D * Math.round(1_000_000.0D * value.doubleValue()));
|
||||
}
|
||||
return new NumericValue(value.doubleValue());
|
||||
}
|
||||
|
||||
@@ -56,30 +70,33 @@ public class NumericValue extends Value
|
||||
}
|
||||
try
|
||||
{
|
||||
if (Double.isInfinite(value)) return (value>0)?"INFINITY":"-INFINITY";
|
||||
if (Double.isNaN(value)) return "NaN";
|
||||
if (abs(value) < epsilon) return (signum(value) < 0)?"-0":"0"; //zero rounding fails with big decimals
|
||||
if (Double.isInfinite(value))
|
||||
{
|
||||
return (value > 0) ? "INFINITY" : "-INFINITY";
|
||||
}
|
||||
if (Double.isNaN(value))
|
||||
{
|
||||
return "NaN";
|
||||
}
|
||||
if (abs(value) < epsilon)
|
||||
{
|
||||
return (signum(value) < 0) ? "-0" : "0"; //zero rounding fails with big decimals
|
||||
}
|
||||
// dobules have 16 point precision, 12 is plenty to display
|
||||
return BigDecimal.valueOf(value).round(displayRounding).stripTrailingZeros().toPlainString();
|
||||
}
|
||||
catch (NumberFormatException exc)
|
||||
catch (final NumberFormatException exc)
|
||||
{
|
||||
throw new InternalExpressionException("Incorrect number format for "+value);
|
||||
throw new InternalExpressionException("Incorrect number format for " + value);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrettyString()
|
||||
{
|
||||
|
||||
if (longValue!= null || getDouble() == (double)getLong())
|
||||
{
|
||||
return Long.toString(getLong());
|
||||
}
|
||||
else
|
||||
{
|
||||
return String.format(Locale.ROOT, "%.1f..", getDouble());
|
||||
}
|
||||
return longValue != null || getDouble() == getLong()
|
||||
? Long.toString(getLong())
|
||||
: String.format(Locale.ROOT, "%.1f..", getDouble());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -87,80 +104,62 @@ public class NumericValue extends Value
|
||||
{
|
||||
return abs(value) > epsilon;
|
||||
}
|
||||
|
||||
public double getDouble()
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
public float getFloat()
|
||||
{
|
||||
return (float) value;
|
||||
}
|
||||
|
||||
private static long floor(double double_1) {
|
||||
long int_1 = (long)double_1;
|
||||
return double_1 < (double)int_1 ? int_1 - 1 : int_1;
|
||||
private static long floor(final double v)
|
||||
{
|
||||
final long invValue = (long) v;
|
||||
return v < invValue ? invValue - 1 : invValue;
|
||||
}
|
||||
|
||||
public long getLong()
|
||||
{
|
||||
if (longValue != null) return longValue;
|
||||
return floor((value+epsilon));
|
||||
return longValue != null ? longValue : Long.valueOf(floor((value + epsilon)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value add(Value v)
|
||||
public Value add(final Value v)
|
||||
{ // TODO test if definintn add(NumericVlaue) woud solve the casting
|
||||
if (v instanceof NumericValue nv)
|
||||
if (v instanceof final NumericValue nv)
|
||||
{
|
||||
if (longValue != null && nv.longValue != null)
|
||||
{
|
||||
return new NumericValue(longValue+nv.longValue);
|
||||
}
|
||||
return new NumericValue(value + nv.value);
|
||||
return new NumericValue(longValue != null && nv.longValue != null ? (longValue + nv.longValue) : (value + nv.value));
|
||||
}
|
||||
return super.add(v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value subtract(Value v) { // TODO test if definintn add(NumericVlaue) woud solve the casting
|
||||
if (v instanceof NumericValue nv)
|
||||
public Value subtract(final Value v)
|
||||
{ // TODO test if definintn add(NumericVlaue) woud solve the casting
|
||||
if (v instanceof final NumericValue nv)
|
||||
{
|
||||
if (longValue != null && nv.longValue != null)
|
||||
{
|
||||
return new NumericValue(longValue-nv.longValue);
|
||||
}
|
||||
return new NumericValue(value - nv.value);
|
||||
return new NumericValue(longValue != null && nv.longValue != null ? (longValue - nv.longValue) : (value - nv.value));
|
||||
}
|
||||
return super.subtract(v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value multiply(Value v)
|
||||
public Value multiply(final Value v)
|
||||
{
|
||||
if (v instanceof NumericValue nv)
|
||||
if (v instanceof final NumericValue nv)
|
||||
{
|
||||
if (longValue != null && nv.longValue != null)
|
||||
{
|
||||
return new NumericValue(longValue*nv.longValue);
|
||||
}
|
||||
return new NumericValue(value * nv.value);
|
||||
return new NumericValue(longValue != null && nv.longValue != null ? (longValue * nv.longValue) : (value * nv.value));
|
||||
}
|
||||
if (v instanceof ListValue)
|
||||
{
|
||||
return v.multiply(this);
|
||||
}
|
||||
return new StringValue(StringUtils.repeat(v.getString(), (int) getLong()));
|
||||
return v instanceof ListValue ? v.multiply(this) : new StringValue(StringUtils.repeat(v.getString(), (int) getLong()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value divide(Value v)
|
||||
public Value divide(final Value v)
|
||||
{
|
||||
//if (1+2==3) throw new ArithmeticException("Booyah");
|
||||
if (v instanceof NumericValue)
|
||||
{
|
||||
return new NumericValue(getDouble() / ((NumericValue) v).getDouble() );
|
||||
}
|
||||
return super.divide(v);
|
||||
return v instanceof final NumericValue nv ? new NumericValue(getDouble() / nv.getDouble()) : super.divide(v);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -170,64 +169,68 @@ public class NumericValue extends Value
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Value o)
|
||||
public int compareTo(final Value o)
|
||||
{
|
||||
if (o.isNull())
|
||||
{
|
||||
return -o.compareTo(this);
|
||||
}
|
||||
if (o instanceof NumericValue no)
|
||||
if (o instanceof final NumericValue no)
|
||||
{
|
||||
if (longValue != null && no.longValue != null)
|
||||
return longValue.compareTo(no.longValue);
|
||||
return Double.compare(value, no.value);
|
||||
return longValue != null && no.longValue != null ? longValue.compareTo(no.longValue) : Double.compare(value, no.value);
|
||||
}
|
||||
return getString().compareTo(o.getString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o)
|
||||
public boolean equals(final Object o)
|
||||
{
|
||||
if (o instanceof Value value) {
|
||||
if (value.isNull()) {
|
||||
if (o instanceof final Value otherValue)
|
||||
{
|
||||
if (otherValue.isNull())
|
||||
{
|
||||
return o.equals(this);
|
||||
}
|
||||
if (o instanceof NumericValue no) {
|
||||
if (longValue != null && no.longValue != null)
|
||||
return longValue.equals(no.longValue);
|
||||
return !this.subtract(no).getBoolean();
|
||||
if (o instanceof final NumericValue no)
|
||||
{
|
||||
return longValue != null && no.longValue != null ? longValue.equals(no.longValue) : !this.subtract(no).getBoolean();
|
||||
}
|
||||
return super.equals(o);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public NumericValue(double value)
|
||||
public NumericValue(final double value)
|
||||
{
|
||||
this.value = value;
|
||||
}
|
||||
private NumericValue(double value, Long longValue)
|
||||
|
||||
private NumericValue(final double value, final Long longValue)
|
||||
{
|
||||
this.value = value;
|
||||
this.longValue = longValue;
|
||||
}
|
||||
|
||||
public NumericValue(String value)
|
||||
public NumericValue(final String value)
|
||||
{
|
||||
BigDecimal decimal = new BigDecimal(value);
|
||||
final BigDecimal decimal = new BigDecimal(value);
|
||||
if (decimal.stripTrailingZeros().scale() <= 0)
|
||||
{
|
||||
try
|
||||
{
|
||||
longValue = decimal.longValueExact();
|
||||
}
|
||||
catch (ArithmeticException ignored) {}
|
||||
catch (final ArithmeticException ignored)
|
||||
{
|
||||
}
|
||||
}
|
||||
this.value = decimal.doubleValue();
|
||||
}
|
||||
public NumericValue(long value)
|
||||
|
||||
public NumericValue(final long value)
|
||||
{
|
||||
this.longValue = value;
|
||||
this.value = (double)value;
|
||||
this.value = (double) value;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -257,31 +260,34 @@ public class NumericValue extends Value
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
if (longValue!= null || Math.abs(Math.floor(value + 0.5D)-value) < epsilon) // is sufficiently close to the integer value
|
||||
return Long.hashCode(getLong());
|
||||
return Double.hashCode(value);
|
||||
// is sufficiently close to the integer value
|
||||
return longValue != null || Math.abs(Math.floor(value + 0.5D) - value) < epsilon ? Long.hashCode(getLong()) : Double.hashCode(value);
|
||||
}
|
||||
|
||||
|
||||
public int getInt()
|
||||
{
|
||||
return (int)getLong();
|
||||
return (int) getLong();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tag toTag(boolean force)
|
||||
public Tag toTag(final boolean force)
|
||||
{
|
||||
if (longValue != null)
|
||||
{
|
||||
if (abs(longValue) < Integer.MAX_VALUE-2)
|
||||
return IntTag.valueOf((int)(long)longValue);
|
||||
if (abs(longValue) < Integer.MAX_VALUE - 2)
|
||||
{
|
||||
return IntTag.valueOf((int) (long) longValue);
|
||||
}
|
||||
return LongTag.valueOf(longValue);
|
||||
}
|
||||
long lv = getLong();
|
||||
if (value == (double)lv)
|
||||
final long lv = getLong();
|
||||
if (value == (double) lv)
|
||||
{
|
||||
if (abs(value) < Integer.MAX_VALUE-2)
|
||||
return IntTag.valueOf((int)lv);
|
||||
if (abs(value) < Integer.MAX_VALUE - 2)
|
||||
{
|
||||
return IntTag.valueOf((int) lv);
|
||||
}
|
||||
return LongTag.valueOf(getLong());
|
||||
}
|
||||
else
|
||||
@@ -294,35 +300,35 @@ public class NumericValue extends Value
|
||||
public JsonElement toJson()
|
||||
{
|
||||
if (longValue != null)
|
||||
{
|
||||
return new JsonPrimitive(longValue);
|
||||
long lv = getLong();
|
||||
if (value == (double)lv)
|
||||
{
|
||||
return new JsonPrimitive(getLong());
|
||||
}
|
||||
else
|
||||
{
|
||||
return new JsonPrimitive(value);
|
||||
}
|
||||
final long lv = getLong();
|
||||
return new JsonPrimitive(value == lv ? getLong() : value);
|
||||
}
|
||||
|
||||
public NumericValue opposite() {
|
||||
if (longValue != null) return new NumericValue(-longValue);
|
||||
return new NumericValue(-value);
|
||||
public NumericValue opposite()
|
||||
{
|
||||
return new NumericValue(longValue != null ? -longValue : -value);
|
||||
}
|
||||
|
||||
public boolean isInteger()
|
||||
{
|
||||
return longValue!= null || getDouble() == (double)getLong();
|
||||
return longValue != null || getDouble() == getLong();
|
||||
}
|
||||
|
||||
public Value mod(NumericValue n2)
|
||||
public Value mod(final NumericValue n2)
|
||||
{
|
||||
if (this.longValue != null && n2.longValue != null)
|
||||
{
|
||||
return new NumericValue(Math.floorMod(longValue, n2.longValue));
|
||||
double x = value;
|
||||
double y = n2.value;
|
||||
if (y == 0) throw new ArithmeticException("Division by zero");
|
||||
return new NumericValue( x - Math.floor(x / y) * y);
|
||||
}
|
||||
final double x = value;
|
||||
final double y = n2.value;
|
||||
if (y == 0)
|
||||
{
|
||||
throw new ArithmeticException("Division by zero");
|
||||
}
|
||||
return new NumericValue(x - Math.floor(x / y) * y);
|
||||
}
|
||||
}
|
||||
@@ -12,11 +12,13 @@ import carpet.script.exception.InternalExpressionException;
|
||||
import carpet.script.exception.InvalidCallbackException;
|
||||
import carpet.script.exception.ThrowStatement;
|
||||
import carpet.script.exception.Throwables;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.OptionalInt;
|
||||
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.ListTag;
|
||||
@@ -29,37 +31,14 @@ import net.minecraft.world.SimpleContainer;
|
||||
import net.minecraft.world.SimpleMenuProvider;
|
||||
import net.minecraft.world.entity.player.Inventory;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.inventory.AbstractContainerMenu;
|
||||
import net.minecraft.world.inventory.AbstractFurnaceMenu;
|
||||
import net.minecraft.world.inventory.AnvilMenu;
|
||||
import net.minecraft.world.inventory.BeaconMenu;
|
||||
import net.minecraft.world.inventory.BlastFurnaceMenu;
|
||||
import net.minecraft.world.inventory.BrewingStandMenu;
|
||||
import net.minecraft.world.inventory.CartographyTableMenu;
|
||||
import net.minecraft.world.inventory.ChestMenu;
|
||||
import net.minecraft.world.inventory.ClickType;
|
||||
import net.minecraft.world.inventory.ContainerListener;
|
||||
import net.minecraft.world.inventory.CraftingMenu;
|
||||
import net.minecraft.world.inventory.DataSlot;
|
||||
import net.minecraft.world.inventory.EnchantmentMenu;
|
||||
import net.minecraft.world.inventory.FurnaceMenu;
|
||||
import net.minecraft.world.inventory.GrindstoneMenu;
|
||||
import net.minecraft.world.inventory.HopperMenu;
|
||||
import net.minecraft.world.inventory.LecternMenu;
|
||||
import net.minecraft.world.inventory.LoomMenu;
|
||||
import net.minecraft.world.inventory.MerchantMenu;
|
||||
import net.minecraft.world.inventory.ShulkerBoxMenu;
|
||||
import net.minecraft.world.inventory.SimpleContainerData;
|
||||
import net.minecraft.world.inventory.Slot;
|
||||
import net.minecraft.world.inventory.SmithingMenu;
|
||||
import net.minecraft.world.inventory.SmokerMenu;
|
||||
import net.minecraft.world.inventory.StonecutterMenu;
|
||||
import net.minecraft.world.inventory.*;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraft.world.item.crafting.Recipe;
|
||||
|
||||
import static net.minecraft.world.inventory.MenuType.*;
|
||||
|
||||
public class ScreenValue extends Value {
|
||||
public class ScreenValue extends Value
|
||||
{
|
||||
private AbstractContainerMenu screenHandler;
|
||||
private ScreenHandlerInventory inventory;
|
||||
|
||||
@@ -69,83 +48,96 @@ public class ScreenValue extends Value {
|
||||
private final String hostname;
|
||||
private final ServerPlayer player;
|
||||
|
||||
|
||||
public static Map<String,ScarpetScreenHandlerFactory> screenHandlerFactories;
|
||||
public static Map<String, ScarpetScreenHandlerFactory> screenHandlerFactories;
|
||||
|
||||
static
|
||||
{
|
||||
screenHandlerFactories = new HashMap<>();
|
||||
|
||||
screenHandlerFactories.put("anvil",(syncId, playerInventory) -> new AnvilMenu(syncId,playerInventory));
|
||||
screenHandlerFactories.put("beacon",(syncId, playerInventory) -> new BeaconMenu(syncId,playerInventory));
|
||||
screenHandlerFactories.put("blast_furnace",(syncId, playerInventory) -> new BlastFurnaceMenu(syncId,playerInventory));
|
||||
screenHandlerFactories.put("brewing_stand",(syncId, playerInventory) -> new BrewingStandMenu(syncId,playerInventory,new SimpleContainer(5),new SimpleContainerData(2)));
|
||||
screenHandlerFactories.put("cartography_table",(syncId, playerInventory) -> new CartographyTableMenu(syncId,playerInventory));
|
||||
screenHandlerFactories.put("crafting",(syncId, playerInventory) -> new CraftingMenu(syncId,playerInventory));
|
||||
screenHandlerFactories.put("enchantment",(syncId, playerInventory) -> new EnchantmentMenu(syncId,playerInventory));
|
||||
screenHandlerFactories.put("furnace",(syncId, playerInventory) -> new FurnaceMenu(syncId,playerInventory));
|
||||
screenHandlerFactories.put("generic_3x3",((syncId, playerInventory) -> new ChestMenu(GENERIC_3x3,syncId,playerInventory,new SimpleContainer(9),1)));
|
||||
screenHandlerFactories.put("generic_9x1",((syncId, playerInventory) -> new ChestMenu(GENERIC_9x1,syncId,playerInventory,new SimpleContainer(9),1)));
|
||||
screenHandlerFactories.put("generic_9x2",((syncId, playerInventory) -> new ChestMenu(GENERIC_9x2,syncId,playerInventory,new SimpleContainer(9*2),2)));
|
||||
screenHandlerFactories.put("generic_9x3",((syncId, playerInventory) -> new ChestMenu(GENERIC_9x3,syncId,playerInventory,new SimpleContainer(9*3),3)));
|
||||
screenHandlerFactories.put("generic_9x4",((syncId, playerInventory) -> new ChestMenu(GENERIC_9x4,syncId,playerInventory,new SimpleContainer(9*4),4)));
|
||||
screenHandlerFactories.put("generic_9x5",((syncId, playerInventory) -> new ChestMenu(GENERIC_9x5,syncId,playerInventory,new SimpleContainer(9*5),5)));
|
||||
screenHandlerFactories.put("generic_9x6",((syncId, playerInventory) -> new ChestMenu(GENERIC_9x6,syncId,playerInventory,new SimpleContainer(9*6),6)));
|
||||
screenHandlerFactories.put("grindstone",(syncId, playerInventory) -> new GrindstoneMenu(syncId,playerInventory));
|
||||
screenHandlerFactories.put("hopper",(syncId, playerInventory) -> new HopperMenu(syncId,playerInventory));
|
||||
screenHandlerFactories.put("lectern",(syncId, playerInventory) -> new LecternMenu(syncId,new SimpleContainer(1),new SimpleContainerData(1)));
|
||||
screenHandlerFactories.put("loom",(syncId, playerInventory) -> new LoomMenu(syncId,playerInventory));
|
||||
screenHandlerFactories.put("merchant",(syncId, playerInventory) -> new MerchantMenu(syncId,playerInventory));
|
||||
screenHandlerFactories.put("shulker_box",(syncId, playerInventory) -> new ShulkerBoxMenu(syncId,playerInventory,new SimpleContainer(9*3)));
|
||||
screenHandlerFactories.put("smithing",(syncId, playerInventory) -> new SmithingMenu(syncId,playerInventory));
|
||||
screenHandlerFactories.put("smoker",(syncId, playerInventory) -> new SmokerMenu(syncId,playerInventory));
|
||||
screenHandlerFactories.put("stonecutter",(syncId, playerInventory) -> new StonecutterMenu(syncId,playerInventory));
|
||||
screenHandlerFactories.put("anvil", AnvilMenu::new);
|
||||
screenHandlerFactories.put("beacon", BeaconMenu::new);
|
||||
screenHandlerFactories.put("blast_furnace", BlastFurnaceMenu::new);
|
||||
screenHandlerFactories.put("brewing_stand", (syncId, playerInventory) -> new BrewingStandMenu(syncId, playerInventory, new SimpleContainer(5), new SimpleContainerData(2)));
|
||||
screenHandlerFactories.put("cartography_table", CartographyTableMenu::new);
|
||||
screenHandlerFactories.put("crafting", CraftingMenu::new);
|
||||
screenHandlerFactories.put("enchantment", EnchantmentMenu::new);
|
||||
screenHandlerFactories.put("furnace", FurnaceMenu::new);
|
||||
screenHandlerFactories.put("generic_3x3", ((syncId, playerInventory) -> new ChestMenu(GENERIC_3x3, syncId, playerInventory, new SimpleContainer(9), 1)));
|
||||
screenHandlerFactories.put("generic_9x1", ((syncId, playerInventory) -> new ChestMenu(GENERIC_9x1, syncId, playerInventory, new SimpleContainer(9), 1)));
|
||||
screenHandlerFactories.put("generic_9x2", ((syncId, playerInventory) -> new ChestMenu(GENERIC_9x2, syncId, playerInventory, new SimpleContainer(9 * 2), 2)));
|
||||
screenHandlerFactories.put("generic_9x3", ((syncId, playerInventory) -> new ChestMenu(GENERIC_9x3, syncId, playerInventory, new SimpleContainer(9 * 3), 3)));
|
||||
screenHandlerFactories.put("generic_9x4", ((syncId, playerInventory) -> new ChestMenu(GENERIC_9x4, syncId, playerInventory, new SimpleContainer(9 * 4), 4)));
|
||||
screenHandlerFactories.put("generic_9x5", ((syncId, playerInventory) -> new ChestMenu(GENERIC_9x5, syncId, playerInventory, new SimpleContainer(9 * 5), 5)));
|
||||
screenHandlerFactories.put("generic_9x6", ((syncId, playerInventory) -> new ChestMenu(GENERIC_9x6, syncId, playerInventory, new SimpleContainer(9 * 6), 6)));
|
||||
screenHandlerFactories.put("grindstone", GrindstoneMenu::new);
|
||||
screenHandlerFactories.put("hopper", HopperMenu::new);
|
||||
screenHandlerFactories.put("lectern", (syncId, playerInventory) -> new LecternMenu(syncId, new SimpleContainer(1), new SimpleContainerData(1)));
|
||||
screenHandlerFactories.put("loom", LoomMenu::new);
|
||||
screenHandlerFactories.put("merchant", MerchantMenu::new);
|
||||
screenHandlerFactories.put("shulker_box", (syncId, playerInventory) -> new ShulkerBoxMenu(syncId, playerInventory, new SimpleContainer(9 * 3)));
|
||||
screenHandlerFactories.put("smithing_legacy", LegacySmithingMenu::new);
|
||||
screenHandlerFactories.put("smithing", SmithingMenu::new);
|
||||
screenHandlerFactories.put("smoker", SmokerMenu::new);
|
||||
screenHandlerFactories.put("stonecutter", StonecutterMenu::new);
|
||||
}
|
||||
|
||||
|
||||
protected interface ScarpetScreenHandlerFactory {
|
||||
protected interface ScarpetScreenHandlerFactory
|
||||
{
|
||||
AbstractContainerMenu create(int syncId, Inventory playerInventory);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public ScreenValue(ServerPlayer player, String type, Component name, FunctionValue callback, Context c) {
|
||||
public ScreenValue(final ServerPlayer player, final String type, final Component name, final FunctionValue callback, final Context c)
|
||||
{
|
||||
this.name = name;
|
||||
this.typestring = type.toLowerCase();
|
||||
if(callback != null) callback.checkArgs(4);
|
||||
if (callback != null)
|
||||
{
|
||||
callback.checkArgs(4);
|
||||
}
|
||||
this.callback = callback;
|
||||
this.hostname = c.host.getName();
|
||||
this.player = player;
|
||||
MenuProvider factory = this.createScreenHandlerFactory();
|
||||
if(factory == null) throw new ThrowStatement(type, Throwables.UNKNOWN_SCREEN);
|
||||
final MenuProvider factory = this.createScreenHandlerFactory();
|
||||
if (factory == null)
|
||||
{
|
||||
throw new ThrowStatement(type, Throwables.UNKNOWN_SCREEN);
|
||||
}
|
||||
this.openScreen(factory);
|
||||
this.inventory = new ScreenHandlerInventory(this.screenHandler);
|
||||
}
|
||||
|
||||
private MenuProvider createScreenHandlerFactory() {
|
||||
if(!screenHandlerFactories.containsKey(this.typestring)) {
|
||||
private MenuProvider createScreenHandlerFactory()
|
||||
{
|
||||
if (!screenHandlerFactories.containsKey(this.typestring))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new SimpleMenuProvider((i, playerInventory, playerEntity) -> {
|
||||
AbstractContainerMenu screen = screenHandlerFactories.get(ScreenValue.this.typestring).create(i,playerInventory);
|
||||
final AbstractContainerMenu screen = screenHandlerFactories.get(ScreenValue.this.typestring).create(i, playerInventory);
|
||||
ScreenValue.this.addListenerCallback(screen);
|
||||
ScreenValue.this.screenHandler = screen;
|
||||
return screen;
|
||||
}, this.name);
|
||||
}
|
||||
|
||||
private void openScreen(MenuProvider factory) {
|
||||
if(this.player == null) return;
|
||||
OptionalInt optionalSyncId = this.player.openMenu(factory);
|
||||
if(optionalSyncId.isPresent() && this.player.containerMenu.containerId == optionalSyncId.getAsInt()) {
|
||||
private void openScreen(final MenuProvider factory)
|
||||
{
|
||||
if (this.player == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
final OptionalInt optionalSyncId = this.player.openMenu(factory);
|
||||
if (optionalSyncId.isPresent() && this.player.containerMenu.containerId == optionalSyncId.getAsInt())
|
||||
{
|
||||
this.screenHandler = this.player.containerMenu;
|
||||
}
|
||||
}
|
||||
|
||||
public void close() {
|
||||
if(this.player.containerMenu != this.player.inventoryMenu) {
|
||||
public void close()
|
||||
{
|
||||
if (this.player.containerMenu != this.player.inventoryMenu)
|
||||
{
|
||||
//prevent recursion when closing screen in closing screen callback by doing this before triggering event
|
||||
this.inventory = null;
|
||||
this.player.containerMenu = this.player.inventoryMenu;
|
||||
@@ -154,11 +146,14 @@ public class ScreenValue extends Value {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isOpen() {
|
||||
if(this.screenHandler == null) {
|
||||
public boolean isOpen()
|
||||
{
|
||||
if (this.screenHandler == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if(this.player.containerMenu.containerId == this.screenHandler.containerId) {
|
||||
if (this.player.containerMenu.containerId == this.screenHandler.containerId)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
this.screenHandler = null;
|
||||
@@ -166,104 +161,124 @@ public class ScreenValue extends Value {
|
||||
}
|
||||
|
||||
|
||||
private boolean callListener(ServerPlayer player, String action, Map<Value,Value> data) {
|
||||
Value playerValue = EntityValue.of(player);
|
||||
Value actionValue = StringValue.of(action);
|
||||
Value dataValue = MapValue.wrap(data);
|
||||
List<Value> args = Arrays.asList(this,playerValue,actionValue,dataValue);
|
||||
CarpetScriptHost appHost = CarpetServer.scriptServer.getAppHostByName(this.hostname);
|
||||
if(appHost == null) {
|
||||
private boolean callListener(final ServerPlayer player, final String action, final Map<Value, Value> data)
|
||||
{
|
||||
final Value playerValue = EntityValue.of(player);
|
||||
final Value actionValue = StringValue.of(action);
|
||||
final Value dataValue = MapValue.wrap(data);
|
||||
final List<Value> args = Arrays.asList(this, playerValue, actionValue, dataValue);
|
||||
final CarpetScriptHost appHost = CarpetServer.scriptServer.getAppHostByName(this.hostname);
|
||||
if (appHost == null)
|
||||
{
|
||||
this.close();
|
||||
this.screenHandler = null;
|
||||
return false;
|
||||
}
|
||||
CommandSourceStack source = player.createCommandSourceStack().withPermission(CarpetSettings.runPermissionLevel);
|
||||
CarpetScriptHost executingHost = appHost.retrieveForExecution(source,player);
|
||||
final CommandSourceStack source = player.createCommandSourceStack().withPermission(CarpetSettings.runPermissionLevel);
|
||||
final CarpetScriptHost executingHost = appHost.retrieveForExecution(source, player);
|
||||
try
|
||||
{
|
||||
Value cancelValue = executingHost.callUDF(source.withPermission(CarpetSettings.runPermissionLevel), callback, args);
|
||||
final Value cancelValue = executingHost.callUDF(source.withPermission(CarpetSettings.runPermissionLevel), callback, args);
|
||||
return cancelValue.getString().equals("cancel");
|
||||
}
|
||||
catch (NullPointerException | InvalidCallbackException | IntegrityException error)
|
||||
catch (final NullPointerException | InvalidCallbackException | IntegrityException error)
|
||||
{
|
||||
CarpetScriptServer.LOG.error("Got exception when running screen event call ", error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void addListenerCallback(AbstractContainerMenu screenHandler) {
|
||||
if(this.callback == null) return;
|
||||
private void addListenerCallback(final AbstractContainerMenu screenHandler)
|
||||
{
|
||||
if (this.callback == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
screenHandler.addSlotListener(new ScarpetScreenHandlerListener() {
|
||||
screenHandler.addSlotListener(new ScarpetScreenHandlerListener()
|
||||
{
|
||||
@Override
|
||||
public boolean onSlotClick(ServerPlayer player, ClickType actionType, int slot, int button) {
|
||||
Map<Value,Value> data = new HashMap<>();
|
||||
data.put(StringValue.of("slot"),slot == AbstractContainerMenu.SLOT_CLICKED_OUTSIDE ? Value.NULL : NumericValue.of(slot));
|
||||
if(actionType == ClickType.QUICK_CRAFT) {
|
||||
data.put(StringValue.of("quick_craft_stage"),NumericValue.of(AbstractContainerMenu.getQuickcraftHeader(button)));
|
||||
public boolean onSlotClick(final ServerPlayer player, final ClickType actionType, final int slot, int button)
|
||||
{
|
||||
final Map<Value, Value> data = new HashMap<>();
|
||||
data.put(StringValue.of("slot"), slot == AbstractContainerMenu.SLOT_CLICKED_OUTSIDE ? Value.NULL : NumericValue.of(slot));
|
||||
if (actionType == ClickType.QUICK_CRAFT)
|
||||
{
|
||||
data.put(StringValue.of("quick_craft_stage"), NumericValue.of(AbstractContainerMenu.getQuickcraftHeader(button)));
|
||||
button = AbstractContainerMenu.getQuickcraftType(button);
|
||||
}
|
||||
data.put(StringValue.of("button"),NumericValue.of(button));
|
||||
return ScreenValue.this.callListener(player,actionTypeToString(actionType),data);
|
||||
data.put(StringValue.of("button"), NumericValue.of(button));
|
||||
return ScreenValue.this.callListener(player, actionTypeToString(actionType), data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onButtonClick(ServerPlayer player, int button) {
|
||||
Map<Value,Value> data = new HashMap<>();
|
||||
data.put(StringValue.of("button"),NumericValue.of(button));
|
||||
return ScreenValue.this.callListener(player,"button",data);
|
||||
public boolean onButtonClick(final ServerPlayer player, final int button)
|
||||
{
|
||||
final Map<Value, Value> data = new HashMap<>();
|
||||
data.put(StringValue.of("button"), NumericValue.of(button));
|
||||
return ScreenValue.this.callListener(player, "button", data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose(ServerPlayer player) {
|
||||
Map<Value,Value> data = new HashMap<>();
|
||||
ScreenValue.this.callListener(player,"close",data);
|
||||
public void onClose(final ServerPlayer player)
|
||||
{
|
||||
final Map<Value, Value> data = new HashMap<>();
|
||||
ScreenValue.this.callListener(player, "close", data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onSelectRecipe(ServerPlayer player, Recipe<?> recipe, boolean craftAll) {
|
||||
Map<Value,Value> data = new HashMap<>();
|
||||
data.put(StringValue.of("recipe"),StringValue.of(recipe.getId().toString()));
|
||||
data.put(StringValue.of("craft_all"),BooleanValue.of(craftAll));
|
||||
return ScreenValue.this.callListener(player,"select_recipe",data);
|
||||
public boolean onSelectRecipe(final ServerPlayer player, final Recipe<?> recipe, final boolean craftAll)
|
||||
{
|
||||
final Map<Value, Value> data = new HashMap<>();
|
||||
data.put(StringValue.of("recipe"), StringValue.of(recipe.getId().toString()));
|
||||
data.put(StringValue.of("craft_all"), BooleanValue.of(craftAll));
|
||||
return ScreenValue.this.callListener(player, "select_recipe", data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void slotChanged(AbstractContainerMenu handler, int slotId, ItemStack stack) {
|
||||
Map<Value,Value> data = new HashMap<>();
|
||||
data.put(StringValue.of("slot"),NumericValue.of(slotId));
|
||||
data.put(StringValue.of("stack"),ValueConversions.of(stack, player.level.registryAccess()));
|
||||
ScreenValue.this.callListener(ScreenValue.this.player,"slot_update",data);
|
||||
public void slotChanged(final AbstractContainerMenu handler, final int slotId, final ItemStack stack)
|
||||
{
|
||||
final Map<Value, Value> data = new HashMap<>();
|
||||
data.put(StringValue.of("slot"), NumericValue.of(slotId));
|
||||
data.put(StringValue.of("stack"), ValueConversions.of(stack, player.level.registryAccess()));
|
||||
ScreenValue.this.callListener(ScreenValue.this.player, "slot_update", data);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dataChanged(AbstractContainerMenu handler, int property, int value) {}
|
||||
public void dataChanged(final AbstractContainerMenu handler, final int property, final int value)
|
||||
{
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private DataSlot getPropertyForType(Class<? extends AbstractContainerMenu> screenHandlerClass, String requiredType, int propertyIndex, String propertyName) {
|
||||
if(screenHandlerClass.isInstance(this.screenHandler)) {
|
||||
private DataSlot getPropertyForType(final Class<? extends AbstractContainerMenu> screenHandlerClass, final String requiredType, final int propertyIndex, final String propertyName)
|
||||
{
|
||||
if (screenHandlerClass.isInstance(this.screenHandler))
|
||||
{
|
||||
return ((ScreenHandlerInterface) this.screenHandler).getProperty(propertyIndex);
|
||||
}
|
||||
if(!this.isOpen()) {
|
||||
if (!this.isOpen())
|
||||
{
|
||||
throw new InternalExpressionException("Screen property cannot be accessed, because the screen is already closed");
|
||||
}
|
||||
throw new InternalExpressionException("Screen property " + propertyName + " expected a " + requiredType + " screen.");
|
||||
}
|
||||
|
||||
private DataSlot getProperty(String propertyName) {
|
||||
return switch (propertyName) {
|
||||
private DataSlot getProperty(final String propertyName)
|
||||
{
|
||||
return switch (propertyName)
|
||||
{
|
||||
case "fuel_progress" -> getPropertyForType(AbstractFurnaceMenu.class, "furnace", 0, propertyName);
|
||||
case "max_fuel_progress" -> getPropertyForType(AbstractFurnaceMenu.class, "furnace", 1, propertyName);
|
||||
case "cook_progress" -> getPropertyForType(AbstractFurnaceMenu.class, "furnace", 2, propertyName);
|
||||
case "max_cook_progress" -> getPropertyForType(AbstractFurnaceMenu.class, "furnace", 3, propertyName);
|
||||
|
||||
case "level_cost" -> getPropertyForType(AnvilMenu.class, "anvil", 0, propertyName);
|
||||
|
||||
case "page" -> getPropertyForType(LecternMenu.class, "lectern", 0, propertyName);
|
||||
|
||||
case "beacon_level" -> getPropertyForType(BeaconMenu.class, "beacon", 0, propertyName);
|
||||
case "primary_effect" -> getPropertyForType(BeaconMenu.class, "beacon", 1, propertyName);
|
||||
case "secondary_effect" -> getPropertyForType(BeaconMenu.class, "beacon", 2, propertyName);
|
||||
|
||||
case "brew_time" -> getPropertyForType(BrewingStandMenu.class, "brewing_stand", 0, propertyName);
|
||||
case "brewing_fuel" -> getPropertyForType(BrewingStandMenu.class, "brewing_stand", 1, propertyName);
|
||||
|
||||
case "enchantment_power_1" -> getPropertyForType(EnchantmentMenu.class, "enchantment", 0, propertyName);
|
||||
case "enchantment_power_2" -> getPropertyForType(EnchantmentMenu.class, "enchantment", 1, propertyName);
|
||||
case "enchantment_power_3" -> getPropertyForType(EnchantmentMenu.class, "enchantment", 2, propertyName);
|
||||
@@ -274,50 +289,55 @@ public class ScreenValue extends Value {
|
||||
case "enchantment_level_1" -> getPropertyForType(EnchantmentMenu.class, "enchantment", 7, propertyName);
|
||||
case "enchantment_level_2" -> getPropertyForType(EnchantmentMenu.class, "enchantment", 8, propertyName);
|
||||
case "enchantment_level_3" -> getPropertyForType(EnchantmentMenu.class, "enchantment", 9, propertyName);
|
||||
|
||||
case "banner_pattern" -> getPropertyForType(LoomMenu.class, "loom", 0, propertyName);
|
||||
|
||||
case "stonecutter_recipe" -> getPropertyForType(StonecutterMenu.class, "stonecutter", 0, propertyName);
|
||||
|
||||
default -> throw new InternalExpressionException("Invalid screen property: " + propertyName);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
public Value queryProperty(String propertyName) {
|
||||
if(propertyName.equals("name")) return FormattedTextValue.of(this.name);
|
||||
if(propertyName.equals("open")) return BooleanValue.of(this.isOpen());
|
||||
DataSlot property = getProperty(propertyName);
|
||||
public Value queryProperty(final String propertyName)
|
||||
{
|
||||
if (propertyName.equals("name"))
|
||||
{
|
||||
return FormattedTextValue.of(this.name);
|
||||
}
|
||||
if (propertyName.equals("open"))
|
||||
{
|
||||
return BooleanValue.of(this.isOpen());
|
||||
}
|
||||
final DataSlot property = getProperty(propertyName);
|
||||
return NumericValue.of(property.get());
|
||||
}
|
||||
|
||||
public Value modifyProperty(String propertyName, List<Value> lv) {
|
||||
DataSlot property = getProperty(propertyName);
|
||||
int intValue = NumericValue.asNumber(lv.get(0)).getInt();
|
||||
public Value modifyProperty(final String propertyName, final List<Value> lv)
|
||||
{
|
||||
final DataSlot property = getProperty(propertyName);
|
||||
final int intValue = NumericValue.asNumber(lv.get(0)).getInt();
|
||||
property.set(intValue);
|
||||
this.screenHandler.sendAllDataToRemote();
|
||||
return Value.TRUE;
|
||||
}
|
||||
|
||||
public AbstractContainerMenu getScreenHandler() {
|
||||
return this.screenHandler;
|
||||
}
|
||||
|
||||
public ServerPlayer getPlayer() {
|
||||
public ServerPlayer getPlayer()
|
||||
{
|
||||
return this.player;
|
||||
}
|
||||
|
||||
public Container getInventory() {
|
||||
public Container getInventory()
|
||||
{
|
||||
return this.inventory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getString() {
|
||||
public String getString()
|
||||
{
|
||||
return this.typestring + "_screen";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getBoolean() {
|
||||
public boolean getBoolean()
|
||||
{
|
||||
return this.isOpen();
|
||||
}
|
||||
|
||||
@@ -328,92 +348,134 @@ public class ScreenValue extends Value {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tag toTag(boolean force) {
|
||||
if(this.screenHandler == null) {
|
||||
public Tag toTag(final boolean force)
|
||||
{
|
||||
if (this.screenHandler == null)
|
||||
{
|
||||
return Value.NULL.toTag(true);
|
||||
}
|
||||
|
||||
ListTag nbtList = new ListTag();
|
||||
for(int i = 0; i < this.screenHandler.slots.size(); i++) {
|
||||
ItemStack itemStack = this.screenHandler.getSlot(i).getItem();
|
||||
final ListTag nbtList = new ListTag();
|
||||
for (int i = 0; i < this.screenHandler.slots.size(); i++)
|
||||
{
|
||||
final ItemStack itemStack = this.screenHandler.getSlot(i).getItem();
|
||||
nbtList.add(itemStack.save(new CompoundTag()));
|
||||
}
|
||||
return nbtList;
|
||||
}
|
||||
|
||||
|
||||
public interface ScarpetScreenHandlerListener extends ContainerListener {
|
||||
public interface ScarpetScreenHandlerListener extends ContainerListener
|
||||
{
|
||||
boolean onSlotClick(ServerPlayer player, ClickType actionType, int slot, int button);
|
||||
|
||||
boolean onButtonClick(ServerPlayer player, int button);
|
||||
|
||||
void onClose(ServerPlayer player);
|
||||
|
||||
boolean onSelectRecipe(ServerPlayer player, Recipe<?> recipe, boolean craftAll);
|
||||
}
|
||||
|
||||
public static class ScreenHandlerInventory implements Container {
|
||||
|
||||
public static class ScreenHandlerInventory implements Container
|
||||
{
|
||||
protected AbstractContainerMenu screenHandler;
|
||||
|
||||
public ScreenHandlerInventory(AbstractContainerMenu screenHandler) {
|
||||
public ScreenHandlerInventory(final AbstractContainerMenu screenHandler)
|
||||
{
|
||||
this.screenHandler = screenHandler;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getContainerSize() {
|
||||
public int getContainerSize()
|
||||
{
|
||||
return this.screenHandler.slots.size() + 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
for(Slot slot : this.screenHandler.slots) {
|
||||
if(slot.hasItem() && !slot.getItem().isEmpty()) return false;
|
||||
public boolean isEmpty()
|
||||
{
|
||||
for (final Slot slot : this.screenHandler.slots)
|
||||
{
|
||||
if (slot.hasItem() && !slot.getItem().isEmpty())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return this.screenHandler.getCarried().isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack getItem(int slot) {
|
||||
if(slot == this.getContainerSize()-1) return this.screenHandler.getCarried();
|
||||
public ItemStack getItem(final int slot)
|
||||
{
|
||||
if (slot == this.getContainerSize() - 1)
|
||||
{
|
||||
return this.screenHandler.getCarried();
|
||||
}
|
||||
return slot >= -1 && slot < this.getContainerSize() ? this.screenHandler.slots.get(slot).getItem() : ItemStack.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removeItem(int slot, int amount) {
|
||||
ItemStack itemStack;
|
||||
if(slot == this.getContainerSize()-1)
|
||||
public ItemStack removeItem(final int slot, final int amount)
|
||||
{
|
||||
final ItemStack itemStack;
|
||||
if (slot == this.getContainerSize() - 1)
|
||||
{
|
||||
itemStack = this.screenHandler.getCarried().split(amount);
|
||||
}
|
||||
else
|
||||
{
|
||||
itemStack = ScreenHandlerInventory.splitStack(this.screenHandler.slots, slot, amount);
|
||||
if (!itemStack.isEmpty()) {
|
||||
}
|
||||
if (!itemStack.isEmpty())
|
||||
{
|
||||
this.setChanged();
|
||||
}
|
||||
return itemStack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ItemStack removeItemNoUpdate(int slot) {
|
||||
ItemStack itemStack;
|
||||
if(slot == this.getContainerSize()-1)
|
||||
public ItemStack removeItemNoUpdate(final int slot)
|
||||
{
|
||||
final ItemStack itemStack;
|
||||
if (slot == this.getContainerSize() - 1)
|
||||
{
|
||||
itemStack = this.screenHandler.getCarried();
|
||||
}
|
||||
else
|
||||
{
|
||||
itemStack = this.screenHandler.slots.get(slot).getItem();
|
||||
if (itemStack.isEmpty()) {
|
||||
}
|
||||
if (itemStack.isEmpty())
|
||||
{
|
||||
return ItemStack.EMPTY;
|
||||
} else {
|
||||
if(slot == this.getContainerSize()-1)
|
||||
}
|
||||
else
|
||||
{
|
||||
if (slot == this.getContainerSize() - 1)
|
||||
{
|
||||
this.screenHandler.setCarried(ItemStack.EMPTY);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.screenHandler.slots.get(slot).set(ItemStack.EMPTY);
|
||||
}
|
||||
return itemStack;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setItem(int slot, ItemStack stack) {
|
||||
if(slot == this.getContainerSize()-1)
|
||||
public void setItem(final int slot, final ItemStack stack)
|
||||
{
|
||||
if (slot == this.getContainerSize() - 1)
|
||||
{
|
||||
this.screenHandler.setCarried(stack);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.screenHandler.slots.get(slot).set(stack);
|
||||
if (!stack.isEmpty() && stack.getCount() > this.getMaxStackSize()) {
|
||||
}
|
||||
if (!stack.isEmpty() && stack.getCount() > this.getMaxStackSize())
|
||||
{
|
||||
stack.setCount(this.getMaxStackSize());
|
||||
}
|
||||
|
||||
@@ -421,18 +483,21 @@ public class ScreenValue extends Value {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setChanged() {
|
||||
|
||||
public void setChanged()
|
||||
{
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean stillValid(Player player) {
|
||||
public boolean stillValid(final Player player)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearContent() {
|
||||
for(Slot slot : this.screenHandler.slots) {
|
||||
public void clearContent()
|
||||
{
|
||||
for (final Slot slot : this.screenHandler.slots)
|
||||
{
|
||||
slot.set(ItemStack.EMPTY);
|
||||
}
|
||||
this.screenHandler.setCarried(ItemStack.EMPTY);
|
||||
@@ -440,13 +505,16 @@ public class ScreenValue extends Value {
|
||||
}
|
||||
|
||||
|
||||
public static ItemStack splitStack(List<Slot> slots, int slot, int amount) {
|
||||
public static ItemStack splitStack(final List<Slot> slots, final int slot, final int amount)
|
||||
{
|
||||
return slot >= 0 && slot < slots.size() && !slots.get(slot).getItem().isEmpty() && amount > 0 ? slots.get(slot).getItem().split(amount) : ItemStack.EMPTY;
|
||||
}
|
||||
}
|
||||
|
||||
private static String actionTypeToString(ClickType actionType) {
|
||||
return switch (actionType) {
|
||||
private static String actionTypeToString(final ClickType actionType)
|
||||
{
|
||||
return switch (actionType)
|
||||
{
|
||||
case PICKUP -> "pickup";
|
||||
case QUICK_MOVE -> "quick_move";
|
||||
case SWAP -> "swap";
|
||||
|
||||
@@ -7,15 +7,17 @@ public class StringValue extends Value
|
||||
{
|
||||
public static Value EMPTY = StringValue.of("");
|
||||
|
||||
private String str;
|
||||
private final String str;
|
||||
|
||||
@Override
|
||||
public String getString() {
|
||||
public String getString()
|
||||
{
|
||||
return str;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getBoolean() {
|
||||
public boolean getBoolean()
|
||||
{
|
||||
return str != null && !str.isEmpty();
|
||||
}
|
||||
|
||||
@@ -25,15 +27,14 @@ public class StringValue extends Value
|
||||
return new StringValue(str);
|
||||
}
|
||||
|
||||
public StringValue(String str)
|
||||
public StringValue(final String str)
|
||||
{
|
||||
this.str = str;
|
||||
}
|
||||
|
||||
public static Value of(String value)
|
||||
public static Value of(final String value)
|
||||
{
|
||||
if (value == null) return Value.NULL;
|
||||
return new StringValue(value);
|
||||
return value == null ? Value.NULL : new StringValue(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -43,7 +44,7 @@ public class StringValue extends Value
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tag toTag(boolean force)
|
||||
public Tag toTag(final boolean force)
|
||||
{
|
||||
return StringTag.valueOf(str);
|
||||
}
|
||||
|
||||
@@ -6,10 +6,12 @@ import carpet.script.Tokenizer;
|
||||
import carpet.script.exception.ExitStatement;
|
||||
import carpet.script.exception.ExpressionException;
|
||||
import carpet.script.exception.InternalExpressionException;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
import net.minecraft.nbt.Tag;
|
||||
|
||||
public class ThreadValue extends Value
|
||||
@@ -18,21 +20,21 @@ public class ThreadValue extends Value
|
||||
private final long id;
|
||||
private static long sequence = 0L;
|
||||
|
||||
public ThreadValue(CompletableFuture<Value> taskFuture)
|
||||
public ThreadValue(final CompletableFuture<Value> taskFuture)
|
||||
{
|
||||
this.taskFuture = taskFuture;
|
||||
this.id = sequence++;
|
||||
}
|
||||
|
||||
public ThreadValue(Value pool, FunctionValue function, Expression expr, Tokenizer.Token token, Context ctx, List<Value> args)
|
||||
public ThreadValue(final Value pool, final FunctionValue function, final Expression expr, final Tokenizer.Token token, final Context ctx, final List<Value> args)
|
||||
{
|
||||
this(getCompletableFutureFromFunction(pool, function, expr, token, ctx, args));
|
||||
Thread.yield();
|
||||
}
|
||||
|
||||
public static CompletableFuture<Value> getCompletableFutureFromFunction(Value pool, FunctionValue function, Expression expr, Tokenizer.Token token, Context ctx, List<Value> args)
|
||||
public static CompletableFuture<Value> getCompletableFutureFromFunction(final Value pool, final FunctionValue function, final Expression expr, final Tokenizer.Token token, final Context ctx, final List<Value> args)
|
||||
{
|
||||
ExecutorService executor = ctx.host.getExecutor(pool);
|
||||
final ExecutorService executor = ctx.host.getExecutor(pool);
|
||||
if (executor == null)
|
||||
{
|
||||
// app is shutting down - no more threads can be spawned.
|
||||
@@ -40,27 +42,22 @@ public class ThreadValue extends Value
|
||||
}
|
||||
else
|
||||
{
|
||||
return CompletableFuture.supplyAsync(
|
||||
() ->
|
||||
{
|
||||
try
|
||||
{
|
||||
return function.execute(ctx, Context.NONE, expr, token, args).evalValue(ctx);
|
||||
}
|
||||
catch (ExitStatement exit)
|
||||
{
|
||||
// app stopped
|
||||
return exit.retval;
|
||||
}
|
||||
catch (ExpressionException exc)
|
||||
{
|
||||
ctx.host.handleExpressionException("Thread failed\n", exc);
|
||||
return Value.NULL;
|
||||
}
|
||||
|
||||
},
|
||||
ctx.host.getExecutor(pool)
|
||||
);
|
||||
return CompletableFuture.supplyAsync(() -> {
|
||||
try
|
||||
{
|
||||
return function.execute(ctx, Context.NONE, expr, token, args).evalValue(ctx);
|
||||
}
|
||||
catch (final ExitStatement exit)
|
||||
{
|
||||
// app stopped
|
||||
return exit.retval;
|
||||
}
|
||||
catch (final ExpressionException exc)
|
||||
{
|
||||
ctx.host.handleExpressionException("Thread failed\n", exc);
|
||||
return Value.NULL;
|
||||
}
|
||||
}, ctx.host.getExecutor(pool));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,12 +84,12 @@ public class ThreadValue extends Value
|
||||
{
|
||||
return taskFuture.get();
|
||||
}
|
||||
catch (ExitStatement exit)
|
||||
catch (final ExitStatement exit)
|
||||
{
|
||||
taskFuture.complete(exit.retval);
|
||||
return exit.retval;
|
||||
}
|
||||
catch (InterruptedException | ExecutionException e)
|
||||
catch (final InterruptedException | ExecutionException e)
|
||||
{
|
||||
return Value.NULL;
|
||||
}
|
||||
@@ -104,19 +101,19 @@ public class ThreadValue extends Value
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o)
|
||||
public boolean equals(final Object o)
|
||||
{
|
||||
if (!(o instanceof ThreadValue))
|
||||
return false;
|
||||
return ((ThreadValue) o).id == this.id;
|
||||
return o instanceof final ThreadValue tv && tv.id == this.id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Value o)
|
||||
public int compareTo(final Value o)
|
||||
{
|
||||
if (!(o instanceof ThreadValue))
|
||||
if (!(o instanceof final ThreadValue tv))
|
||||
{
|
||||
throw new InternalExpressionException("Cannot compare tasks to other types");
|
||||
return (int) (this.id - ((ThreadValue) o).id);
|
||||
}
|
||||
return (int) (this.id - tv.id);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -126,9 +123,12 @@ public class ThreadValue extends Value
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tag toTag(boolean force)
|
||||
public Tag toTag(final boolean force)
|
||||
{
|
||||
if (!force) throw new NBTSerializableValue.IncompatibleTypeException(this);
|
||||
if (!force)
|
||||
{
|
||||
throw new NBTSerializableValue.IncompatibleTypeException(this);
|
||||
}
|
||||
return getValue().toTag(true);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,14 +4,15 @@ import carpet.script.exception.InternalExpressionException;
|
||||
import com.google.gson.JsonElement;
|
||||
import net.minecraft.nbt.Tag;
|
||||
|
||||
public class UndefValue extends NullValue {
|
||||
public class UndefValue extends NullValue
|
||||
{
|
||||
public static final UndefValue UNDEF = new UndefValue();
|
||||
|
||||
private RuntimeException getError() {
|
||||
return new InternalExpressionException("variable "+boundVariable+" was used before initialization under 'strict' app config");
|
||||
private RuntimeException getError()
|
||||
{
|
||||
return new InternalExpressionException("variable " + boundVariable + " was used before initialization under 'strict' app config");
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getString()
|
||||
{
|
||||
@@ -43,28 +44,32 @@ public class UndefValue extends NullValue {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value slice(long fromDesc, Long toDesc) {
|
||||
throw getError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public NumericValue opposite() {
|
||||
throw getError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int length() {
|
||||
throw getError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Value o)
|
||||
public Value slice(final long fromDesc, final Long toDesc)
|
||||
{
|
||||
throw getError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value in(Value value) {
|
||||
public NumericValue opposite()
|
||||
{
|
||||
throw getError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int length()
|
||||
{
|
||||
throw getError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(final Value o)
|
||||
{
|
||||
throw getError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value in(final Value value)
|
||||
{
|
||||
throw getError();
|
||||
}
|
||||
|
||||
@@ -81,13 +86,14 @@ public class UndefValue extends NullValue {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tag toTag(boolean force)
|
||||
public Tag toTag(final boolean force)
|
||||
{
|
||||
throw getError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value split(Value delimiter) {
|
||||
public Value split(final Value delimiter)
|
||||
{
|
||||
throw getError();
|
||||
}
|
||||
|
||||
@@ -98,29 +104,31 @@ public class UndefValue extends NullValue {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isNull() {
|
||||
throw getError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value add(Value v) {
|
||||
throw getError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value subtract(Value v)
|
||||
public boolean isNull()
|
||||
{
|
||||
throw getError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value multiply(Value v)
|
||||
public Value add(final Value v)
|
||||
{
|
||||
throw getError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value divide(Value v)
|
||||
public Value subtract(final Value v)
|
||||
{
|
||||
throw getError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value multiply(final Value v)
|
||||
{
|
||||
throw getError();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Value divide(final Value v)
|
||||
{
|
||||
throw getError();
|
||||
}
|
||||
|
||||
@@ -4,23 +4,25 @@ import carpet.CarpetSettings;
|
||||
import carpet.script.exception.InternalExpressionException;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.regex.PatternSyntaxException;
|
||||
|
||||
import net.minecraft.nbt.Tag;
|
||||
|
||||
public abstract class Value implements Comparable<Value>, Cloneable
|
||||
{
|
||||
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 final NumericValue FALSE = BooleanValue.FALSE;
|
||||
public static final NumericValue TRUE = BooleanValue.TRUE;
|
||||
public static final NumericValue ZERO = new NumericValue(0);
|
||||
public static final NumericValue ONE = new NumericValue(1);
|
||||
|
||||
public static NullValue NULL = NullValue.NULL;
|
||||
public static UndefValue UNDEF = UndefValue.UNDEF;
|
||||
public static final NullValue NULL = NullValue.NULL;
|
||||
public static final UndefValue UNDEF = UndefValue.UNDEF;
|
||||
|
||||
public String boundVariable;
|
||||
|
||||
@@ -28,29 +30,32 @@ public abstract class Value implements Comparable<Value>, Cloneable
|
||||
{
|
||||
return boundVariable != null;
|
||||
}
|
||||
|
||||
public String getVariable()
|
||||
{
|
||||
return boundVariable;
|
||||
}
|
||||
public Value reboundedTo(String var)
|
||||
|
||||
public Value reboundedTo(final String value)
|
||||
{
|
||||
Value copy;
|
||||
final Value copy;
|
||||
try
|
||||
{
|
||||
copy = (Value)clone();
|
||||
copy = (Value) clone();
|
||||
}
|
||||
catch (CloneNotSupportedException e)
|
||||
catch (final CloneNotSupportedException e)
|
||||
{
|
||||
// should not happen
|
||||
CarpetSettings.LOG.error("Failed to clone variable", e);
|
||||
throw new InternalExpressionException("Variable of type "+getTypeString()+" is not cloneable. Tell gnembon about it, this shoudn't happen");
|
||||
throw new InternalExpressionException("Variable of type " + getTypeString() + " is not cloneable. Tell gnembon about it, this shoudn't happen");
|
||||
}
|
||||
copy.boundVariable = var;
|
||||
copy.boundVariable = value;
|
||||
return copy;
|
||||
}
|
||||
public Value bindTo(String var)
|
||||
|
||||
public Value bindTo(final String value)
|
||||
{
|
||||
this.boundVariable = var;
|
||||
this.boundVariable = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -64,35 +69,37 @@ public abstract class Value implements Comparable<Value>, Cloneable
|
||||
|
||||
public abstract boolean getBoolean();
|
||||
|
||||
public Value add(Value o) {
|
||||
public Value add(final Value o)
|
||||
{
|
||||
if (o instanceof FormattedTextValue)
|
||||
{
|
||||
return FormattedTextValue.combine(this, o);
|
||||
}
|
||||
String leftStr = this.getString();
|
||||
String rightStr = o.getString();
|
||||
final String leftStr = this.getString();
|
||||
final String rightStr = o.getString();
|
||||
return new StringValue(leftStr + rightStr);
|
||||
}
|
||||
public Value subtract(Value v)
|
||||
|
||||
public Value subtract(final Value v)
|
||||
{
|
||||
return new StringValue(this.getString().replace(v.getString(),""));
|
||||
return new StringValue(this.getString().replace(v.getString(), ""));
|
||||
}
|
||||
public Value multiply(Value v)
|
||||
|
||||
public Value multiply(final Value v)
|
||||
{
|
||||
if (v instanceof NumericValue || v instanceof ListValue)
|
||||
{
|
||||
return v.multiply(this);
|
||||
}
|
||||
return new StringValue(this.getString()+"."+v.getString());
|
||||
return v instanceof NumericValue || v instanceof ListValue
|
||||
? v.multiply(this)
|
||||
: new StringValue(this.getString() + "." + v.getString());
|
||||
}
|
||||
public Value divide(Value v)
|
||||
|
||||
public Value divide(final Value v)
|
||||
{
|
||||
if (v instanceof NumericValue)
|
||||
if (v instanceof final NumericValue number)
|
||||
{
|
||||
String lstr = getString();
|
||||
return new StringValue(lstr.substring(0, (int)(lstr.length()/ ((NumericValue) v).getDouble())));
|
||||
final String lstr = getString();
|
||||
return new StringValue(lstr.substring(0, (int) (lstr.length() / number.getDouble())));
|
||||
}
|
||||
return new StringValue(getString()+"/"+v.getString());
|
||||
return new StringValue(getString() + "/" + v.getString());
|
||||
}
|
||||
|
||||
public Value()
|
||||
@@ -103,18 +110,18 @@ public abstract class Value implements Comparable<Value>, Cloneable
|
||||
@Override
|
||||
public int compareTo(final Value o)
|
||||
{
|
||||
if (o instanceof NumericValue || o instanceof ListValue || o instanceof ThreadValue)
|
||||
{
|
||||
return -o.compareTo(this);
|
||||
}
|
||||
return getString().compareTo(o.getString());
|
||||
return o instanceof NumericValue || o instanceof ListValue || o instanceof ThreadValue
|
||||
? -o.compareTo(this)
|
||||
: getString().compareTo(o.getString());
|
||||
}
|
||||
|
||||
@Override // for hashmap key access, and == operator
|
||||
public boolean equals(final Object o)
|
||||
{
|
||||
if (o instanceof Value)
|
||||
return this.compareTo((Value) o)==0;
|
||||
if (o instanceof final Value v)
|
||||
{
|
||||
return this.compareTo(v) == 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -126,50 +133,65 @@ public abstract class Value implements Comparable<Value>, Cloneable
|
||||
{
|
||||
throw new InternalExpressionException(boundVariable+ " cannot be assigned a new value");
|
||||
}*/
|
||||
throw new InternalExpressionException(getString()+ " is not a variable");
|
||||
|
||||
throw new InternalExpressionException(getString() + " is not a variable");
|
||||
}
|
||||
}
|
||||
|
||||
public Value in(Value value1)
|
||||
public Value in(final Value value1)
|
||||
{
|
||||
final Pattern p;
|
||||
try
|
||||
{
|
||||
p = Pattern.compile(value1.getString());
|
||||
}
|
||||
catch (PatternSyntaxException pse)
|
||||
catch (final PatternSyntaxException pse)
|
||||
{
|
||||
throw new InternalExpressionException("Incorrect matching pattern: "+pse.getMessage());
|
||||
throw new InternalExpressionException("Incorrect matching pattern: " + pse.getMessage());
|
||||
}
|
||||
final Matcher m = p.matcher(this.getString());
|
||||
if (!m.find()) return Value.NULL;
|
||||
int gc = m.groupCount();
|
||||
if (gc == 0) return new StringValue(m.group());
|
||||
if (gc == 1) return StringValue.of(m.group(1));
|
||||
List<Value> groups = new ArrayList<>(gc);
|
||||
if (!m.find())
|
||||
{
|
||||
return Value.NULL;
|
||||
}
|
||||
final int gc = m.groupCount();
|
||||
if (gc == 0)
|
||||
{
|
||||
return new StringValue(m.group());
|
||||
}
|
||||
if (gc == 1)
|
||||
{
|
||||
return StringValue.of(m.group(1));
|
||||
}
|
||||
final List<Value> groups = new ArrayList<>(gc);
|
||||
for (int i = 1; i <= gc; i++)
|
||||
{
|
||||
groups.add(StringValue.of(m.group(i)));
|
||||
}
|
||||
return ListValue.wrap(groups);
|
||||
}
|
||||
|
||||
public int length()
|
||||
{
|
||||
return getString().length();
|
||||
}
|
||||
|
||||
public Value slice(long fromDesc, Long toDesc)
|
||||
public Value slice(final long fromDesc, final Long toDesc)
|
||||
{
|
||||
String value = this.getString();
|
||||
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+1);
|
||||
if (from > to) return StringValue.EMPTY;
|
||||
final String value = this.getString();
|
||||
final int size = value.length();
|
||||
final int from = ListValue.normalizeIndex(fromDesc, size);
|
||||
if (toDesc == null)
|
||||
{
|
||||
return new StringValue(value.substring(from));
|
||||
}
|
||||
final int to = ListValue.normalizeIndex(toDesc, size + 1);
|
||||
if (from > to)
|
||||
{
|
||||
return StringValue.EMPTY;
|
||||
}
|
||||
return new StringValue(value.substring(from, to));
|
||||
}
|
||||
|
||||
|
||||
public Value split(Value delimiter)
|
||||
{
|
||||
if (delimiter == null)
|
||||
@@ -180,20 +202,20 @@ public abstract class Value implements Comparable<Value>, Cloneable
|
||||
{
|
||||
return ListValue.wrap(Arrays.stream(getString().split(delimiter.getString())).map(StringValue::new));
|
||||
}
|
||||
catch (PatternSyntaxException pse)
|
||||
catch (final PatternSyntaxException pse)
|
||||
{
|
||||
throw new InternalExpressionException("Incorrect pattern for 'split': "+pse.getMessage());
|
||||
throw new InternalExpressionException("Incorrect pattern for 'split': " + pse.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public double readDoubleNumber()
|
||||
{
|
||||
String s = getString();
|
||||
final String s = getString();
|
||||
try
|
||||
{
|
||||
return Double.valueOf(s);
|
||||
return Double.parseDouble(s);
|
||||
}
|
||||
catch (NumberFormatException e)
|
||||
catch (final NumberFormatException e)
|
||||
{
|
||||
return Double.NaN;
|
||||
}
|
||||
@@ -212,21 +234,20 @@ public abstract class Value implements Comparable<Value>, Cloneable
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
String stringVal = getString();
|
||||
if (stringVal.isEmpty()) return 0;
|
||||
return ("s"+stringVal).hashCode();
|
||||
final String stringVal = getString();
|
||||
return stringVal.isEmpty() ? 0 : ("s" + stringVal).hashCode();
|
||||
}
|
||||
|
||||
public Value deepcopy()
|
||||
{
|
||||
try
|
||||
{
|
||||
return (Value)this.clone();
|
||||
return (Value) this.clone();
|
||||
}
|
||||
catch (CloneNotSupportedException e)
|
||||
catch (final CloneNotSupportedException e)
|
||||
{
|
||||
// should never happen
|
||||
throw new InternalExpressionException("Cannot make a copy of value: "+this);
|
||||
throw new InternalExpressionException("Cannot make a copy of value: " + this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -237,12 +258,18 @@ public abstract class Value implements Comparable<Value>, Cloneable
|
||||
return new JsonPrimitive(getString());
|
||||
}
|
||||
|
||||
public boolean isNull() { return false; }
|
||||
public boolean isNull()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return retrieves useful in-run value of an optimized code-base value.
|
||||
* For immutable values (most of them) it can return itself,
|
||||
* but for mutables, it needs to be its copy or deep copy.
|
||||
*/
|
||||
public Value fromConstant() { return this; }
|
||||
public Value fromConstant()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -46,6 +46,7 @@ import net.minecraft.world.phys.AABB;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import net.minecraft.world.scores.Objective;
|
||||
import net.minecraft.world.scores.criteria.ObjectiveCriteria;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
@@ -60,58 +61,65 @@ import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
|
||||
public class ValueConversions
|
||||
{
|
||||
public static Value of(BlockPos pos)
|
||||
public static Value of(final BlockPos pos)
|
||||
{
|
||||
return ListValue.of(new NumericValue(pos.getX()), new NumericValue(pos.getY()), new NumericValue(pos.getZ()));
|
||||
}
|
||||
|
||||
public static Value of(Vec3 vec)
|
||||
public static Value of(final Vec3 vec)
|
||||
{
|
||||
return ListValue.of(new NumericValue(vec.x), new NumericValue(vec.y), new NumericValue(vec.z));
|
||||
}
|
||||
|
||||
public static Value of(ColumnPos cpos) { return ListValue.of(new NumericValue(cpos.x()), new NumericValue(cpos.z()));}
|
||||
public static Value of(final ColumnPos cpos)
|
||||
{
|
||||
return ListValue.of(new NumericValue(cpos.x()), new NumericValue(cpos.z()));
|
||||
}
|
||||
|
||||
public static Value of(ServerLevel world)
|
||||
public static Value of(final ServerLevel world)
|
||||
{
|
||||
return of(world.dimension().location());
|
||||
}
|
||||
|
||||
public static Value of(MaterialColor color) {return ListValue.of(StringValue.of(BlockInfo.mapColourName.get(color)), ofRGB(color.col));}
|
||||
public static Value of(final MaterialColor color)
|
||||
{
|
||||
return ListValue.of(StringValue.of(BlockInfo.mapColourName.get(color)), ofRGB(color.col));
|
||||
}
|
||||
|
||||
public static <T extends Number> Value of(MinMaxBounds<T> range) { return ListValue.of(NumericValue.of(range.getMin()), NumericValue.of(range.getMax()));}
|
||||
public static <T extends Number> Value of(final MinMaxBounds<T> range)
|
||||
{
|
||||
return ListValue.of(NumericValue.of(range.getMin()), NumericValue.of(range.getMax()));
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public static Value of(ItemStack stack)
|
||||
public static Value of(final ItemStack stack)
|
||||
{
|
||||
if (stack == null || stack.isEmpty())
|
||||
return Value.NULL;
|
||||
return ListValue.of(
|
||||
return stack == null || stack.isEmpty() ? Value.NULL : ListValue.of(
|
||||
of(BuiltInRegistries.ITEM.getKey(stack.getItem())),
|
||||
new NumericValue(stack.getCount()),
|
||||
NBTSerializableValue.fromStack(stack)
|
||||
);
|
||||
}
|
||||
public static Value of(ItemStack stack, RegistryAccess regs) {
|
||||
if (stack == null || stack.isEmpty())
|
||||
return Value.NULL;
|
||||
return ListValue.of(
|
||||
|
||||
public static Value of(final ItemStack stack, final RegistryAccess regs)
|
||||
{
|
||||
return stack == null || stack.isEmpty() ? Value.NULL : ListValue.of(
|
||||
of(regs.registryOrThrow(Registries.ITEM).getKey(stack.getItem())),
|
||||
new NumericValue(stack.getCount()),
|
||||
NBTSerializableValue.fromStack(stack)
|
||||
);
|
||||
}
|
||||
|
||||
public static Value of(Objective objective)
|
||||
public static Value of(final Objective objective)
|
||||
{
|
||||
return ListValue.of(
|
||||
StringValue.of(objective.getName()),
|
||||
StringValue.of(objective.getCriteria().getName())
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
public static Value of(ObjectiveCriteria criteria)
|
||||
public static Value of(final ObjectiveCriteria criteria)
|
||||
{
|
||||
return ListValue.of(
|
||||
StringValue.of(criteria.getName()),
|
||||
@@ -120,20 +128,22 @@ public class ValueConversions
|
||||
}
|
||||
|
||||
|
||||
public static Value of(ParticleOptions particle)
|
||||
public static Value of(final ParticleOptions particle)
|
||||
{
|
||||
String repr = particle.writeToString();
|
||||
if (repr.startsWith("minecraft:")) return StringValue.of(repr.substring(10));
|
||||
return StringValue.of(repr);
|
||||
final String repr = particle.writeToString();
|
||||
return StringValue.of(repr.startsWith("minecraft:") ? repr.substring(10) : repr);
|
||||
}
|
||||
|
||||
public static Value ofRGB(int value) {return new NumericValue(value*256+255 );}
|
||||
public static Value ofRGB(final int value)
|
||||
{
|
||||
return new NumericValue(value * 256 + 255);
|
||||
}
|
||||
|
||||
public static Level dimFromValue(Value dimensionValue, MinecraftServer server)
|
||||
public static Level dimFromValue(final Value dimensionValue, final MinecraftServer server)
|
||||
{
|
||||
if (dimensionValue instanceof EntityValue)
|
||||
{
|
||||
return ((EntityValue)dimensionValue).getEntity().getCommandSenderWorld();
|
||||
return ((EntityValue) dimensionValue).getEntity().getCommandSenderWorld();
|
||||
}
|
||||
else if (dimensionValue instanceof BlockValue bv)
|
||||
{
|
||||
@@ -148,23 +158,16 @@ public class ValueConversions
|
||||
}
|
||||
else
|
||||
{
|
||||
String dimString = dimensionValue.getString().toLowerCase(Locale.ROOT);
|
||||
switch (dimString)
|
||||
{
|
||||
case "nether":
|
||||
case "the_nether":
|
||||
return server.getLevel(Level.NETHER);
|
||||
case "end":
|
||||
case "the_end":
|
||||
return server.getLevel(Level.END);
|
||||
case "overworld":
|
||||
case "over_world":
|
||||
return server.getLevel(Level.OVERWORLD);
|
||||
default:
|
||||
final String dimString = dimensionValue.getString().toLowerCase(Locale.ROOT);
|
||||
return switch (dimString) {
|
||||
case "nether", "the_nether" -> server.getLevel(Level.NETHER);
|
||||
case "end", "the_end" -> server.getLevel(Level.END);
|
||||
case "overworld", "over_world" -> server.getLevel(Level.OVERWORLD);
|
||||
default -> {
|
||||
ResourceKey<Level> dim = null;
|
||||
ResourceLocation id = new ResourceLocation(dimString);
|
||||
final ResourceLocation id = new ResourceLocation(dimString);
|
||||
// not using RegistryKey.of since that one creates on check
|
||||
for (ResourceKey<Level> world : (server.levelKeys()))
|
||||
for (final ResourceKey<Level> world : (server.levelKeys()))
|
||||
{
|
||||
if (id.equals(world.location()))
|
||||
{
|
||||
@@ -173,36 +176,48 @@ public class ValueConversions
|
||||
}
|
||||
}
|
||||
if (dim == null)
|
||||
{
|
||||
throw new ThrowStatement(dimString, Throwables.UNKNOWN_DIMENSION);
|
||||
return server.getLevel(dim);
|
||||
}
|
||||
}
|
||||
yield server.getLevel(dim);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static Value of(ResourceKey<?> dim)
|
||||
public static Value of(final ResourceKey<?> dim)
|
||||
{
|
||||
return of(dim.location());
|
||||
}
|
||||
|
||||
public static Value of(TagKey<?> tagKey) { return of(tagKey.location()); }
|
||||
public static Value of(final TagKey<?> tagKey)
|
||||
{
|
||||
return of(tagKey.location());
|
||||
}
|
||||
|
||||
public static Value of(ResourceLocation id)
|
||||
public static Value of(final ResourceLocation id)
|
||||
{
|
||||
if (id == null) // should be Value.NULL
|
||||
{
|
||||
return Value.NULL;
|
||||
}
|
||||
return new StringValue(simplify(id));
|
||||
}
|
||||
|
||||
public static String simplify(ResourceLocation id)
|
||||
public static String simplify(final ResourceLocation id)
|
||||
{
|
||||
if (id == null) // should be Value.NULL
|
||||
{
|
||||
return "";
|
||||
}
|
||||
if (id.getNamespace().equals("minecraft"))
|
||||
{
|
||||
return id.getPath();
|
||||
}
|
||||
return id.toString();
|
||||
}
|
||||
|
||||
public static Value of(GlobalPos pos)
|
||||
public static Value of(final GlobalPos pos)
|
||||
{
|
||||
return ListValue.of(
|
||||
ValueConversions.of(pos.dimension()),
|
||||
@@ -210,14 +225,13 @@ public class ValueConversions
|
||||
);
|
||||
}
|
||||
|
||||
public static Value fromPath(ServerLevel world, Path path)
|
||||
public static Value fromPath(final ServerLevel world, final Path path)
|
||||
{
|
||||
List<Value> nodes = new ArrayList<>();
|
||||
//for (PathNode node: path.getNodes())
|
||||
final List<Value> nodes = new ArrayList<>();
|
||||
for (int i = 0, len = path.getNodeCount(); i < len; i++)
|
||||
{
|
||||
Node node = path.getNode(i);
|
||||
nodes.add( ListValue.of(
|
||||
final Node node = path.getNode(i);
|
||||
nodes.add(ListValue.of(
|
||||
new BlockValue(null, world, node.asBlockPos()),
|
||||
new StringValue(node.type.name().toLowerCase(Locale.ROOT)),
|
||||
new NumericValue(node.costMalus),
|
||||
@@ -227,101 +241,106 @@ public class ValueConversions
|
||||
return ListValue.wrap(nodes);
|
||||
}
|
||||
|
||||
public static Value fromTimedMemory(Entity e, long expiry, Object v)
|
||||
public static Value fromTimedMemory(final Entity e, final long expiry, final Object v)
|
||||
{
|
||||
Value ret = fromEntityMemory(e, v);
|
||||
if (ret.isNull() || expiry == Long.MAX_VALUE) return ret;
|
||||
return ListValue.of(ret, new NumericValue(expiry));
|
||||
final Value ret = fromEntityMemory(e, v);
|
||||
return ret.isNull() || expiry == Long.MAX_VALUE ? ret : ListValue.of(ret, new NumericValue(expiry));
|
||||
}
|
||||
|
||||
private static Value fromEntityMemory(Entity e, Object v)
|
||||
private static Value fromEntityMemory(final Entity e, Object v)
|
||||
{
|
||||
if (v instanceof GlobalPos pos)
|
||||
{
|
||||
return of(pos);
|
||||
}
|
||||
if (v instanceof Entity)
|
||||
if (v instanceof final Entity entity)
|
||||
{
|
||||
return new EntityValue((Entity)v);
|
||||
return new EntityValue(entity);
|
||||
}
|
||||
if (v instanceof BlockPos)
|
||||
if (v instanceof final BlockPos pos)
|
||||
{
|
||||
return new BlockValue(null, (ServerLevel) e.getCommandSenderWorld(), (BlockPos) v);
|
||||
return new BlockValue(null, e.getCommandSenderWorld(), pos);
|
||||
}
|
||||
if (v instanceof Number)
|
||||
if (v instanceof final Number number)
|
||||
{
|
||||
return new NumericValue(((Number) v).doubleValue());
|
||||
return new NumericValue(number.doubleValue());
|
||||
}
|
||||
if (v instanceof Boolean)
|
||||
if (v instanceof final Boolean bool)
|
||||
{
|
||||
return BooleanValue.of((Boolean) v);
|
||||
return BooleanValue.of(bool);
|
||||
}
|
||||
if (v instanceof UUID)
|
||||
if (v instanceof final UUID uuid)
|
||||
{
|
||||
return ofUUID( (ServerLevel) e.getCommandSenderWorld(), (UUID)v);
|
||||
return ofUUID((ServerLevel) e.getCommandSenderWorld(), uuid);
|
||||
}
|
||||
if (v instanceof DamageSource source)
|
||||
if (v instanceof final DamageSource source)
|
||||
{
|
||||
return ListValue.of(
|
||||
new StringValue(source.getMsgId()),
|
||||
source.getEntity()==null?Value.NULL:new EntityValue(source.getEntity())
|
||||
source.getEntity() == null ? Value.NULL : new EntityValue(source.getEntity())
|
||||
);
|
||||
}
|
||||
if (v instanceof Path)
|
||||
if (v instanceof final Path path)
|
||||
{
|
||||
return fromPath((ServerLevel)e.getCommandSenderWorld(), (Path)v);
|
||||
return fromPath((ServerLevel) e.getCommandSenderWorld(), path);
|
||||
}
|
||||
if (v instanceof PositionTracker)
|
||||
if (v instanceof final PositionTracker tracker)
|
||||
{
|
||||
return new BlockValue(null, e.getCommandSenderWorld(), ((PositionTracker)v).currentBlockPosition());
|
||||
return new BlockValue(null, e.getCommandSenderWorld(), tracker.currentBlockPosition());
|
||||
}
|
||||
if (v instanceof WalkTarget)
|
||||
if (v instanceof final WalkTarget target)
|
||||
{
|
||||
return ListValue.of(
|
||||
new BlockValue(null, e.getCommandSenderWorld(), ((WalkTarget)v).getTarget().currentBlockPosition()),
|
||||
new NumericValue(((WalkTarget) v).getSpeedModifier()),
|
||||
new NumericValue(((WalkTarget) v).getCloseEnoughDist())
|
||||
new BlockValue(null, e.getCommandSenderWorld(), target.getTarget().currentBlockPosition()),
|
||||
new NumericValue(target.getSpeedModifier()),
|
||||
new NumericValue(target.getCloseEnoughDist())
|
||||
);
|
||||
}
|
||||
if (v instanceof NearestVisibleLivingEntities nvle) {
|
||||
if (v instanceof final NearestVisibleLivingEntities nvle)
|
||||
{
|
||||
v = StreamSupport.stream(nvle.findAll(entity -> true).spliterator(), false).toList();
|
||||
}
|
||||
if (v instanceof Set)
|
||||
if (v instanceof final Set<?> set)
|
||||
{
|
||||
v = new ArrayList<>(((Set<?>) v));
|
||||
v = new ArrayList<>(set);
|
||||
}
|
||||
if (v instanceof List<?> l)
|
||||
if (v instanceof final List<?> l)
|
||||
{
|
||||
if (l.isEmpty()) return ListValue.of();
|
||||
Object el = l.get(0);
|
||||
if (el instanceof Entity)
|
||||
if (l.isEmpty())
|
||||
{
|
||||
return ListValue.wrap(l.stream().map(o -> new EntityValue((Entity)o)));
|
||||
return ListValue.of();
|
||||
}
|
||||
if (el instanceof GlobalPos)
|
||||
final Object el = l.get(0);
|
||||
if (el instanceof final Entity entity)
|
||||
{
|
||||
return ListValue.wrap(l.stream().map(o -> of((GlobalPos) o)));
|
||||
return ListValue.wrap(l.stream().map(o -> new EntityValue(entity)));
|
||||
}
|
||||
if (el instanceof final GlobalPos pos)
|
||||
{
|
||||
return ListValue.wrap(l.stream().map(o -> of(pos)));
|
||||
}
|
||||
}
|
||||
return Value.NULL;
|
||||
}
|
||||
|
||||
private static Value ofUUID(ServerLevel entityWorld, UUID uuid)
|
||||
private static Value ofUUID(final ServerLevel entityWorld, final UUID uuid)
|
||||
{
|
||||
Entity current = entityWorld.getEntity(uuid);
|
||||
final Entity current = entityWorld.getEntity(uuid);
|
||||
return ListValue.of(
|
||||
current == null?Value.NULL:new EntityValue(current),
|
||||
current == null ? Value.NULL : new EntityValue(current),
|
||||
new StringValue(uuid.toString())
|
||||
);
|
||||
}
|
||||
public static Value of(AABB box)
|
||||
|
||||
public static Value of(final AABB box)
|
||||
{
|
||||
return ListValue.of(
|
||||
ListValue.fromTriple(box.minX, box.minY, box.minZ),
|
||||
ListValue.fromTriple(box.maxX, box.maxY, box.maxZ)
|
||||
);
|
||||
}
|
||||
public static Value of(BoundingBox box)
|
||||
|
||||
public static Value of(final BoundingBox box)
|
||||
{
|
||||
return ListValue.of(
|
||||
ListValue.fromTriple(box.minX(), box.minY(), box.minZ()),
|
||||
@@ -329,17 +348,23 @@ public class ValueConversions
|
||||
);
|
||||
}
|
||||
|
||||
public static Value of(StructureStart structure, final RegistryAccess regs)
|
||||
public static Value of(final StructureStart structure, final RegistryAccess regs)
|
||||
{
|
||||
if (structure == null || structure == StructureStart.INVALID_START) return Value.NULL;
|
||||
BoundingBox boundingBox = structure.getBoundingBox();
|
||||
if (boundingBox.maxX() < boundingBox.minX() || boundingBox.maxY() < boundingBox.minY() || boundingBox.maxZ() < boundingBox.minZ()) return Value.NULL;
|
||||
Map<Value, Value> ret = new HashMap<>();
|
||||
ret.put(new StringValue("box"), of(boundingBox));
|
||||
List<Value> pieces = new ArrayList<>();
|
||||
for (StructurePiece piece : structure.getPieces())
|
||||
if (structure == null || structure == StructureStart.INVALID_START)
|
||||
{
|
||||
BoundingBox box = piece.getBoundingBox();
|
||||
return Value.NULL;
|
||||
}
|
||||
final BoundingBox boundingBox = structure.getBoundingBox();
|
||||
if (boundingBox.maxX() < boundingBox.minX() || boundingBox.maxY() < boundingBox.minY() || boundingBox.maxZ() < boundingBox.minZ())
|
||||
{
|
||||
return Value.NULL;
|
||||
}
|
||||
final Map<Value, Value> ret = new HashMap<>();
|
||||
ret.put(new StringValue("box"), of(boundingBox));
|
||||
final List<Value> pieces = new ArrayList<>();
|
||||
for (final StructurePiece piece : structure.getPieces())
|
||||
{
|
||||
final BoundingBox box = piece.getBoundingBox();
|
||||
if (box.maxX() >= box.minX() && box.maxY() >= box.minY() && box.maxZ() >= box.minZ())
|
||||
{
|
||||
pieces.add(ListValue.of(
|
||||
@@ -354,41 +379,51 @@ public class ValueConversions
|
||||
return MapValue.wrap(ret);
|
||||
}
|
||||
|
||||
public static Value fromProperty(BlockState state, Property<?> p)
|
||||
public static Value fromProperty(final BlockState state, final Property<?> p)
|
||||
{
|
||||
Comparable<?> object = state.getValue(p);
|
||||
if (object instanceof Boolean || object instanceof Number) return StringValue.of(object.toString());
|
||||
if (object instanceof StringRepresentable)
|
||||
final Comparable<?> object = state.getValue(p);
|
||||
if (object instanceof Boolean || object instanceof Number)
|
||||
{
|
||||
return StringValue.of(((StringRepresentable) object).getSerializedName());
|
||||
return StringValue.of(object.toString());
|
||||
}
|
||||
throw new InternalExpressionException("Unknown property type: "+p.getName());
|
||||
if (object instanceof final StringRepresentable stringRepresentable)
|
||||
{
|
||||
return StringValue.of(stringRepresentable.getSerializedName());
|
||||
}
|
||||
throw new InternalExpressionException("Unknown property type: " + p.getName());
|
||||
}
|
||||
|
||||
record SlotParam(/* Nullable */ String type, int id) {
|
||||
public ListValue build() {
|
||||
record SlotParam(/* Nullable */ String type, int id)
|
||||
{
|
||||
public ListValue build()
|
||||
{
|
||||
return ListValue.of(StringValue.of(type), new NumericValue(id));
|
||||
}
|
||||
}
|
||||
|
||||
private static final Int2ObjectMap<SlotParam> slotIdsToSlotParams = new Int2ObjectOpenHashMap<>() {{
|
||||
private static final Int2ObjectMap<SlotParam> slotIdsToSlotParams = new Int2ObjectOpenHashMap<>()
|
||||
{{
|
||||
int n;
|
||||
//covers blocks, player hotbar and inventory, and all default inventories
|
||||
for(n = 0; n < 54; ++n) {
|
||||
for (n = 0; n < 54; ++n)
|
||||
{
|
||||
put(n, new SlotParam(null, n));
|
||||
}
|
||||
for(n = 0; n < 27; ++n) {
|
||||
for (n = 0; n < 27; ++n)
|
||||
{
|
||||
put(200 + n, new SlotParam("enderchest", n));
|
||||
}
|
||||
|
||||
// villager
|
||||
for(n = 0; n < 8; ++n) {
|
||||
for (n = 0; n < 8; ++n)
|
||||
{
|
||||
put(300 + n, new SlotParam(null, n));
|
||||
}
|
||||
|
||||
// horse, llamas, donkeys, etc.
|
||||
// two first slots are for saddle and armour
|
||||
for(n = 0; n < 15; ++n) {
|
||||
for (n = 0; n < 15; ++n)
|
||||
{
|
||||
put(500 + n, new SlotParam(null, n + 2));
|
||||
}
|
||||
// weapon main hand
|
||||
@@ -396,7 +431,8 @@ public class ValueConversions
|
||||
// offhand
|
||||
put(99, new SlotParam("equipment", 5));
|
||||
// feet, legs, chest, head
|
||||
for(n = 0; n < 4; ++n) {
|
||||
for (n = 0; n < 4; ++n)
|
||||
{
|
||||
put(100 + n, new SlotParam("equipment", n + 1));
|
||||
}
|
||||
//horse defaults saddle
|
||||
@@ -407,26 +443,25 @@ public class ValueConversions
|
||||
//hashMap.put("horse.chest", 499);
|
||||
}};
|
||||
|
||||
public static Value ofVanillaSlotResult(int itemSlot)
|
||||
public static Value ofVanillaSlotResult(final int itemSlot)
|
||||
{
|
||||
SlotParam ret = slotIdsToSlotParams.get(itemSlot);
|
||||
if (ret == null) return ListValue.of(Value.NULL, new NumericValue(itemSlot));
|
||||
return ret.build();
|
||||
final SlotParam ret = slotIdsToSlotParams.get(itemSlot);
|
||||
return ret == null ? ListValue.of(Value.NULL, new NumericValue(itemSlot)) : ret.build();
|
||||
}
|
||||
|
||||
public static Value ofBlockPredicate(RegistryAccess registryAccess, Predicate<BlockInWorld> blockPredicate)
|
||||
public static Value ofBlockPredicate(final RegistryAccess registryAccess, final Predicate<BlockInWorld> blockPredicate)
|
||||
{
|
||||
BlockPredicateInterface predicateData = (BlockPredicateInterface) blockPredicate;
|
||||
final BlockPredicateInterface predicateData = (BlockPredicateInterface) blockPredicate;
|
||||
final Registry<Block> blocks = registryAccess.registryOrThrow(Registries.BLOCK);
|
||||
return ListValue.of(
|
||||
predicateData.getCMBlockState()==null?Value.NULL:of(blocks.getKey(predicateData.getCMBlockState().getBlock())),
|
||||
predicateData.getCMBlockTagKey()==null?Value.NULL:of(blocks.getTag(predicateData.getCMBlockTagKey()).get().key()),
|
||||
predicateData.getCMBlockState() == null ? Value.NULL : of(blocks.getKey(predicateData.getCMBlockState().getBlock())),
|
||||
predicateData.getCMBlockTagKey() == null ? Value.NULL : of(blocks.getTag(predicateData.getCMBlockTagKey()).get().key()),
|
||||
MapValue.wrap(predicateData.getCMProperties()),
|
||||
predicateData.getCMDataTag() == null?Value.NULL:new NBTSerializableValue(predicateData.getCMDataTag())
|
||||
predicateData.getCMDataTag() == null ? Value.NULL : new NBTSerializableValue(predicateData.getCMDataTag())
|
||||
);
|
||||
}
|
||||
|
||||
public static ItemStack getItemStackFromValue(Value value, boolean withCount, RegistryAccess regs)
|
||||
public static ItemStack getItemStackFromValue(final Value value, final boolean withCount, final RegistryAccess regs)
|
||||
{
|
||||
if (value.isNull())
|
||||
{
|
||||
@@ -439,7 +474,7 @@ public class ValueConversions
|
||||
{
|
||||
if (list.length() != 3)
|
||||
{
|
||||
throw new ThrowStatement("item definition from list of size "+list.length(), Throwables.UNKNOWN_ITEM);
|
||||
throw new ThrowStatement("item definition from list of size " + list.length(), Throwables.UNKNOWN_ITEM);
|
||||
}
|
||||
final List<Value> items = list.getItems();
|
||||
name = items.get(0).getString();
|
||||
@@ -447,7 +482,7 @@ public class ValueConversions
|
||||
{
|
||||
count = NumericValue.asNumber(items.get(1)).getInt();
|
||||
}
|
||||
Value nbtValue = items.get(2);
|
||||
final Value nbtValue = items.get(2);
|
||||
if (!nbtValue.isNull())
|
||||
{
|
||||
nbtTag = ((NBTSerializableValue) NBTSerializableValue.fromValue(nbtValue)).getCompoundTag();
|
||||
@@ -457,12 +492,12 @@ public class ValueConversions
|
||||
{
|
||||
name = value.getString();
|
||||
}
|
||||
ItemInput itemInput = NBTSerializableValue.parseItem(name, nbtTag, regs);
|
||||
final ItemInput itemInput = NBTSerializableValue.parseItem(name, nbtTag, regs);
|
||||
try
|
||||
{
|
||||
return itemInput.createItemStack(count,false);
|
||||
return itemInput.createItemStack(count, false);
|
||||
}
|
||||
catch (CommandSyntaxException cse)
|
||||
catch (final CommandSyntaxException cse)
|
||||
{
|
||||
if (!withCount)
|
||||
{
|
||||
@@ -475,31 +510,56 @@ public class ValueConversions
|
||||
}
|
||||
}
|
||||
|
||||
public static Value guess(ServerLevel serverWorld, Object o) {
|
||||
public static Value guess(final ServerLevel serverWorld, final Object o)
|
||||
{
|
||||
if (o == null)
|
||||
{
|
||||
return Value.NULL;
|
||||
if (o instanceof List)
|
||||
return ListValue.wrap(((List<?>) o).stream().map(oo -> guess(serverWorld, oo)));
|
||||
if (o instanceof BlockPos)
|
||||
return new BlockValue(null, serverWorld, (BlockPos)o);
|
||||
if (o instanceof Entity)
|
||||
return EntityValue.of((Entity) o);
|
||||
if (o instanceof Vec3)
|
||||
return of((Vec3)o);
|
||||
if (o instanceof Vec3i)
|
||||
return of(new BlockPos((Vec3i)o));
|
||||
if (o instanceof AABB)
|
||||
return of((AABB)o);
|
||||
if (o instanceof BoundingBox)
|
||||
return of((BoundingBox) o);
|
||||
if (o instanceof ItemStack)
|
||||
return of((ItemStack)o, serverWorld.registryAccess());
|
||||
if (o instanceof Boolean)
|
||||
return BooleanValue.of((Boolean) o);
|
||||
if (o instanceof Number)
|
||||
return NumericValue.of((Number) o);
|
||||
if (o instanceof ResourceLocation)
|
||||
return of((ResourceLocation)o);
|
||||
}
|
||||
if (o instanceof final List<?> list)
|
||||
{
|
||||
return ListValue.wrap(list.stream().map(oo -> guess(serverWorld, oo)));
|
||||
}
|
||||
if (o instanceof final BlockPos pos)
|
||||
{
|
||||
return new BlockValue(null, serverWorld, pos);
|
||||
}
|
||||
if (o instanceof final Entity e)
|
||||
{
|
||||
return EntityValue.of(e);
|
||||
}
|
||||
if (o instanceof final Vec3 vec3)
|
||||
{
|
||||
return of(vec3);
|
||||
}
|
||||
if (o instanceof final Vec3i vec3i)
|
||||
{
|
||||
return of(new BlockPos(vec3i));
|
||||
}
|
||||
if (o instanceof final AABB aabb)
|
||||
{
|
||||
return of(aabb);
|
||||
}
|
||||
if (o instanceof final BoundingBox bb)
|
||||
{
|
||||
return of(bb);
|
||||
}
|
||||
if (o instanceof final ItemStack itemStack)
|
||||
{
|
||||
return of(itemStack, serverWorld.registryAccess());
|
||||
}
|
||||
if (o instanceof final Boolean bool)
|
||||
{
|
||||
return BooleanValue.of(bool);
|
||||
}
|
||||
if (o instanceof final Number number)
|
||||
{
|
||||
return NumericValue.of(number);
|
||||
}
|
||||
if (o instanceof final ResourceLocation resourceLocation)
|
||||
{
|
||||
return of(resourceLocation);
|
||||
}
|
||||
return StringValue.of(o.toString());
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user