From 239aa9e14336064b51d635e8b76194e1567cb7d5 Mon Sep 17 00:00:00 2001 From: gnembon Date: Mon, 12 Oct 2020 23:01:44 -0300 Subject: [PATCH] added `system_info`, deprecated `seed` --- docs/scarpet/Full.md | 61 ++++++++- docs/scarpet/api/Auxiliary.md | 52 +++++++- docs/scarpet/resources/editors/idea/2.txt | 2 +- .../scarpet/resources/editors/npp/scarpet.xml | 4 +- src/main/java/carpet/script/ScriptHost.java | 9 ++ .../java/carpet/script/api/Monitoring.java | 17 +++ .../java/carpet/script/utils/SystemInfo.java | 124 ++++++++++++++++++ 7 files changed, 258 insertions(+), 11 deletions(-) create mode 100644 src/main/java/carpet/script/utils/SystemInfo.java diff --git a/docs/scarpet/Full.md b/docs/scarpet/Full.md index 69b6e7e9..87375758 100644 --- a/docs/scarpet/Full.md +++ b/docs/scarpet/Full.md @@ -309,8 +309,10 @@ block positions. All variables starting with `_` are read-only, and cannot be de `scarpet` accepts numeric and string liters constants. Numbers look like `1, 2.5, -3e-7, 0xff,` and are internally represented primarily as Java's `double` but `scarpet` will try to trim trailing zeros as much as possible so if you -need to use them as integers or even longs - you can. However any operation on a number even if it can be properly -represented as long, but not a double type, will make them round up to be doubles. +need to use them as integers or even longs - you can. Long values will also not loose their long precision in addition, +subtraction, negation and multiplication, however any other operation that is not guaranteed to return a long value +(like division) on a number even if it can be properly +represented as long, will make them convert to doubles. Strings use single quoting, for multiple reasons, but primarily to allow for easier use of strings inside doubly quoted command arguments (when passing a script as a parameter of `/script fill` @@ -3356,7 +3358,7 @@ stack at slot. Returns previous stack in that slot.
 inventory_set(player(), 0, 0) => ['stone', 64, {}] // player had a stack of stone in first hotbar slot
 inventory_set(player(), 0, 6) => ['diamond', 64, {}] // changed stack of diamonds in player slot to 6
-inventory_set(player(), 0, 1, 'diamond_axe','{Damage:5}') => null //added slightly damaged pick to first player slot
+inventory_set(player(), 0, 1, 'diamond_axe','{Damage:5}') => null //added slightly damaged diamond axe to first player slot
 
