diff --git a/docs/scarpet/Full.md b/docs/scarpet/Full.md index bd8c1ae3..c21745b7 100644 --- a/docs/scarpet/Full.md +++ b/docs/scarpet/Full.md @@ -804,6 +804,9 @@ a = 3; task_thread('temp', _(outer(a), b) -> foo(a,b), 5) => Another example of running the same thing passing arguments using closure over anonymous function as well as passing a parameter. +In case you want to create a task based on a function that is not defined in your module, please read the tips on + "Passing function references to other modules of your application" section in the `call(...)` section. + ### `sleep()` `sleep(timeout)`, `sleep(timeout, close_expr)` @@ -1373,7 +1376,7 @@ foo(... x) -> ... # all arguments for foo are included in the list ### `import(module_name, symbols ...)` Imports symbols from other apps and libraries into the current one: global variables or functions, allowing to use -them in the current app. This include other symbols imported by these modules. Scarpet supports cicular dependencies, +them in the current app. This includes other symbols imported by these modules. Scarpet supports circular dependencies, but if symbols are used directly in the module body rather than functions, it may not be able to retrieve them. Returns full list of available symbols that could be imported from this module, which can be used to debug import issues, and list contents of libraries. @@ -1382,9 +1385,57 @@ issues, and list contents of libraries. calls a user defined function with specified arguments. It is equivalent to calling `function(args...)` directly except you can use it with function value, or name instead. This means you can pass functions to other user defined -functions as arguments and call them with `call` internally. And since function definitions return the defined +functions as arguments and call them with `call` internally. Since function definitions return the defined function, they can be defined in place as anonymous functions. +#### Passing function references to other modules of your application + +In case a function is defined by its name, Scarpet will attempt to resolve its definition in the given module and its imports, +meaning if the call is in a imported library, and not in the main module of your app, and that function is not visible from the +library perspective, but in the app, it won't be call-able. In case you pass a function name to a separate module in your app, +it should import back that method from the main module for visibility. + +Check an example of a problematic code of a library that expects a function value as a passed argument and how it is called in +the parent app: +``` +//app.sc +import('lib', 'callme'); +foo(x) -> x*x; +test() -> callme('foo' , 5); +``` +``` +//lib.scl +callme(fun, arg) -> call(fun, arg); +``` + +In this case `'foo'` will fail to dereference in `lib` as it is not visible by name. In tightly coupled modules, where `lib` is just +a component of your `app` you can use circular import to acknowledge the symbol from the other module (pretty much like +imports in Java classes), and that solves the issue but makes the library dependent on the main app: +``` +//lib.scl +import('app','foo'); +callme(fun, arg) -> call(fun, arg); +``` +You can circumvent that issue by explicitly dereferencing the local function where it is used as a lambda argument created +in the module in which the requested function is visible: +``` +//app.sc +import('lib', 'callme'); +foo(x) -> x*x; +test() -> callme(_(x) -> foo(x), 5); +``` +``` +//lib.scl +callme(fun, arg) -> call(fun, arg); +``` +Or by passing an explicit reference to the function, instead of calling it by name: +``` +//app.sc +import('lib', 'callme'); +global_foohandler = (foo(x) -> x*x); +test() -> callme(global_foohandler, 5); +``` + Little technical note: the use of `_` in expression passed to built in functions is much more efficient due to not creating new call stacks for each invoked function, but anonymous functions is the only mechanism available for programmers with their own lambda arguments @@ -2184,6 +2235,16 @@ Causes a block to be harvested by a specified player entity. Honors player item tool if applicable. If the entity is not a valid player, no block gets destroyed. If a player is not allowed to break that block, a block doesn't get destroyed either. +### `weather()`,`weather(type)`,`weather(type, ticks)` + +If called with no args, returns `'clear'`, `'rain` or `'thunder'` based on the current weather. If thundering, will +always return `'thunder'`, if not will return `'rain'` or `'clear'` based on the current weather. + +With one arg, (either `'clear'`, `'rain` or `'thunder'`), returns the number of remaining ticks for that weather type. +NB: It can thunder without there being a thunderstorm, there has to be both rain and thunder to form a storm. + +With two args, sets the weather to `type` for `ticks` ticks. + ## Block and World querying ### `pos(block), pos(entity)` @@ -2754,14 +2815,16 @@ the future. These functions help scan larger areas of blocks without using generic loop functions, like nested `loop`. -### `scan(center, range, lower_range?, expr)` +### `scan(center, range, upper_range?, expr)` Evaluates expression over area of blocks defined by its center `center = (cx, cy, cz)`, expanded in all directions by `range = (dx, dy, dz)` blocks, or optionally in negative with `range` coords, and `upper_range` coords in -positive values. -`center` can be defined either as three coordinates, a list of three coords, or a block value. -`range` and `lower_range` can have the same representations, just if its a block, it computes the distance to the center +positive values, so you can use that if you know the lower coord, and dimension by calling `'scan(center, 0, 0, 0, w, h, d, ...)`. + +`center` can be defined either as three coordinates, a single tuple of three coords, or a block value. +`range` and `upper_range` can have the same representations, just if they are block values, it computes the distance to the center as range instead of taking the values as is. + `expr` receives `_x, _y, _z` as coords of current analyzed block and `_`, which represents the block itself. Returns number of successful evaluations of `expr` (with `true` boolean result) unless called in void context, @@ -2786,7 +2849,7 @@ Returns the list of 6 neighbouring blocks to the argument. Commonly used with ot for(neighbours(x,y,z),air(_)) => 4 // number of air blocks around a block -### `rect(centre, range?, positive_range?)` +### `rect(centre, range?, upper_range?)` Returns an iterator, just like `range` function that iterates over a rectangular area of blocks. If only center point is specified, it iterates over 27 blocks. If `range` arguments are specified, expands selection by the respective @@ -2794,7 +2857,7 @@ number of blocks in each direction. If `positive_range` arguments are specified, it uses `range` for negative offset, and `positive_range` for positive. `centre` can be defined either as three coordinates, a list of three coords, or a block value. -`range` and `positive_range` can have the same representations, just if its a block, it computes the distance to the center +`range` and `positive_range` can have the same representations, just if they are block values, it computes the distance to the center as range instead of taking the values as is.` ### `diamond(centre_pos, radius?, height?)` @@ -2878,10 +2941,13 @@ belonging to that group. Returns entities of a specified type in an area centered on `center` and at most `distance` blocks away from -the center point. Uses the same `type` selectors as `entities_list`. +the center point/area. Uses the same `type` selectors as `entities_list`. `center` and `distance` can either be a triple of coordinates or three consecutive arguments for `entity_area`. `center` can -also be represented as a block, in this case the search box will be centered on the middle of the block. +also be represented as a block, in this case the search box will be centered on the middle of the block, or an entity - in this case +entire bounding box of the entity serves as a 'center' of search which is then expanded in all directions with the `'distance'` vector. + +In any case - returns all entities which bounding box collides with the bounding box defined by `'center'` and `'disteance'`. entity_area is simpler than `entity_selector` and runs about 20% faster, but is limited to predefined selectors and cuboid search area. @@ -3197,6 +3263,18 @@ Number indicating remaining entity health, or `null` if not applicable. Retrieves player hunger related information. For non-players, returns `null`. +### `query(e, 'absorption')` + +Gets the absorption of the player (yellow hearts, e.g when having a golden apple.) + +### `query(e,'xp')` +### `query(e,'xp_level')` +### `query(e,'xp_progress')` +### `query(e,'score')` + +Numbers related to player's xp. `xp` is the overall xp player has, `xp_level` is the levels seen in the hotbar, +`xp_progress` is a float between 0 and 1 indicating the percentage of the xp bar filled, and `score` is the number displayed upon death + ### `query(e, 'air')` Number indicating remaining entity health, or `null` if not applicable. @@ -3507,6 +3585,20 @@ Will set entity on fire for `ticks` ticks. Set to 0 to extinguish. Modifies directly player raw hunger components. Has no effect on non-players +### `modify(e, 'absorption', value)` + +Sets the absorption value for the player. Each point is half a yellow heart. + +### `modify(e, 'add_xp', value)` +### `modify(e, 'xp_level', value)` +### `modify(e, 'xp_progress', value)` +### `modify(e, 'xp_score', value)` + +Manipulates player xp values - `'add_xp'` the method you probably want to use +to manipulate how much 'xp' an action should give. `'xp_score'` only affects the number you see when you die, and +`'xp_progress'` controls the xp progressbar above the hotbar, should take values from 0 to 1, but you can set it to any value, +maybe you will get a double, who knows. + ### `modify(e, 'air', ticks)` Modifies entity air @@ -3550,6 +3642,10 @@ Required arguments: `entity, amount, source, attacking_entity` It doesn't mean that all entity types will have a chance to execute a given event, but entities will not error when you attach an inapplicable event to it. +In case you want to pass an event handler that is not defined in your module, please read the tips on + "Passing function references to other modules of your application" section in the `call(...)` section. + + ### `entity_load_handler(descriptor / descriptors, function)`, `entity_load_handler(descriptor / descriptors, call_name, ... args?)` Attaches a callback to when any entity matching the following type / types is loaded in the game, allowing to grab a handle @@ -4075,6 +4171,10 @@ handle_event('tick', _() -> foo('tick happened')); // built-in event handle_event('tick', null) // nah, ima good, kthxbai +In case you want to pass an event handler that is not defined in your module, please read the tips on + "Passing function references to other modules of your application" section in the `call(...)` section. + + ### `signal_event(event, target_player?, ... args?)` Fires a specific event. If the event does not exist (only `handle_event` creates missing new events), or provided argument list @@ -4489,9 +4589,10 @@ Other value types will only be converted to tags (including NBT tags) if `force` extra treatment when loading them back from NBT, but using `force` true will always produce output / never produce an exception. -### `print(expr)` +### `print(expr)`, `print(player/player_list, expr)` Displays the result of the expression to the chat. Overrides default `scarpet` behaviour of sending everyting to stderr. +Can optionally define player or list of players to send the message to. ### `format(components, ...)`, `format(l(components, ...))` @@ -4740,6 +4841,9 @@ algorithm has taken in to account last time mobs spawned. Schedules a user defined function to run with a specified `delay` ticks of delay. Scheduled functions run at the end of the tick, and they will run in order they were scheduled. +In case you want to schedule a function that is not defined in your module, please read the tips on + "Passing function references to other modules of your application" section in the `call(...)` section. + ### `statistic(player, category, entry)` Queries in-game statistics for certain values. Categories include: @@ -4779,7 +4883,9 @@ Available options in the scarpet app space: * `world_path` - full path to the world saves folder * `world_folder` - name of the direct folder in the saves that holds world files * `world_carpet_rules` - returns all Carpet rules in a map form (`rule`->`value`). Note that the values are always returned as strings, so you can't do boolean comparisons directly. Includes rules from extensions with their namespace (`namespace:rule`->`value`). You can later listen to rule changes with the `on_carpet_rule_changes(rule, newValue)` event. - + * `world_gamerules` - returns all gamerules in a map form (`rule`->`value`). Like carpet rules, values are returned as strings, so you can use appropriate value conversions using `bool()` or `number()` to convert them to other values. Gamerules are read-only to discourage app programmers to mess up with the settings intentionally applied by server admins. Isn't that just super annoying when a datapack messes up with your gamerule settings? It is still possible to change them though using `run('gamerule ...`. + + 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 @@ -4809,7 +4915,8 @@ Available options in the scarpet app space: * `java_process_cpu_load` - current percentage of CPU used by JVM Scarpet related properties - * `scarpet_version` - returns the version of the carpet your scarpet comes with.# `/script run` command + * `scarpet_version` - returns the version of the carpet your scarpet comes with. +# `/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 6362b82d..06fa8784 100644 --- a/docs/scarpet/api/Auxiliary.md +++ b/docs/scarpet/api/Auxiliary.md @@ -193,9 +193,10 @@ Other value types will only be converted to tags (including NBT tags) if `force` extra treatment when loading them back from NBT, but using `force` true will always produce output / never produce an exception. -### `print(expr)` +### `print(expr)`, `print(player/player_list, expr)` Displays the result of the expression to the chat. Overrides default `scarpet` behaviour of sending everyting to stderr. +Can optionally define player or list of players to send the message to. ### `format(components, ...)`, `format(l(components, ...))` @@ -444,6 +445,9 @@ algorithm has taken in to account last time mobs spawned. Schedules a user defined function to run with a specified `delay` ticks of delay. Scheduled functions run at the end of the tick, and they will run in order they were scheduled. +In case you want to schedule a function that is not defined in your module, please read the tips on + "Passing function references to other modules of your application" section in the `call(...)` section. + ### `statistic(player, category, entry)` Queries in-game statistics for certain values. Categories include: @@ -483,7 +487,9 @@ Available options in the scarpet app space: * `world_path` - full path to the world saves folder * `world_folder` - name of the direct folder in the saves that holds world files * `world_carpet_rules` - returns all Carpet rules in a map form (`rule`->`value`). Note that the values are always returned as strings, so you can't do boolean comparisons directly. Includes rules from extensions with their namespace (`namespace:rule`->`value`). You can later listen to rule changes with the `on_carpet_rule_changes(rule, newValue)` event. - + * `world_gamerules` - returns all gamerules in a map form (`rule`->`value`). Like carpet rules, values are returned as strings, so you can use appropriate value conversions using `bool()` or `number()` to convert them to other values. Gamerules are read-only to discourage app programmers to mess up with the settings intentionally applied by server admins. Isn't that just super annoying when a datapack messes up with your gamerule settings? It is still possible to change them though using `run('gamerule ...`. + + 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 @@ -513,4 +519,4 @@ Available options in the scarpet app space: * `java_process_cpu_load` - current percentage of CPU used by JVM Scarpet related properties - * `scarpet_version` - returns the version of the carpet your scarpet comes with. \ No newline at end of file + * `scarpet_version` - returns the version of the carpet your scarpet comes with. diff --git a/docs/scarpet/api/BlockIterations.md b/docs/scarpet/api/BlockIterations.md index 29d39cf3..1a5e6091 100644 --- a/docs/scarpet/api/BlockIterations.md +++ b/docs/scarpet/api/BlockIterations.md @@ -2,14 +2,16 @@ These functions help scan larger areas of blocks without using generic loop functions, like nested `loop`. -### `scan(center, range, lower_range?, expr)` +### `scan(center, range, upper_range?, expr)` Evaluates expression over area of blocks defined by its center `center = (cx, cy, cz)`, expanded in all directions by `range = (dx, dy, dz)` blocks, or optionally in negative with `range` coords, and `upper_range` coords in -positive values. -`center` can be defined either as three coordinates, a list of three coords, or a block value. -`range` and `lower_range` can have the same representations, just if its a block, it computes the distance to the center +positive values, so you can use that if you know the lower coord, and dimension by calling `'scan(center, 0, 0, 0, w, h, d, ...)`. + +`center` can be defined either as three coordinates, a single tuple of three coords, or a block value. +`range` and `upper_range` can have the same representations, just if they are block values, it computes the distance to the center as range instead of taking the values as is. + `expr` receives `_x, _y, _z` as coords of current analyzed block and `_`, which represents the block itself. Returns number of successful evaluations of `expr` (with `true` boolean result) unless called in void context, @@ -34,7 +36,7 @@ Returns the list of 6 neighbouring blocks to the argument. Commonly used with ot for(neighbours(x,y,z),air(_)) => 4 // number of air blocks around a block -### `rect(centre, range?, positive_range?)` +### `rect(centre, range?, upper_range?)` Returns an iterator, just like `range` function that iterates over a rectangular area of blocks. If only center point is specified, it iterates over 27 blocks. If `range` arguments are specified, expands selection by the respective @@ -42,7 +44,7 @@ number of blocks in each direction. If `positive_range` arguments are specified, it uses `range` for negative offset, and `positive_range` for positive. `centre` can be defined either as three coordinates, a list of three coords, or a block value. -`range` and `positive_range` can have the same representations, just if its a block, it computes the distance to the center +`range` and `positive_range` can have the same representations, just if they are block values, it computes the distance to the center as range instead of taking the values as is.` ### `diamond(centre_pos, radius?, height?)` diff --git a/docs/scarpet/api/BlocksAndWorldAccess.md b/docs/scarpet/api/BlocksAndWorldAccess.md index a6f65f63..86d70243 100644 --- a/docs/scarpet/api/BlocksAndWorldAccess.md +++ b/docs/scarpet/api/BlocksAndWorldAccess.md @@ -212,6 +212,16 @@ Causes a block to be harvested by a specified player entity. Honors player item tool if applicable. If the entity is not a valid player, no block gets destroyed. If a player is not allowed to break that block, a block doesn't get destroyed either. +### `weather()`,`weather(type)`,`weather(type, ticks)` + +If called with no args, returns `'clear'`, `'rain` or `'thunder'` based on the current weather. If thundering, will +always return `'thunder'`, if not will return `'rain'` or `'clear'` based on the current weather. + +With one arg, (either `'clear'`, `'rain` or `'thunder'`), returns the number of remaining ticks for that weather type. +NB: It can thunder without there being a thunderstorm, there has to be both rain and thunder to form a storm. + +With two args, sets the weather to `type` for `ticks` ticks. + ## Block and World querying ### `pos(block), pos(entity)` diff --git a/docs/scarpet/api/Entities.md b/docs/scarpet/api/Entities.md index e73c0697..8ecb1af0 100644 --- a/docs/scarpet/api/Entities.md +++ b/docs/scarpet/api/Entities.md @@ -74,10 +74,13 @@ belonging to that group. Returns entities of a specified type in an area centered on `center` and at most `distance` blocks away from -the center point. Uses the same `type` selectors as `entities_list`. +the center point/area. Uses the same `type` selectors as `entities_list`. `center` and `distance` can either be a triple of coordinates or three consecutive arguments for `entity_area`. `center` can -also be represented as a block, in this case the search box will be centered on the middle of the block. +also be represented as a block, in this case the search box will be centered on the middle of the block, or an entity - in this case +entire bounding box of the entity serves as a 'center' of search which is then expanded in all directions with the `'distance'` vector. + +In any case - returns all entities which bounding box collides with the bounding box defined by `'center'` and `'disteance'`. entity_area is simpler than `entity_selector` and runs about 20% faster, but is limited to predefined selectors and cuboid search area. @@ -393,6 +396,18 @@ Number indicating remaining entity health, or `null` if not applicable. Retrieves player hunger related information. For non-players, returns `null`. +### `query(e, 'absorption')` + +Gets the absorption of the player (yellow hearts, e.g when having a golden apple.) + +### `query(e,'xp')` +### `query(e,'xp_level')` +### `query(e,'xp_progress')` +### `query(e,'score')` + +Numbers related to player's xp. `xp` is the overall xp player has, `xp_level` is the levels seen in the hotbar, +`xp_progress` is a float between 0 and 1 indicating the percentage of the xp bar filled, and `score` is the number displayed upon death + ### `query(e, 'air')` Number indicating remaining entity health, or `null` if not applicable. @@ -703,6 +718,20 @@ Will set entity on fire for `ticks` ticks. Set to 0 to extinguish. Modifies directly player raw hunger components. Has no effect on non-players +### `modify(e, 'absorption', value)` + +Sets the absorption value for the player. Each point is half a yellow heart. + +### `modify(e, 'add_xp', value)` +### `modify(e, 'xp_level', value)` +### `modify(e, 'xp_progress', value)` +### `modify(e, 'xp_score', value)` + +Manipulates player xp values - `'add_xp'` the method you probably want to use +to manipulate how much 'xp' an action should give. `'xp_score'` only affects the number you see when you die, and +`'xp_progress'` controls the xp progressbar above the hotbar, should take values from 0 to 1, but you can set it to any value, +maybe you will get a double, who knows. + ### `modify(e, 'air', ticks)` Modifies entity air @@ -746,6 +775,10 @@ Required arguments: `entity, amount, source, attacking_entity` It doesn't mean that all entity types will have a chance to execute a given event, but entities will not error when you attach an inapplicable event to it. +In case you want to pass an event handler that is not defined in your module, please read the tips on + "Passing function references to other modules of your application" section in the `call(...)` section. + + ### `entity_load_handler(descriptor / descriptors, function)`, `entity_load_handler(descriptor / descriptors, call_name, ... args?)` Attaches a callback to when any entity matching the following type / types is loaded in the game, allowing to grab a handle diff --git a/docs/scarpet/api/Events.md b/docs/scarpet/api/Events.md index e64aad1f..b337090d 100644 --- a/docs/scarpet/api/Events.md +++ b/docs/scarpet/api/Events.md @@ -263,6 +263,10 @@ handle_event('tick', _() -> foo('tick happened')); // built-in event handle_event('tick', null) // nah, ima good, kthxbai +In case you want to pass an event handler that is not defined in your module, please read the tips on + "Passing function references to other modules of your application" section in the `call(...)` section. + + ### `signal_event(event, target_player?, ... args?)` Fires a specific event. If the event does not exist (only `handle_event` creates missing new events), or provided argument list diff --git a/docs/scarpet/language/FunctionsAndControlFlow.md b/docs/scarpet/language/FunctionsAndControlFlow.md index 2d6a8d00..3d11e8bf 100644 --- a/docs/scarpet/language/FunctionsAndControlFlow.md +++ b/docs/scarpet/language/FunctionsAndControlFlow.md @@ -135,7 +135,7 @@ foo(... x) -> ... # all arguments for foo are included in the list ### `import(module_name, symbols ...)` Imports symbols from other apps and libraries into the current one: global variables or functions, allowing to use -them in the current app. This include other symbols imported by these modules. Scarpet supports cicular dependencies, +them in the current app. This includes other symbols imported by these modules. Scarpet supports circular dependencies, but if symbols are used directly in the module body rather than functions, it may not be able to retrieve them. Returns full list of available symbols that could be imported from this module, which can be used to debug import issues, and list contents of libraries. @@ -144,9 +144,57 @@ issues, and list contents of libraries. calls a user defined function with specified arguments. It is equivalent to calling `function(args...)` directly except you can use it with function value, or name instead. This means you can pass functions to other user defined -functions as arguments and call them with `call` internally. And since function definitions return the defined +functions as arguments and call them with `call` internally. Since function definitions return the defined function, they can be defined in place as anonymous functions. +#### Passing function references to other modules of your application + +In case a function is defined by its name, Scarpet will attempt to resolve its definition in the given module and its imports, +meaning if the call is in a imported library, and not in the main module of your app, and that function is not visible from the +library perspective, but in the app, it won't be call-able. In case you pass a function name to a separate module in your app, +it should import back that method from the main module for visibility. + +Check an example of a problematic code of a library that expects a function value as a passed argument and how it is called in +the parent app: +``` +//app.sc +import('lib', 'callme'); +foo(x) -> x*x; +test() -> callme('foo' , 5); +``` +``` +//lib.scl +callme(fun, arg) -> call(fun, arg); +``` + +In this case `'foo'` will fail to dereference in `lib` as it is not visible by name. In tightly coupled modules, where `lib` is just +a component of your `app` you can use circular import to acknowledge the symbol from the other module (pretty much like +imports in Java classes), and that solves the issue but makes the library dependent on the main app: +``` +//lib.scl +import('app','foo'); +callme(fun, arg) -> call(fun, arg); +``` +You can circumvent that issue by explicitly dereferencing the local function where it is used as a lambda argument created +in the module in which the requested function is visible: +``` +//app.sc +import('lib', 'callme'); +foo(x) -> x*x; +test() -> callme(_(x) -> foo(x), 5); +``` +``` +//lib.scl +callme(fun, arg) -> call(fun, arg); +``` +Or by passing an explicit reference to the function, instead of calling it by name: +``` +//app.sc +import('lib', 'callme'); +global_foohandler = (foo(x) -> x*x); +test() -> callme(global_foohandler, 5); +``` + Little technical note: the use of `_` in expression passed to built in functions is much more efficient due to not creating new call stacks for each invoked function, but anonymous functions is the only mechanism available for programmers with their own lambda arguments diff --git a/docs/scarpet/language/SystemFunctions.md b/docs/scarpet/language/SystemFunctions.md index 0959f243..1f3768e6 100644 --- a/docs/scarpet/language/SystemFunctions.md +++ b/docs/scarpet/language/SystemFunctions.md @@ -122,6 +122,9 @@ a = 3; task_thread('temp', _(outer(a), b) -> foo(a,b), 5) => Another example of running the same thing passing arguments using closure over anonymous function as well as passing a parameter. +In case you want to create a task based on a function that is not defined in your module, please read the tips on + "Passing function references to other modules of your application" section in the `call(...)` section. + ### `sleep()` `sleep(timeout)`, `sleep(timeout, close_expr)` diff --git a/src/main/java/carpet/fakes/HungerManagerInterface.java b/src/main/java/carpet/fakes/HungerManagerInterface.java index b4df2334..f6a2787c 100644 --- a/src/main/java/carpet/fakes/HungerManagerInterface.java +++ b/src/main/java/carpet/fakes/HungerManagerInterface.java @@ -3,4 +3,5 @@ package carpet.fakes; public interface HungerManagerInterface { float getExhaustionCM(); void setExhaustionCM(float exhaust); + void setSaturationCM(float saturn); } diff --git a/src/main/java/carpet/fakes/ServerWorldInterface.java b/src/main/java/carpet/fakes/ServerWorldInterface.java new file mode 100644 index 00000000..67414706 --- /dev/null +++ b/src/main/java/carpet/fakes/ServerWorldInterface.java @@ -0,0 +1,7 @@ +package carpet.fakes; + +import net.minecraft.world.level.ServerWorldProperties; + +public interface ServerWorldInterface { + ServerWorldProperties getWorldPropertiesCM(); +} diff --git a/src/main/java/carpet/mixins/HungerManagerMixin_scarpetEventsMixin.java b/src/main/java/carpet/mixins/HungerManagerMixin_scarpetEventsMixin.java index 3e6fcdc2..2f841f1c 100644 --- a/src/main/java/carpet/mixins/HungerManagerMixin_scarpetEventsMixin.java +++ b/src/main/java/carpet/mixins/HungerManagerMixin_scarpetEventsMixin.java @@ -8,13 +8,11 @@ import org.spongepowered.asm.mixin.Shadow; @Mixin(HungerManager.class) public class HungerManagerMixin_scarpetEventsMixin implements HungerManagerInterface { @Shadow private float exhaustion; - public float getExhaustionCM(){ - return exhaustion; - } - + @Shadow private float foodSaturationLevel; @Override - public void setExhaustionCM(float exhaust) - { - exhaustion = exhaust; - } + public float getExhaustionCM(){ return exhaustion; } + @Override + public void setExhaustionCM(float exhaust) { exhaustion = exhaust; } + @Override + public void setSaturationCM(float saturn) { foodSaturationLevel = saturn; } } diff --git a/src/main/java/carpet/mixins/ServerWorld_scarpetEventMixin.java b/src/main/java/carpet/mixins/ServerWorld_scarpetMixin.java similarity index 87% rename from src/main/java/carpet/mixins/ServerWorld_scarpetEventMixin.java rename to src/main/java/carpet/mixins/ServerWorld_scarpetMixin.java index 8ca7259a..99598274 100644 --- a/src/main/java/carpet/mixins/ServerWorld_scarpetEventMixin.java +++ b/src/main/java/carpet/mixins/ServerWorld_scarpetMixin.java @@ -1,6 +1,7 @@ package carpet.mixins; import carpet.CarpetSettings; +import carpet.fakes.ServerWorldInterface; import carpet.script.CarpetEventServer; import net.minecraft.class_5568; import net.minecraft.class_5579; @@ -11,7 +12,9 @@ import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.ChunkPos; import net.minecraft.util.profiler.Profiler; import net.minecraft.world.chunk.WorldChunk; +import net.minecraft.world.level.ServerWorldProperties; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Redirect; @@ -22,7 +25,7 @@ import static carpet.script.CarpetEventServer.Event.ENTITY_LOAD; import static carpet.script.CarpetEventServer.Event.LIGHTNING; @Mixin(ServerWorld.class) -public class ServerWorld_scarpetEventMixin +public class ServerWorld_scarpetMixin implements ServerWorldInterface { @Inject(method = "tickChunk", locals = LocalCapture.CAPTURE_FAILHARD, at = @At( value = "INVOKE", @@ -58,4 +61,9 @@ public class ServerWorld_scarpetEventMixin return success; } + @Shadow + private ServerWorldProperties worldProperties; + public ServerWorldProperties getWorldPropertiesCM(){ + return worldProperties; + } } diff --git a/src/main/java/carpet/script/ScriptHost.java b/src/main/java/carpet/script/ScriptHost.java index b494c9de..d984673f 100644 --- a/src/main/java/carpet/script/ScriptHost.java +++ b/src/main/java/carpet/script/ScriptHost.java @@ -18,13 +18,10 @@ import java.util.Locale; import java.util.Map; import java.util.Random; import java.util.Set; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadPoolExecutor; import java.util.function.Predicate; -import java.util.stream.Collectors; import java.util.stream.Stream; public abstract class ScriptHost @@ -189,7 +186,13 @@ public abstract class ScriptHost public FunctionValue getAssertFunction(Module module, String name) { FunctionValue ret = getFunction(module, name); - if (ret == null) throw new InternalExpressionException("Function "+name+" is not defined yet"); + if (ret == null) + { + if (module == main) + 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.getName()+"'"); + } return ret; } private FunctionValue getFunction(Module module, String name) diff --git a/src/main/java/carpet/script/api/Auxiliary.java b/src/main/java/carpet/script/api/Auxiliary.java index d190e209..2cecd66b 100644 --- a/src/main/java/carpet/script/api/Auxiliary.java +++ b/src/main/java/carpet/script/api/Auxiliary.java @@ -18,6 +18,7 @@ import carpet.script.argument.FunctionArgument; import carpet.script.argument.Vector3Argument; import carpet.script.exception.ExitStatement; import carpet.script.exception.InternalExpressionException; +import carpet.script.utils.FixedCommandSource; import carpet.script.utils.ShapeDispatcher; import carpet.script.utils.WorldTools; import carpet.script.value.EntityValue; @@ -34,7 +35,6 @@ import carpet.script.value.ValueConversions; import carpet.utils.Messenger; import com.google.common.collect.ImmutableMap; import net.minecraft.block.BlockState; -import net.minecraft.command.argument.EntityAnchorArgumentType; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityType; import net.minecraft.entity.EquipmentSlot; @@ -49,7 +49,6 @@ import net.minecraft.network.packet.s2c.play.TitleS2CPacket; import net.minecraft.network.packet.s2c.play.TitleS2CPacket.Action; import net.minecraft.particle.ParticleEffect; import net.minecraft.server.MinecraftServer; -import net.minecraft.server.command.CommandOutput; import net.minecraft.server.command.ServerCommandSource; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.world.ServerWorld; @@ -63,7 +62,6 @@ import net.minecraft.util.InvalidIdentifierException; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.ChunkPos; import net.minecraft.util.math.EulerAngle; -import net.minecraft.util.math.Vec2f; import net.minecraft.util.math.Vec3d; import net.minecraft.util.registry.DynamicRegistryManager; import net.minecraft.util.registry.Registry; @@ -331,7 +329,7 @@ public class Auxiliary { { Value nameValue = lv.get(0).evalValue(c); name = nameValue instanceof NullValue ? "" : nameValue.getString(); - pointLocator = Vector3Argument.findIn(cc, lv, 1, true); + pointLocator = Vector3Argument.findIn(cc, lv, 1, true, false); if (lv.size()>pointLocator.offset) { BlockArgument blockLocator = BlockArgument.findIn(cc, lv, pointLocator.offset, true, true, false); @@ -467,28 +465,29 @@ public class Auxiliary { { if (lv.size() == 0 || lv.size() > 2) throw new InternalExpressionException("'print' takes one or two arguments"); ServerCommandSource s = ((CarpetContext)c).s; + MinecraftServer server = s.getMinecraftServer(); Value res = lv.get(0).evalValue(c); + List targets = null; if (lv.size() == 2) { - ServerPlayerEntity player = EntityValue.getPlayerByValue(s.getMinecraftServer(), res); - if (player == null) return LazyValue.NULL; - s = player.getCommandSource(); + List playerValues = (res instanceof ListValue)?((ListValue) res).getItems():Collections.singletonList(res); + List playerTargets = new ArrayList<>(); + playerValues.forEach(pv -> { + ServerPlayerEntity player = EntityValue.getPlayerByValue(server, pv); + if (player == null) throw new InternalExpressionException("Cannot target player "+pv.getString()+" in print"); + playerTargets.add(player); + }); + targets = playerTargets; res = lv.get(1).evalValue(c); } - if (res instanceof FormattedTextValue) + Text message = (res instanceof FormattedTextValue)?((FormattedTextValue) res).getText():new LiteralText(res.getString()); + if (targets == null) { - s.sendFeedback(((FormattedTextValue) res).getText(), false); + s.sendFeedback(message, false); } else { - if (s.getEntity() instanceof PlayerEntity) - { - Messenger.m((PlayerEntity) s.getEntity(), "w " + res.getString()); - } - else - { - Messenger.m(s, "w " + res.getString()); - } + targets.forEach(p -> p.getCommandSource().sendFeedback(message, false)); } Value finalRes = res; return (_c, _t) -> finalRes; // pass through for variables @@ -565,29 +564,14 @@ public class Auxiliary { expression.addLazyFunction("run", 1, (c, t, lv) -> { BlockPos target = ((CarpetContext)c).origin; - Vec3d posf = new Vec3d((double)target.getX()+0.5D,(double)target.getY(),(double)target.getZ()+0.5D); + Vec3d posf = new Vec3d((double)target.getX()+0.5D, target.getY(),(double)target.getZ()+0.5D); ServerCommandSource s = ((CarpetContext)c).s; try { Value[] error = {Value.NULL}; List output = new ArrayList<>(); Value retval = new NumericValue(s.getMinecraftServer().getCommandManager().execute( - new ServerCommandSource( - CommandOutput.DUMMY, posf, Vec2f.ZERO, s.getWorld(), CarpetSettings.runPermissionLevel, - s.getName(), s.getDisplayName(), s.getMinecraftServer(), s.getEntity(), true, - (ctx, succ, res) -> { }, EntityAnchorArgumentType.EntityAnchor.FEET) - { - @Override - public void sendError(Text message) - { - error[0] = new FormattedTextValue(message); - } - @Override - public void sendFeedback(Text message, boolean broadcastToOps) - { - output.add(new FormattedTextValue(message)); - } - }, + new FixedCommandSource(s, posf, error, output), lv.get(0).evalValue(c).getString()) ); Value ret = ListValue.of(retval, ListValue.wrap(output), error[0]); diff --git a/src/main/java/carpet/script/api/Entities.java b/src/main/java/carpet/script/api/Entities.java index 361e0e9f..ead7f75f 100644 --- a/src/main/java/carpet/script/api/Entities.java +++ b/src/main/java/carpet/script/api/Entities.java @@ -194,14 +194,24 @@ public class Entities { if (lv.size()<3) throw new InternalExpressionException("'entity_area' requires entity type, center and range arguments"); String who = lv.get(0).evalValue(c).getString(); CarpetContext cc = (CarpetContext)c; - Vector3Argument centerLocator = Vector3Argument.findIn(cc, lv, 1); - Vec3d center = centerLocator.vec; - if (centerLocator.fromBlock) center.add(0.5, 0.5, 0.5); + Vector3Argument centerLocator = Vector3Argument.findIn(cc, lv, 1, false, true); + + Box centerBox; + if (centerLocator.entity != null) + { + centerBox = centerLocator.entity.getBoundingBox(); + } + else + { + Vec3d center = centerLocator.vec; + if (centerLocator.fromBlock) center.add(0.5, 0.5, 0.5); + centerBox = new Box(center, center); + } Vector3Argument rangeLocator = Vector3Argument.findIn(cc, lv, centerLocator.offset); if (rangeLocator.fromBlock) throw new InternalExpressionException("Range of 'entity_area' cannot come from a block argument"); Vec3d range = rangeLocator.vec; - Box area = new Box(center, center).expand(range.x, range.y, range.z); + Box area = centerBox.expand(range.x, range.y, range.z); EntityValue.EntityClassDescriptor eDesc = EntityValue.getEntityDescriptor(who); List entityList = ((CarpetContext)c).s.getWorld().getEntitiesByType(eDesc.directType, area,eDesc.filteringPredicate); Value retval = ListValue.wrap(entityList.stream().map(EntityValue::new).collect(Collectors.toList())); diff --git a/src/main/java/carpet/script/api/WorldAccess.java b/src/main/java/carpet/script/api/WorldAccess.java index 75b0c711..ae7ce8e8 100644 --- a/src/main/java/carpet/script/api/WorldAccess.java +++ b/src/main/java/carpet/script/api/WorldAccess.java @@ -6,6 +6,7 @@ import carpet.fakes.BiomeArrayInterface; import carpet.fakes.ChunkGeneratorInterface; import carpet.fakes.ChunkTicketManagerInterface; import carpet.fakes.ServerChunkManagerInterface; +import carpet.fakes.ServerWorldInterface; import carpet.fakes.SpawnHelperInnerInterface; import carpet.fakes.ThreadedAnvilChunkStorageInterface; import carpet.helpers.FeatureGenerator; @@ -58,6 +59,7 @@ import net.minecraft.item.SwordItem; import net.minecraft.item.TridentItem; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.Tag; +import net.minecraft.server.MinecraftServer; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.world.ChunkTicket; import net.minecraft.server.world.ChunkTicketType; @@ -89,6 +91,7 @@ import net.minecraft.world.gen.ChunkRandom; import net.minecraft.world.gen.chunk.ChunkGenerator; import net.minecraft.world.gen.feature.ConfiguredStructureFeature; import net.minecraft.world.gen.feature.StructureFeature; +import net.minecraft.world.level.ServerWorldProperties; import net.minecraft.world.poi.PointOfInterest; import net.minecraft.world.poi.PointOfInterestStorage; import net.minecraft.world.poi.PointOfInterestType; @@ -413,6 +416,67 @@ public class WorldAccess { }); + expression.addLazyFunction("weather",-1,(c, t, lv) -> { + ServerWorld world = ((CarpetContext) c).s.getWorld(); + + if(lv.size()==0)//cos it can thunder when raining or when clear. + { + Value ret = new StringValue(world.isThundering() ? "thunder" : (world.isRaining() ? "rain" : "clear")); + return (_c, _t) -> ret; + } + + Value weather = lv.get(0).evalValue(c); + ServerWorldProperties worldProperties = ((ServerWorldInterface) world).getWorldPropertiesCM(); + if(lv.size()==1) + { + int ticks; + switch (weather.getString().toLowerCase(Locale.ROOT)) { + case "clear": + ticks = worldProperties.getClearWeatherTime(); + break; + case "rain": + ticks = world.isRaining()? worldProperties.getRainTime():0;//cos if not it gives 1 for some reason + break; + case "thunder": + ticks = world.isThundering()? worldProperties.getThunderTime():0;//same dealio here + break; + default: + throw new InternalExpressionException("Weather can only be 'clear', 'rain' or 'thunder'"); + } + Value ret = new NumericValue(ticks); + return (_c,_t) -> ret; + } + if(lv.size()==2) + { + int ticks = NumericValue.asNumber(lv.get(1).evalValue(c), "tick_time in 'weather'").getInt(); + switch (weather.getString().toLowerCase(Locale.ROOT)) + { + case "clear": + world.setWeather(ticks,0,false,false); + break; + + case "rain": + world.setWeather(0,ticks,true,false); + break; + + case "thunder": + world.setWeather( + 0, + ticks,//this is used to set thunder time, idk why... + true, + true + ); + break; + + default: + throw new InternalExpressionException("Weather can only be 'clear', 'rain' or 'thunder'"); + } + Value ret = NumericValue.of(ticks); + return (_c, _t) -> ret; + } + throw new InternalExpressionException("'weather' requires 0, 1 or 2 arguments"); + }); + expression.addLazyFunction("pos", 1, (c, t, lv) -> { Value arg = lv.get(0).evalValue(c); diff --git a/src/main/java/carpet/script/argument/Vector3Argument.java b/src/main/java/carpet/script/argument/Vector3Argument.java index 6c408089..003ffc4c 100644 --- a/src/main/java/carpet/script/argument/Vector3Argument.java +++ b/src/main/java/carpet/script/argument/Vector3Argument.java @@ -4,9 +4,11 @@ import carpet.script.CarpetContext; import carpet.script.LazyValue; import carpet.script.exception.InternalExpressionException; import carpet.script.value.BlockValue; +import carpet.script.value.EntityValue; import carpet.script.value.ListValue; import carpet.script.value.NumericValue; import carpet.script.value.Value; +import net.minecraft.entity.Entity; import net.minecraft.util.math.Vec3d; import java.util.List; @@ -16,7 +18,8 @@ public class Vector3Argument extends Argument public Vec3d vec; public final double yaw; public final double pitch; - public boolean fromBlock; + public boolean fromBlock = false; + public Entity entity = null; private Vector3Argument(Vec3d v, int o) { super(o); @@ -38,13 +41,19 @@ public class Vector3Argument extends Argument return this; } + private Vector3Argument withEntity(Entity e) + { + entity = e; + return this; + } + public static Vector3Argument findIn(CarpetContext c, List params, int offset) { - return findIn(c,params, offset, false); + return findIn(c,params, offset, false, false); } - public static Vector3Argument findIn(CarpetContext c, List params, int offset, boolean optionalDirection) + public static Vector3Argument findIn(CarpetContext c, List params, int offset, boolean optionalDirection, boolean optionalEntity) { try { @@ -54,6 +63,11 @@ public class Vector3Argument extends Argument // pos + 0.5v return (new Vector3Argument(Vec3d.ofCenter(((BlockValue) v1).getPos()), 1+offset)).fromBlock(); } + if (optionalEntity && v1 instanceof EntityValue) + { + Entity e = ((EntityValue) v1).getEntity(); + return new Vector3Argument(e.getPos(), 1+offset).withEntity(e); + } if (v1 instanceof ListValue) { List args = ((ListValue) v1).getItems(); diff --git a/src/main/java/carpet/script/utils/FixedCommandSource.java b/src/main/java/carpet/script/utils/FixedCommandSource.java new file mode 100644 index 00000000..c88e9874 --- /dev/null +++ b/src/main/java/carpet/script/utils/FixedCommandSource.java @@ -0,0 +1,206 @@ +package carpet.script.utils; + +import carpet.CarpetSettings; +import carpet.script.value.FormattedTextValue; +import carpet.script.value.Value; +import com.mojang.brigadier.ResultConsumer; +import com.mojang.brigadier.context.CommandContext; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import net.minecraft.command.argument.EntityAnchorArgumentType; +import net.minecraft.entity.Entity; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.command.CommandOutput; +import net.minecraft.server.command.ServerCommandSource; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.text.Text; +import net.minecraft.util.math.Vec2f; +import net.minecraft.util.math.Vec3d; + +import java.util.List; +import java.util.function.BinaryOperator; + +public class FixedCommandSource extends ServerCommandSource +{ + private Vec3d position; + private ServerWorld world; + private final String simpleName; + private final Text name; + private final MinecraftServer server; + private Entity entity; + private ResultConsumer resultConsumer; + private EntityAnchorArgumentType.EntityAnchor entityAnchor; + private Vec2f rotation; + private final Value[] error; + private final List chatOutput; + + public FixedCommandSource(ServerCommandSource original, Vec3d pos, Value[] error, List chatOutput) + { + super(CommandOutput.DUMMY, pos, Vec2f.ZERO, original.getWorld(), CarpetSettings.runPermissionLevel, + original.getName(), original.getDisplayName(), original.getMinecraftServer(), original.getEntity(), true, + (ctx, succ, res) -> { }, EntityAnchorArgumentType.EntityAnchor.FEET); + position = pos; + world = original.getWorld(); + simpleName = original.getName(); + name = original.getDisplayName(); + server = original.getMinecraftServer(); + entity = original.getEntity(); + resultConsumer = (ctx, succ, res) -> { }; + entityAnchor = original.getEntityAnchor(); + rotation = Vec2f.ZERO; + this.error = error; + this.chatOutput = chatOutput; + } + + @Override + public ServerCommandSource withEntity(Entity entity) + { + this.entity = entity; + return this; + } + + @Override + public ServerCommandSource withPosition(Vec3d position) + { + this.position = position; + return this; + } + + @Override + public ServerCommandSource withRotation(Vec2f rotation) + { + this.rotation = rotation; + return this; + } + + @Override + public ServerCommandSource withConsumer(ResultConsumer consumer) + { + this.resultConsumer = consumer; + return this; + } + + @Override + public ServerCommandSource mergeConsumers(ResultConsumer consumer, BinaryOperator> binaryOperator) + { + return this.withConsumer(binaryOperator.apply(this.resultConsumer, consumer)); + } + + @Override + public ServerCommandSource withSilent() + { + return this; + } + + @Override + public ServerCommandSource withLevel(int level) + { + return this; + } + + @Override + public ServerCommandSource withMaxLevel(int level) + { + return this; + } + + @Override + public ServerCommandSource withEntityAnchor(EntityAnchorArgumentType.EntityAnchor anchor) + { + entityAnchor = anchor; + return this; + } + + @Override + public ServerCommandSource withWorld(ServerWorld world) + { + this.world = world; + return this; + } + + @Override + public Text getDisplayName() + { + return name; + } + + @Override + public String getName() + { + return simpleName; + } + + @Override + public Vec3d getPosition() + { + return position; + } + + @Override + public ServerWorld getWorld() + { + return world; + } + + @Override + public Entity getEntity() + { + return entity; + } + + @Override + public Entity getEntityOrThrow() throws CommandSyntaxException + { + if (entity == null) { + throw REQUIRES_ENTITY_EXCEPTION.create(); + } else { + return entity; + } + } + + @Override + public ServerPlayerEntity getPlayer() throws CommandSyntaxException + { + if (!(this.entity instanceof ServerPlayerEntity)) { + throw REQUIRES_PLAYER_EXCEPTION.create(); + } else { + return (ServerPlayerEntity)this.entity; + } + } + + @Override + public Vec2f getRotation() + { + return rotation; + } + + @Override + public MinecraftServer getMinecraftServer() + { + return server; + } + + @Override + public EntityAnchorArgumentType.EntityAnchor getEntityAnchor() + { + return entityAnchor; + } + + + @Override + public void onCommandComplete(CommandContext context, boolean success, int result) + { + } + + @Override + public void sendError(Text message) + { + error[0] = new FormattedTextValue(message); + } + @Override + public void sendFeedback(Text message, boolean broadcastToOps) + { + chatOutput.add(new FormattedTextValue(message)); + } + +} diff --git a/src/main/java/carpet/script/utils/SystemInfo.java b/src/main/java/carpet/script/utils/SystemInfo.java index 86b13735..e5984e7b 100644 --- a/src/main/java/carpet/script/utils/SystemInfo.java +++ b/src/main/java/carpet/script/utils/SystemInfo.java @@ -13,6 +13,7 @@ import carpet.settings.ParsedRule; import carpet.settings.SettingsManager; import com.sun.management.OperatingSystemMXBean; import net.minecraft.util.WorldSavePath; +import net.minecraft.world.GameRules; import java.lang.management.ManagementFactory; import java.nio.file.Path; @@ -124,6 +125,17 @@ public class SystemInfo { }); return carpetRules; }); + put("world_gamerules", c->{ + Map rules = new HashMap<>(); + final GameRules gameRules = c.s.getWorld().getGameRules(); + GameRules.accept(new GameRules.Visitor() { + @Override + public > void visit(GameRules.Key key, GameRules.Type type) { + rules.put(StringValue.of(key.getName()), StringValue.of(gameRules.get(key).toString())); + } + }); + return MapValue.wrap(rules); + }); put("scarpet_version", c -> StringValue.of(CarpetSettings.carpetVersion)); }}; diff --git a/src/main/java/carpet/script/value/EntityValue.java b/src/main/java/carpet/script/value/EntityValue.java index 2d4ada73..0ab267f9 100644 --- a/src/main/java/carpet/script/value/EntityValue.java +++ b/src/main/java/carpet/script/value/EntityValue.java @@ -31,6 +31,7 @@ import net.minecraft.nbt.StringTag; import net.minecraft.nbt.Tag; import net.minecraft.network.packet.s2c.play.EntityPassengersSetS2CPacket; import net.minecraft.network.packet.s2c.play.EntityPositionS2CPacket; +import net.minecraft.network.packet.s2c.play.ExperienceBarUpdateS2CPacket; import net.minecraft.network.packet.s2c.play.PlayerPositionLookS2CPacket; import net.minecraft.command.EntitySelector; import net.minecraft.command.EntitySelectorReader; @@ -514,6 +515,31 @@ public class EntityValue extends Value return Value.NULL; }); + put("absorption",(e, a)->{ + if(e instanceof PlayerEntity) return new NumericValue(((PlayerEntity) e).getAbsorptionAmount()); + return Value.NULL; + }); + + put("xp",(e, a)->{ + if(e instanceof PlayerEntity) return new NumericValue(((PlayerEntity) e).totalExperience); + return Value.NULL; + }); + + put("xp_level", (e, a)->{ + if(e instanceof PlayerEntity) return new NumericValue(((PlayerEntity) e).experienceLevel); + return Value.NULL; + }); + + put("xp_progress", (e, a)->{ + if(e instanceof PlayerEntity) return new NumericValue(((PlayerEntity) e).experienceProgress); + return Value.NULL; + }); + + put("score", (e, a)->{ + if(e instanceof PlayerEntity) return new NumericValue(((PlayerEntity) e).getScore()); + return Value.NULL; + }); + put("jumping", (e, a) -> { if (e instanceof LivingEntity) { @@ -1343,11 +1369,36 @@ public class EntityValue extends Value if(e instanceof PlayerEntity) ((PlayerEntity) e).getHungerManager().addExhaustion((int) NumericValue.asNumber(v).getLong()); }); - put("saturation", (e, v)-> { - if(e instanceof PlayerEntity) ((PlayerEntity) e).getHungerManager().setSaturationLevelClient((float)NumericValue.asNumber(v).getLong()); + put("absorption", (e, v) -> { + if (e instanceof PlayerEntity) ((PlayerEntity) e).setAbsorptionAmount((float) NumericValue.asNumber(v, "absorbtion").getLong()); }); - put("air", (e, v) -> e.setAir(NumericValue.asNumber(v).getInt())); + put("add_xp", (e, v) -> { + if (e instanceof PlayerEntity) ((PlayerEntity) e).addExperience(NumericValue.asNumber(v, "add_xp").getInt()); + }); + + put("xp_level", (e, v) -> { + if (e instanceof PlayerEntity) ((PlayerEntity) e).addExperienceLevels(NumericValue.asNumber(v, "xp_level").getInt()-((PlayerEntity) e).experienceLevel); + }); + + put("xp_progress", (e, v) -> { + if (e instanceof ServerPlayerEntity) + { + ServerPlayerEntity p = (ServerPlayerEntity) e; + p.experienceProgress = NumericValue.asNumber(v, "xp_progress").getFloat(); + p.networkHandler.sendPacket(new ExperienceBarUpdateS2CPacket(p.experienceProgress, p.totalExperience, p.experienceLevel)); + } + }); + + put("xp_score", (e, v) -> { + if (e instanceof PlayerEntity) ((PlayerEntity) e).setScore(NumericValue.asNumber(v, "xp_score").getInt()); + }); + + put("saturation", (e, v)-> { + if(e instanceof PlayerEntity) ((HungerManagerInterface) ((PlayerEntity) e).getHungerManager()).setSaturationCM(NumericValue.asNumber(v, "saturation").getFloat()); + }); + + put("air", (e, v) -> e.setAir(NumericValue.asNumber(v, "air").getInt())); put("breaking_progress", (e, a) -> { if (e instanceof ServerPlayerEntity) diff --git a/src/main/resources/assets/carpet/scripts/ai_tracker.sc b/src/main/resources/assets/carpet/scripts/ai_tracker.sc index c1985ecb..058a2c34 100644 --- a/src/main/resources/assets/carpet/scripts/ai_tracker.sc +++ b/src/main/resources/assets/carpet/scripts/ai_tracker.sc @@ -145,9 +145,8 @@ global_functions = { [10+half_width,10+height,10+half_width], 0x65432100, 'buddy detection', false ); - [x,y,z] = pos(e); current_id = e~'id'; - buddies = entity_area('villager', x, y+height/2, z, 10+half_width, 10+height/2, 10+half_width); + buddies = entity_area('villager', e, 10, 10, 10); nb = length(buddies); for (filter(buddies, _~'id' != current_id), visuals+=['line', global_duration, 'from', pos(e), 'to', pos(_), 'color', 0xffff00ff]; @@ -515,9 +514,8 @@ __tick_tracker() -> return() ); p = player(); - [px, py, pz] = pos(p); in_dimension(p, - for (entity_area('valid', px, py, pz, global_range, global_range, global_range), + for (entity_area('valid', p, global_range, global_range, global_range), __handle_entity(_) ) ); diff --git a/src/main/resources/carpet.mixins.json b/src/main/resources/carpet.mixins.json index 8bf78b46..28efdc12 100644 --- a/src/main/resources/carpet.mixins.json +++ b/src/main/resources/carpet.mixins.json @@ -170,7 +170,7 @@ "ChunkTicketManager_scarpetMixin", "StatType_scarpetMixin", "PointOfInterest_scarpetMixin", - "ServerWorld_scarpetEventMixin", + "ServerWorld_scarpetMixin", "HungerManagerMixin_scarpetEventsMixin", "RecipeManager_scarpetMixin",