diff --git a/docs/scarpet/api/Entities.md b/docs/scarpet/api/Entities.md index 47c79225..9d8c4473 100644 --- a/docs/scarpet/api/Entities.md +++ b/docs/scarpet/api/Entities.md @@ -422,6 +422,10 @@ Numbers related to player's xp. `xp` is the overall xp player has, `xp_level` is Number indicating remaining entity health, or `null` if not applicable. +### `query(e, 'language')` + +Returns `null` for any non-player entity, if not returns the player's language as a string. + ### `query(e, 'holds', slot?)` Returns triple of short name, stack count, and NBT of item held in `slot`, or `null` if nothing or not applicable. Available options for `slot` are: diff --git a/docs/scarpet/language/FunctionsAndControlFlow.md b/docs/scarpet/language/FunctionsAndControlFlow.md index a0a867e0..f60aa07e 100644 --- a/docs/scarpet/language/FunctionsAndControlFlow.md +++ b/docs/scarpet/language/FunctionsAndControlFlow.md @@ -267,6 +267,7 @@ but like everywhere else, doing that sounds like a bad idea. - `io_exception`: This is the parent for any exception that occurs due to an error handling external data. - `nbt_error`: Incorrect input/output NBT file. - `json_error`: Incorrect input/output JSON data. + - `b64_error`: Incorrect input/output b64 (base 64) string - `user_exception`: Exception thrown by default with `throw` function. Synopsis: diff --git a/docs/scarpet/language/LoopsAndHigherOrderFunctions.md b/docs/scarpet/language/LoopsAndHigherOrderFunctions.md index 33d52fa1..cc8bd883 100644 --- a/docs/scarpet/language/LoopsAndHigherOrderFunctions.md +++ b/docs/scarpet/language/LoopsAndHigherOrderFunctions.md @@ -146,3 +146,4 @@ it will be used from now on as a new value for the accumulator. reduce([1,2,3,4],_a+_,0) => 10 reduce([1,2,3,4],_a*_,1) => 24 + diff --git a/docs/scarpet/language/Operators.md b/docs/scarpet/language/Operators.md index 9bfdd110..cd6860d7 100644 --- a/docs/scarpet/language/Operators.md +++ b/docs/scarpet/language/Operators.md @@ -243,16 +243,34 @@ identical with `fun(1, 2, 3)`. For maps, it unpacks them to a list of key-value In function signatures it identifies a vararg parameter. -`fun(a, b, ... rest) -> [a, b, rest]; fun(1, 2, 3, 4)` => `[1, 2, [3, 4]]` +
+fun(a, b, ... rest) -> [a, b, rest]; fun(1, 2, 3, 4)    => [1, 2, [3, 4]]
+
Effects of `...` can be surprisingly lasting. It is kept through the use of variables and function calls. -`fun(a, b, ... rest) -> [a, b, ... rest]; fun(1, 2, 3, 4)` => `[1, 2, 3, 4]` -`args() -> ... [1, 2, 3]; sum(a, b, c) -> a+b+c; sum(args())` => `6` -`a = ... [1, 2, 3]; sum(a, b, c) -> a+b+c; sum(a)` => `6` +
+fun(a, b, ... rest) -> [a, b, ... rest]; fun(1, 2, 3, 4)    => [1, 2, 3, 4]
+args() -> ... [1, 2, 3]; sum(a, b, c) -> a+b+c; sum(args())   => 6
+a = ... [1, 2, 3]; sum(a, b, c) -> a+b+c; sum(a)   => 6
+
Unpacking mechanics can be used for list and map constriction, not just for function calls. -`[...range(5), pi, ...range(5,-1,-1)]` => `[0, 1, 2, 3, 4, 3.14159265359, 5, 4, 3, 2, 1, 0]` -`{ ... map(range(5), _ -> _*_ )}` => `{0: 0, 1: 1, 2: 4, 3: 9, 4: 16}` -`{...{1 -> 2, 3 -> 4}, ...{5 -> 6, 7 -> 8}}` => `{1: 2, 3: 4, 5: 6, 7: 8}` +
+[...range(5), pi, ...range(5,-1,-1)]   => [0, 1, 2, 3, 4, 3.14159265359, 5, 4, 3, 2, 1, 0]
+{ ... map(range(5),  _  -> _*_ )}   => {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
+{...{1 -> 2, 3 -> 4}, ...{5 -> 6, 7 -> 8}}   => {1: 2, 3: 4, 5: 6, 7: 8}
+
+ +Fine print: unpacking of argument lists happens just before functions are evaluated. +This means that in some situations, for instance +when an expression is expected (`map(list, expr)`), or a function should not evaluate some (most!) of its arguments (`if(...)`), +unpacking cannot be used, and will be ignored, leaving `... list` identical to `list`. +Functions that don't honor unpacking mechanics, should have no use for it at the first place + (i.e. have one, or very well-defined, and very specific parameters), +so some caution (prior testing) is advised. Some of these multi-argument built-in functions are + `if`, `try`, `sort_key`, `system_variable_get`, `synchronize`, `sleep`, `in_dimension`, +all container functions (`get`, `has`, `put`, `delete`), +and all loop functions (`while`, `loop`, `map`, `filter`, `first`, `all`, `c_for`, `for` and`reduce`). + diff --git a/docs/scarpet/language/SystemFunctions.md b/docs/scarpet/language/SystemFunctions.md index 49690f57..da4389fa 100644 --- a/docs/scarpet/language/SystemFunctions.md +++ b/docs/scarpet/language/SystemFunctions.md @@ -350,6 +350,15 @@ This will give you a date: It is currently `hrs`:`mins` and `secs` seconds on the `date`th of `month`, `year` +### `encode_b64(string)`, `decode_b64(string)` + +Encode or decode a string from b64, throwing a `b64_error` exception if it's invalid + +### `encode_json(value)`, `decode_json(string)` + +Encodes a value as a json string, and decodes a json string as a valid value, throwing a `json_error` exception if it +doesn't parse properly + ### `profile_expr(expression)` Returns number of times given expression can be run in 50ms time. Useful to profile and optimize your code. diff --git a/src/main/java/carpet/fakes/ClientSettingsC2SPacketInterface.java b/src/main/java/carpet/fakes/ClientSettingsC2SPacketInterface.java new file mode 100644 index 00000000..c881ee95 --- /dev/null +++ b/src/main/java/carpet/fakes/ClientSettingsC2SPacketInterface.java @@ -0,0 +1,5 @@ +package carpet.fakes; + +public interface ClientSettingsC2SPacketInterface { + public String getLanguage(); +} diff --git a/src/main/java/carpet/fakes/ServerPlayerEntityInterface.java b/src/main/java/carpet/fakes/ServerPlayerEntityInterface.java index 9fd69a30..173081b1 100644 --- a/src/main/java/carpet/fakes/ServerPlayerEntityInterface.java +++ b/src/main/java/carpet/fakes/ServerPlayerEntityInterface.java @@ -7,5 +7,5 @@ public interface ServerPlayerEntityInterface EntityPlayerActionPack getActionPack(); void invalidateEntityObjectReference(); boolean isInvalidEntityObject(); - + String getLanguage(); } diff --git a/src/main/java/carpet/mixins/ClientSettingsC2SPacket_scarpetMixin.java b/src/main/java/carpet/mixins/ClientSettingsC2SPacket_scarpetMixin.java new file mode 100644 index 00000000..66a722c8 --- /dev/null +++ b/src/main/java/carpet/mixins/ClientSettingsC2SPacket_scarpetMixin.java @@ -0,0 +1,18 @@ +package carpet.mixins; + +import carpet.fakes.ClientSettingsC2SPacketInterface; +import net.minecraft.network.packet.c2s.play.ClientSettingsC2SPacket; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(ClientSettingsC2SPacket.class) +public class ClientSettingsC2SPacket_scarpetMixin implements ClientSettingsC2SPacketInterface { + @Shadow + private String language; + + @Override + public String getLanguage(){ + return this.language; + } +} + diff --git a/src/main/java/carpet/mixins/ServerPlayerEntity_scarpetEventMixin.java b/src/main/java/carpet/mixins/ServerPlayerEntity_scarpetEventMixin.java index b1b375f9..6ef2e4e0 100644 --- a/src/main/java/carpet/mixins/ServerPlayerEntity_scarpetEventMixin.java +++ b/src/main/java/carpet/mixins/ServerPlayerEntity_scarpetEventMixin.java @@ -1,5 +1,6 @@ package carpet.mixins; +import carpet.fakes.ClientSettingsC2SPacketInterface; import carpet.fakes.EntityInterface; import carpet.fakes.ServerPlayerEntityInterface; import carpet.script.EntityEventsGroup; @@ -7,6 +8,7 @@ import com.mojang.authlib.GameProfile; import net.minecraft.entity.Entity; import net.minecraft.entity.damage.DamageSource; import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.network.packet.c2s.play.ClientSettingsC2SPacket; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.world.ServerWorld; import net.minecraft.stat.Stat; @@ -126,4 +128,16 @@ public abstract class ServerPlayerEntity_scarpetEventMixin extends PlayerEntity { return isInvalidReference; } + + //getting player language + private String language; + + public String getLanguage(){ + return this.language; + } + + @Inject(method = "setClientSettings", at = @At("HEAD")) + public void setClientSettings(ClientSettingsC2SPacket packet, CallbackInfo ci){ + this.language = ((ClientSettingsC2SPacketInterface) packet).getLanguage(); + } } diff --git a/src/main/java/carpet/script/exception/Throwables.java b/src/main/java/carpet/script/exception/Throwables.java index 3f811cfe..fa751b2d 100644 --- a/src/main/java/carpet/script/exception/Throwables.java +++ b/src/main/java/carpet/script/exception/Throwables.java @@ -27,6 +27,7 @@ public class Throwables { 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); /** diff --git a/src/main/java/carpet/script/language/DataStructures.java b/src/main/java/carpet/script/language/DataStructures.java index b97d3b49..6b75e624 100644 --- a/src/main/java/carpet/script/language/DataStructures.java +++ b/src/main/java/carpet/script/language/DataStructures.java @@ -4,6 +4,9 @@ import carpet.script.Context; import carpet.script.Expression; import carpet.script.LazyValue; import carpet.script.exception.InternalExpressionException; +import carpet.script.exception.ThrowStatement; +import carpet.script.exception.Throwables; +import carpet.script.utils.ScarpetJsonDeserializer; import carpet.script.value.ContainerValueInterface; import carpet.script.value.LContainerValue; import carpet.script.value.LazyListValue; @@ -12,8 +15,12 @@ import carpet.script.value.MapValue; import carpet.script.value.NumericValue; import carpet.script.value.StringValue; import carpet.script.value.Value; +import com.google.gson.JsonParseException; +import com.google.gson.JsonParser; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; +import java.util.Base64; import java.util.Collections; import java.util.List; import java.util.stream.Collectors; @@ -320,5 +327,23 @@ public class DataStructures { Value ret = new NumericValue(((ContainerValueInterface) container).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()))); + expression.addUnaryFunction("decode_b64", v -> { + try { + return StringValue.of(new String(Base64.getDecoder().decode(v.getString()), StandardCharsets.ISO_8859_1));//using this charset cos it's the one used in decoding function + } catch (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 { + return new ScarpetJsonDeserializer().deserialize(new JsonParser().parse(v.getString()), null, null); + } catch (JsonParseException jpe){ + throw new ThrowStatement("Invalid json string: " + v.getString(), Throwables.JSON_ERROR); + } + }); } } diff --git a/src/main/java/carpet/script/value/EntityValue.java b/src/main/java/carpet/script/value/EntityValue.java index 490cd9a4..b6eab7f1 100644 --- a/src/main/java/carpet/script/value/EntityValue.java +++ b/src/main/java/carpet/script/value/EntityValue.java @@ -551,6 +551,12 @@ public class EntityValue extends Value }); put("air", (e, a) -> new NumericValue(e.getAir())); + put("language", (e, a)->{ + if(!(e instanceof ServerPlayerEntity)) + return NULL; + String lang = ((ServerPlayerEntityInterface) e).getLanguage(); + return StringValue.of(lang); + }); put("persistence", (e, a) -> { if (e instanceof MobEntity) return new NumericValue(((MobEntity) e).isPersistent()); return Value.NULL; diff --git a/src/main/resources/carpet.mixins.json b/src/main/resources/carpet.mixins.json index e2fd33b2..8186b459 100644 --- a/src/main/resources/carpet.mixins.json +++ b/src/main/resources/carpet.mixins.json @@ -75,6 +75,7 @@ "ExperienceOrbEntityMixin", "PlayerActionC2SPacketMixin", "ServerPlayerEntityMixin", + "ClientSettingsC2SPacket_scarpetMixin", "PlayerManager_coreMixin", "PlayerManager_fakePlayersMixin", "RedstoneWireBlockMixin",