### `inventory_find(inventory, item, start_slot?, ), inventory_find(inventory, null, start_slot?)` @@ -4015,9 +4017,10 @@ loop(1000,game_tick()) // runs the game as fast as it can for 1000 ticks loop(1000,game_tick(100)) // runs the game twice as slow for 1000 ticks -### `seed()` -Returns current world seed. +### `seed()` deprecated + +Returns current world seed. Function is deprecated, use `system_info('world_seed')` insteads. ### `current_dimension()` @@ -4064,7 +4067,53 @@ The call will return `null` if the statistics options are incorrect, or player d If the player encountered the statistic, or game created for him empty one, it will return a number. Scarpet will not affect the entries of the statistics, even if it is just creating empty ones. With `null` response it could either mean your input is wrong, or statistic effectively has a value of `0`. -# `/script run` command + + +### `system_info()`, `system_info(property)` +Fetches the value of a system property or returns all inforation as a map when called without any arguments. It can be used to +fetch various information, mostly not changing, or only available via low level +system calls. In all cirumstances, these are only provided as read-only. + +Available options in the scarpet app space: + * `app_name` - current app name or `null` if its a default app + * `app_list` - list of all loaded apps excluding default commandline app + * `app_scope` - scope of the global variables and function. Available options is `player` and `global` + * `app_player` - returns a player list that have app run under them. For `global` apps, the list is always empty + + Relevant world related properties + * `world_name` - name of the world + * `world_seed` - a numeric seed of the world + * `world_path` - full path to the world saves folder + * `world_folder` - name of the direct folder in the saves that holds world files + + Relevant gameplay related properties + * `game_difficulty` - current difficulty of the game: `'peacefu'`, `'easy'`, `'normal'`, or `'hard'` + * `game_hardcore` - boolean whether the game is in hardcore mode + * `game_storage_format` - format of the world save files, either `'McRegion'` or `'Anvil'` + * `game_default_gamemode` - default gamemode for new players + * `game_max_players` - max allowed players when joining the world + * `game_view_distance` - the view distance + * `game_mod_name` - the name of the base mod. Expect `'fabric'` + * `game_version` - base version of the game + + Server related properties + + * `server_motd` - the motd of the server visible when joining + * `server_ip` - IP adress of the game hosted + * `server_whitelisted` - boolean indicating whether the access to the server is only for whitelisted players + * `server_whitelist` - list of players allowed to log in + * `server_banned_players` - list of banned player names + * `server_banned_ips` - list of banned IP addresses + + System related properties + * `java_max_memory` - maximum allowed memory accessible by JVM + * `java_allocated_memory` - currently allocated memory by JVM + * `java_used_memory` - currently used memory by JVM + * `java_cpu_count` - number of processors + * `java_version` - version of Java + * `java_bits` - number indicating how many bits the Java has, 32 or 64 + * `java_system_cpu_load` - current percentage of CPU used by the system + * `java_process_cpu_load` - current percentage of CPU used by JVM# `/script run` command Primary way to input commands. The command executes in the context, position, and dimension of the executing player, commandblock, etc... The command receives 4 variables, `x`, `y`, `z` and `p` indicating position and diff --git a/docs/scarpet/api/Auxiliary.md b/docs/scarpet/api/Auxiliary.md index 38644763..4cccc0d3 100644 --- a/docs/scarpet/api/Auxiliary.md +++ b/docs/scarpet/api/Auxiliary.md @@ -401,9 +401,10 @@ loop(1000,game_tick()) // runs the game as fast as it can for 1000 ticks loop(1000,game_tick(100)) // runs the game twice as slow for 1000 ticks -### `seed()` -Returns current world seed. +### `seed()` deprecated + +Returns current world seed. Function is deprecated, use `system_info('world_seed')` insteads. ### `current_dimension()` @@ -450,3 +451,50 @@ The call will return `null` if the statistics options are incorrect, or player d If the player encountered the statistic, or game created for him empty one, it will return a number. Scarpet will not affect the entries of the statistics, even if it is just creating empty ones. With `null` response it could either mean your input is wrong, or statistic effectively has a value of `0`. + + +### `system_info()`, `system_info(property)` +Fetches the value of a system property or returns all inforation as a map when called without any arguments. It can be used to +fetch various information, mostly not changing, or only available via low level +system calls. In all cirumstances, these are only provided as read-only. + +Available options in the scarpet app space: + * `app_name` - current app name or `null` if its a default app + * `app_list` - list of all loaded apps excluding default commandline app + * `app_scope` - scope of the global variables and function. Available options is `player` and `global` + * `app_player` - returns a player list that have app run under them. For `global` apps, the list is always empty + + Relevant world related properties + * `world_name` - name of the world + * `world_seed` - a numeric seed of the world + * `world_path` - full path to the world saves folder + * `world_folder` - name of the direct folder in the saves that holds world files + + Relevant gameplay related properties + * `game_difficulty` - current difficulty of the game: `'peacefu'`, `'easy'`, `'normal'`, or `'hard'` + * `game_hardcore` - boolean whether the game is in hardcore mode + * `game_storage_format` - format of the world save files, either `'McRegion'` or `'Anvil'` + * `game_default_gamemode` - default gamemode for new players + * `game_max_players` - max allowed players when joining the world + * `game_view_distance` - the view distance + * `game_mod_name` - the name of the base mod. Expect `'fabric'` + * `game_version` - base version of the game + + Server related properties + + * `server_motd` - the motd of the server visible when joining + * `server_ip` - IP adress of the game hosted + * `server_whitelisted` - boolean indicating whether the access to the server is only for whitelisted players + * `server_whitelist` - list of players allowed to log in + * `server_banned_players` - list of banned player names + * `server_banned_ips` - list of banned IP addresses + + System related properties + * `java_max_memory` - maximum allowed memory accessible by JVM + * `java_allocated_memory` - currently allocated memory by JVM + * `java_used_memory` - currently used memory by JVM + * `java_cpu_count` - number of processors + * `java_version` - version of Java + * `java_bits` - number indicating how many bits the Java has, 32 or 64 + * `java_system_cpu_load` - current percentage of CPU used by the system + * `java_process_cpu_load` - current percentage of CPU used by JVM \ No newline at end of file diff --git a/docs/scarpet/resources/editors/idea/2.txt b/docs/scarpet/resources/editors/idea/2.txt index 0f9fc736..4deb4e35 100644 --- a/docs/scarpet/resources/editors/idea/2.txt +++ b/docs/scarpet/resources/editors/idea/2.txt @@ -83,7 +83,6 @@ scoreboard_add scoreboard_remove scoreboard_display see_sky -seed set set_biome set_poi @@ -100,6 +99,7 @@ structures structure_eligibility structure_references suffocates +system_info tick_time ticks_randomly top diff --git a/docs/scarpet/resources/editors/npp/scarpet.xml b/docs/scarpet/resources/editors/npp/scarpet.xml index 5831aa62..21a37c1d 100644 --- a/docs/scarpet/resources/editors/npp/scarpet.xml +++ b/docs/scarpet/resources/editors/npp/scarpet.xml @@ -68,9 +68,9 @@ scan volume neighbours rect diamond - format logger sound particle particle_line particle_rect + format logger sound particle particle_line particle_rect system_info - run save tick_time world_time day_time last_tick_times game_tick seed current_dimension in_dimension view_distance + run save tick_time world_time day_time last_tick_times game_tick current_dimension in_dimension view_distance plop harvest destroy schedule nbt draw_shape create_marker remove_all_markers diff --git a/src/main/java/carpet/script/ScriptHost.java b/src/main/java/carpet/script/ScriptHost.java index abdaa8fd..536bd4fb 100644 --- a/src/main/java/carpet/script/ScriptHost.java +++ b/src/main/java/carpet/script/ScriptHost.java @@ -14,6 +14,7 @@ import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Random; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Predicate; import java.util.stream.Stream; @@ -331,6 +332,14 @@ public abstract class ScriptHost perUser = isPerUser; } + public boolean isPerUser() {return perUser;} + + public Set getUserList() + { + return userHosts.keySet(); + } + + public void resetErrorSnooper() { errorSnooper=null; diff --git a/src/main/java/carpet/script/api/Monitoring.java b/src/main/java/carpet/script/api/Monitoring.java index 1a8ee653..a3b97b9b 100644 --- a/src/main/java/carpet/script/api/Monitoring.java +++ b/src/main/java/carpet/script/api/Monitoring.java @@ -5,6 +5,7 @@ import carpet.script.CarpetContext; import carpet.script.Expression; import carpet.script.LazyValue; import carpet.script.exception.InternalExpressionException; +import carpet.script.utils.SystemInfo; import carpet.script.value.ListValue; import carpet.script.value.MapValue; import carpet.script.value.NumericValue; @@ -21,8 +22,24 @@ import java.util.Locale; import java.util.Map; public class Monitoring { + public static void apply(Expression expression) { + expression.addLazyFunction("system_info", -1, (c, t, lv) -> + { + if (lv.size() == 0) + { + Value ret = SystemInfo.getAll((CarpetContext) c); + return (cc, tt) -> ret; + } + if (lv.size() == 1) { + String what = lv.get(0).evalValue(c).getString(); + Value res = SystemInfo.get(what, (CarpetContext) c); + if (res == null) throw new InternalExpressionException("Unknown option for 'system_info': " + what); + return (cc, tt) -> res; + } + throw new InternalExpressionException("'system_info' requires one or no parameters"); + }); // game processed snooper functions expression.addLazyFunction("get_mob_counts", -1, (c, t, lv) -> { diff --git a/src/main/java/carpet/script/utils/SystemInfo.java b/src/main/java/carpet/script/utils/SystemInfo.java new file mode 100644 index 00000000..87c9e0d7 --- /dev/null +++ b/src/main/java/carpet/script/utils/SystemInfo.java @@ -0,0 +1,124 @@ +package carpet.script.utils; + +import carpet.script.CarpetContext; +import carpet.script.CarpetScriptHost; +import carpet.script.value.ListValue; +import carpet.script.value.MapValue; +import carpet.script.value.NumericValue; +import carpet.script.value.StringValue; +import carpet.script.value.Value; +import com.sun.management.OperatingSystemMXBean; +import net.minecraft.util.WorldSavePath; + +import java.lang.management.ManagementFactory; +import java.nio.file.Path; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.function.Function; +import java.util.stream.Collectors; + +public class SystemInfo { + private static final Map> options = new HashMap>(){{ + put("app_name", c -> + { + String name = c.host.getName(); + return name == null?Value.NULL:new StringValue(name); + }); + put("app_list", c -> ListValue.wrap(((CarpetScriptHost)c.host).getScriptServer().modules.keySet().stream().filter(Objects::nonNull).map(StringValue::new).collect(Collectors.toList()))); + put("app_scope", c -> StringValue.of((c.host).isPerUser()?"player":"global")); + put("app_players", c -> ListValue.wrap(c.host.getUserList().stream().map(StringValue::new).collect(Collectors.toList()))); + + put("world_name", c -> new StringValue(c.s.getMinecraftServer().getSaveProperties().getLevelName())); + put("world_seed", c -> new NumericValue(c.s.getWorld().getSeed())); + put("server_motd", c -> StringValue.of(c.s.getMinecraftServer().getServerMotd())); + put("world_path", c -> StringValue.of(c.s.getMinecraftServer().getSavePath(WorldSavePath.ROOT).toString())); + put("world_folder", c -> { + Path serverPath = c.s.getMinecraftServer().getSavePath(WorldSavePath.ROOT); + int nodeCount = serverPath.getNameCount(); + if (nodeCount < 2) return Value.NULL; + String tlf = serverPath.getName(nodeCount-2).toString(); + return StringValue.of(tlf); + }); + put("game_difficulty", c -> StringValue.of(c.s.getMinecraftServer().getSaveProperties().getDifficulty().getName())); + put("game_hardcore", c -> new NumericValue(c.s.getMinecraftServer().getSaveProperties().isHardcore())); + put("game_storage_format", c -> StringValue.of(c.s.getMinecraftServer().getSaveProperties().getFormatName(c.s.getMinecraftServer().getSaveProperties().getVersion()))); + put("game_default_gamemode", c -> StringValue.of(c.s.getMinecraftServer().getDefaultGameMode().getName())); + put("game_max_players", c -> new NumericValue(c.s.getMinecraftServer().getMaxPlayerCount())); + put("game_view_distance", c -> new NumericValue(c.s.getMinecraftServer().getPlayerManager().getViewDistance())); + put("game_mod_name", c -> StringValue.of(c.s.getMinecraftServer().getServerModName())); + put("game_version", c -> StringValue.of(c.s.getMinecraftServer().getVersion())); + + put("server_ip", c -> StringValue.of(c.s.getMinecraftServer().getServerIp())); + put("server_whitelisted", c -> new NumericValue(c.s.getMinecraftServer().isEnforceWhitelist())); + put("server_whitelist", c -> { + MapValue whitelist = new MapValue(Collections.emptyList()); + for (String s: c.s.getMinecraftServer().getPlayerManager().getWhitelistedNames()) + { + whitelist.append(StringValue.of(s)); + } + return whitelist; + }); + put("server_banned_players", c -> { + MapValue whitelist = new MapValue(Collections.emptyList()); + for (String s: c.s.getMinecraftServer().getPlayerManager().getUserBanList().getNames()) + { + whitelist.append(StringValue.of(s)); + } + return whitelist; + }); + put("server_banned_ips", c -> { + MapValue whitelist = new MapValue(Collections.emptyList()); + for (String s: c.s.getMinecraftServer().getPlayerManager().getIpBanList().getNames()) + { + whitelist.append(StringValue.of(s)); + } + return whitelist; + }); + + 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_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"}) + { + 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( + OperatingSystemMXBean.class); + return new NumericValue(osBean.getSystemCpuLoad()); + }); + put("java_process_cpu_load", c -> { + OperatingSystemMXBean osBean = ManagementFactory.getPlatformMXBean( + OperatingSystemMXBean.class); + return new NumericValue(osBean.getProcessCpuLoad()); + }); + + + + + + + + + + }}; + public static Value get(String what, CarpetContext cc) + { + return options.getOrDefault(what, c -> null).apply(cc); + } + public static Value getAll(CarpetContext cc) + { + return MapValue.wrap(options.entrySet().stream().collect(Collectors.toMap(e -> new StringValue(e.getKey()), e -> e.getValue().apply(cc)))); + } + +}