diff --git a/.gitignore b/.gitignore index 7c9ff93b..2105712d 100644 --- a/.gitignore +++ b/.gitignore @@ -5,7 +5,6 @@ build/ out/ classes/ publish/ -docs/ # idea diff --git a/docs/scarpet/api/Auxiliary.md b/docs/scarpet/api/Auxiliary.md new file mode 100644 index 00000000..3b152291 --- /dev/null +++ b/docs/scarpet/api/Auxiliary.md @@ -0,0 +1,246 @@ +# Auxiliary aspects + +Collection of other methods that control smaller, yet still important aspects of the game + +## Sounds + +### `sound(name, pos, volume?, pitch?)` + +Plays a specific sound `name`, at block or position `pos`, with optional `volume` and modified `pitch`. `pos` can be +either a block, triple of coords, or a list of thee numbers. Uses the same options as a corresponding `playsound` command. + +## Particles + +### `particle(name, pos, count?. spread?, speed?, playername?)` + +Renders a cloud of particles `name` centered around `pos` position, by default `count` 10 of them, default `speed` +of 0, and to all players nearby, but these options can be changed via optional arguments. Follow vanilla `/particle` +command on details on those options. Valid particle names are +for example `'angry_villager', 'item diamond', 'block stone', 'dust 0.8 0.1 0.1 4'`. + +### `particle_line(name, pos, pos2, density?)` + +Renders a line of particles from point `pos` to `pos2` with supplied density (defaults to 1), which indicates how far +apart you would want particles to appear, so `0.1` means one every 10cm. + +### `particle_rect(name, pos, pos2, density?)` + +Renders a cuboid of particles between point `pos` to `pos2` with supplied density. + +## Markers + +### `create_marker(text, pos, rotation?, block?)` + +Spawns a (permanent) marker entity with text or block at position. Returns that entity for further manipulations. +Unloading the app that spawned them will cause all the markers from the loaded portion of the world to be removed. +Also - if the game loads that marker in the future and the app is not loaded, it will be removed as well. + +### `remove_all_markers()` + +Removes all scarpet markers from the loaded portion of the world created by this app, in case you didn't want to do + the proper cleanup. + +## System function + +### `nbt(expr)` + +Treats the argument as a nbt serializable string and returns its nbt value. In case nbt is not in a correct nbt +compound tag format, it will return `null` value. + +Consult section about container operations in `Expression` to learn about possible operations on nbt values. + +### `escape_nbt(expr)` + +Excapes all the special characters in the string or nbt tag and returns a string that can be stored in nbt directly +as a string value. + +### `print(expr)` + +Displays the result of the expression to the chat. Overrides default `scarpet` behaviour of sending everyting to stderr. + +### `logger(expr)` + +Prints the message to system logs, and not to chat. + +### `run(expr)` + +Runs a vanilla command from the string result of the `expr` and returns its success count + +
+run('fill 1 1 1 10 10 10 air') -> 123 // 123 block were filled, this operation was successful 123 times out of a possible 1000 blocks volume
+run('give @s stone 4') -> 1 // this operation was successful once
+
+ +### `save()` + +Performs autosave, saves all chunks, player data, etc. Useful for programs where autosave is disabled due to +performance reasons and saves the world only on demand. + +### `load_app_data(), load_app_data(file)` + +Loads the app data associated with the app from the world /scripts folder. Without argument returns the memory +managed and buffered / throttled NBT tag. With a file name - reads explicitly a file with that name from the +scripts folder. + +You can use app data to save non-vanilla information separately from the world and other scripts. + +### `store_app_data(tag), store_app_data(tag, file)` + +Stores the app data associated with the app from the world /scripts folder. With the `file` parameter saves +immediately and with every call, without `file` parameter, it may take up to 10 seconds for the output file +to sync preventing flickering in case this tag changes frequently. It will be synced when server closes. + +### `tick_time()` + +Returns game tick counter. Can be used to run certain operations every n-th ticks, or to count in-game time + +### `game_tick(mstime?)` + +Causes game to run for one tick. By default runs it and returns control to the program, but can optionally +accept expected tick length, in milliseconds. You can't use it to permanently change the game speed, but setting +longer commands with custom tick speeds can be interrupted via `/script stop` command + +
+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. + +### `current_dimension()` + +Returns current dimension that the script runs in. + +### `in_dimension(smth, expr)` + +Evaluates the expression `expr` with different dimension execution context. `smth` can be an entity, +world-localized block, so not `block('stone')`, or a string representing a dimension like: + `'nether'`, `'the_nether'`, `'end'` or `'overworld'`, etc. + +### `schedule(delay, function, args...)` + +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. + +### `statistic(player, category, entry)` + +Queries in-game statistics for certain values. Categories include: + +* `mined`: blocks mined +* `crafted`: items crafted +* `used`: items used +* `broken`: items broken +* `picked_up`: items picked up +* `dropped`: items dropped +* `killed`: mobs killed +* `killed_by`: blocks mined +* `custom`: various random stats + +For the options of `entry`, consult your statistics page, or give it a guess. + +The call will return `null` if the statistics options are incorrect, or player didn't get these in their history. +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 has effectively a value of `0`. + +### `plop(pos, what)` + +Plops a structure or a feature at a given `pos`, so block, triple position coordinates or a list of coordinates. +To `what` gets plopped and exactly where it often depends on the feature or structure itself. For example, all +structures are chunk aligned, and often span multiple chunks. Repeated calls to plop a structure in the same chunk +would result either in the same structure generated on top of each other, or with different state, but same position. +Most structures generate at specific altitudes, which are hardcoded, or with certain blocks around them. API will +cancel all extra position / biome / random requirements for structure / feature placement, but some hardcoded +limitations may still cause some of structures/features not to place. Some features require special blocks to be +present, like coral -> water or ice spikes -> snow block, and for some features, like fossils, placement is all sorts +of messed up. This can be partially avoided for structures by setting their structure information via `set_structure`, +which sets it without looking into world blocks, and then use `plop` to fill it with blocks. This may, or may not work. + +All generated structures will retain their properties, like mob spawning, however in many cases the world / dimension +itself has certain rules to spawn mobs, like plopping a nether fortress in the overworld will not spawn nether mobs, +because nether mobs can spawn only in the nether, but plopped in the nether - will behave like a valid nether fortress. + +`plop` will not use world random number generator to generate structures and features, but its own. This has a benefit +that they will generate properly randomly, not the same time every time. + +Structure list: + +* `monument`: Ocean Monument. Generates at fixed Y coordinate, surrounds itself with water. +* `fortress`: Nether Fortress. Altitude varies, but its bounded by the code. +* `mansion`: Woodland Mansion +* `jungle_temple`: Jungle Temple +* `desert_temple`: Desert Temple. Generates at fixed Y altitude. +* `end_city`: End City with Shulkers +* `igloo`: Igloo +* `shipwreck`: Shipwreck, version1? +* `shipwreck2`: Shipwreck, version2? +* `witch_hut` +* `ocean_ruin`, `ocean_ruin_small`, `ocean_ruin_tall`: Stone variants of ocean ruins. +* `ocean_ruin_warm`, `ocean_ruin_warm_small`, `ocean_ruin_warm_tall`: Sandstone variants of ocean ruins. +* `treasure`: A treasure chest. Yes, its a whole structure. +* `pillager_outpost`: A pillager outpost. +* `mineshaft`: A mineshaft. +* `mineshaft_mesa`: A Mesa (Badlands) version of a mineshaft. +* `village`: Plains, oak village. +* `village_desert`: Desert, sandstone village. +* `village_savanna`: Savanna, acacia village. +* `village_taiga`: Taiga, spruce village. +* `village_snowy`: Resolute, Canada. + +Feature list: + +* `oak` +* `oak_beehive`: oak with a hive (1.15+). +* `oak_large`: oak with branches. +* `oak_large_beehive`: oak with branches and a beehive (1.15+). +* `birch` +* `birch_large`: tall variant of birch tree. +* `shrub`: low bushes that grow in jungles. +* `shrub_acacia`: low bush but configured with acacia (1.14 only) +* `shrub_snowy`: low bush with white blocks (1.14 only) +* `jungle`: a tree +* `jungle_large`: 2x2 jungle tree +* `spruce` +* `spruce_large`: 2x2 spruce tree +* `pine`: spruce with minimal leafage (1.15+) +* `pine_large`: 2x2 spruce with minimal leafage (1.15+) +* `spruce_matchstick`: see 1.15 pine (1.14 only). +* `spruce_matchstick_large`: see 1.15 pine_large (1.14 only). +* `dark_oak` +* `acacia` +* `oak_swamp`: oak with more leaves and vines. +* `well`: desert well +* `grass`: a few spots of tall grass +* `grass_jungle`: little bushier grass feature (1.14 only) +* `lush_grass`: grass with patchy ferns (1.15+) +* `tall_grass`: 2-high grass patch (1.15+) +* `fern`: a few random 2-high ferns +* `cactus`: random cacti +* `dead_bush`: a few random dead bushi +* `fossils`: underground fossils, placement little wonky +* `mushroom_brown`: large brown mushroom. +* `mushroom_red`: large red mushroom. +* `ice_spike`: ice spike. Require snow block below to place. +* `glowstone`: glowstone cluster. Required netherrack above it. +* `melon`: a patch of melons +* `melon_pile`: a pile of melons (1.15+) +* `pumpkin`: a patch of pumpkins +* `pumpkin_pile`: a pile of pumpkins (1.15+) +* `sugarcane` +* `lilypad` +* `dungeon`: Dungeon. These are hard to place, and fail often. +* `iceberg`: Iceberg. Generate at sea level. +* `iceberg_blue`: Blue ice iceberg. +* `lake` +* `lava_lake` +* `end_island` +* `chorus`: Chorus plant. Require endstone to place. +* `sea_grass`: a patch of sea grass. Require water. +* `sea_grass_river`: a variant. +* `kelp` +* `coral_tree, coral_mushroom, coral_claw`: various coral types, random color. +* `coral`: random coral structure. Require water to spawn. +* `sea_pickle` +* `boulder`: A rocky, mossy formation from a giant taiga biome. Doesn't update client properly, needs relogging. diff --git a/docs/scarpet/api/BlockIterations.md b/docs/scarpet/api/BlockIterations.md new file mode 100644 index 00000000..304815cf --- /dev/null +++ b/docs/scarpet/api/BlockIterations.md @@ -0,0 +1,34 @@ +# Iterating over larger areas of blocks + +These functions help scan larger areas of blocks without using generic loop functions, like nested `loop`. + +### `scan(cx, cy, cz, dx, dy, dz, px?, py?, pz?, expr)` + +Evaluates expression over area of blocks defined by its center (`cx, cy, cz`), expanded in all directions +by `dx, dy, dz` blocks, or optionally in negative with `d` coords, and `p` coords in positive values. `expr` +receives `_x, _y, _z` as coords of current analyzed block and `_`, which represents the block itself. + +### `volume(x1, y1, z1, x2, y2, z2, expr)` + +Evaluates expression for each block in the area, the same as the `scan`function, but using two opposite corners of +the rectangular cuboid. Any corners can be specified, its like you would do with `/fill` command. + +### `neighbours(x, y, z), neighbours(block), neighbours(l(x,y,z))` + +Returns the list of 6 neighbouring blocks to the argument. Commonly used with other loop functions like `for`. + +
+for(neighbours(x,y,z),air(_)) => 4 // number of air blocks around a block
+
+ +### `rect(cx, cy, cz, dx?, dy?, dz?, px?, py?, pz?)` + +returns an iterator, just like `range` function that iterates over rectangular cubarea of blocks. If only center +point is specified, it iterates over 27 blocks. If `d` arguments are specified, expands selection of respective +number of blocks in each direction. If `p` arguments are specified, it uses `d` for negative offset, and `p` for positive. + +### `diamond(cx, cy, cz, radius?, height?)` + +Iterates over a diamond like area of blocks. With no radius and height, its 7 blocks centered around the middle +(block + neighbours). With a radius specified, it expands shape on x and z coords, and with a custom height, on y. +Any of these can be zero as well. radius of 0 makes a stick, height of 0 makes a diamond shape pad. diff --git a/docs/scarpet/api/BlocksAndWorldAccess.md b/docs/scarpet/api/BlocksAndWorldAccess.md new file mode 100644 index 00000000..99b48fe7 --- /dev/null +++ b/docs/scarpet/api/BlocksAndWorldAccess.md @@ -0,0 +1,530 @@ +# Blocks / World API + +## Specifying blocks + +### `block(x, y, z)`, `block(l(x,y,z))`, `block(state)` + +Returns either a block from specified location, or block with a specific state (as used by `/setblock` command), +so allowing for block properties, block entity data etc. Blocks otherwise can be referenced everywhere by its simple +string name, but its only used in its default state. + +
+block('air')  => air
+block('iron_trapdoor[half=top]')  => iron_trapdoor
+block(0,0,0) == block('bedrock')  => 1
+block('hopper[facing=north]{Items:[{Slot:1b,id:"minecraft:slime_ball",Count:16b}]}') => hopper
+
+ +Retrieving a block with `block` function has also a side-effect of evaluating its current state and data. +So if you use it later it will reflect block state and data of the block that was when block was called, rather than +when it was used. Block values passed in various places like `scan` functions, etc, are not fully evaluated unless +its properties are needed. This means that if the block at the location changes before its queried in the program this +might result in getting the later state, which might not be desired. Consider the following example: + +
set(10,10,10,'stone');
+scan(10,10,10,0,0,0, b = _);
+set(10,10,10,'air');
+print(b); // 'air', block was remembered 'lazily', and evaluated by `print`, when it was already set to air
+set(10,10,10,'stone');
+scan(10,10,10,0,0,0, b = block(_));
+set(10,10,10,'air');
+print(b); // 'stone', block was evaluated 'eagerly' but call to `block`
+
+ +## World Manipulation + +All the functions below can be used with block value, queried with coord triple, or 3-long list. All `pos` in the +functions referenced below refer to either method of passing block position. + +### `set(pos, block, property?, value?, ...)` + +First part of the `set` function is either a coord triple, list of three numbers, or other block with coordinates. +Second part, `block` is either block value as a result of `block()` function string value indicating the block name, +and optional `property - value` pairs for extra block properties. If `block` is specified only by name, then if a +destination block is the same the `set` operation is skipped, otherwise is executed, for other potential extra +properties. + +The returned value is either the block state that has been set, or `false` if block setting was skipped + +
+set(0,5,0,'bedrock')  => bedrock
+set(l(0,5,0), 'bedrock')  => bedrock
+set(block(0,5,0), 'bedrock')  => bedrock
+scan(0,5,0,0,0,0,set(_,'bedrock'))  => 1
+set(pos(players()), 'bedrock')  => bedrock
+set(0,0,0,'bedrock')  => 0   // or 1 in overworlds generated in 1.8 and before
+scan(0,100,0,20,20,20,set(_,'glass'))
+    // filling the area with glass
+scan(0,100,0,20,20,20,set(_,block('glass')))
+    // little bit faster due to internal caching of block state selectors
+b = block('glass'); scan(0,100,0,20,20,20,set(_,b))
+    // yet another option, skips all parsing
+set(x,y,z,'iron_trapdoor')  // sets bottom iron trapdoor
+set(x,y,z,'iron_trapdoor[half=top]')  // Incorrect. sets bottom iron trapdoor - no parsing of properties
+set(x,y,z,'iron_trapdoor','half','top') // correct - top trapdoor
+set(x,y,z, block('iron_trapdoor[half=top]')) // also correct, block() provides extra parsing
+set(x,y,z,'hopper[facing=north]{Items:[{Slot:1b,id:"minecraft:slime_ball",Count:16b}]}') // extra block data
+
+ +### `without_updates(expr)` + +Evaluates subexpression without causing updates when blocks change in the world + +Consider following scenario: We would like to generate a bunch of terrain in a flat world following a perling noise +generator. The following code causes cascading effect as blocks placed on chunk borders will cause other chunks to get +loaded to full, thus generated: + +
+__config() -> m(l('scope', 'global'));
+__on_chunk_generated(x, z) -> (
+  scan(x,0,z,0,0,0,15,15,15,
+    if (perlin(_x/16, _y/8, _z/16) > _y/16,
+      set(_, 'black_stained_glass');
+    )
+  )
+)
+
+ +The following addition resolves this issue, by not allowing block updates pass chunk borders: + +
+__config() -> m(l('scope', 'global'));
+__on_chunk_generated(x, z) -> (
+  without_updates(
+    scan(x,0,z,0,0,0,15,15,15,
+      if (perlin(_x/16, _y/8, _z/16) > _y/16,
+        set(_, 'black_stained_glass');
+      )
+    )
+  )
+)
+
+ +### `place_item(item, pos, facing?, sneak?)` + +Places a given item in the world like it was placed by a player. Item names are default minecraft item name, +less the minecraft prefix. Default facing is 'up', but there are other options: 'down', 'north', 'east', 'south', +'west', but also there are other secondary directions important for placement of blocks like stairs, doors, etc. +Try experiment with options like 'north-up' which is placed facing north with cursor pointing to the upper part of the +block, or 'up-north', which means a block placed facing up (player looking down) and placed smidge away of the block +center towards north. Optional sneak is a boolean indicating if a player would be sneaking while placing the +block - this option only affects placement of chests and scaffolding at the moment. Returns true if placement was +successful, false otherwise. + +
+place_item('stone',x,y,z) // places a stone block on x,y,z block
+place_item('piston,x,y,z,'down') // places a piston facing down
+place_item('carrot',x,y,z) // attempts to plant a carrot plant. Returns true if could place carrots at that position.
+
+ +### `set_poi(pos, type, occupancy?)` + +Sets a Point of Interest (POI) of a specified type with optional custom occupancy. By default new POIs are not occupied. +If type is `null`, POI at position is removed. In any case, previous POI is also removed. Available POI types are: + +* `'unemployed', 'armorer', 'butcher', 'cartographer', 'cleric', 'farmer', 'fisherman', 'fletcher', 'leatherworker', 'librarian', 'mason', 'nitwit', 'shepherd', 'toolsmith', 'weaponsmith', 'home', 'meeting', 'beehive', 'bee_nest', 'nether_portal'` + +Interestingly, `unemployed`, and `nitwit` are not used in the game, meaning, they could be used as permanent spatial +markers for scarpet apps. `meeting` is the only one with increased max occupancy of 32. + +### `set_biome(pos, biome_name)` + +changes biome at that block position. + +### `update(pos)` + +Causes a block update at position. + +### `block_tick(pos)` + +Causes a block to tick at position. + +### `random_tick(pos)` + +Causes a random tick at position. + +### `destroy(pos), destroy(pos, -1), destroy(pos, ), destroy(pos, tool, nbt?)` + +Destroys the block like it was mined by a player. Add -1 for silk touch, and positive number for fortune level. +If tool is specified, and optionally its nbt, it will use that tool and will attempt to mine the block with this tool. +If called without item context, this function, unlike harvest, will affect all kinds of blocks. If called with item +in context, it will fail to break blocks that cannot be broken by a survival player. + +Without item context it returns `false` if failed to destroy the block and `true` if block breaking was successful. +In item context, `true` means that breaking item has no nbt to use, `null` indicating that the tool should be +considered broken in process, and `nbt` type value, for a resulting NBT tag on a hypothetical tool. Its up to the +programmer to use that nbt to apply it where it belong + +Here is a sample code that can be used to mine blocks using items in player inventory, without using player context +for mining. Obviously, in this case the use of `harvest` would be much more applicable: + +
+mine(x,y,z) ->
+(
+   p = player();
+   slot = p~'selected_slot';
+   item_tuple = inventory_get(p, slot);
+   if (!item_tuple, destroy(x,y,z,'air'); return()); // empty hand, just break with 'air'
+   l(item, count, tag) = item_tuple;
+   tag_back = destroy(x,y,z, item, tag);
+   if (tag_back == false, // failed to break the item
+	     return(tag_back)
+   );
+   if (tag_back == true, // block broke, tool has no tag
+	     return(tag_back)
+   );
+   if (tag_back == null, //item broke
+	     delete(tag:'Damage');
+	     inventory_set(p, slot, count-1, item, tag);
+	     return(tag_back)
+   );
+   if (type(tag_back) == 'nbt', // item didn't break, here is the effective nbt
+	     inventory_set(p, slot, count, item, tag_back);
+	     return(tag_back)
+   );
+   print('How did we get here?');
+)
+
+ +### `harvest(player, pos)` + +Causes a block to be harvested by a specified player entity. Honors player item enchantments, as well as damages the +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. + +## Block and World querying + +### `pos(block), pos(entity)` + +Returns a triple of coordinates of a specified block or entity. Technically entities are queried with `query` function +and the same can be achieved with `query(entity,'pos')`, but for simplicity `pos` allows to pass all positional objects. + +
+pos(block(0,5,0))  => l(0,5,0)
+pos(players()) => l(12.3, 45.6, 32.05)
+pos(block('stone'))  => Error: Cannot fetch position of an unrealized block
+
+ +### `pos_offset(pos, direction, amount?)` + +Returns a coords triple that is offset in a specified `direction` by `amount` of blocks. The default offset amount is +1 block. To offset into opposite facing, use negative numbers for the `amount`. + +
+pos_offset(block(0,5,0), 'up', 2)  => l(0,7,0)
+pos_offset(l(0,5,0), 'up', -2 ) => l(0,3,0)
+
+ +### `block_properties(pos)` + +Returns a list of available block properties for a particular block. If a block has no properties, returns an empty list. + +### `property(pos, name)` + +Returns property of block at `pos`, or specified by `block` argument. If a block doesn't have that property, `null` +value is returned. Returned values are always strings. It is expected from the user to know what to expect and convert +values to numbers using `number()` function or booleans using `bool()` function. + +
+set(x,y,z,'iron_trapdoor','half','top'); property(x,y,z,'half')  => top
+set(x,y,z,'air'); property(x,y,z,'half')  => null
+property(block('iron_trapdoor[half=top]'),'half')  => top
+property(block('iron_trapdoor[half=top]'),'powered')  => false
+bool(property(block('iron_trapdoor[half=top]'),'powered'))  => 0
+
+ +### `block_data(pos)` + +Return NBT string associated with specific location, or null if the block does not carry block data. Can be currently +used to match specific information from it, or use it to copy to another block + +
    block_data(x,y,z) => '{TransferCooldown:0,x:450,y:68, ... }'
+
+ +### `poi(pos), poi(pos, radius), poi(pos, radius, status)` + +Queries a POI (Point of Interest) at a given position, returning `null` if none is found, or tuple of poi type and its +occupancy load. With optional `radius` and `status`, returns a list of POIs around `pos` within a given `radius`. +If `status` is specified (either 'available', or 'occupied') returns only POIs with that status. The return format is +again, poi type, occupancy load, and extra tripple of coordinates. + +Querying for POIs using the radius is intended use of POI mechanics and ability of accessing individual POIs +via `poi(pos)` in only provided for completness. + +
+poi(x,y,z) => null  // nothing set at position
+poi(x,y,z) => ['meeting',3]  // its a bell-type meeting point occupied by 3 villagers
+poi(x,y,z,5) => []  // nothing around
+poi(x,y,z,5) => [['nether_portal',0,[7,8,9]],['nether_portal',0,[7,9,9]]] // two portal blocks in the range
+
+ +### `biome(pos)` + +returns biome at that block position. + +### `solid(pos)` + +Boolean function, true if the block is solid + +### `air(pos)` + +Boolean function, true if a block is air.... or cave air... or void air.... or any other air they come up with. + +### `liquid(pos)` + +Boolean function, true if the block is liquid, or liquidlogged + +### `flammable(pos)` + +Boolean function, true if the block is flammable + +### `transparent(pos)` + +Boolean function, true if the block is transparent + +### `opacity(pos)` + +Numeric, returning opacity level of a block + +### `blocks_daylight(pos)` + +Boolean function, true if the block blocks daylight + +### `emitted_light(pos)` + +Numeric, returning light level emitted from block + +### `light(pos)` + +Integer function, returning total light level at position + +### `block_light(pos)` + +Integer function, returning block light at position. From torches and other light sources. + +### `sky_light(pos)` + +Numeric function, returning sky light at position. From the sky access. + +### `see_sky(pos)` + +Boolean function, returning true if the block can see sky. + +### `hardness(pos)` + +Numeric function, indicating hardness of a block. + +### `blast_resistance(pos)` + +Numeric function, indicating blast_resistance of a block. + +### `in_slime_chunk(pos)` + +Boolean indicating if the given block position is in a slime chunk. + +### `top(type, pos)` + +Returns the Y value of the topmost block at given x, z coords (y value of a block is not important), according to the +heightmap specified by `type`. Valid options are: + +* `light`: topmost light blocking block (1.13 only) +* `motion`: topmost motion blocking block +* `terrain`: topmost motion blocking block except leaves +* `ocean_floor`: topmost non-water block +* `surface`: topmost surface block + +
+top('motion', x, y, z)  => 63
+top('ocean_floor', x, y, z)  => 41
+
+ +### `loaded(pos)` + +Boolean function, true if the block is accessible for the game mechanics. Normally `scarpet` doesn't check if operates +on loaded area - the game will automatically load missing blocks. We see this as advantage. Vanilla `fill/clone` +commands only check the specified corners for loadness. + +To check if block is truly loaded, I mean in memory, use `generation_status(x) != null`, as chunks can still be loaded +outside of the playable area, just are not used any of the game mechanics processes. + +
+loaded(pos(players()))  => 1
+loaded(100000,100,1000000)  => 0
+
+ +### `(Deprecated) loaded_ep(pos)` + +Boolean function, true if the block is loaded and entity processing, as per 1.13.2 + +Deprecated as of scarpet 1.6, use `loaded_status(x) > 0`, or just `loaded(x)` with the same effect + +### `loaded_status(pos)` + +Returns loaded status as per new 1.14 chunk ticket system, 0 for inaccessible, 1 for border chunk, 2 for ticking, +3 for entity ticking + +### `generation_status(pos), generation_status(pos, true)` + +Returns generation status as per new 1.14 chunk ticket system. Can return any value from several available but chunks +can only be valid in a few states: `full`, `features`, `liquid_carvers`, and `structure_starts`. Returns `null` +if the chunk is not in memory unless called with optional `true`. + +### `structures(pos), structures(pos, structure_name)` + +Returns structure information for a given block position. Note that structure information is the same for all the +blocks from the same chunk. `structures` function can be called with a block, or a block and a structure name. In +the first case it returns a map of structures at a given position, keyed by structure name, with values indicating +the bounding box of the structure - a pair of two 3-value coords (see examples). When called with an extra structure +name, returns list of components for that structure, with their name, direction and two sets of coordinates +indicating the bounding box of the structure piece. + +### `structure_references(pos), structure_references(pos, structure_name)` + +Returns structure information that a chunk with a given block position is part of. `structure_references` function +can be called with a block, or a block and a structure name. In the first case it returns a list of structure names +that give chunk belongs to. When called with an extra structure name, returns list of positions pointing to the +lowest block position in chunks that hold structure starts for these structures. You can query that chunk structures +then to get its bounding boxes. + +### `set_structure(pos, structure_name), set_structure(pos, structure_name, null)` + +Creates or removes structure information of a structure associated with a chunk of `pos`. Unlike `plop`, blocks are +not placed in the world, only structure information is set. For the game this is a fully functional structure even +if blocks are not set. To remove structure a given point is in, use `structure_references` to find where current +structure starts. + +### `suffocates(pos)` + +Boolean function, true if the block causes suffocation. + +### `power(pos)` + +Numeric function, returning redstone power level at position. + +### `ticks_randomly(pos)` + +Boolean function, true if the block ticks randomly. + +### `blocks_movement(pos)` + +Boolean function, true if block at position blocks movement. + +### `block_sound(pos)` + +Returns the name of sound type made by the block at position. One of: + +* `wood` +* `gravel` +* `grass` +* `stone` +* `metal` +* `glass` +* `wool` +* `sand` +* `snow` +* `ladder` +* `anvil` +* `slime` +* `sea_grass` +* `coral` + +### `material(pos)` + +Returns the name of material of the block at position. very useful to target a group of blocks. One of: + +* `air` +* `void` +* `portal` +* `carpet` +* `plant` +* `water_plant` +* `vine` +* `sea_grass` +* `water` +* `bubble_column` +* `lava` +* `snow_layer` +* `fire` +* `redstone_bits` +* `cobweb` +* `redstone_lamp` +* `clay` +* `dirt` +* `grass` +* `packed_ice` +* `sand` +* `sponge` +* `wood` +* `wool` +* `tnt` +* `leaves` +* `glass` +* `ice` +* `cactus` +* `stone` +* `iron` +* `snow` +* `anvil` +* `barrier` +* `piston` +* `coral` +* `gourd` +* `dragon_egg` +* `cake` + +### `map_colour(pos)` + +Returns the map colour of a block at position. One of: + +* `air` +* `grass` +* `sand` +* `wool` +* `tnt` +* `ice` +* `iron` +* `foliage` +* `snow` +* `clay` +* `dirt` +* `stone` +* `water` +* `wood` +* `quartz` +* `adobe` +* `magenta` +* `light_blue` +* `yellow` +* `lime` +* `pink` +* `gray` +* `light_gray` +* `cyan` +* `purple` +* `blue` +* `brown` +* `green` +* `red` +* `black` +* `gold` +* `diamond` +* `lapis` +* `emerald` +* `obsidian` +* `netherrack` +* `white_terracotta` +* `orange_terracotta` +* `magenta_terracotta` +* `light_blue_terracotta` +* `yellow_terracotta` +* `lime_terracotta` +* `pink_terracotta` +* `gray_terracotta` +* `light_gray_terracotta` +* `cyan_terracotta` +* `purple_terracotta` +* `blue_terracotta` +* `brown_terracotta` +* `green_terracotta` +* `red_terracotta` +* `black_terracotta` diff --git a/docs/scarpet/api/Entities.md b/docs/scarpet/api/Entities.md new file mode 100644 index 00000000..921b9e89 --- /dev/null +++ b/docs/scarpet/api/Entities.md @@ -0,0 +1,471 @@ +# Entity API + +## Entity Selection + +Entities have to be fetched before using them. Entities can also change their state between calls to the script if +game happens either in between separate calls to the programs, or if the program calls `game_tick` on its own. +In this case - entities would need to be re-fetched, or the code should account for entities getting dead. + +### `player(), player(type), player(name)` + +With no arguments, it returns the calling player or the player closest to the caller. Note that the main context +will receive `p` variable pointing to this player. With `type` or `name` specified it will try first to match a type, +returning a list of players matching a type, and if this fails, will assume its player name query retuning player with +that name, or `null` if no player was found. With `'all'`, list of all players in the game, in all dimensions, so end +user needs to be cautious, that you might be referring to wrong blocks and entities around the player in question. +With `type = '*'` it returns all players in caller dimension, `'survival'` returns all survival and adventure players, +`'creative'` returns all creative players, `'spectating'` returns all spectating players, and `'!spectating'`, +all not-spectating players. If all fails, with `name`, the player in question, if is logged in. + +### `entity_id(uuid), entity_id(id)` + +Fetching entities wither by their ID obtained via `entity ~ 'id'`, which is unique for a dimension and current world +run, or by UUID, obtained via `entity ~ 'uuid'`. It returns null if no such entity is found. Safer way to 'store' +entities between calls, as missing entities will be returning `null`. Both calls using UUID or numerical ID are `O(1)`, +but obviously using UUIDs takes more memory and compute. + +### `entity_list(type)` + +Returns global lists of entities in the current dimension of a specified type. Currently the following +selectors are available: + +* `*`: all entites +* `living` +* `items` +* `players` +* `!players` + +### `entity_area(type, cx, cy, cz, dx, dy, dz)` + +Returns entities of a specified type in an area centered on `cx, cy, cz` and at most `dx, dy, dz` blocks away from +the center point. Uses same selectors as `entities_list` + +### `entity_selector(selector)` + +Returns entities satisfying given vanilla entity selector. Most complex among all the methods of selecting entities, +but the most capable. Selectors are cached so should be as fast as other methods of selecting entities. + +### `spawn(name, pos, nbt?)` + +Spawns and places an entity in world, like `/summon` vanilla command. Requires a position to spawn, and optional +extra nbt data to merge with the entity. What makes it different from calling `run('summon ...')`, is the fact that +you get the entity back as a return value, which is swell. + +## Entity Manipulation + +Unlike with blocks, that use plethora of vastly different querying functions, entities are queried with `query` +function and altered via `modify` function. Type of information needed or values to be modified are different for +each call. + +Using `~` (in) operator is an alias for `query`. Especially useful if a statement has no arguments, +which in this case can be radically simplified + +
+query(p, 'name') <=> p ~ 'name'     // much shorter and cleaner
+query(p, 'holds', 'offhand') <=> p ~ l('holds', 'offhand')    // not really but can be done
+
+ +### `query(e,'removed')` + +Boolean. True if the entity is removed + +### `query(e,'id')` + +Returns numerical id of the entity. Most efficient way to keep track of entites in a script. +Ids are only unique within current game session (ids are not preserved between restarts), +and dimension (each dimension has its own ids which can overlap. + +### `query(e,'uuid')` + +Returns UUID (unique id) of the entity. Can be used to access entities with the other vanilla commands and +remains unique regardless of the dimension, and is preserved between game restarts. Apparently players cannot be +accessed via UUID, but name instead. + +
+map(entities_area('*',x,y,z,30,30,30),run('kill '+query(_,'id'))) // doesn't kill the player
+
+ +### `query(e,'pos')` + +Triple of entity position + +### `query(e,'location')` + +Quin-tuple of entity position (x, y, and z coords), and rotation (yaw, pitch) + +### `query(e,'x'), query(e,'y'), query(e,'z')` + +Respective entity coordinate + +### `query(e,'pitch'), query(e,'yaw')` + +Pitch and Yaw or where entity is looking. + +### `query(e,'look')` + +Returns a 3d vector where the entity is looking. + +### `query(e,'motion')` + +Triple of entity motion vector, `l(motion_x, motion_y, motion_z)` + +### `query(e,'motion_x'), query(e,'motion_y'), query(e,'motion_z')` + +Respective component of the motion vector + +### `query(e,'name'), query(e,'custom_name'), query(e,'type')` + +String of entity name + +
query(e,'name')  => Leatherworker
+query(e,'custom_name')  => null
+query(e,'type')  => villager
+
+ +### `query(e,'is_riding')` + +Boolean. True if riding another entity. + +### `query(e,'is_ridden')` + +Boolean. True if another entity is riding it. + +### `query(e,'passengers')` + +List of entities riding the entity. + +### `query(e,'mount')` + +Entity that `e` rides. + +### `query(e,'tags')` + +List of entity tags. + +### `query(e,'has_tags',tag)` + +Boolean, True if the entity is marked with `tag`. + +### `query(e,'is_burning')` + +Boolean, True if the entity is burning. + +### `query(e,'fire')` + +Number of remaining ticks of being on fire. + +### `query(e,'silent')` + +Boolean, True if the entity is silent. + +### `query(e,'gravity')` + +Boolean, True if the entity is affected by gravity, like most entities do. + +### `query(e,'immune_to_fire')` + +Boolean, True if the entity is immune to fire. + +### `query(e,'dimension')` + +Name of the dimension entity is in. + +### `query(e,'height')` + +Height of the entity. + +### `query(e,'width')` + +Width of the entity. + +### `query(e,'eye_height')` + +Eye height of the entity. + +### `query(e,'age')` + +Age, in ticks, of the entity, i.e. how long it existed. + +### `query(e,'breeding_age')` + +Breeding age of passive entity, in ticks. If negative it it time to adulthood, if positive, breeding cooldown + +### `query(e,'despawn_timer')` + +For living entities - the number of ticks they fall outside of immediate player presence. + +### `query(e,'item')` + +The item triple (name, count, nbt) if its an item entity, `null` otherwise + +### `query(e,'count')` + +Number of items in a stack from item entity, `null` otherwise + +### `query(e,'pickup_delay')` + +Retrieves pickup delay timeout for an item entity, `null` otherwise + +### `query(e,'is_baby')` + +Boolean, true if its a baby. + +### `query(e,'target')` + +Returns mob's attack target or null if none or not applicable. + +### `query(e,'home')` + +Returns creature's home position or null if none or not applicable. + +### `query(e,'sneaking')` + +Boolean, true if entity is sneaking. + +### `query(e,'sprinting')` + +Boolean, true if entity is sprinting. + +### `query(e,'swimming')` + +Boolean, true if entity is swimming. + +### `query(e,'jumping')` + +Boolean, true if entity is jumping. + +### `query(e,'gamemode')` + +String with gamemode, or `null` if not a player. + +### `query(e,'gamemode_id')` + +Good'ol gamemode id, or null if not a player. + +### `query(e,'player_type')` + +Returns `null` if the argument is not a player, otherwise: + +* `singleplayer`: for singleplayer game +* `multiplayer`: for players on a dedicated server +* `lan_host`: for singleplayer owner that opened the game to LAN +* `lan_player`: for all other players that connected to a LAN host +* `fake`: any carpet-spanwed fake player +* `shadow`: any carpet-shadowed real player +* `realms`: ? + +### `query(e,'permission_level')` + +Player's permission level, or `null` if not applicable for this entity. + +### `query(e,'effect',name?)` + +Without extra arguments, it returns list of effect active on a living entity. Each entry is a triple of short +effect name, amplifier, and remaining duration. With an argument, if the living entity has not that potion active, +returns `null`, otherwise return a tuple of amplifier and remaining duration. + +
+query(p,'effect')  => [[haste, 0, 177], [speed, 0, 177]]
+query(p,'effect','haste')  => [0, 177]
+query(p,'effect','resistance')  => null
+
+ +### `query(e,'health')` + +Number indicating remaining entity health, or `null` if not applicable. + +### `query(e,'holds',slot?)` + +Returns triple of short name, stack count, and NBT of item held in `slot`. Available options for `slot` are: + +* `mainhand` +* `offhand` +* `head` +* `chest` +* `legs` +* `feet` + +If `slot` is not specified, it defaults to the main hand. + +### `query(e,'selected_slot')` + +Number indicating the selected slot of entity inventory. Currently only applicable to players. + +### `query(e,'facing', order?)` + +Returns where the entity is facing. optional order (number from 0 to 5, and negative), indicating primary directions +where entity is looking at. From most prominent (order 0) to opposite (order 5, or -1) + +### `query(e,'trace', reach?, options?...)` + +Returns the result of ray tracing from entity perspective, indicating what it is looking at. Default reach is 4.5 +blocks (5 for creative players), and by default it traces for blocks and entities, identical to player attack tracing +action. This can be customized with `options`, use 'blocks' to trace for blocks, 'liquids' to include liquid blocks +as possible results, and 'entities' to trace entities. Any combination of the above is possible. When tracing +entities and blocks, blocks will take over the priority even if transparent or non-colliding +(aka fighting chickens in tall grass). + +Regardless of the options selected, the result could be `null` if nothing is in reach, entity, if look targets an +entity, and block value if block is in reach. + +### `query(e,'nbt',path?)` + +Returns full NBT of the entity. If path is specified, it fetches only that portion of the NBT, that corresponds to the +path. For specification of `path` attribute, consult vanilla `/data get entity` command. + +Note that calls to `nbt` are considerably more expensive comparing to other calls in Minecraft API, and should only +be used when there is no other option. Returned value is of type `nbt`, which can be further manipulated with nbt +type objects via `get, put, has, delete`, so try to use API calls first for that. + +## Entity Modification + +Like with entity querying, entity modifications happen through one function. Most position and movements modifications +don't work currently on players as their position is controlled by clients. + +Currently there is no ability to modify NBT directly, but you could always use `run('data modify entity` + +### `modify(e,'remove')` + +Removes (not kills) entity from the game. + +### `modify(e,'kill')` + +Kills the entity. + +### `modify(e, 'pos', x, y, z), modify(e, 'pos', l(x,y,z) )` + +Moves the entity to a specified coords. + +### `modify(e, 'location', x, y, z, yaw, pitch), modify(e, 'location', l(x, y, z, yaw, pitch) )` + +Changes full location vector all at once. + +### `modify(e, 'x', x), modify(e, 'y', y), modify(e, 'z', z)` + +Moves the entity in.... one direction. + +### `modify(e, 'pitch', pitch), modify(e, 'yaw', yaw)` + +Changes entity's pitch or yaw. + +### `modify(e, 'move', x, y, z), modify(e, 'move', l(x,y,z) )` + +Moves the entity by a vector from its current location. + +### `modify(e, 'motion', x, y, z), modify(e, 'motion', l(x,y,z) )` + +Sets the motion vector (where and how much entity is moving). + +### `modify(e, 'motion_z', x), modify(e, 'motion_y', y), modify(e, 'motion_z', z)` + +Sets the corresponding component of the motion vector. + +### `modify(e, 'accelerate', x, y, z), modify(e, 'accelerate', l(x, y, z) )` + +Adds a vector to the motion vector. Most realistic way to apply a force to an entity. + +### `modify(e, 'custom_name'), modify(e, 'custom_name', name )` + +### `modify(e, 'age', number)` + +Modifies entity's internal age counter. Fiddling with this will affect directly behaviours of complex +entities, so use it with caution. + +### `modify(e, 'pickup_delay', number)` + +### `modify(e, 'breeding_age', number)` + +### `modify(e, 'despawn_timer', number)` + +Sets a custom despawn timer value. + +### `modify(e, 'dismount')` + +Dismounts riding entity. + +### `modify(e, 'mount', other)` + +Mounts the entity to the `other`. + +### `modify(e, 'drop_passengers')` + +Shakes off all passengers. + +### `modify(e, 'mount_passengers', passenger, ? ...), modify(e, 'mount_passengers', l(passengers) )` + +Mounts on all listed entities on `e`. + +### `modify(e, 'tag', tag, ? ...), modify(e, 'tag', l(tags) )` + +Adds tag / tags to the entity. + +### `modify(e, 'clear_tag', tag, ? ...), modify(e, 'clear_tag', l(tags) )` + +Removes tag from entity. + +### `modify(e, 'talk')` + +Make noises. + +### `modify(e, 'ai', boolean)` + +If called with `false` value, it will disable AI in the mob. `true` will enable it again. + +### `modify(e, 'no_clip', boolean)` + +Sets if the entity obeys any collisions, including collisions with the terrain and basic physics. Not affecting +players, since they are controlled client side + +### `modify(e, 'effect', name, duration?, amplifier?, show_particles?, show_icon?)` + +Applies status effect to the living entity. Takes several optional parameters, which default to `0`, `true` +and `true`. If no duration is specified, or it is null or 0, the effect is removed. + +### `modify(e, 'home', null), modify(e, 'home', block, distance?), modify(e, 'home', x, y, z, distance?)` + +Sets AI to stay around the home position, within `distance` blocks from it. `distance` defaults to 16 blocks. +`null` removes it. _May_ not work fully with mobs that have this AI built in, like Villagers. + +## Entity Events + +There is a number of events that happen to entities that you can attach your own code to in the form of event handlers. +The event handler is any function that runs in your package that accepts certain expected parameters, which you can +expand with your own arguments. When it comes to the moment when the given command needs to be executed, it does so +providing that number of arguments it accepts is equal number of event arguments, and extra arguments passed when +defining the callback with `entity_event` + +The following events can be handled by entities. + +* `'on_tick'`: executes every tick right before entity is ticked in the game. Required arguments: `entity` +* `'on_death'`: executes once when a living entity dies. Required arguments: `entity, reason` +* `'on_removed'`: execute once when an entity is removed. Required arguments: `entity` +* `'on_damaged'`: executed every time a living entity is about to receive damage. +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 will attach inapplicable event to it. + +### `entity_event(e, event, call_name, args...)` + +Attaches specific function from the current package to be called upon the `event`, with extra `args` curried to the +original required arguments for the event handler + +
+protect_villager(entity, amount, source, source_entity, healing_player) ->
+(
+   if(source_entity && source_entity~'type' != 'player',
+      modify(entity, 'health', amount + entity~'health' );
+      particle('end_rod', pos(entity)+l(0,3,0));
+      print(str('%s healed thanks to %s', entity, healing_player))
+   )
+);
+__on_player_interacts_with_entity(player, entity, hand) ->
+(
+   if (entity~'type' == 'villager',
+      entity_event(entity, 'on_damage', 'protect_villager', player~'name')
+   )
+)
+
+ +In this case this will protect a villager from entity damage (zombies, etc.) except players by granting all the +health back to the villager after being harmed. diff --git a/docs/scarpet/api/Events.md b/docs/scarpet/api/Events.md new file mode 100644 index 00000000..5df38a5b --- /dev/null +++ b/docs/scarpet/api/Events.md @@ -0,0 +1,52 @@ +# Scarpet events system + +Provides the ability to execute specific function whenever an event occurs. The functions to be registered need to +conform with the arguments to the event specification. When loading module functions, each function that starts +with `__on_...` and has the required arguments, will be bound automatically. In case of player specific modules, +all player action events will be directed to the appropriate player space, and all tick events will be executed in +the global context, so its not a good idea to mix these two, so use either of these, or use commands to call tick +events directly, or handle player specific data inside a module. In near future using global events +(on_tick, on_chunk_generated) will be not allowed in player scoped apps. + +### Event list + +Here is a list of events that can be handled by scarpet. This list includes prefixes required by modules to autoload +them, but you can add any function to any event if it accepts required number of parameters: + +
+__on_tick()         // can access blocks and entities in the overworld
+__on_tick_nether()  // can access blocks and entities in the nether
+__on_tick_ender()   // can access blocks and entities in the end
+__on_chunk_generated(x,z) // called after a chunk is promoted to the full chunk,
+                          // prodiving lowest x and z coords in the chunk
+                          // event will not work with optifine installed in the game
+// player specific callbacks
+__on_player_uses_item(player, item_tuple, hand)  // right click action
+__on_player_releases_item(player, item_tuple, hand)  // client action (e.g. bow)
+__on_player_finishes_using_item(player, item_tuple, hand))  // server action (e.g. food), called item is from before it is used.
+__on_player_clicks_block(player, block, face)  // left click attack on a block
+__on_player_breaks_block(player, block) // called after block is broken (the caller receives previous blockstate)
+__on_player_right_clicks_block(player, item_tuple, hand, block, face, hitvec)  // player right clicks block with anything
+__on_player_interacts_with_block(player, hand, block, face, hitvec)  //right click on a block resulted in activation of said block
+__on_player_places_block(player, item_tuple, hand, block) // player have just placed the block.
+__on_player_interacts_with_entity(player, entity, hand)
+__on_player_attacks_entity(player, entity)
+__on_player_rides(player, forward, strafe, jumping, sneaking)
+__on_player_jumps(player)
+__on_player_deploys_elytra(player)
+__on_player_wakes_up(player)
+__on_player_starts_sneaking(player)
+__on_player_stops_sneaking(player)
+__on_player_starts_sprinting(player)
+__on_player_stops_sprinting(player)
+__on_player_drops_item(player)
+__on_player_drops_stack(player)
+__on_statistic(player, category, event, value) // player statistic changes
+
+ +### `/script event` command + +used to display current events and bounded functions. use `add_to` ro register new event, or `remove_from` to +unbind a specific function from an event. Function to be bounded to an event needs to have the same number of +parameters as the action is attempting to bind to (see list above). All calls in modules loaded via `/script load` +that have functions listed above will be automatically bounded and unbounded when script is unloaded. diff --git a/docs/scarpet/api/Inventories.md b/docs/scarpet/api/Inventories.md new file mode 100644 index 00000000..972cf5b5 --- /dev/null +++ b/docs/scarpet/api/Inventories.md @@ -0,0 +1,116 @@ +# Inventory and Items API + +## Manipulating inventories of blocks and entities + +Most functions in this category require inventory as the first argument. Inventory could be specified by an entity, +or a block, or position (three coordinates) of a potential block with inventory. Player enderchest inventory require +two arguments, keyword `'enderchest'`, followed by the player entity argument. If the entity or a block doesn't have +an inventory, they typically do nothing and return null. + +Most items returned are in the form of a triple of item name, count, and nbt or the extra data associated with an item. +Manipulating of the nbt data can be costly, but retrieving them from the tuple to match other aspects is cheap + +### `stack_limit(item)` + +Returns number indicating what is the stack limit for the item. Its typically 1 (non-stackable), 16 (like buckets), +or 64 - rest. It is recommended to consult this, as other inventory API functions ignore normal stack limits, and +it is up to the programmer to keep it at bay. As of 1.13, game checks for negative numbers and setting an item to +negative is the same as empty. + +
+stack_limit('wooden_axe') => 1
+stack_limit('ender_pearl') => 16
+stack_limit('stone') => 64
+
+ +### `item_category(item)` + +Returns the string representing the category of a given item, like `building_blocks`, `combat`, or `tools`. + +
+item_category('wooden_axe') => tools
+item_category('ender_pearl') => misc
+item_category('stone') => building_blocks
+
+ +### `inventory_size(inventory)` + +Returns the size of the inventory for the entity or block in question. Returns null if the block or entity don't +have an inventory + +
+inventory_size(player()) => 41
+inventory_size('enderchest', player()) => 27 // enderchest
+inventory_size(x,y,z) => 27 // chest
+inventory_size(block(pos)) => 5 // hopper
+
+ +### `inventory_has_items(inventory)` + +Returns true, if the inventory is not empty, false if it is empty, and null, if its not an inventory. + +
    inventory_has_items(player()) => true
+    inventory_has_items(x,y,z) => false // empty chest
+    inventory_has_items(block(pos)) => null // stone
+
+ +### `inventory_get(inventory, slot)` + +Returns the item in the corresponding inventory slot, or null if slot empty or inventory is invalid. You can use +negative numbers to indicate slots counted from 'the back'. + +
+inventory_get(player(), 0) => null // nothing in first hotbar slot
+inventory_get(x,y,z, 5) => ['stone', 1, {}]
+inventory_get(player(), -1) => ['diamond_pickaxe', 1, {Damage:4}] // slightly damaged diamond pick in the offhand
+
+ +### `inventory_set(inventory, slot, count, item?, nbt?)` + +Modifies or sets a stack in inventory. specify count 0 to empty the slot. If item is not specified, keeps existing +item, just modifies the count. If item is provided - replaces current item. If nbt is provided - adds a tag to the +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_find(inventory, item, start_slot?, ), inventory_find(inventory, null, start_slot?)` + +Finds the first slot with a corresponding item in the inventory, or if queried with null: the first empty slot. +Returns slot number if found, or null otherwise. Optional start_slot argument allows to skip all preceeding slots +allowing for efficient (so not slot-by-slot) inventory search for items. + +
+inventory_find(player(), 'stone') => 0 // player has stone in first hotbar slot
+inventory_find(player(), null) => null // player's inventory has no empty spot
+while( (slot = inventory_find(p, 'diamond', slot)) != null, 41, drop_item(p, slot) )
+    // spits all diamonds from player inventory wherever they are
+inventory_drop(x,y,z, 0) => 64 // removed and spawned in the world a full stack of items
+
+ +### `inventory_remove(inventory, item, amount?)` + +Removes amount (defaults to 1) of item from inventory. If the inventory doesn't have the defined amount, nothing +happens, otherwise the given amount of items is removed wherever they are in the inventory. Returns boolean +whether the removal operation was successful. Easiest way to remove a specific item from player inventory +without specifying the slot. + +
+inventory_remove(player(), 'diamond') => 1 // removed diamond from player inventory
+inventory_remove(player(), 'diamond', 100) => 0 // player doesn't have 100 diamonds, nothing happened
+
+ +### `drop_item(inventory, slot, amount?, )` + +Drops the items from indicated inventory slot, like player that Q's an item or villager, that exchanges food. +You can Q items from block inventories as well. default amount is 0 - which is all from the slot. +NOTE: hoppers are quick enough to pick all the queued items from their inventory anyways. +Returns size of the actual dropped items. + +
+inventory_drop(player(), 0, 1) => 1 // Q's one item on the ground
+inventory_drop(x,y,z, 0) => 64 // removed and spawned in the world a full stack of items
+
diff --git a/docs/scarpet/api/Overview.md b/docs/scarpet/api/Overview.md new file mode 100644 index 00000000..4f6b0e74 --- /dev/null +++ b/docs/scarpet/api/Overview.md @@ -0,0 +1,13 @@ +# Minecraft specific API and `scarpet` language add-ons and commands + +Here is the gist of the Minecraft related functions. Otherwise the CarpetScript could live without Minecraft. + +## Dimension warning + +One note, which is important is that most of the calls for entities and blocks would refer to the current +dimension of the caller, meaning, that if we for example list all the players using `player('all')` function, +if a player is in the other dimension, calls to entities and blocks around that player would point incorrectly. +Moreover, running commandblocks in the spawn chunks would mean that commands will always refer to the overworld +blocks and entities. In case you would want to run commands across all dimensions, just run three of them, +using `/execute in overworld/the_nether/the_end run script run ...` and query players using `player('*')`, +which only returns players in current dimension, or use `in_dimension(expr)` function. \ No newline at end of file diff --git a/docs/scarpet/api/ScriptCommand.md b/docs/scarpet/api/ScriptCommand.md new file mode 100644 index 00000000..0ac2ff36 --- /dev/null +++ b/docs/scarpet/api/ScriptCommand.md @@ -0,0 +1,185 @@ +# `/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 +the executing entity of the command. You will receive tab completion suggestions as you type your code suggesting +functions and global variables. It is advisable to use `/execute in ... at ... as ... run script run ...` or similar, +to simulate running commands in a different scope. + +# `/script load / unload (global?)`, `/script in ` commands + +`load / unload` commands allow for very conventient way of writing your code, providing it to the game and +distribute with your worlds without the need of use of commandblocks. Just place your scarpet code in the +/scripts folder of your world files and make sure it ends with `.sc` extension. The good thing about editing that +code is that you can no only use normal editing without the need of marking of newlines, +but you can also use comments in your code. + +a comment is anything that starts with a double slash, and continues to the end of the line: + +
+foo = 1;
+//This is a comment
+bar = 2;
+// This never worked, so I commented it out
+// baz = foo()
+
+ +### `/script load/unload (?global)` + +Loading operation will load that script code from disk and execute it right away. You would probably use it to load +some stored procedures to be used for later. To reload the module, just type `/script load` again. Reloading removes +all the current global state (globals and functions) that were added later by the module. + +Loading a module, if it contains a `__command()` method, will attempt to registed a command with that app name, +and register all public (no underscore) functions available in the module as subcommands. It will also bind specific +events to the event system (check Events section for details). + +Loaded apps have the ability to store and load external files, especially their persistent tag state. For that +check `load_app_data` and `store_app_data` functions. + +If an app defines `__config` method, and that method returns a map, it will be used to apply custom settings +for this app. Currently the following options are supported: + +* `scope`: default scope for global variables for the app, Default is 'player' which means that globals and defined +functions will be unique for each player so that apps for each player will run in isolation. This is useful in +tool-like applications. Another option is 'global' which shares global state for all runs on the server - applicable +to 'block' like solutions, where custom behaviours are applied to blocks. +* `stay_loaded`: defaults to false. If true, and `/carpet scriptsAutoload` is turned on, the following apps will +stay loaded after startup. Otherwise, after reading a code, and fetching the config, server will drop them down. +This is to allow to store multiple apps on the server/world and selectively decide which one should be running at +startup. WARNING: all apps will run once at startup anyways, so be aware that their actions that are called +statically, will be performed once anyways. + +Unloading an app removes all of its state from the game, disables commands, and removes bounded events, and +saves its global state. If more cleanup is needed, one can define an `__on_close()` function which will be +executed when the module is unloaded, or server is closing, or crashing. However, there is no need to do that +explicitly for the things mentioned in the previous statement. + +Scripts can be loaded in shared(global) and player mode. Default is player, so all globals and stored functions are +individual for each player, meaning scripts don't need to worry about making sure they store some intermittent +data for each player independently. In global mode - all global values and stored functions are shared among all +players. To access specific player data with commandblocks, use `/execute as (player) run script in (app) run ...`. +To access global/server state, you need to disown the command from any player, so use a commandblock, or any +arbitrary entity: `/execute as @e[type=bat,limit=1] run script in (module) globals` for instance. + +### `/script in ...` + +Allows to run normal /script commands in a specific app, like `run, invoke,..., globals` etc... + +# `/script invoke / invokepoint / invokearea`, `/script globals` commands + +`invoke` family of commands provide convenient way to invoke stored procedures (i.e. functions that has been +defined previously by any running script. To view current stored procedure set, +run `/script globals`(or `/script globals all` to display all functions even hidden ones), to define a new stored +procedure, just run a `/script run function(a,b) -> ( ... )` command with your procedure once, and to forget a +procedure, use `undef` function: `/script run undef('function')` + +### `/script invoke ...` + +Equivalent of running `/script run fun(args, ...)`, but you get the benefit of getting the tab completion of the +command name, and lower permission level required to run these (since player is not capable of running any custom +code in this case, only this that has been executed before by an operator). Arguments will be checked for validity, +and you can only pass simple values as arguments (strings, numbers, or `null` value). Use quotes to include +whitespaces in argument strings. + +Command will check provided arguments with required arguments (count) and fail if not enough or too much +arguments are provided. Operators defining functions are advised to use descriptive arguments names, as these +will be visible for invokers and form the base of understanding what each argument does. + +`invoke` family of commands will tab complete any stored function that does not start with `'_'`, it will still +allow to run procedures starting with `'_'` but not suggest them, and ban execution of any hidden stored procedures, +so ones that start with `'__'`. In case operator needs to use subroutines for convenience and don't want to expose +them to the `invoke` callers, they can use this mechanic. + +
+/script run example_function(const, phrase, price) -> print(const+' '+phrase+' '+price)
+/script invoke example_function pi costs 5
+
+ +### `/script invokepoint ...` + +It is equivalent to `invoke` except it assumes that the first three arguments are coordinates, and provides +coordinates tab completion, with `looking at...` mechanics for convenience. All other arguments are expected +at the end + +### `/script invokearea ...` + +It is equivalent to `invoke` except it assumes that the first three arguments are one set of ccordinates, +followed by the second set of coordinates, providing tab completion, with `looking at...` mechanics for convenience, +followed by any other required arguments + +# `/script scan`, `/script fill` and `/script outline` commands + +These commands can be used to evaluate an expression over an area of blocks. They all need to have specified the +origin of the analyzed area (which is used as referenced (0,0,0), and two corners of an area to analyzed. If you +would want that the script block coordinates refer to the actual world coordinates, use origin of `0 0 0`, or if +it doesn't matter, duplicating coordinates of one of the corners is the easiest way. + +These commands, unlike raw `/script run` are limited by vanilla fill / clone command limit of 32k blocks, which can +be altered with carpet mod's own `/carpet fillLimit` command. + +### `/script scan origin corner corner expr` + +Evaluates expression for each point in the area and returns number of successes (result was positive). Since the +command by itself doesn't affect the area, the effects would be in side effects. + +### `/script fill origin corner corner "expr" (? replace )` + +Think of it as a regular fill command, that sets blocks based on whether a result of the command was successful. +Note that the expression is in quotes. Thankfully string constants in `scarpet` use single quotes. Can be used to +fill complex geometric shapes. + +### `/script outline origin corner corner "expr" (? replace )` + +Similar to `fill` command it evaluates an expression for each block in the area, but in this case setting blocks +where condition was true and any of the neighbouring blocks were evaluated negatively. This allows to create surface +areas, like sphere for example, without resorting to various rounding modes and tricks. + +Here is an example of seven ways to draw a sphere of radius of 32 blocks around 0 100 0: + +
+/script outline 0 100 0 -40 60 -40 40 140 40 "x*x+y*y+z*z <  32*32" white_stained_glass replace air
+/script outline 0 100 0 -40 60 -40 40 140 40 "x*x+y*y+z*z <= 32*32" white_stained_glass replace air
+/script outline 0 100 0 -40 60 -40 40 140 40 "x*x+y*y+z*z <  32.5*32.5" white_stained_glass replace air
+/script fill    0 100 0 -40 60 -40 40 140 40 "floor(sqrt(x*x+y*y+z*z)) == 32" white_stained_glass replace air
+/script fill    0 100 0 -40 60 -40 40 140 40 "round(sqrt(x*x+y*y+z*z)) == 32" white_stained_glass replace air
+/script fill    0 100 0 -40 60 -40 40 140 40 "ceil(sqrt(x*x+y*y+z*z)) == 32" white_stained_glass replace air
+/draw sphere 0 100 0 32 white_stained_glass replace air // fluffy ball round(sqrt(x*x+y*y+z*z)-rand(abs(y)))==32
+
+ +The last method is the one that world edit is using (part of carpet mod). It turns out that the outline method +with `32.5` radius, fill method with `round` function and draw command are equivalent + +# `script stop/script resume` command + +`/script stop` allows to stop execution of any script currently running that calls the `game_tick()` function which +allows the game loop to regain control of the game and process other commands. This will also make sure that all +current and future programs will stop their execution. Execution of all programs will be prevented +until `/script resume` command is called. + +Lets look at the following example. This is a program computes Fibonacci number in a recursive manner: + +
+fib(n) -> if(n<3, 1, fib(n-1)+fib(n-2) ); fib(8)
+
+ +That's really bad way of doing it, because the higher number we need to compute the compute requirements will +rise exponentially with `n`. It takes a little over 50 milliseconds to do fib(24), so above one tick, but about +a minute to do fib(40). Calling fib(40) will not only freeze the game, but also you woudn't be able to interrupt +its execution. We can modify the script as follows + +
+fib(n) -> ( game_tick(50); if(n<3, 1, fib(n-1)+fib(n-2) ) ); fib(40)
+
+ +But this would never finish as such call would finish after `~ 2^40` ticks. To make our computations responsive, +yet able to respond to user interactions, other commands, as well as interrupt execution, we could do the following: + +
+fib(n) -> ( if(n==23, game_tick(50) ); if(n<3, 1, fib(n-1)+fib(n-2) ) ); fib(40)
+
+ +This would slow down the computation of fib(40) from a minute to two, but allows the game to keep continue running +and be responsive to commands, using about half of each tick to advance the computation. Obviously depending on the +problem, and available hardware, certain things can take more or less time to execute, so portioning of work with +calling `gametick` should be balanced in each case separately diff --git a/docs/scarpet/language/Containers.md b/docs/scarpet/language/Containers.md new file mode 100644 index 00000000..3064b89f --- /dev/null +++ b/docs/scarpet/language/Containers.md @@ -0,0 +1,241 @@ +# Lists, Maps and API support for Containers + +Scarpet supports basic container types: lists and maps (aka hashmaps, dicts etc..) + +## Container manipulation + +Here is a list of operations that work on all types of containers: lists, maps, as well as other Minecraft specific +modifyable containers, like NBTs + +### `get(container, address, ...), get(lvalue), ':' operator` + +Returns the value at `address` element from the `value`. For lists it indicates an index, use negative numbers to +reach elements from the end of the list. `get` call will always be able to find the index. In case there is few +items, it will loop over + +for maps, retrieves the value under the key specified in the `address` or null otherwise + +[Minecraft specific usecase]: In case `value` is of `nbt` type, uses address as the nbt path to query, returning null, +if path is not found, one value if there was one match, or list of values if result is a list. Returned elements can +be of numerical type, string texts, or another compound nbt tags + +In case to simplify the access with nested objects, you can add chain of addresses to the arguments of `get` rather +than calling it multiple times. `get(get(foo,a),b)` is equivalent to `get(foo, a, b)`, or `foo:a:b`. + +
+get(l(range(10)), 5)  => 5
+get(l(range(10)), -1)  => 9
+get(l(range(10)), 10)  => 0
+l(range(10)):93  => 3
+get(player() ~ 'nbt', 'Health') => 20 // inefficient way to get player health, use player() ~ 'health' instead
+get(m( l('foo',2), l('bar',3), l('baz',4) ), 'bar')  => 3
+
+ +### `has(container, address, ...), has(lvalue)` + +Similar to `get`, but returns boolean value indicating if the given index / key / path is in the container. +Can be used to determine if `get(...)==null` means the element doesn't exist, or the stored value for this +address is `null`, and is cheaper to run than `get`. + +Like get, it can accept multiple addresses for chains in nested containers. In this case `has(foo:a:b)` is +equivalent to `has(get(foo,a), b)` or `has(foo, a, b)` + +### `delete(container, address, ...), delete(lvalue)` + +Removes specific entry from the container. For the lists - removes the element and shrinks it. For maps, it +removes the key from the map, and for nbt - removes content from a given path. For lists and maps returns previous +entry at the address, for nbt's - number of removed objects, with 0 indicating that the original value was unaffected. + +Like with the `get` and `has`, `delete` can accept chained addresses, as well as l-value container access, removing +the value from the leaf of the path provided, so `delete(foo, a, b)` is the +same as `delete(get(foo,a),b)` or `delete(foo:a:b)` + +Returns true, if container was changed, false, if it was left unchanged, and null if operation was invalid. + +### `put(container, address, value), put(container, address, value, mode), put(lvalue, value)` + +**Lists** + +Modifies the container by replacing the value under the address with the supplied `value`. For lists, a valid +index is required, but can be negative as well to indicate positions from the end of the list. If `null` is +supplied as the address, it always means - add to the end of the list. + +There are three modes that lists can have items added to them: + +* `replace`(default): Replaces item under given index(address). Doesn't change the size of the array +unless `null` address is used, which is an exception and then it appends to the end +* `insert`: Inserts given element at a specified index, shifting the rest of the array to make space for the item. +Note that index of -1 points to the last element of the list, thus inserting at that position and moving the previous +last element to the new last element position. To insert at the end, use `+=` operator, or `null` address in put +* `extend`: treats the supplied value as an iterable set of values to insert at a given index, extending the list +by this amount of items. Again use `null` address/index to point to the end of the list + +Due to the extra mode parameter, there is no chaining for `put`, but you can still use l-value container access to +indicate container and address, so `put(foo, key, value)` is the same as `put(foo:key, value)` or `foo:key=value` + +Returns true, if container got modified, false otherwise, and null if operation was invalid. + +**Maps** + +For maps there are no modes available (yet, seems there is no reason to). It replaces the value under the supplied +key (address), or sets it if not currently present. + +**NBT Tags** + +The address for nbt values is a valid nbt path that you would use with `/data` command, and tag is any tag that +would be applicable for a given insert operation. Note that to distinguish between proper types (like integer types, +you need to use command notation, i.e. regular ints is `123`, while byte size int would be `123b` and an explicit +string would be `"5"`, so it helps that scarpet uses single quotes in his strings. Unlike for lists and maps, it +returns the number of affected nodes, or 0 if none were affected. + +There are three modes that NBT tags can have items added to them: + +* `replace`(default): Replaces item under given path(address). Removes them first if possible, and then adds given +element to the supplied position. The target path can indicate compound tag keys, lists, or individual elements +of the lists. +* ``: Index for list insertions. Inserts given element at a specified index, inside a list specified with the +path address. Fails if list is not specified. It behaves like `insert` mode for lists, i.e. it is not removing any +of the existing elements. Use `replace` to remove and replace existing element. +* `merge`: assumes that both path and replacement target are of compound type (dictionaries, maps, `{}` types), +and merges keys from `value` with the compound tag under the path + +
+a = l(1, 2, 3); put(a, 1, 4); a  => [1, 4, 3]
+a = l(1, 2, 3); put(a, null, 4); a  => [1, 2, 3, 4]
+a = l(1, 2, 3); put(a, 1, 4, 'insert'); a  => [1, 4, 2, 3]
+a = l(1, 2, 3); put(a, null, l(4, 5, 6), 'extend'); a  => [1, 2, 3, 4, 5, 6]
+a = l(1, 2, 3); put(a, 1, l(4, 5, 6), 'extend'); a  => [1, 4, 5, 6, 2, 3]
+a = l(l(0,0,0),l(0,0,0),l(0,0,0)); put(a.1, 1, 1); a  => [[0, 0, 0], [0, 1, 0], [0, 0, 0]]
+a = m(1,2,3,4); put(a, 5, null); a  => {1: null, 2: null, 3: null, 4: null, 5: null}
+tag = nbt('{}'); put(tag, 'BlockData.Properties', '[1,2,3,4]'); tag  => {BlockData:{Properties:[1,2,3,4]}}
+tag = nbt('{a:[{lvl:3},{lvl:5},{lvl:2}]}'); put(tag, 'a[].lvl', 1); tag  => {a:[{lvl:1},{lvl:1},{lvl:1}]}
+tag = nbt('{a:[{lvl:[1,2,3]},{lvl:[3,2,1]},{lvl:[4,5,6]}]}'); put(tag, 'a[].lvl', 1, 2); tag
+     => {a:[{lvl:[1,2,1,3]},{lvl:[3,2,1,1]},{lvl:[4,5,1,6]}]}
+tag = nbt('{a:[{lvl:[1,2,3]},{lvl:[3,2,1]},{lvl:[4,5,6]}]}'); put(tag, 'a[].lvl[1]', 1); tag
+     => {a:[{lvl:[1,1,3]},{lvl:[3,1,1]},{lvl:[4,1,6]}]}
+
+ +## List operations + +### `l(values ...), l(iterator)` + +Creates a list of values of the expressions passed as parameters. It can be used as an L-value and if all +elements are variables, you coujld use it to return multiple results from one function call, if that +function returns a list of results with the same size as the `l` call uses. In case there is only one +argument and it is an iterator (vanilla expression specification has `range`, but Minecraft API implements +a bunch of them, like `diamond`), it will convert it to a proper list. Iterators can only be used in high order +functions, and are treated as empty lists, unless unrolled with `l` + +
+l(1,2,'foo') => [1, 2, foo]
+l() => [] (empty list)
+l(range(10)) => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
+l(1, 2) = l(3, 4) => Error: l is not a variable
+l(foo, bar) = l(3,4); foo==3 && bar==4 => 1
+l(foo, bar, baz) = l(2, 4, 6); l(min(foo, bar), baz) = l(3, 5); l(foo, bar, baz)  => [3, 4, 5]
+
+ +In the last example `l(min(foo, bar), baz)` creates a valid L-value, as min(foo, bar) finds the lower of the +variables (in this case `foo`) creating a valid assignable L-list of [foo, baz], and these values +will be assigned new values + +### `join(delim, list), join(delim, values ...)` + +Returns a string that contains joined elements of the list, iterator, or all values, +concatenated with `delim` delimiter + +
+join('-',range(10))  => 0-1-2-3-4-5-6-7-8-9
+join('-','foo')  => foo
+join('-', 'foo', 'bar')  => foo-bar
+
+ +### `split(delim, expr)` + +Splits a string undr `expr` by `delim` which can be a regular expression + +
+split('',foo)  => [f, o, o]
+split('.','foo.bar')  => []
+split('\\.','foo.bar')  => [foo, bar]
+
+ +### `slice(expr, from, to?)` + +extracts a substring, or sublist (based on the type of the result of the expression under expr with +starting index of `from`, and ending at `to` if provided, or the end, if omitted + +
+slice(l(0,1,2,3,4,5), 1, 3)  => [1, 2, 3]
+slice('foobar', 0, 1)  => 'f'
+slice('foobar', 3)  => 'bar'
+slice(range(10), 3, 5)  => [3, 4, 5]
+slice(range(10), 5)  => [5, 6, 7, 8, 9]
+
+ +### `sort(list), sort(values ...)` + +Sorts in the default sortographical order either all arguments, or a list if its the only argument. +It returns a new sorted list, not affecting the list passed to the argument + +
sort(3,2,1)  => [1, 2, 3]
+sort('a',3,11,1)  => [1, 3, 11, 'a']
+list = l(4,3,2,1); sort(list)  => [1, 2, 3, 4]
+
+ +### `sort_key(list, key_expr)` + +Sorts a copy of the list in the order or keys as defined by the `key_expr` for each element + +
+sort_key([1,3,2],_)  => [1, 2, 3]
+sort_key([1,3,2],-_)  => [3, 2, 1]
+sort_key(l(range(10)),rand(1))  => [1, 0, 9, 6, 8, 2, 4, 5, 7, 3]
+sort_key(l(range(20)),str(_))  => [0, 1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 2, 3, 4, 5, 6, 7, 8, 9]
+
+ +### `range(to), range(from, to), range(from, to, step)` + +Creates a range of numbers from `from`, no greater/larger than `to`. The `step` parameter dictates not only the +increment size, but also direction (can be negative). The returned value is not a proper list, just the iterator +but if for whatever reason you need a proper list with all items evaluated, use `l(range(to))`. +Primarily to be used in higher order functions + +
+range(10)  => [...]
+l(range(10))  => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
+map(range(10),_*_)  => [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
+reduce(range(10),_a+_, 0)  => 45
+range(5,10)  => [5, 6, 7, 8, 9]
+range(20, 10, -2)  => [20, 18, 16, 14, 12]
+
+ +### `element(list, index)(deprecated)` + +Legacy support for older method that worked only on lists. Please use `get(...)` for equivalent support, or `.` +operator. Also previous unique behaviours with `put` on lists has been removed to support all type of +container types. + +## Map operations + +Scarpet supports map structures, aka hashmaps, dicts etc. Map structure can also be used, with `null` values as sets. +Apart from container access functions, (`. , get, put, has, delete`), the following functions: + +### `m(values ...), m(iterator), m(key_value_pairs)` + +creates and initializes a map with supplied keys, and values. If the arguments contains a flat list, these are all +treated as keys with no value, same goes with the iterator - creates a map that behaves like a set. If the +arguments is a list of lists, they have to have two elements each, and then first is a key, and second, a value + +
+m(1,2,'foo') => {1: null, 2: null, foo: null}
+m() => {} (empty map)
+m(range(10)) => {0: null, 1: null, 2: null, 3: null, 4: null, 5: null, 6: null, 7: null, 8: null, 9: null}
+m(l(1, 2), l(3, 4)) => {1: 2, 3: 4}
+reduce(range(10), put(_a, _, _*_); _a, m())
+     => {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81}
+
+ +### `keys(map), values(map), pairs(map)` + +Returns full lists of keys, values and key-value pairs (2-element lists) for all the entries in the map diff --git a/docs/scarpet/language/FunctionsAndControlFlow.md b/docs/scarpet/language/FunctionsAndControlFlow.md new file mode 100644 index 00000000..03accaec --- /dev/null +++ b/docs/scarpet/language/FunctionsAndControlFlow.md @@ -0,0 +1,170 @@ +# User-defined functions and program control flow + +## Writing programs with more than 1 line + +### `Operator ;` + +To effectively write programs that have more than one line, a programmer needs way to specify a sequence of commands +that execute one after another. In `scarpet` this can be achieved with `;`. Its an operator, and by separating +statements with semicolons. And since whitespaces and +`$` (commandline visible newline separator) +sign are all treats as whitespaces, how you layout your code doesn't matter, as long as it is readable to everyone involved. + +
+expr;
+expr;
+expr;
+expr
+
+ +Notice that the last expression is not followed by a semicolon. Since instruction separation is functional +in `scarpet`, and not barely an instruction delimiter, terminating the code with a dangling operator wouldn't +be valid. Having said that, since many programming languages don't care about the number of op terminators +programmers use, carpet preprocessor will remove all unnecessary semicolons from scripts when compiled. + +In general `expr; expr; expr; expr` is equivalent to `(((expr ; expr) ; expr) ; expr)`. + +Result of the evaluated expression is the same as the result of the second expression, but first expression +is also evaluated for side-effects + +
+expr1 ; expr2 => expr2  // with expr1 as a side-effect
+
+ +## Global variables + +All defined functions are compiled, stored persistently, and available globally - accessible to all other scripts. +Functions can only be undefined via call to `undef('fun')`, which would erase global entry for function `fun`. +Since all variables have local scope inside each function, one way to share large objects is via global variables + +Any variable that is used with a name that starts with `'global_'` will be stored and accessible globally, not, +inside current scope. It will also persist across scripts, so if a procedure needs to use its own construct, it +needs to define it, or initialize it explicitly, or undefine it via `undef` + +
+a() -> global_list+=1; global_list = l(1,2,3); a(); a(); global_list  // => [1,2,3,1,1]
+
+ +### `Operator ->` + +To organize code better than a flat sequence of operations, one can define functions. Definition is correct +if has the following form + +
+fun(args, ...) -> expr
+
+ +Where `fun(args, ...)` is a function signature indicating function name, number of arguments, and their names, +and expr is an expression (can be complex) that is evaluated when `fun` is called. Names in the signature don't +need to be used anywhere else, other occurrences of these names will be masked in this function scope. Function +call creates new scope for variables inside `expr`, so all non-global variables are not visible from the caller +scope. All parameters are passed by value to the new scope, including lists and other containers, however their +copy will be shallow. + +The function returns its name as a string, which means it can be used to call it later with the `call` function + +Using `_` as the function name creates anonymous function, so each time `_` function is defined, it will be given +a unique name, which you can pass somewhere else to get this function `call`ed. + +
+a(lst) -> lst+=1; list = l(1,2,3); a(list); a(list); list  // => [1,2,3]
+
+ +In case the inner function wants to operate and modify larger objects, lists from the outer scope, but not global, +it needs to use `outer` function in function signature + +### `outer(arg)` + +`outer` function can only be used in the function signature, and it will cause an error everywhere else. It +saves the value of that variable from the outer scope and allows its use in the inner scope. This is a similar +behaviour to using outer variables in lambda function definitions from Java, except here you have to specify +which variables you want to use, and borrow + +This mechanism can be used to use static mutable objects without the need of using `global_...` variables + +
+list = l(1,2,3); a(outer(list)) -> list+=1;  a(); a(); list  // => [1,2,3,1,1]
+
+ +The return value of a function is the value of the last expression. This as the same effect as using outer or +global lists, but is more expensive + +
+a(lst) -> lst+=1; list = l(1,2,3); list=a(list); list=a(list); list  // => [1,2,3,1,1]
+
+ +Ability to combine more statements into one expression, with functions, passing parameters, and global and outer +scoping allow to organize even larger scripts + +### `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, +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. + +### `call(function, args.....)` + +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 +function, they can be defined in place as anonymous functions. + +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 + +
+my_map(list, function) -> map(list, call(function, _));
+my_map(l(1,2,3), _(x) -> x*x);    // => [1,4,9]
+profile_expr(my_map(l(1,2,3), _(x) -> x*x));   // => ~32000
+sq(x) -> x*x; profile_expr(my_map(l(1,2,3), 'sq'));   // => ~36000
+sq = (_(x) -> x*x); profile_expr(my_map(l(1,2,3), sq));   // => ~36000
+profile_expr(map(l(1,2,3), _*_));   // => ~80000
+
+ +## Control flow + +### `return(expr?)` + +Sometimes its convenient to break the organized control flow, or it is not practical to pass the final result value of +a function to the last statement, in this case a return statement can be used + +If no argument is provided - returns null value. + +
+def() -> (
+   expr1;
+   expr2;
+   return(expr3); // function terminates returning expr3
+   expr4;     // skipped
+   expr5      // skipped
+)
+
+ +In general its cheaper to leave the last expression as a return value, rather than calling +returns everywhere, but it would often lead to a messy code. + +### `exit(expr?)` + +It terminates entire program passing `expr` as the result of the program execution, or null if omitted. + +### `try(expr, catch_expr(_)?) ... throw(value?)` + +`try` function evaluates expression, and continues further unless `throw` function is called anywhere +inside `expr`. In that case the `catch_expr` is evaluates with `_` set to the argument `throw` was called with. +This mechanic accepts skipping thrown value - it throws null instead, and catch expression - then try returns +null as well This mechanism allows to terminate large portion of a convoluted call stack and continue program +execution. There is only one level of exceptions currently in carpet, so if the inner function also defines +the `try` catchment area, it will received the exception first, but it can technically rethrow the value its +getting for the outer scope. Unhandled throw acts like an exit statement. + +### `if(cond, expr, cond?, expr?, ..., default?)` + +If statement is a function that takes a number of conditions that are evaluated one after another and if any of +them turns out true, its `expr` gets returned, otherwise, if all conditions fail, the return value is `default` +expression, or `null` if default is skipped + +`if` function is equivalent to `if (cond) expr; else if (cond) expr; else default;` from Java, +just in a functional form diff --git a/docs/scarpet/language/LoopsAndHigherOrderFunctions.md b/docs/scarpet/language/LoopsAndHigherOrderFunctions.md new file mode 100644 index 00000000..d79eb59f --- /dev/null +++ b/docs/scarpet/language/LoopsAndHigherOrderFunctions.md @@ -0,0 +1,132 @@ +# Loops, and higher order functions + +Efficient use of these functions can greatly simplify your programs and speed them up, as these functions will +internalize most of the operations that need to be applied on multiple values at the same time. Most of them take +a `list` argument which can be any iterable structure in scarpet, including generators, like `rect`, or `range`, +and maps, where the iterator returns all the map keys + +## Loops + +### `break(), break(expr), continue(), continue(expr)` + +These allow to control execution of a loop either skipping current iteration code, using `continue`, or finishing the +current loop, using `break`. `break` and `continue` can only be used inside `for, while, loop, map, filter and reduce` +functions, while `break` can be used in `first` as well. Outside of the internal expressions of these functions, +calling `break` or `continue` will cause an error. In case of the nexted loops, and more complex setups, use +custom `try` and `throw` setup. + +Please check corresponding loop function description what `continue` and `break` do in their contexts, but in +general case, passed values to `break` and `continue` will be used in place of the return value of the internal +iteration expression. + +### `for(list,expr(_,_i))` + +Evaluates expression over list of items from the `list`. Supplies `_`(value) and `_i`(iteration number) to the `expr`. + +Returns the number of times `expr` was successful. Uses `continue` and `break` argument in place of the returned +value from the `expr`(if supplied), to determine if the iteration was successful. + +
+check_prime(n) -> !first( range(2, sqrt(n)+1), !(n % _) );
+for(range(1000000,1100000),check_prime(_))  => 7216
+
+ +From which we can learn that there is 7216 primes between 1M and 1.1M + +### `while(cond, limit)` + +Evaluates expression `expr` repeatedly until condition `cond` becomes false, but not more than `limit` times. +Returns the result of the last `expr` evaluation, or `null` if nothing was successful. Both `expr` and `cond` will +recveived a bound variable `_` indicating current iteration, so its a number. + +
+while(a<100,10,a=_*_)  => 81 // loop exhausted via limit
+while(a<100,20,a=_*_)  => 100 // loop stopped at condition, but a has already been assigned
+while(_*_<100,20,a=_*_)  => 81 // loop stopped at condition, before a was assigned a value
+
+ +### `loop(num,expr(_),exit(_)?)` + +Evaluates expression `expr`, `num` number of times. code>expr receives `_` system variable indicating the iteration. + +
+loop(5, game_tick())  => repeat tick 5 times
+list = l(); loop(5, x = _; loop(5, list += l(x, _) ) ); list
+  // double loop, produces: [[0, 0], [0, 1], [0, 2], [0, 3], [0, 4], [1, 0], [1, 1], ... , [4, 2], [4, 3], [4, 4]]
+
+ +In this small example we will search for first 10 primes, apparently including 0: + +
+check_prime(n) -> !first( range(2, sqrt(n)+1), !(n % _) );
+primes = l();
+loop(10000, if(check_prime(_), primes += _ ; if (length(primes) >= 10, break())));
+primes
+// outputs: [0, 1, 2, 3, 5, 7, 11, 13, 17, 19]
+
+ +## Higher Order Functions + +### `map(list,expr(_,_i))` + +Converts a `list` of values, to another list where each value is result of an expression `v = expr(_, _i)` +where `_` is passed as each element of the list, and `_i` is the index of such element. If `break` is called the +map returns whatever collected thus far. If `continue` and `break` are used with supplied argument, it is used in +place of the resulting map element, otherwise current element is skipped. + +
+map(range(10), _*_)  => [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
+map(players('*'), _+' is stoopid') [gnembon is stoopid, herobrine is stoopid]
+
+ +### `filter(list,expr(_,_i))` + +filters `list` elements returning only these that return positive result of the `expr`. With `break` and `continue` +statements, the supplied value can be used as a boolean check instead. + +
+filter(range(100), !(_%5), _*_>1000)  => [0, 5, 10, 15, 20, 25, 30]
+map(filter(entity_list('*'),_=='Witch'), query(_,'pos') )  => [[1082.5, 57, 1243.5]]
+
+ +### `first(list,expr(_,_i))` + +Finds and returns the first item in the list that satisfies `expr`. It sets `_` for current element value, +and `_i` for index of that element. `break` can be called inside the iteration code, using its argument value +instead of the current item. `continue` has no sense and cannot be called inside `first` call. + +
+first(range(1000,10000), n=_; !first( range(2, sqrt(n)+1), !(n % _) ) )  => 1009 // first prime after 1000
+
+ +Notice in the example above, that we needed to rename the outer `_` to be albe to use in in the inner `first` call + +### `all(list,expr(_,_i))` + +Returns `true` if all elements on the list satisfy the condition. Its roughly equivalent +to `all(list,expr) <=> for(list,expr)==length(list)`. `expr` also receives bound `_` and `_i` variables. `break` +and `continue` have no sense and cannot be used inside of `expr` body. + +
+all([1,2,3], check_prime(_))  => 1
+all(neighbours(x,y,z), _=='stone')  => 1 // if all neighbours of [x, y, z] are stone
+map(filter(rect(0,4,0,1000,0,1000), l(x,y,z)=pos(_); all(rect(x,y,z,1,0,1),_=='bedrock') ), pos(_) )
+  => [[-298, 4, -703], [-287, 4, -156], [-269, 4, 104], [242, 4, 250], [-159, 4, 335], [-208, 4, 416], [-510, 4, 546], [376, 4, 806]]
+    // find all 3x3 bedrock structures in the top bedrock layer
+map( filter( rect(0,4,0,1000,1,1000,1000,0,1000), l(x,y,z)=pos(_);
+        all(rect(x,y,z,1,0,1),_=='bedrock') && for(rect(x,y-1,z,1,1,1,1,0,1),_=='bedrock')<8),
+   pos(_) )  => [[343, 3, -642], [153, 3, -285], [674, 3, 167], [-710, 3, 398]]
+    // ditto, but requiring at most 7 bedrock block in the 18 blocks below them
+
+ +### `reduce(list,expr(_a,_,_i), initial)` + +Applies `expr` for each element of the list and saves the result in `_a` accumulator. Consecutive calls to `expr` +can access that value to apply more values. You also need to specify the initial value to apply for the +accumulator. `break` can be used to terminate reduction prematurely. If a value is provided to `break` or `continue`, +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
+
\ No newline at end of file diff --git a/docs/scarpet/language/Math.md b/docs/scarpet/language/Math.md new file mode 100644 index 00000000..5331d238 --- /dev/null +++ b/docs/scarpet/language/Math.md @@ -0,0 +1,120 @@ +# Arithmetic operations + +## Basic Arithmetic Functions + +There is bunch of them - they require a number and spit out a number, doing what you would expect them to do. + +### `fact(n)` + +Factorial of a number, a.k.a `n!`, just not in `scarpet`. Gets big... quick... + +### `sqrt(n)` + +Square root. For other fancy roots, use `^`, math and yo noggin. Imagine square roots on a tree... + +### `abs(n)` + +Absolut value. + +### `round(n)` + +Closest integer value. Did you know the earth is also round? + +### `floor(n)` + +Highest integer that is still no larger then `n`. Insert a floor pun here. + +### `ceil(n)` + +First lucky integer that is not smalller than `n`. As you would expect, ceiling is typically right above the floor. + +### `ln(n)` + +Natural logarithm of `n`. Naturally. + +### `ln1p(n)` + +Natural logarithm of `n+1`. Very optimistic. + +### `log10(n)` + +Decimal logarithm of `n`. Its ceiling is the length of its floor. + +### `log(n)` + +Binary logarithm of `n`. Finally, a proper one, not like the previous 11. + +### `log1p(n)` + +Binary logarithm of `n+1`. Also always positive. + +### `mandelbrot(a, b, limit)` + +Computes the value of the mandelbrot set, for set `a` and `b` spot. Spot the beetle. Why not. + +### `min(arg, ...), min(list), max(arg, ...), max(list)` + +Compute minimum or maximum of supplied arguments assuming default sorthoraphical order. +In case you are missing `argmax`, just use `a ~ max(a)`, little less efficient, but still fun. + +Interesting bit - `min` and `max` don't remove variable associations from arguments, which means can be used as +LHS of assignments (obvious case), or argument spec in function definitions (far less obvious). + +
+a = 1; b = 2; min(a,b) = 3; l(a,b)  => [3, 2]
+a = 1; b = 2; fun(x, min(a,b)) -> l(a,b); fun(3,5)  => [5, 0]
+
+ +Absolutely no idea, how the latter might be useful in practice. But since it compiles, can ship it. + +### `relu(n)` + +Linear rectifier of `n`. 0 below 0, n above. Why not. `max(0,n)` with less moral repercussions. + +## Trigonometric / Geometric Functions + +### `sin(x)` + +### `cos(x)` + +### `tan(x)` + +### `asin(x)` + +### `acos(x)` + +### `atan(x)` + +### `atan2(x,y)` + +### `sinh(x)` + +### `cosh(x)` + +### `tanh(x)` + +### `sec(x)` + +### `csc(x)` + +### `sech(x)` + +### `csch(x)` + +### `cot(x)` + +### `acot(x)` + +### `coth(x)` + +### `asinh(x)` + +### `acosh(x)` + +### `atanh(x)` + +### `rad(deg)` + +### `deg(rad)` + +Use as you wish diff --git a/docs/scarpet/language/Operators.md b/docs/scarpet/language/Operators.md new file mode 100644 index 00000000..9fc49180 --- /dev/null +++ b/docs/scarpet/language/Operators.md @@ -0,0 +1,215 @@ +# Operators + +There is a number of operators you can use inside the expressions. Those could be considered generic type operators +that apply to most data types. They also follow standard operator precedence, i.e. `2+2*2` is understood +as `2+(2*2)`, not `(2+2)*2`, otherwise they are applied from left to right, i.e. `2+4-3` is interpreted +as `(2+4)-3`, which in case of numbers doesn't matter, but since `scarpet` allows for mixing all value types +the associativity would matter, and may lead to unintended effects: + +Important operator is function definition `->` operator. It will be covered +in {@link carpet.script.Expression#UserDefinedFunctionsAndControlFlow} + +
+'123'+4-2 => ('123'+4)-2 => '1234'-2 => '134'
+'123'+(4-2) => '123'+2 => '1232'
+3*'foo' => 'foofoofoo'
+1357-5 => 1352
+1357-'5' => 137
+3*'foo'-'o' => 'fff'
+l(1,3,5)+7 => l(8,10,12)
+
+ +As you can see, values can behave differently when mixed with other types in the same expression. +In case values are of the same types, the result tends to be obvious, but `Scarpet` tries to make sense of whatever +it has to deal with + +## Operator Precedence + +Here is the complete list of operators in `scarpet` including control flow operators. Note, that commas and brackets +are not technically operators, but part of the language, even if they look like them: + +* Match, Get `~ :` +* Unary `+ - !` +* Exponent `^` +* Multiplication `* / %` +* Addition `+ -` +* Comparison `== != > >= <= <` +* Logical And`&&` +* Logical Or `||` +* Assignment `= += <>` +* Definition `->` +* Next statement`;` +* Comma `,` +* Bracket `( )` + +### `Get, Accessor Operator :` + +Operator version of the `get(...)` function to access elements of lists, maps, and potentially other containers +(i.e. NBTs). It is important to distinguish from `~` operator, which is a matching operator, which is expected to +perform some extra computations to retrieve the result, while `:` should be straightforward and immediate, and +the source object should behave like a container and support full container API, +meaning `get(...)`, `put(...)`, `delete(...)`, and `has(...)` functions + +For certain operators and functions (get, put, delete, has, =, +=) objects can use `:` annotated fields as l-values, +meaning construct like `foo:0 = 5`, would act like `put(foo, 0, 5)`, rather than `get(foo, 0) = 5`, +which would result in an error. + +TODO: add more information about l-value behaviour. + +### `Matching Operator ~` + +This operator should be understood as 'matches', 'contains', 'is_in', or 'find me some stuff about something else. +For strings it matches the right operand as a regular expression to the left, returning the first match. +This can be used to extract information from unparsed nbt's in a more convoluted way (use `get(...)` for +more appropriate way of doing it). For lists it checks if an element is in the list, and returns the +index of that element, or `null` if no such element was found, especially that the use of `first(...)` +function will not return the index. Currently it doesn't have any special behaviour for numbers - it checks for +existence of characters in string representation of the left operand with respect of the regular expression on +the right hand side. + +In Minecraft API portion `entity ~ feature` is a shortcode for `query(entity,feature)` for queries that do not take +any extra arguments. + +
+l(1,2,3) ~ 2  => 1
+l(1,2,3) ~ 4  => null
+'foobar' ~ '.b'  => 'ob'
+player('*') ~ 'gnembon'  // null unless player gnembon is logged in (better to use player('gnembon') instead
+p ~ 'sneaking' // if p is an entity returns whether p is sneaking
+
+ +Or a longer example of an ineffective way to searching for a squid + +
+entities = entities_area('all',x,y,z,100,10,100);
+sid = entities ~ 'Squid';
+if(sid != null, run('execute as '+query(get(entities,sid),'id')+' run say I am here '+query(get(entities,sid),'pos') ) )
+
+ +Or an example to find if a player has specific enchantment on a held axe (either hand) and get its level +(not using proper NBTs query support via `get(...)`): + +
+global_get_enchantment(p, ench) -> (
+$   for(l('main','offhand'),
+$      holds = query(p, 'holds', _);
+$      if( holds,
+$         l(what, count, nbt) = holds;
+$         if( what ~ '_axe' && nbt ~ ench,
+$            lvl = max(lvl, number(nbt ~ '(?<=lvl:)\\d') )
+$         )
+$      )
+$   );
+$   lvl
+$);
+/script run global_get_enchantment(players(), 'sharpness')
+
+ +### `Basic Arithmetic Operators + - * /` + +Allows to add the results of two expressions. If the operands resolve to numbers, the result is arithmetic operation. +In case of strings, adding or subtracting from a string results in string concatenation and removal of substrings +from that string. Multiplication of strings and numbers results in repeating the string N times and division results +in taking the first k'th part of the string, so that `str*n/n ~ str` In case first operand is a list, either it +results in a new list with all elements modified one by one with the other operand, or if the operand is a list +with the same number of items - element-wise addition/subtraction + +Examples: + +
+2+3 => 5
+'foo'+3+2 => 'foo32'
+'foo'+(3+2) => 'foo5'
+3+2+'bar' => '5bar'
+'foo'*3 => 'foofoofoo'
+'foofoofoo' / 3 => 'foo'
+'foofoofoo'-'o' => 'fff'
+l(1,2,3)+1  => l(2,3,4)
+b = l(100,63,100); b+l(10,0,10)  => l(110,63,110)
+
+ +### `Just Operators % ^` + +The modulo and exponent (power) operators work only if both operands are numbers + +
pi^pi%euler  => 1.124....
+-9 % 4  => -1
+9 % -4  => 0 ¯\_(ツ)_/¯ Java
+-3 ^ 2  => 9
+-3 ^ pi => // Error
+
+ +### `Comparison Operators == != < > <= >=` + +Allows to compare the results of two expressions. For numbers it is considers arithmetic order of numbers, for +strings - lexicographical, nulls are always 'less' than everything else, and lists check their elements - +if the sizes are different, the size matters, otherwise, pairwise comparisons for each elements are performed. +The same order rules than with all these operators are used with the default sortographical order as used by `sort` +function. All of these are true: + +
+null == null
+null != false
+0 == false
+1 == true
+null < 0
+null < -1000
+1000 < 'a'
+'bar' < 'foo'
+3 == 3.0
+
+ +### `Logical Operators && ||` + +These operator compute respective boolean operation on the operands. What it important is that if calculating of the +second operand is not necessary, it won't be evaluated, which means one can use them as conditional statements. In +case of success returns first positive operand (`||`) or last one (`&&`). + +
+true || false  => 1
+null || false => 0
+null != false || run('kill gnembon')  => 1 // gnembon survives
+null != false && run('kill gnembon')  => 0 // when cheats not allowed
+null != false && run('kill gnembon')  => 1 // gnembon dies, cheats allowed
+
+ +### `Assignment Operators = <> +=` + +A set of assignment operators. All require bounded variable on the LHS, `<>` requires bounded arguments on the +right hand side as well (bounded, meaning being variables). Additionally they can also handle list constructors +with all bounded variables, and work then as list assignment operators. When `+=` is used on a list, it extends +that list of that element, and returns the list (old == new). `scarpet` doesn't support currently removal of items. +Removal of items can be obtaine via `filter` command, and reassigning it fo the same variable. Both operations would +require rewriting of the array anyways. + +
+a = 5  => a == 5
+l(a,b,c) = l(3,4,5) => a==3, b==4, c==5
+l(minx,maxx) = sort(xi,xj);  // minx assumes min(xi, xj) and maxx, max(xi, xj)
+l(a,b,c,d,e,f) = l(range(6)); l(a,b,c) <> l(d,e,f); l(a,b,c,d,e,f)  => [3,4,5,0,1,2]
+a = l(1,2,3); a += 4  => [1,2,3,4]
+a = l(1,2,3,4); a = filter(a,_!=2)  => [1,3,4]
+
+ +### `Unary Operators - +` + +Require a number, flips the sign. One way to assert it's a number is by crashing the script. gg. + +
+-4  => -4
++4  => 4
++'4'  // Error message
+
+ +### `Negation Operator !` + +flips boolean condition of the expression. Equivalent of `bool(expr)==false` + +
+!true  => 0
+!false  => 1
+!null  => 1
+!5  => 0
+!l() => 1
+!l(null) => 0
+
diff --git a/docs/scarpet/language/Overview.md b/docs/scarpet/language/Overview.md new file mode 100644 index 00000000..a63629da --- /dev/null +++ b/docs/scarpet/language/Overview.md @@ -0,0 +1,276 @@ +# Fundamental components of `scarpet` programming language (towards version 1.7). + +Scarpet (a.k.a. Carpet Script, or Script for Carpet) is a programming language +designed to provide the ability to write custom programs to run within Minecraft +and interact with the world. + +The project was initially built based on the EvalEx project, however it now diverged +so far away from the original implementation, it would be hard to tell it without +this mention. EvalEx is a handy expression evaluator for Java, that allows to evaluate +simple mathematical and boolean expressions. For more information, +see: [EvalEx GitHub repository](https://github.com/uklimaschewski/EvalEx) + +This specification is divided into two sections: this one is agnostic to any +Minecraft related features and could function on its own, and CarpetExpression +for Minecraft specific routines and world manipulation functions. + +# Synopsis + +
+script run print('Hello World!')
+
+ +or an OVERLY complex example: + +
+/script run
+    block_check(x1, y1, z1, x2, y2, z2, block_to_check) ->
+    (
+        l(minx, maxx) = sort(l(x1, x2));
+        l(miny, maxy) = sort(l(y1, y2));
+        l(minz, maxz) = sort(l(z1, z2));
+        'Need to compute the size of the area of course';
+        'Cause this language doesn\'t support comments in the command mode';
+        xsize = maxx - minx + 1;
+        ysize = maxy - miny + 1;
+        zsize = maxz - minz + 1;
+        total_count = 0;
+        loop(xsize,
+            xx = minx + _ ;
+            loop(ysize,
+                yy = miny + _ ;
+                loop(zsize,
+                    zz = minz + _ ;
+                    if ( block(xx,yy,zz) == block_to_check,
+                        total_count += ceil(rand(1))
+                    )
+                )
+            )
+        );
+        total_count
+    );
+    check_area_around_closest_player_for_block(block_to_check) ->
+    (
+        closest_player = player();
+        l(posx, posy, posz) = query(closest_player, 'pos');
+        total_count = block_check( posx-8,1,posz-8, posx+8,17,posz+8, block_to_check);
+        print('There is '+total_count+' of '+block_to_check+' around you')
+    )
+/script invoke check_area_around_closest_player_for_block 'diamond_ore'
+
+ +or simply + +
+/script run print('There is'+for(rect(x,9,z,8,8,8), _ == 'diamond_ore')+' diamond ore around you')
+
+ +It definitely pays to check what higher level `scarpet` functions have to offer. + +# Programs + +You can think of an program like a mathematical expression, like `"2.4*sin(45)/(2-4)"` or `"sin(y)>0 & max(z, 3)>3"`. +Writing a program, is like writing a `2+3`, just a bit longer. + +## Basic language components + +Programs consist of constants, like `2`, `3.14`, `pi`, or `'foo'`, operators like `+`, `/`, `->`, variables which you +can define, like `foo` or special ones that will be defined for you, like `_x`, or `_` , which I specific to a each +built in function, and functions with name, and arguments in the form of `f(a,b,c)`, where `f` is the function name, +and `a, b, c` are the arguments which can be any other expression. And that's all the parts of the language, so all +in all - sounds quite simple. + +## Code flow + +Like any other proper programming language, `scarpet` needs brackets, basically to identify where stuff begins and +where it ends. In the languages that uses much more complicated constructs, like Java, they tend to use all sort of +them, round ones to indicate function calls, curly to indicate section of code, square to access lists, pointy for +generic types etc... I mean - there is no etc, cause they have exhausted all the bracket options... + +`Scarpet` is different, since it runs everything based on functions (although its not per se a functional +language like lisp) only needs the round brackets for everything, and it is up to the programmer to organize +its code so its readable, as adding more brackets does not have any effect on the performance of the programs +as they are compiled before they are executed. Look at the following example usage of `if()` function: + +
+if(x<y+6,set(x,8+y,z,'air');plop(x,top('surface',x,z),z,'birch'),sin(query(player(),'yaw'))>0.5,plop(0,0,0,'boulder'),particle('fire',x,y,z))
+
+ +Would you prefer to read + +
+if(   x<y+6,
+           set(x,8+y,z,'air');
+           plop(x,top('surface',x,z),z,'birch'),
+      sin(query(player(),'yaw'))>0.5,
+           plop(0,0,0,'boulder'),
+      particle('fire',x,y,z)
+)
+
+ +Or rather: + +
+if
+(
+    x<y+6,
+    (
+        set(x,8+y,z,'air');
+        plop(x,top('surface',x,z),z,'birch')
+    ),
+    sin(query(player(),'yaw'))>0.5,
+    (
+        plop(0,0,0,'boulder')
+    ),
+    particle('fire',x,y,z)
+)
+
+ +Whichever style you prefer it doesn't matter. It typically depends on the situation and the complexity of the +subcomponents. No matter how many whitespaces and extra brackets you add - the code will evaluate to exactly the +same expression, and will run exactly the same, so make sure your programs are nice and clean so others don't +have problems with them + +## Functions and scoping + +Users can define functions in the form `fun(args....) -> expression` and they are compiled and saved for further +execution in this but also subsequent calls of /script command. Functions can also be assigned to variables, +passed as arguments, called with `call` function, but in most cases you would want to call them directly by +name, in the form of `fun(args...)`. This means that once defined functions are saved with the world for +further use. For variables, there are two types of them, global - which are shared anywhere in the code, +and those are all which name starts with 'global_', and local variables which is everything else and those +are only visible inside each function. This also means that all the parameters in functions are +passed 'by value', not 'by reference'. + +## Outer variables + +Functions can still 'borrow' variables from the outer scope, by adding them to the function signature wrapped +around built-in function `outer`. It adds the specified value to the function call stack so they behave exactly +like capturing lambdas in Java, but unlike java captured variables don't need to be final. Scarpet will just +attach their new values at the time of the function definition, even if they change later. Most value will be +copied, but mutable values, like maps or lists, allow to keep the 'state' with the function, allowing them to +have memory and act like objects so to speak. . Check `outer(var)` for details. + +## Code delivery, line indicators + +Note that this should only apply to pasting your code to execute with commandblock. Scarpet recommends placing +your code in apps (files with `.sc` extension that can be placed inside "/scripts" folder in the world files and +loaded as a scarpet app with command `/script load [app_name]`. Scarpet apps loaded from disk should only +contain code, no need to start with "/script run" prefix + +The following is the code that could be provided in a `foo.sc` app file located in world `/scripts` folder + +
+run_program() -> (
+  loop( 10,
+    // looping 10 times
+    // comments are allowed in scripts located in world files
+    // since we can tell where that line ends
+    foo = floor(rand(10));
+    check_not_zero(foo);
+    print(_+' - foo: '+foo);
+    print('  reciprocal: '+  _/foo )
+  )
+);
+check_not_zero(foo) -> (
+  if (foo==0, foo = 1)
+)
+
+ +Which we then call in-game with: + +
+/script load foo
+/script in foo invoke run_program
+
+ +However the following code can also be input as a command, or in a command block. + +Since the maximum command that can be input to the chat is limited in length, you will be probably inserting your +programs by pasting them to command blocks or reading from world files, however pasting to command blocks will +remove some whitespaces and squish your newlines making the code not readable. If you are pasting a program that +is perfect and will never cause an error, I salute you, but for the most part it is quite likely that your program +might break, either at compile time, when its initially analyzed, or at execute time, when you suddenly attempt to +divide something by zero. In these cases you would want to get a meaningful error message, but for that you would +need to indicate for the compiler where did you put these new lines, since command block would squish them. For that, +place at the beginning of the line to let the compiler know where are you. This makes so that `$` is the only +character that is illegal in programs, since it will be replaced with new lines. As far as I know, `$` is not +used anywhere inside Minecraft identifiers, so this shouldn't hinder the abilities of your programs. + +Consider the following program executed as command block command: + +
+/script run
+run_program() -> (
+  loop( 10,
+    foo = floor(rand(_));
+    check_not_zero(foo);
+    print(_+' - foo: '+foo);
+    print('  reciprocal: '+  _/foo )
+  )
+);
+check_not_zero(foo) -> (
+   if (foo==0, foo = 1)
+)
+
+ +Lets say that the intention was to check if the bar is zero and prevent division by zero in print, but because +the `foo` is passed as a variable, it never changes the original foo value. Because of the inevitable division +by zero, we get the following message: + +
+Your math is wrong, Incorrect number format for NaN at pos 98
+run_program() -> ( loop( 10, foo = floor(rand(_)); check_not_zero(foo); print(_+' - foo: '+foo);
+HERE>> print(' reciprocal: '+ _/foo ) ));check_not_zero(foo) -> ( if (foo==0, foo = 1))
+
+ +As we can see, we got our problem where the result of the mathematical operation was not a number +(infinity, so not a number), however by pasting our program into the command made it squish the newlines so +while it is clear where the error happened and we still can track the error down, the position of the error (98) +is not very helpful and wouldn't be useful if the program gets significantly longer. To combat this issue we can +precede every line of the script with dollar signs `$`: + +
+/script run
+$run_program() -> (
+$  loop( 10,
+$    foo = floor(rand(_));
+$    check_not_zero(foo);
+$    print(_+' - foo: '+foo);
+$    print('  reciprocal: '+  _/foo )
+$  )
+$);
+$check_not_zero(foo) -> (
+$   if (foo==0, foo = 1)
+$)
+
+ +Then we get the following error message + +
+Your math is wrong, Incorrect number format for NaN at line 7, pos 2
+  print(_+' - foo: '+foo);
+   HERE>> print(' reciprocal: '+ _/foo )
+  )
+
+ +As we can note not only we get much more concise snippet, but also information about the line number and position, +so means its way easier to locate the potential problems problem + +Obviously that's not the way we intended this program to work. To get it `foo` modified via a function call, +we would either return it as a result and assign it to the new variable: + +
+foo = check_not_zero(foo);
+...
+check_not_zero(foo) -> if(foo == 0, 1, foo)
+
+ +.. or convert it to a global variable, which in this case passing as an argument is not required + +
+global_foo = floor(rand(10));
+check_foo_not_zero();
+...
+check_foo_not_zero() -> if(global_foo == 0, global_foo = 1)
+
\ No newline at end of file diff --git a/docs/scarpet/language/SystemFunctions.md b/docs/scarpet/language/SystemFunctions.md new file mode 100644 index 00000000..502a3c63 --- /dev/null +++ b/docs/scarpet/language/SystemFunctions.md @@ -0,0 +1,324 @@ +# System functions + +## Type conversion functions + +### `copy(expr)` + +Returns the deep copy of the expression. Can be used to copy mutable objects, like maps and lists + +### `type(expr)` + +Returns the string value indicating type of the expression. Possible outcomes +are `null, number, string, list, iterator`, as well as minecraft related concepts like `block, entity, nbt` + +### `bool(expr)` + +Returns a boolean context of the expression. Note that there are no true/false values in scarpet. `true` is +alias of 1, and `false` is 0\. Bool is also interpreting string values as boolean, which is different from other +places where boolean context can be used. This can be used in places where API functions return string values to +represent binary values + +
+bool(pi) => 1
+bool(false) => 0
+bool('') => 0
+bool(l()) => 0
+bool(l('')) => 1
+bool('foo') => 1
+bool('false') => 0
+bool('nulL') => 0
+if('false',1,0) => 1
+
+ +### `number(expr)` + +Returns a numeric context of the expression. Can be used to read numbers from strings + +
+number(null) => null
+number(false) => 0
+number('') => null
+number('3.14') => 3.14
+number(l()) => 0
+number(l('')) => 1
+number('foo') => null
+number('3bar') => null
+number('2')+number('2') => 4
+
+ +### `str(expr, params? ... ), str(expr, param_list)` + +Returns a formatted string representing expression. Accepts formatting style accepted by `String.format`. +Supported types (with `"%?"` syntax): + +* d, o, x: integers +* a, e, f, g: floats +* b: booleans +* s: strings + +
+str(null) => null
+str(false) => 0
+str('') => null
+str('3.14') => 3.14
+str(l()) => 0
+str(l('')) => 1
+str('foo') => null
+str('3bar') => null
+str(2)+str(2) => 22
+str('pi: %.2f',pi) => 'pi: 3.14'
+str('player at: %d %d %d',pos(player())) => 'player at: 567, -2423, 124'
+
+ +* * * +## Threading and Parallel Execution + +Scarpet allows to run threads of execution in parallel to the main script execution thread. In Minecraft, the main +thread scripts are executed on the main server thread. Since Minecraft is inherently NOT thread safe, it is not that +beneficial to parallel execution in order to access world resources faster. Both `GetBlockState` and `setBlockState` +are not thread safe and require the execution to park on the server thread, where these requests can be executed in +the off-tick time in between ticks that didn't take 50ms. There are however benefits of running things in parallel, +like fine time control not relying on the tick clock, or running things independent on each other. You can still run +your actions on tick-by-tick basis, either taking control of the execution using `game_tick()` API function +(nasty solution), or scheduling tick using `schedule()` function (much nicer solution), but threading often gives +the neatest solution to solve problems in parallel (see scarpet camera). + +Due to limitations with the game, there are some limits to the threading as well. You cannot for +instance `join_task()` at all from the main script and server thread, because any use of Minecraft specific +function that require any world access, will require to park and join on the main thread to get world access, +meaning that calling join on that task would inevitably lead to a typical deadlock. You can still join tasks +from other threads, just because the only possibility of a deadlock in this case would come explicitly from your +bad code, not the internal world access behaviour. Some things tough like player or entity manipulation, can be +effectively parallelized. + +### `task(function, ?args...., ?executor)` + +Creates and runs a parallel task, returning the handle to the task object. Task will return the return value of the +function when its completed, or will return `null` immediately if task is still in progress, so grabbing a value of +a task object is non-blocking. Function can be either function value, or function lambda, or a name of an existing +defined function. In case function needs arguments to be called with, they should be supplied after the function +name, or value. Optional `executor` identifier is to place the task in a specific queue identified by this value. +The default thread value is the `null` thread. There is no limits on number of parallel tasks for any executor, +so using different queues is solely for synchronization purposes. Also, since `task` function knows how many extra +arguments it requires, the use of tailing optional parameter for the custom executor thread pool is not ambiguous. + +
+task( _() -> print('Hello Other World') )  => Runs print command on a separate thread
+foo(a, b) -> print(a+b); task('foo',2,2)  => Uses existing function definition to start a task
+task('foo',3,5,'temp');  => runs function foo with a different thread executor, identified as 'temp'
+a = 3; task( _(outer(a), b) -> foo(a,b), 5, 'temp')  
+    => Another example of running the same thing passing arguments using closure over anonymous function as well as passing a parameter.
+
+ +### `task_count(executor?)` + +If no argument provided, returns total number of tasks being executed in parallel at this moment using scarpet +threading system. If the executor is provided, returns number of active tasks for that provider. Use `task_count(null)` +to get the task count of the default executor only. + +### `task_value(task)` + +Returns the task return value, or `null` if task hasn't finished yet. Its a non-blocking operation. Unlike `join_task`, +can be called on any task at any point + +### `task_join(task)` + +Waits for the task completion and returns its computed value. If the task has already finished returns it immediately. +Unless taking the task value directly, i.e. via `task_value`, this operation is blocking. Since Minecraft has a +limitation that all world access operations have to be performed on the main game thread in the off-tick time, +joining any tasks that use Minecraft API from the main thread would mean automatic lock, so joining from the main +thread is not allowed. Join tasks from other threads, if you really need to, or communicate asynchronously with +the task via globals or function data / arguments to monitor its progress, communicate, get partial results, +or signal termination. + +### `task_completed(task)` + +Returns true if task has completed, or false otherwise. + +### `synchronize(lock, expression)` + +Evaluates `expression` synchronized with respect to the lock `lock`. Returns the value of the expression. + +* * * + +## Auxiliary functions + +### `lower(expr), upper(expr), title(expr)` + +Returns lowercase, uppercase or titlecase representation of a string representation of the passed expression + +
+lower('aBc') => 'abc'
+upper('aBc') => 'ABC'
+title('aBc') => 'Abc'
+
+ +### `replace(string, regex, repl?); replace_first(string, regex, repl?)` + +Replaces all, or first occurence of a regular expression in the string with `repl` expression, +or nothing, if not specified + +
+replace('abbccddebfg','b+','z')  // => azccddezfg
+replace_first('abbccddebfg','b+','z')  // => azccddebfg
+
+ +### `length(expr)` + +Returns length of the expression, the length of the string, the length of the integer part of the number, +or length of the list + +
+length(pi) => 1
+length(pi*pi) => 1
+length(pi^pi) => 2
+length(l()) => 0
+length(l(1,2,3)) => 3
+length('') => 0
+length('foo') => 3
+
+ +### `rand(expr), rand(expr, seed)` + +returns a random number from `0.0` (inclusive) to `expr` (exclusive). In boolean context (in conditions, +boolean functions, or `bool`), returns false if the randomly selected value is less than 1\. This means +that `rand(2)` returns true half of the time and `rand(5)` returns true for 80% (4/5) of the time. If seed is not +provided, uses a random seed. If seed is provided, each consecutive call to rand() will act like 'next' call to the +same random object. Scarpet keeps track of up to 1024 custom random number generators, so if you exceed this number +(per app), then your random sequence will revert to the beginning. + +
+map(range(10), floor(rand(10))) => [5, 8, 0, 6, 9, 3, 9, 9, 1, 8]
+map(range(10), bool(rand(2))) => [1, 1, 1, 0, 0, 1, 1, 0, 0, 0]
+map(range(10), str('%.1f',rand(_))) => [0.0, 0.4, 0.6, 1.9, 2.8, 3.8, 5.3, 2.2, 1.6, 5.6]
+
+ +### `perlin(x), perlin(x, y), perlin(x, y, z), perlin(x, y, z, seed)` + +returns a noise value from `0.0` to `1.0` (roughly) for 1, 2 or 3 dimensional coordinate. The default seed it samples +from is `0`, but seed can be specified as a 4th argument as well. In case you need 1D or 2D noise values with custom +seed, use `null` for `z`, or `y` and `z` arguments respectively. + +Perlin noise is based on a square grid and generates rougher maps comparing to Simplex, which is creamier. +Querying for lower-dimensional result, rather than affixing unused dimensions to constants has a speed benefit, + +Thou shall not sample from noise changing seed frequently. Scarpet will keep track of the last 256 perlin seeds +used for sampling providing similar speed comparing to the default seed of `0`. In case the app engine uses more +than 256 seeds at the same time, switching between them can get much more expensive. + +### `simplex(x, y), simplex(x, y, z), simplex(x, y, z, seed)` + +returns a noise value from `0.0` to `1.0` (roughly) for 2 or 3 dimensional coordinate. The default seed it samples +from is `0`, but seed can be specified as a 4th argument as well. In case you need 2D noise values with custom seed, +use `null` for `z` argument. + +Simplex noise is based on a triangular grid and generates smoother maps comparing to Perlin. To sample 1D simplex +noise, affix other coordinate to a constant. + +Thou shall not sample from noise changing seed frequently. Scarpet will keep track of the last 256 simplex seeds +used for sampling providing similar speed comparing to the default seed of `0`. In case the app engine uses more +than 256 seeds at the same time, switching between them can get much more expensive. + +### `print(expr)` + +prints the value of the expression to chat. Passes the result of the argument to the output unchanged, +so `print`-statements can be weaved in code to debug programming issues + +
+print('foo') => results in foo, prints: foo
+a = 1; print(a = 5) => results in 5, prints: 5
+a = 1; print(a) = 5 => results in 5, prints: 1
+print('pi = '+pi) => prints: pi = 3.141592653589793
+print(str('pi = %.2f',pi)) => prints: pi = 3.14
+
+ +### `sleep(expr)` + +Halts the execution of the program (and the game itself) for `expr` milliseconds. All in all, its better to +use `game_tick(expr)` to let the game do its job while the program waits + +
+sleep(50)
+
+ +### `time()` + +Returns the number of milliseconds since 'some point', like Java's `System.nanoTime()`. +It returns a float, which has 1 microsecond precision (0.001 ms) + +
+start_time = time();
+flip_my_world_upside_down();
+print(str('this took %d milliseconds',time()-start_time))
+
+ +### `profile_expr(expression)` + +Returns number of times given expression can be run in 50ms time. Useful to profile and optimize your code. +Note that, even if its only a number, it WILL run these commands, so if they are destructive, you need to be careful. + +* * * + +## Access to variables and stored functions (use with caution) + +### `var(expr)` + +Returns the variable under the name of the string value of the expression. Allows to manipulate variables in more +programmatic manner, which allows to use local variable set with a hash map type key-value access, +can also be used with global variables + +
+a = 1; var('a') = 'foo'; a => a == 'foo'
+
+ +### `undef(expr)` + +Removes all bindings of a variable with a name of `expr`. Removes also all function definitions with that name. +It can affect global variable pool, and local variable set for a particular function. + +
+inc(i) -> i+1; foo = 5; inc(foo) => 6
+inc(i) -> i+1; foo = 5; undef('foo'); inc(foo) => 1
+inc(i) -> i+1; foo = 5; undef('inc'); undef('foo'); inc(foo) => Error: Function inc is not defined yet at pos 53
+undef('pi')  => bad idea - removes hidden variable holding the pi value
+undef('true')  => even worse idea, unbinds global true value, all references to true would now refer to the default 0
+
+ +### `vars(prefix)` + +It returns all names of variables from local scope (if prefix does not start with 'global') or global variables +(otherwise). Here is a larger example that uses combination of `vars` and `var` functions to be +used for object counting + +
+/script run
+$ count_blocks(ent) -> (
+$   l(cx, cy, cz) = query(ent, 'pos');
+$   scan(cx, cy, cz, 16, 16, 16, var('count_'+_) += 1);
+$   for ( sort_key( vars('count_'), -var(_)),
+$     print(str( '%s: %d', slice(_,6), var(_) ))
+$   )
+$ )
+/script run count_blocks(player())
+
+ +* * * + +## System key-value storage + +Scarpet runs apps in isolation. The can share code via use of shared libraries, but each library that is imported to +each app is specific to that app. Apps can store and fetch state from disk, but its restricted to specific locations +meaning apps cannot interact via disk either. To facilitate communication for interappperability, scarpet hosts its +own key-value storage that is shared between all apps currently running on the host, providing methods for getting an +associated value with optional setting it if not present, and an operation of modifying a content of a system +global value. + +### `system_variable_get(key, default_value ?)` + +Returns the variable from the system shared key-value storage keyed with a `key` value, optionally if value is +not present, and default expression is provided, sets a new value to be associated with that key + +### `system_variable_set(key, new_value)` + +Returns the variable from the system shared key-value storage keyed with a `key` value, and sets a new +mapping for the key \ No newline at end of file diff --git a/docs/scarpet/language/VariablesAndConstants.md b/docs/scarpet/language/VariablesAndConstants.md new file mode 100644 index 00000000..e3478327 --- /dev/null +++ b/docs/scarpet/language/VariablesAndConstants.md @@ -0,0 +1,29 @@ +# Variables and Constants + +`scarpet` provides a number of constants that can be used literally in scripts + +* `null`: nothing, zilch, not even false +* `true`: pure true, or just 1 (one) +* `false`: false truth, or true falsth, 0 (zero) actually +* `pi`: for the fans of perimeters, its a perimeter of an apple pi of diameter 1\. About 3.14 +* `euler`: clever guy. Derivative of its exponent is goto 1\. About 2.72 + +Apart from that, there is a bunch of system variables, that start with `_` that are set by `scarpet` built-ins, +like `_`, typically each consecutive value in loops, `_i` indicating iteration, or `_a` like an accumulator +for `reduce` function. Certain calls to Minecraft specific calls would also set `_x`, `_y`, `_z`, indicating +block positions. All variables starting with `_` are read-only, and cannot be declared and modified in client code. + +## Literals + +`scarpet` accepts numeric and string liters constants. Numbers look like `1, 2.5, -3e-7, 0xff,` and are internally +represented as Java's `double` but `scarpet` will try to trim trailing zeros as much as possible so if you need to +use them as intergers, you can. Strings using 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` +for example, or when typing in jsons inside scarpet to feed back into a `/data merge` command for example. +Strings also use backslashes `\` for quoting special characters, in both plain strings and regular expressions + +
+'foo'
+print('This doesn\'t work')
+nbt ~ '\\.foo'   // matching '.' as a '.', not 'any character match'
+
diff --git a/src/main/java/carpet/script/CarpetExpression.java b/src/main/java/carpet/script/CarpetExpression.java index a6cc6344..926fa326 100644 --- a/src/main/java/carpet/script/CarpetExpression.java +++ b/src/main/java/carpet/script/CarpetExpression.java @@ -133,21 +133,6 @@ import static java.lang.Math.max; import static java.lang.Math.min; import static java.lang.Math.sqrt; -/** - *

Minecraft specific API and scarpet language add-ons and commands

- *

Here is the gist of the Minecraft related functions. Otherwise the CarpetScript could live without Minecraft.

- *

Dimension warning

- *

One note, which is important is that most of the calls for entities and blocks - * would refer to the current dimension of the caller, meaning, that if we for example - * list all the players using player('all') function, if a player is in the - * other dimension, calls to entities and blocks around that player would point incorrectly. - * Moreover, running commandblocks in the spawn chunks would mean that commands will always - * refer to the overworld blocks and entities. - * In case you would want to run commands across all dimensions, just run three of them, using - * /execute in overworld/the_nether/the_end run script run ... and query - * players using player('*'), which only returns players in current dimension, or use - * in_dimension(expr) function.

- */ public class CarpetExpression { @@ -163,42 +148,6 @@ public class CarpetExpression private static final Map DIRECTION_MAP = Arrays.stream(Direction.values()).collect(Collectors.toMap(Direction::getName, (direction) -> direction)); - /** - *

script stop/script resume command

- *
- *

- * /script stop allows to stop execution of any script currently running that calls the - * game_tick() function which - * allows the game loop to regain control of the game and process other commands. This will also make sure - * that all current and future programs will stop their execution. Execution of all programs will be - * prevented until /script resume command is called. - *

- *

Lets look at the following example. This is a program computes Fibonacci number in a recursive manner:

- *
-     * fib(n) -> if(n<3, 1, fib(n-1)+fib(n-2) ); fib(8)
-     * 
- *

That's really bad way of doing it, because the higher number we need to compute the compute requirements will rise - * exponentially with n. It takes a little over 50 milliseconds to do fib(24), so above one tick, - * but about a minute to do fib(40). Calling fib(40) will not only freeze the game, but also you woudn't be able to interrupt - * its execution. We can modify the script as follows

- *
fib(n) -> ( game_tick(50); if(n<3, 1, fib(n-1)+fib(n-2) ) ); fib(40)
- *

But this would never finish as such call would finish after ~ 2^40 ticks. To make our computations - * responsive, yet able to respond to user interactions, other commands, as well as interrupt execution, - * we could do the following:

- *
fib(n) -> ( if(n==23, game_tick(50) ); if(n<3, 1, fib(n-1)+fib(n-2) ) ); fib(40)
- *

This would slow down the computation of fib(40) from a minute to two, but allows the game to keep continue running - * and be responsive to commands, using about half of each tick to advance the computation. - * Obviously depending on the problem, and available hardware, certain things can take - * more or less time to execute, so portioning of work with calling gametick should be balanced in each - * case separately

- *
- * @param doStop . - */ - public void BreakExecutionOfAllScriptsWithCommands(boolean doStop) - { - //unused - accessed via CarpetScriptServer - } - private LazyValue booleanStateTest( Context c, String name, @@ -433,441 +382,6 @@ public class CarpetExpression } } - /** - *

Blocks / World API

- *
- *

Specifying blocks

- *

block(x, y, z), block(l(x,y,z)), block(state)

- *

Returns either a block from specified location, or block with a specific state - * (as used by /setblock command), so allowing for block properties, block entity data etc. - * Blocks otherwise can be referenced everywhere by its simple string name, but its only used in its default state

- *
-     * block('air')  => air
-     * block('iron_trapdoor[half=top]')  => iron_trapdoor
-     * block(0,0,0) == block('bedrock')  => 1
-     * block('hopper[facing=north]{Items:[{Slot:1b,id:"minecraft:slime_ball",Count:16b}]}') => hopper
-     * 
- *

Retrieving a block with block function has also a side-effect of evaluating its current state and data. - * so if you use it later it will reflect block state and data of the block that was when block was called, rather than - * when it was used. Block values passed in various places like scan functions, etc, are not fully evaluated - * unless its properties are needed. This means that if the block at the location changes before its queried in the program - * this might result in getting the later state, which might not be desired. Consider the following example:

- *
-     * set(10,10,10,'stone');
-     * scan(10,10,10,0,0,0, b = _);
-     * set(10,10,10,'air');
-     * print(b); // 'air', block was remembered 'lazily', and evaluated by `print`, when it was already set to air
-     *
-     * set(10,10,10,'stone');
-     * scan(10,10,10,0,0,0, b = block(_));
-     * set(10,10,10,'air');
-     * print(b); // 'stone', block was evaluated 'eagerly' but call to `block`
-     *
-     * 
- *

World Manipulation

- *

All the functions below can be used with block value, queried with coord triple, or 3-long list. - * All pos in the functions referenced below refer to either method of passing block position

- *

set(pos, block, property?, value?, ...)

- *

First part of the set function is either a coord triple, list of three numbers, or other block - * with coordinates. Second part, block is either block value as a result of block() function - * string value indicating the block name, and optional property - value pairs for extra block properties. - * If block is specified only by name, then if a destination block is the same the set operation - * is skipped, otherwise is executed, for other potential extra properties

- *

The returned value is either the block state that has been set, or false if block setting was skipped

- *
-     * set(0,5,0,'bedrock')  => bedrock
-     * set(l(0,5,0), 'bedrock')  => bedrock
-     * set(block(0,5,0), 'bedrock')  => bedrock
-     * scan(0,5,0,0,0,0,set(_,'bedrock'))  => 1
-     * set(pos(players()), 'bedrock')  => bedrock
-     * set(0,0,0,'bedrock')  => 0   // or 1 in overworlds generated in 1.8 and before
-     * scan(0,100,0,20,20,20,set(_,'glass'))
-     *     // filling the area with glass
-     * scan(0,100,0,20,20,20,set(_,block('glass')))
-     *     // little bit faster due to internal caching of block state selectors
-     * b = block('glass'); scan(0,100,0,20,20,20,set(_,b))
-     *     // yet another option, skips all parsing
-     * set(x,y,z,'iron_trapdoor')  // sets bottom iron trapdoor
-     * set(x,y,z,'iron_trapdoor[half=top]')  // Incorrect. sets bottom iron trapdoor - no parsing of properties
-     * set(x,y,z,'iron_trapdoor','half','top') // correct - top trapdoor
-     * set(x,y,z,block('iron_trapdoor[half=top]')) // also correct, block() provides extra parsing
-     * set(x,y,z,'hopper[facing=north]{Items:[{Slot:1b,id:"minecraft:slime_ball",Count:16b}]}') // extra block data
-     * 
- *

without_updates(expr)

- *

Evaluates subexpression without causing updates when blocks change in the world

- *

Consider following scenario: We would like to generate a bunch of terrain in a flat world - * following a perling noise generator. The following code causes cascading effect as blocks placed on chunk borders will - * cause other chunks to get loaded to full, thus generated:

- *
-     * __config() -> m(l('scope', 'global'));
-     * __on_chunk_generated(x, z) -> (
-     *   scan(x,0,z,0,0,0,15,15,15,
-     *     if (perlin(_x/16, _y/8, _z/16) > _y/16,
-     *       set(_, 'black_stained_glass');
-     *     )
-     *   )
-     * )
-     * 
- *

The following addition resolves this issue, by not allowing block updates pass chunk borders:

- *
-     * __config() -> m(l('scope', 'global'));
-     * __on_chunk_generated(x, z) -> (
-     *   without_updates(
-     *     scan(x,0,z,0,0,0,15,15,15,
-     *       if (perlin(_x/16, _y/8, _z/16) > _y/16,
-     *         set(_, 'black_stained_glass');
-     *       )
-     *     )
-     *   )
-     * )
-     * 
- *

place_item(item, pos, facing?, sneak?)

- *

Places a given item in the world like it was placed by a player. Item names are default minecraft item name, - * less the minecraft prefix. Default facing is 'up', but there are other options: 'down', 'north', 'east', - * 'south', 'west', but also there are other secondary directions important for placement of blocks like stairs, doors, etc. - * Try experiment with options like 'north-up' which is placed facing north with cursor pointing to the upper part of the block, - * or 'up-north', which means a block placed facing up (player looking down) and placed smidge away of the block center - * towards north. Optional sneak is a boolean indicating if a player would be sneaking while placing the block - - * this option only affects placement of chests and scaffolding at the moment. - * Returns true if placement was successful, false otherwise.

- *
-     *     place_item('stone',x,y,z) // places a stone block on x,y,z block
-     *     place_item('piston,x,y,z,'down') // places a piston facing down
-     *     place_item('carrot',x,y,z) // attempts to plant a carrot plant. Returns true if could place carrots at that position.
-     * 
- *

set_poi(pos, type, occupancy?)

- *

Sets a Point of Interest (POI) of a specified type with optional custom occupancy. By default new POIs are not - * occupied. If type is null, POI at position is removed. In any case, previous POI is also removed. - * Available POI types are:

- *
    - *
  • 'unemployed', 'armorer', 'butcher', 'cartographer', 'cleric', 'farmer', 'fisherman', 'fletcher', 'leatherworker', - * 'librarian', 'mason', 'nitwit', 'shepherd', 'toolsmith', 'weaponsmith', 'home', 'meeting', 'beehive', 'bee_nest', 'nether_portal'
  • - *
- *

Interestingly, unemployed, and nitwit are not used in the game, meaning, they could be used - * as permanent spatial markers for scarpet apps. meeting is the only one with increased max occupancy of 32.

- *

set_biome(pos, biome_name)

- *

changes biome at that block position.

- *

update(pos)

- *

Causes a block update at position.

- *

block_tick(pos)

- *

Causes a block to tick at position.

- *

random_tick(pos)

- *

Causes a random tick at position.

- *

destroy(pos), destroy(pos, -1), destroy(pos, <N>), destroy(pos, tool, nbt?)

- *

Destroys the block like it was mined by a player. Add -1 for silk touch, and positive number for fortune level. - * If tool is specified, and optionally its nbt, it will use that tool and will attempt to mine the block with this tool. - * If called without item context, this function, unlike harvest, will affect all kinds of blocks. If called with item in context, - * it will fail to break blocks that cannot be broken by a survival player.

- *

Without item context it returns false if failed to destroy the block and true if block breaking was - * successful. In item context, true means that breaking item has no nbt to use, null indicating that - * the tool should be considered broken in process, and nbt type value, for a resulting NBT tag on a hypothetical tool. - * Its up to the programmer to use that nbt to apply it where it belong - *

- *

Here is a sample code that can be used to mine blocks using items in player inventory, without using player context for mining. - * Obviously, in this case the use of harvest would be much more applicable:

- *
-     * mine(x,y,z) ->
-     * (
-     *    p = player();
-     *    slot = p~'selected_slot';
-     *    item_tuple = inventory_get(p, slot);
-     *    if (!item_tuple, destroy(x,y,z,'air'); return()); // empty hand, just break with 'air'
-     *    l(item, count, tag) = item_tuple;
-     *    tag_back = destroy(x,y,z, item, tag);
-     *    if (tag_back == false, // failed to break the item
-     * 	     return(tag_back)
-     *    );
-     *    if (tag_back == true, // block broke, tool has no tag
-     * 	     return(tag_back)
-     *    );
-     *    if (tag_back == null, //item broke
-     * 	     delete(tag:'Damage');
-     * 	     inventory_set(p, slot, count-1, item, tag);
-     * 	     return(tag_back)
-     *    );
-     *    if (type(tag_back) == 'nbt', // item didn't break, here is the effective nbt
-     * 	     inventory_set(p, slot, count, item, tag_back);
-     * 	     return(tag_back)
-     *    );
-     *    print('How did we get here?');
-     * )
-     * 
- *

harvest(player, pos)

- *

Causes a block to be harvested by a specified player entity. Honors player item enchantments, as well as damages the - * 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.

- * - *

Block and World querying

- * - *

pos(block), pos(entity)

- *

Returns a triple of coordinates of a specified block or entity. Technically entities are queried with - * query function and the same can be achieved with query(entity,'pos'), but for simplicity - * pos allows to pass all positional objects.

- *
-     *     pos(block(0,5,0))  => l(0,5,0)
-     *     pos(players()) => l(12.3, 45.6, 32.05)
-     *     pos(block('stone'))  => Error: Cannot fetch position of an unrealized block
-     * 
- *

pos_offset(pos, direction, amount?)

- *

Returns a coords triple that is offset in a specified direction by amount of blocks. - * The default offset amount is 1 block. To offset into opposite facing, use negative numbers for the amount. - *

- *
-     *     pos_offset(block(0,5,0), 'up', 2)  => l(0,7,0)
-     *     pos_offset(l(0,5,0), 'up', -2 ) => l(0,3,0)
-     * 
- *

block_properties(pos)

- *

Returns a list of available block properties for a particular block. If a block has no properties, returns an empty list.

- *

property(pos, name)

- *

Returns property of block at pos, or specified by block argument. If a block doesn't - * have that property, null value is returned. Returned values are always strings. It is expected from - * the user to know what to expect and convert values to numbers using number() function or booleans - * using bool() function.

- *
-     *     set(x,y,z,'iron_trapdoor','half','top'); property(x,y,z,'half')  => top
-     *     set(x,y,z,'air'); property(x,y,z,'half')  => null
-     *     property(block('iron_trapdoor[half=top]'),'half')  => top
-     *     property(block('iron_trapdoor[half=top]'),'powered')  => false
-     *     bool(property(block('iron_trapdoor[half=top]'),'powered'))  => 0
-     * 
- *

block_data(pos)

- *

Return NBT string associated with specific location, or null if the block does not carry block data. - * Can be currently used to match specific information from it, or use it to copy to another block

- *
-     *     block_data(x,y,z) => '{TransferCooldown:0,x:450,y:68, ... }'
-     * 
- *

poi(pos), poi(pos, radius), poi(pos, radius, status)

- *

Queries a POI (Point of Interest) at a given position, returning null if none is found, or tuple of - * poi type and its occupancy load. With optional radius and status, returns a list of POIs - * around pos within a given radius. If status is specified (either 'available', - * or 'occupied') returns only POIs with that status. The return format is again, poi type, occupancy load, and extra tripple of - * coordinates.

- *

Querying for POIs using the radius is intended use of POI mechanics and ability of accessing individual POIs via - * poi(pos) in only provided for completness.

- *
-     *     poi(x,y,z) => null  // nothing set at position
-     *     poi(x,y,z) => ['meeting',3]  // its a bell-type meeting point occupied by 3 villagers
-     *     poi(x,y,z,5) => []  // nothing around
-     *     poi(x,y,z,5) => [['nether_portal',0,[7,8,9]],['nether_portal',0,[7,9,9]]] // two portal blocks in the range
-     * 
- *

biome(pos)

- *

returns biome at that block position.

- *

solid(pos)

- *

Boolean function, true if the block is solid

- *

air(pos)

- *

Boolean function, true if a block is air.... or cave air... - * or void air.... or any other air they come up with.

- *

liquid(pos)

- *

Boolean function, true if the block is liquid, or liquidlogged

- *

flammable(pos)

- *

Boolean function, true if the block is flammable

- *

transparent(pos)

- *

Boolean function, true if the block is transparent

- *

opacity(pos)

- *

Numeric, returning opacity level of a block

- *

blocks_daylight(pos)

- *

Boolean function, true if the block blocks daylight

- *

emitted_light(pos)

- *

Numeric, returning light level emitted from block

- *

light(pos)

- *

Integer function, returning total light level at position

- *

block_light(pos)

- *

Integer function, returning block light at position. From torches and other light sources.

- *

sky_light(pos)

- *

Numeric function, returning sky light at position. From the sky access.

- *

see_sky(pos)

- *

Boolean function, returning true if the block can see sky.

- *

hardness(pos)

- *

Numeric function, indicating hardness of a block.

- *

blast_resistance(pos)

- *

Numeric function, indicating blast_resistance of a block.

- *

in_slime_chunk(pos)

- *

Boolean indicating if the given block position is in a slime chunk.

- - *

top(type, pos)

- *

Returns the Y value of the topmost block at given x, z coords (y value of a block is not important), according to the - * heightmap specified by type. Valid options are:

- *
    - *
  • light: topmost light blocking block (1.13 only)
  • - *
  • motion: topmost motion blocking block
  • - *
  • terrain: topmost motion blocking block except leaves
  • - *
  • ocean_floor: topmost non-water block
  • - *
  • surface: topmost surface block
  • - *
- *
-     * top('motion', x, y, z)  => 63
-     * top('ocean_floor', x, y, z)  => 41
-     * 
- *

loaded(pos)

- *

Boolean function, true if the block is accessible for the game mechanics. - * Normally scarpet doesn't check if operates on - * loaded area - the game will automatically load missing blocks. We see this as advantage. - * Vanilla fill/clone commands only check the specified corners for loadness.

- *

To check if block is truly loaded, I mean in memory, use generation_status(x) != null, as - * chunks can still be loaded outside of the playable area, just are not used any of the game mechanics processes.

- *
-     * loaded(pos(players()))  => 1
-     * loaded(100000,100,1000000)  => 0
-     * 
- *

(Deprecated) loaded_ep(pos)

- *

Boolean function, true if the block is loaded and entity processing, as per 1.13.2

- *

Deprecated as of scarpet 1.6, use loaded_status(x) > 0, or - * just loaded(x) with the same effect

- *

loaded_status(pos)

- *

Returns loaded status as per new 1.14 chunk ticket system, 0 for inaccessible, 1 for border chunk, - * 2 for ticking, 3 for entity ticking

- *

generation_status(pos), generation_status(pos, true)

- *

Returns generation status as per new 1.14 chunk ticket system. Can return any value from several available - * but chunks can only be valid in a few states: full, features, liquid_carvers, - * and structure_starts. Returns null if the chunk is not in memory unless called with optional - * true.

- *

structures(pos), structures(pos, structure_name)

- *

Returns structure information for a given block position. Note that structure information is the same for - * all the blocks from the same chunk. structures function can be called with a block, or a block and - * a structure name. In the first case it returns a map of structures at a given position, keyed by structure name, - * with values indicating the bounding box of the structure - a pair of two 3-value coords (see examples). When - * called with an extra structure name, returns list of components for that structure, with their name, direction - * and two sets of coordinates indicating the bounding box of the structure piece.

- *

structure_references(pos), structure_references(pos, structure_name)

- *

Returns structure information that a chunk with a given block position is part of. - * structure_references function can be called with a block, or a block and - * a structure name. In the first case it returns a list of structure names that give chunk belongs to. When - * called with an extra structure name, returns list of positions pointing to the lowest block position in chunks that - * hold structure starts for these structures. You can query that chunk structures then to get its bounding boxes.

- *

set_structure(pos, structure_name), set_structure(pos, structure_name, null)

- *

Creates or removes structure information of a structure associated with a chunk of pos. - * Unlike plop, blocks are not placed in the world, only structure information is set. For the game this is a fully - * functional structure even if blocks are not set. To remove structure a given point is in, - * use structure_references to find where current structure starts.

- - *

suffocates(pos)

- *

Boolean function, true if the block causes suffocation.

- *

power(pos)

- *

Numeric function, returning redstone power level at position.

- *

ticks_randomly(pos)

- *

Boolean function, true if the block ticks randomly.

- *

blocks_movement(pos)

- *

Boolean function, true if block at position blocks movement.

- *

block_sound(pos)

- *

Returns the name of sound type made by the block at position. One of:

- *
    - *
  • wood
  • - *
  • gravel
  • - *
  • grass
  • - *
  • stone
  • - *
  • metal
  • - *
  • glass
  • - *
  • wool
  • - *
  • sand
  • - *
  • snow
  • - *
  • ladder
  • - *
  • anvil
  • - *
  • slime
  • - *
  • sea_grass
  • - *
  • coral
  • - *
- *

material(pos)

- *

Returns the name of material of the block at position. very useful to target a group of blocks. One of:

- *
    - *
  • air
  • - *
  • void
  • - *
  • portal
  • - *
  • carpet
  • - *
  • plant
  • - *
  • water_plant
  • - *
  • vine
  • - *
  • sea_grass
  • - *
  • water
  • - *
  • bubble_column
  • - *
  • lava
  • - *
  • snow_layer
  • - *
  • fire
  • - *
  • redstone_bits
  • - *
  • cobweb
  • - *
  • redstone_lamp
  • - *
  • clay
  • - *
  • dirt
  • - *
  • grass
  • - *
  • packed_ice
  • - *
  • sand
  • - *
  • sponge
  • - *
  • wood
  • - *
  • wool
  • - *
  • tnt
  • - *
  • leaves
  • - *
  • glass
  • - *
  • ice
  • - *
  • cactus
  • - *
  • stone
  • - *
  • iron
  • - *
  • snow
  • - *
  • anvil
  • - *
  • barrier
  • - *
  • piston
  • - *
  • coral
  • - *
  • gourd
  • - *
  • dragon_egg
  • - *
  • cake
  • - *
- *

map_colour(pos)

- *

Returns the map colour of a block at position. One of:

- *
    - *
  • air
  • - *
  • grass
  • - *
  • sand
  • - *
  • wool
  • - *
  • tnt
  • - *
  • ice
  • - *
  • iron
  • - *
  • foliage
  • - *
  • snow
  • - *
  • clay
  • - *
  • dirt
  • - *
  • stone
  • - *
  • water
  • - *
  • wood
  • - *
  • quartz
  • - *
  • adobe
  • - *
  • magenta
  • - *
  • light_blue
  • - *
  • yellow
  • - *
  • lime
  • - *
  • pink
  • - *
  • gray
  • - *
  • light_gray
  • - *
  • cyan
  • - *
  • purple
  • - *
  • blue
  • - *
  • brown
  • - *
  • green
  • - *
  • red
  • - *
  • black
  • - *
  • gold
  • - *
  • diamond
  • - *
  • lapis
  • - *
  • emerald
  • - *
  • obsidian
  • - *
  • netherrack
  • - *
  • white_terracotta
  • - *
  • orange_terracotta
  • - *
  • magenta_terracotta
  • - *
  • light_blue_terracotta
  • - *
  • yellow_terracotta
  • - *
  • lime_terracotta
  • - *
  • pink_terracotta
  • - *
  • gray_terracotta
  • - *
  • light_gray_terracotta
  • - *
  • cyan_terracotta
  • - *
  • purple_terracotta
  • - *
  • blue_terracotta
  • - *
  • brown_terracotta
  • - *
  • green_terracotta
  • - *
  • red_terracotta
  • - *
  • black_terracotta
  • - *
- *
- */ - public void API_BlockManipulation() { this.expr.addLazyFunction("block", -1, (c, t, lv) -> @@ -1740,103 +1254,6 @@ public class CarpetExpression } - /** - *

Inventory and Items API

- *
- *

Manipulating inventories of blocks and entities

- *

Most functions in this category require inventory as the first argument. Inventory could be specified by - * an entity, or a block, or position (three coordinates) of a potential block with inventory. Player enderchest inventory - * require two arguments, keyword 'enderchest', followed by the player entity argument. - * If the entity or a block doesn't - * have an inventory, they typically do nothing and return null.

- *

Most items returned are in the form of - * a triple of item name, count, and nbt or the extra data associated with an item. - * Manipulating of the nbt data can be costly, but retrieving them from the tuple to match other aspects is cheap

- *

stack_limit(item)

- *

Returns number indicating what is the stack limit for the item. Its typically 1 (non-stackable), - * 16 (like buckets), or 64 - rest. It is recommended to consult this, as other inventory API functions - * ignore normal stack limits, and it is up to the programmer to keep it at bay. As of 1.13, game checks for negative - * numbers and setting an item to negative is the same as empty.

- *
-     *     stack_limit('wooden_axe') => 1
-     *     stack_limit('ender_pearl') => 16
-     *     stack_limit('stone') => 64
-     * 
- *

item_category(item)

- *

Returns the string representing the category of a given item, like `building_blocks`, `combat`, or `tools`.

- *
-     *     item_category('wooden_axe') => tools
-     *     item_category('ender_pearl') => misc
-     *     item_category('stone') => building_blocks
-     * 
- *

inventory_size(inventory)

- *

Returns the size of the inventory for the entity or block in question. Returns null if the block or entity - * don't have an inventory

- *
-     *     inventory_size(player()) => 41
-     *     inventory_size('enderchest', player()) => 27 // enderchest
-     *     inventory_size(x,y,z) => 27 // chest
-     *     inventory_size(block(pos)) => 5 // hopper
-     * 
- *

inventory_has_items(inventory)

- *

Returns true, if the inventory is not empty, false if it is empty, and null, if its not an inventory.

- *
-     *     inventory_has_items(player()) => true
-     *     inventory_has_items(x,y,z) => false // empty chest
-     *     inventory_has_items(block(pos)) => null // stone
-     * 
- *

inventory_get(inventory, slot)

- *

Returns the item in the corresponding inventory slot, or null if slot empty or inventory is invalid. - * You can use negative numbers to indicate slots counted from 'the back'.

- *
-     *     inventory_get(player(), 0) => null // nothing in first hotbar slot
-     *     inventory_get(x,y,z, 5) => ['stone', 1, {}]
-     *     inventory_get(player(), -1) => ['diamond_pickaxe', 1, {Damage:4}] // slightly damaged diamond pick in the offhand
-     * 
- * - *

inventory_set(inventory, slot, count, item?, nbt?)

- *

Modifies or sets a stack in inventory. specify count 0 to empty the slot. - * If item is not specified, keeps existing item, just modifies the count. If item is provided - replaces current item. - * If nbt is provided - adds a tag to the 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_find(inventory, item, start_slot?, ), inventory_find(inventory, null, start_slot?)

- *

Finds the first slot with a corresponding item in the inventory, or if queried with null: the first empty slot. - * Returns slot number if found, or null otherwise. Optional start_slot argument allows to skip all preceeding slots - * allowing for efficient (so not slot-by-slot) inventory search for items.

- *
-     *     inventory_find(player(), 'stone') => 0 // player has stone in first hotbar slot
-     *     inventory_find(player(), null) => null // player's inventory has no empty spot
-     *     while( (slot = inventory_find(p, 'diamond', slot)) != null, 41, drop_item(p, slot) )
-     *         // spits all diamonds from player inventory wherever they are
-     *     inventory_drop(x,y,z, 0) => 64 // removed and spawned in the world a full stack of items
-     * 
- * - *

inventory_remove(inventory, item, amount?)

- *

Removes amount (defaults to 1) of item from inventory. If the inventory doesn't have the defined amount, nothing - * happens, otherwise the given amount of items is removed wherever they are in the inventory. Returns boolean whether - * the removal operation was successful. Easiest way to remove a specific item from player inventory without specifying - * the slot.

- *
-     *     inventory_remove(player(), 'diamond') => 1 // removed diamond from player inventory
-     *     inventory_remove(player(), 'diamond', 100) => 0 // player doesn't have 100 diamonds, nothing happened
-     * 
- * - *

drop_item(inventory, slot, amount?, )

- *

Drops the items from indicated inventory slot, like player that Q's an item or villager, that exchanges food. - * You can Q items from block inventories as well. default amount is 0 - which is all from the slot. NOTE: hoppers are quick - * enough to pick all the queued items from their inventory anyways. - * Returns size of the actual dropped items.

- *
-     *     inventory_drop(player(), 0, 1) => 1 // Q's one item on the ground
-     *     inventory_drop(x,y,z, 0) => 64 // removed and spawned in the world a full stack of items
-     * 
- *
- */ public void API_InventoryManipulation() { this.expr.addLazyFunction("stack_limit", 1, (c, t, lv) -> @@ -2109,320 +1526,6 @@ public class CarpetExpression } } - - /** - *

Entity API

- *
- *

Entity Selection

- *

Entities have to be fetched before using them. Entities can also change their state between calls to the script - * if game happens either in between separate calls to the programs, or if the program calls game_tick - * on its own. In this case - entities would need to be re-fetched, or the code should account for entities getting dead

- *

player(), player(type), player(name)

- *

- * With no arguments, it returns the calling player or the player closest to the caller. Note that the main context - * will receive p variable pointing to this player. With type or name specified - * it will try first to match a type, returning a list of players matching a type, and if this fails, will assume its - * player name query retuning player with that name, or null if no player was found. - * With 'all', list of all players in the game, in all dimensions, so end user needs to be cautious, that - * you might be referring to wrong blocks and entities around the player in question. - * WIth type = '*' it returns all players in caller dimension, 'survival' returns all survival - * and adventure players, 'creative' returns all creative players, 'spectating' returns all spectating - * players, and '!spectating', all not-spectating players. If all fails, - * with name, the player in question, if is logged in.

- *

entity_id(uuid), entity_id(id)

- *

Fetching entities wither by their ID obtained via entity ~ 'id', which is unique - * for a dimension and current world run, or by UUID, obtained via entity ~ 'uuid'. - * It returns null if no such entity - * is found. Safer way to 'store' entities between calls, as missing entities will be returning null. - * Both calls using UUID or numerical ID are O(1), but obviously using UUIDs takes more memory and compute.

- *

entity_list(type)

- *

Returns global lists of entities in the current dimension of a specified type. Currently the following selectors are available:

- *
    - *
  • *: all
  • - *
  • living
  • - *
  • items
  • - *
  • players
  • - *
  • !players
  • - *
- * - *

entity_area(type, cx, cy, cz, dx, dy, dz)

- *

Returns entities of a specified type in an area centered on cx, cy, cz and - * at most dx, dy, dz blocks away from the center point. Uses same selectors as entities_list

- * - *

entity_selector(selector)

- *

Returns entities satisfying given vanilla entity selector. Most complex among all the methods of selecting - * entities, but the most capable. Selectors are cached so should be as fast as other methods of selecting entities.

- * - *

spawn(name, pos, nbt?)

- *

Spawns and places an entity in world, like /summon vanilla command. - * Requires a position to spawn, and optional extra nbt data to merge with the entity. What makes it different from calling - * run('summon ...'), is the fact that you get the entity back as a return value, which is swell.

- * - *

Entity Manipulation

- * - *

Unlike with blocks, that use plethora of vastly different querying functions, entities are queried with - * query function and altered via modify function. Type of information needed or - * values to be modified are different for each call

- *

Using ~ (in) operator is an alias for query. Especially useful if a statement has - * no arguments, which in this case can be radically simplified

- *
-     *     query(p, 'name') <=> p ~ 'name'     // much shorter and cleaner
-     *     query(p, 'holds', 'offhand') <=> p ~ l('holds', 'offhand')    // not really but can be done
-     * 
- *

query(e,'removed')

- *

Boolean. True if the entity is removed

- *

query(e,'id')

- *

Returns numerical id of the entity. Most efficient way to keep track of entites in a script. Ids are only unique - * within current game session (ids are not preserved between restarts), and dimension (each dimension has its own ids - * which can overlap.

- *

query(e,'uuid')

- *

Returns UUID (unique id) of the entity. Can be used to access entities with the other vanilla commands and remains unique - * regardless of the dimension, and is preserved between game restarts. - * Apparently players cannot be accessed via UUID, but name instead.

- *
-     * map(entities_area('*',x,y,z,30,30,30),run('kill '+query(_,'id'))) // doesn't kill the player
-     * 
- *

query(e,'pos')

- *

Triple of entity position

- *

query(e,'location')

- *

Quin-tuple of entity position (x, y, and z coords), and rotation (yaw, pitch)

- *

query(e,'x'), query(e,'y'), query(e,'z')

- *

Respective entity coordinate

- *

query(e,'pitch'), query(e,'yaw')

- *

Pitch and Yaw or where entity is looking.

- *

query(e,'look')

- *

Returns a 3d vector where the entity is looking.

- *

query(e,'motion')

- *

Triple of entity motion vector, l(motion_x, motion_y, motion_z)

- *

query(e,'motion_x'), query(e,'motion_y'), query(e,'motion_z')

- *

Respective component of the motion vector

- *

query(e,'name'), query(e,'custom_name'), query(e,'type')

- *

String of entity name

- *
-     * query(e,'name')  => Leatherworker
-     * query(e,'custom_name')  => null
-     * query(e,'type')  => villager
-     * 
- *

query(e,'is_riding')

- *

Boolean. True if riding another entity.

- *

query(e,'is_ridden')

- *

Boolean. True if another entity is riding it.

- *

query(e,'passengers')

- *

List of entities riding the entity.

- *

query(e,'mount')

- *

Entity that e rides.

- *

query(e,'tags')

- *

List of entity tags.

- *

query(e,'has_tags',tag)

- *

Boolean, True if the entity is marked with tag.

- *

query(e,'is_burning')

- *

Boolean, True if the entity is burning.

- *

query(e,'fire')

- *

Number of remaining ticks of being on fire.

- *

query(e,'silent')

- *

Boolean, True if the entity is silent.

- *

query(e,'gravity')

- *

Boolean, True if the entity is affected by gravity, like most entities do.

- *

query(e,'immune_to_fire')

- *

Boolean, True if the entity is immune to fire.

- *

query(e,'dimension')

- *

Name of the dimension entity is in.

- *

query(e,'height')

- *

Height of the entity.

- *

query(e,'width')

- *

Width of the entity.

- *

query(e,'eye_height')

- *

Eye height of the entity.

- *

query(e,'age')

- *

Age, in ticks, of the entity, i.e. how long it existed.

- *

query(e,'breeding_age')

- *

Breeding age of passive entity, in ticks. If negative it it time to adulthood, if positive, breeding cooldown

- *

query(e,'despawn_timer')

- *

For living entities - the number of ticks they fall outside of immediate player presence.

- *

query(e,'item')

- *

The item triple (name, count, nbt) if its an item entity, null otherwise

- *

query(e,'count')

- *

Number of items in a stack from item entity, null otherwise

- *

query(e,'pickup_delay')

- *

Retrieves pickup delay timeout for an item entity, null otherwise

- *

query(e,'is_baby')

- *

Boolean, true if its a baby.

- *

query(e,'target')

- *

Returns mob's attack target or null if none or not applicable.

- *

query(e,'home')

- *

Returns creature's home position or null if none or not applicable.

- *

query(e,'sneaking')

- *

Boolean, true if entity is sneaking.

- *

query(e,'sprinting')

- *

Boolean, true if entity is sprinting.

- *

query(e,'swimming')

- *

Boolean, true if entity is swimming.

- *

query(e,'jumping')

- *

Boolean, true if entity is jumping.

- *

query(e,'gamemode')

- *

String with gamemode, or null if not a player.

- *

query(e,'gamemode_id')

- *

Good'ol gamemode id, or null if not a player.

- *

query(e,'player_type')

- *

Returns null if the argument is not a player, otherwise:

- *
    - *
  • singleplayer: for singleplayer game
  • - *
  • multiplayer: for players on a dedicated server
  • - *
  • lan_host: for singleplayer owner that opened the game to LAN
  • - *
  • lan_player: for all other players that connected to a LAN host
  • - *
  • fake: any carpet-spanwed fake player
  • - *
  • shadow: any carpet-shadowed real player
  • - *
  • realms: ?
  • - *
- *

query(e,'permission_level')

- *

Player's permission level, or null if not applicable for this entity.

- * - *

query(e,'effect',name?)

- *

Without extra arguments, it returns list of effect active on a living entity. - * Each entry is a triple of short effect name, amplifier, and remaining duration. - * With an argument, if the living entity has not that potion active, returns null, otherwise - * return a tuple of amplifier and remaining duration

- *
-     * query(p,'effect')  => [[haste, 0, 177], [speed, 0, 177]]
-     * query(p,'effect','haste')  => [0, 177]
-     * query(p,'effect','resistance')  => null
-     * 
- *

query(e,'health')

- *

Number indicating remaining entity health, or null if not applicable.

- * - *

query(e,'holds',slot?)

- *

Returns triple of short name, stack count, and NBT of item held in slot. - * Available options for slot are:

- *
    - *
  • mainhand
  • - *
  • offhand
  • - *
  • head
  • - *
  • chest
  • - *
  • legs
  • - *
  • feet
  • - *
- *

If slot is not specified, it defaults to the main hand.

- *

query(e,'selected_slot')

- *

Number indicating the selected slot of entity inventory. Currently only applicable to players.

- *

query(e,'facing', order?)

- *

Returns where the entity is facing. optional order (number from 0 to 5, and negative), indicating - * primary directions where entity is looking at. From most prominent (order 0) to opposite (order 5, or -1)

- *

query(e,'trace', reach?, options?...)

- *

Returns the result of ray tracing from entity perspective, indicating what it is looking at. Default reach is 4.5 - * blocks (5 for creative players), and by default it traces for blocks and entities, identical to player attack tracing action. - * This can be customized with options, use 'blocks' to trace for blocks, 'liquids' to include liquid blocks - * as possible results, and 'entities' to trace entities. Any combination of the above is possible. When tracing - * entities and blocks, blocks will take over the priority even if transparent or non-colliding (aka fighting chickens - * in tall grass).

- *

Regardless of the options selected, the result could be null if nothing is in reach, entity, if look - * targets an entity, and block value if block is in reach.

- *

query(e,'nbt',path?)

- *

Returns full NBT of the entity. If path is specified, it fetches only that portion of the NBT, - * that corresponds to the path. For specification of path attribute, consult - * vanilla /data get entity command.

- *

Note that calls to nbt are considerably more expensive comparing to other - * calls in Minecraft API, and should only be used when there is no other option. Returned value is of type nbt, - * which can be further manipulated with nbt type objects via get, put, has, delete, so try to use API calls - * first for that.

- *

Entity Modification

- *

Like with entity querying, entity modifications happen through one function. Most position and movements - * modifications don't work currently on players as their position is controlled by clients.

- *

Currently there is no ability to modify NBT directly, but you could always use run('data modify entity

- *

modify(e,'remove')

- *

Removes (not kills) entity from the game.

- *

modify(e,'kill')

- *

Kills the entity.

- *

modify(e, 'pos', x, y, z), modify(e, 'pos', l(x,y,z) )

- *

Moves the entity to a specified coords.

- *

modify(e, 'location', x, y, z, yaw, pitch), modify(e, 'location', l(x, y, z, yaw, pitch) )

- *

Changes full location vector all at once.

- *

modify(e, 'x', x), modify(e, 'y', y), modify(e, 'z', z)

- *

Moves the entity in.... one direction.

- *

modify(e, 'pitch', pitch), modify(e, 'yaw', yaw)

- *

Changes entity's pitch or yaw.

- *

modify(e, 'move', x, y, z), modify(e, 'move', l(x,y,z) )

- *

Moves the entity by a vector from its current location.

- *

modify(e, 'motion', x, y, z), modify(e, 'motion', l(x,y,z) )

- *

Sets the motion vector (where and how much entity is moving).

- *

modify(e, 'motion_z', x), modify(e, 'motion_y', y), modify(e, 'motion_z', z)

- *

Sets the corresponding component of the motion vector.

- *

modify(e, 'accelerate', x, y, z), modify(e, 'accelerate', l(x, y, z) )

- *

Adds a vector to the motion vector. Most realistic way to apply a force to an entity.

- *

modify(e, 'custom_name'), modify(e, 'custom_name', name )

- *

modify(e, 'age', number)

- *

Modifies entity's internal age counter. Fiddling with this will affect directly - * behaviours of complex entities, so use it with caution.

- *

modify(e, 'pickup_delay', number)

- *

modify(e, 'breeding_age', number)

- *

modify(e, 'despawn_timer', number)

- *

Sets a custom despawn timer value.

- *

modify(e, 'dismount')

- *

Dismounts riding entity.

- *

modify(e, 'mount', other)

- *

Mounts the entity to the other.

- *

modify(e, 'drop_passengers')

- *

Shakes off all passengers.

- *

modify(e, 'mount_passengers', passenger, ? ...), modify(e, 'mount_passengers', l(passengers) )

- *

Mounts on all listed entities on e.

- *

modify(e, 'tag', tag, ? ...), modify(e, 'tag', l(tags) )

- *

Adds tag / tags to the entity.

- *

modify(e, 'clear_tag', tag, ? ...), modify(e, 'clear_tag', l(tags) )

- *

Removes tag from entity.

- *

modify(e, 'talk')

- *

Make noises.

- *

modify(e, 'ai', boolean)

- *

If called with false value, it will disable AI in the mob. true will enable it again.

- *

modify(e, 'no_clip', boolean)

- *

Sets if the entity obeys any collisions, including collisions with the terrain and basic physics. - * Not affecting players, since they are controlled client side

- *

modify(e, 'effect', name, duration?, amplifier?, show_particles?, show_icon?)

- *

Applies status effect to the living entity. Takes several optional parameters, which default to 0, - * true and true. If no duration is specified, or it is null or 0, the effect is removed.

- *

modify(e, 'home', null), modify(e, 'home', block, distance?), modify(e, 'home', x, y, z, distance?)

- *

Sets AI to stay around the home position, within distance blocks from it. distance - * defaults to 16 blocks. null removes it. May not work fully with mobs that have this AI built in, like - * Villagers.

- * - *

Entity Events

- *

There is a number of events that happen to entities that you can attach your own code to in the form of event - * handlers. The event handler is any function that runs in your package that accepts certain expected parameters, which - * you can expand with your own arguments. When it comes to the moment when the given command needs to be executed, it - * does so providing that number of arguments it accepts is equal number of event arguments, and extra arguments passed when - * defining the callback with entity_event

- *

The following events can be handled by entities.

- *
    - *
  • 'on_tick': executes every tick right before entity is ticked in the game. Required arguments: entity
  • - *
  • 'on_death': executes once when a living entity dies. Required arguments: entity, reason
  • - *
  • 'on_removed': execute once when an entity is removed. Required arguments: entity
  • - *
  • 'on_damaged': executed every time a living entity is about to receive damage. 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 will attach inapplicable event to it.

- *

entity_event(e, event, call_name, args...)

- *

Attaches specific function from the current package to be called upon the event, with extra args - * curried to the original required arguments for the event handler

- *
-     * protect_villager(entity, amount, source, source_entity, healing_player) ->
-     * (
-     * 	 if(source_entity && source_entity~'type' != 'player',
-     * 	   modify(entity, 'health', amount + entity~'health' );
-     * 	   particle('end_rod', pos(entity)+l(0,3,0));
-     * 	   print(str('%s healed thanks to %s', entity, healing_player))
-     * 	 )
-     * );
-     *
-     * __on_player_interacts_with_entity(player, entity, hand) ->
-     * (
-     *   if (entity~'type' == 'villager',
-     * 	   entity_event(entity, 'on_damage', 'protect_villager', player~'name')
-     *   )
-     * )
-     * 
- *

In this case this will protect a villager from entity damage (zombies, etc.) except players by granting all the health - * back to the villager after being harmed.

- *
- */ - public void API_EntityManipulation() { this.expr.addLazyFunction("player", -1, (c, t, lv) -> { @@ -2685,37 +1788,6 @@ public class CarpetExpression }); } - /** - *

Iterating over larger areas of blocks

- *
- *

These functions help scan larger areas of blocks without using generic loop functions, - * like nested loop.

- *

- *

scan(cx, cy, cz, dx, dy, dz, px?, py?, pz?, expr)

- *

Evaluates expression over area of blocks defined by its center (cx, cy, cz), - * expanded in all directions by dx, dy, dz blocks, or optionally in negative with d coords, - * and p coords in positive values. expr receives _x, _y, _z - * as coords of current analyzed block and _ which represents the block itself.

- *

volume(x1, y1, z1, x2, y2, z2, expr)

- *

Evaluates expression for each block in the area, the same as the scanfunction, but using two opposite - * corners of the rectangular cuboid. Any corners can be specified, its like you would do with /fill command

- *

neighbours(x, y, z), neighbours(block), neighbours(l(x,y,z))

- *

Returns the list of 6 neighbouring blocks to the argument. Commonly used with other loop functions like for

- *
-     * for(neighbours(x,y,z),air(_)) => 4 // number of air blocks around a block
-     * 
- *

rect(cx, cy, cz, dx?, dy?, dz?, px?, py?, pz?)

- *

returns an iterator, just like range function that iterates over rectangular cubarea of blocks. If - * only center point is specified, it iterates over 27 blocks. If d arguments are specified, expands selection - * of respective number of blocks in each direction. If p arguments are specified, it uses d for - * negative offset, and p for positive.

- *

diamond(cx, cy, cz, radius?, height?)

- *

Iterates over a diamond like area of blocks. With no radius and height, its 7 blocks centered around the middle - * (block + neighbours). With a radius specified, it expands shape on x and z coords, and with a custom height, on y. Any of these can be - * zero as well. radius of 0 makes a stick, height of 0 makes a diamond shape pad.

- *
- */ - public void API_IteratingOverAreasOfBlocks() { this.expr.addLazyFunction("scan", -1, (c, t, lv) -> @@ -3131,213 +2203,6 @@ public class CarpetExpression }); } - //TODO sounds - /** - *

Auxiliary aspects

- *
- *

Collection of other methods that control smaller, yet still important aspects of the game

- *

Sounds

- *

sound(name, pos, volume?, pitch?)

- *

Plays a specific sound name, at block or position pos, with optional - * volume and modified pitch. pos can be either a block, triple of coords, - * or a list of thee numbers. Uses the same options as a corresponding playsound command.

- *

Particles

- *

particle(name, pos, count?. spread?, speed?, playername?)

- *

Renders a cloud of particles name centered around pos position, by default - * count 10 of them, default speed of 0, and to all players nearby, but these - * options can be changed via optional arguments. Follow vanilla /particle command on details on those - * options. Valid particle names are for example - * 'angry_villager', 'item diamond', 'block stone', 'dust 0.8 0.1 0.1 4'

- *

particle_line(name, pos, pos2, density?)

- *

Renders a line of particles from point pos to pos2 with supplied density (defaults 1), - * which indicates how far part you would want particles to appear, so 0.1 means one every 10cm.

- *

particle_rect(name, pos, pos2, density?)

- *

Renders a cuboid of particles between point pos to pos2 with supplied density.

- *

Markers

- *

create_marker(text, pos, rotation?, block?)

- *

Spawns a (permanent) marker entity with text or block at position. Returns that entity for further manipulations. - * Unloading the app that spawned them will cause all the markers from the loaded portion of the world to be removed. - * Also - if the game loads that marker in the future and the app is not loaded, it will be removed as well.

- *

remove_all_markers()

- *

Removes all scarpet markers from the loaded portion of the world created by this app, in case you didn't want - * to do the proper cleanup

- *

System function

- *

nbt(expr)

- *

Treats the argument as a nbt serializable string and returns its nbt value. - * In case nbt is not in a correct nbt compound tag format, it will return null value.

- *

Consult section about container operations in Expression to learn about possible operations on nbt values.

- *

escape_nbt(expr)

- *

Excapes all the special characters in the string or nbt tag and returns a string that can be stored in nbt directly as - * a string value.

- *

print(expr)

- *

Displays the result of the expression to the chat. Overrides default scarpet behaviour of - * sending everyting to stderr.

- *

logger(expr)

- *

Prints the message to system logs, and not to chat.

- *

run(expr)

- *

Runs a vanilla command from the string result of the expr and returns its success count

- *
-     * run('fill 1 1 1 10 10 10 air') -> 123 // 123 block were filled, this operation was successful 123 times out of a possible 1000 blocks volume
-     * run('give @s stone 4') -> 1 // this operation was successful once
-     * 
- *

save()

- *

Performs autosave, saves all chunks, player data, etc. Useful for programs where autosave is disabled - * due to performance reasons and saves the world only on demand.

- *

load_app_data(), load_app_data(file)

- *

Loads the app data associated with the app from the world /scripts folder. Without argument returns the memory - * managed and buffered / throttled NBT tag. With a file name - reads explicitly a file with that name from the scripts - * folder.

- *

You can use app data to save non-vanilla information separately from the world and other scripts.

- *

store_app_data(tag), store_app_data(tag, file)

- *

Stores the app data associated with the app from the world /scripts folder. With the file parameter - * saves immediately and with every call, without file parameter, it may take up to 10 seconds for the output - * file to sync preventing flickering in case this tag changes frequently. It will be synced when server closes.

- *

tick_time()

- *

Returns game tick counter. Can be used to run certain operations every n-th ticks, or to count in-game time

- *

game_tick(mstime?)

- *

Causes game to run for one tick. By default runs it and returns control to the program, but can optionally - * accept expected tick length, in milliseconds. You can't use it to permanently change the game speed, but - * setting longer commands with custom tick speeds can be interrupted via /script stop command

- *
-     * 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.

- *

current_dimension()

- *

Returns current dimension that the script runs in.

- *

in_dimension(smth, expr)

- *

Evaluates the expression expr with different dimension execution context. smth can - * be an entity, world-localized block, so not block('stone'), or a string representing a dimension like: - * 'nether', 'the_nether', 'end' or 'overworld', etc.

- *

schedule(delay, function, args...)

- *

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.

- *

statistic(player, category, entry)

- *

Queries in-game statistics for certain values. Categories include:

- *
    - *
  • mined: blocks mined
  • - *
  • crafted: items crafted
  • - *
  • used: items used
  • - *
  • broken: items broken
  • - *
  • picked_up: items picked up
  • - *
  • dropped: items dropped
  • - *
  • killed: mobs killed
  • - *
  • killed_by: blocks mined
  • - *
  • custom: various random stats
  • - *
- *

For the options of entry, consult your statistics page, or give it a guess.

- *

The call will return null if the statistics options are incorrect, or player didn't get these - * in their history. 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 has effectively - * a value of 0.

- *

plop(pos, what)

- *

Plops a structure or a feature at a given pos, so block, triple position coordinates - * or a list of coordinates. To what gets plopped and exactly where it often depends on the - * feature or structure itself. For example, all structures are chunk aligned, and often span multiple chunks. - * Repeated calls to plop a structure in the same chunk would result either in the same structure generated on - * top of each other, or with different state, but same position. Most - * structures generate at specific altitudes, which are hardcoded, or with certain blocks around them. API will cancel - * all extra position / biome / random requirements for structure / feature placement, but some hardcoded limitations - * may still cause some of structures/features not to place. Some features require special blocks to be present, like - * coral -> water or ice spikes -> snow block, and for some features, like fossils, placement is all sorts of - * messed up. This can be partially avoided for structures by setting their structure information via set_structure - * which sets it without looking into world blocks, and then use plop to fill it with blocks. This may, or may not work

- *

- * All generated structures will retain their properties, like mob spawning, however in many cases the world / dimension - * itself has certain rules to spawn mobs, like plopping a nether fortress in the overworld will not spawn nether mobs - * because nether mobs can spawn only in the nether, but plopped in the nether - will behave like a valid nether - * fortress.

- *

plop will not use world random number generator to generate structures and features, but its own. - * This has a benefit that they will generate properly randomly, not the same time every time

- *

Structure list:

- *
    - * - *
  • monument: Ocean Monument. Generates at fixed Y coordinate, surrounds itself with water.
  • - *
  • fortress: Nether Fortress. Altitude varies, but its bounded by the code.
  • - *
  • mansion: Woodland Mansion
  • - *
  • jungle_temple: Jungle Temple
  • - *
  • desert_temple: Desert Temple. Generates at fixed Y altitude.
  • - *
  • end_city: End City with Shulkers
  • - *
  • igloo: Igloo
  • - *
  • shipwreck: Shipwreck, version1?
  • - *
  • shipwreck2: Shipwreck, version2?
  • - *
  • witch_hut
  • - *
  • ocean_ruin, ocean_ruin_small, ocean_ruin_tall: Stone variants of ocean ruins.
  • - *
  • ocean_ruin_warm, ocean_ruin_warm_small, ocean_ruin_warm_tall: Sandstone variants of ocean ruins.
  • - *
  • treasure: A treasure chest. Yes, its a whole structure.
  • - *
  • pillager_outpost: A pillager outpost.
  • - *
  • mineshaft: A mineshaft.
  • - *
  • mineshaft_mesa: A Mesa (Badlands) version of a mineshaft.
  • - *
  • village: Plains, oak village.
  • - *
  • village_desert: Desert, sandstone village.
  • - *
  • village_savanna: Savanna, acacia village.
  • - *
  • village_taiga: Taiga, spruce village.
  • - *
  • village_snowy: Resolute, Canada.
  • - *
- *

Feature list:

- *
    - *
  • oak
  • - *
  • oak_beehive: oak with a hive (1.15+).
  • - *
  • oak_large: oak with branches.
  • - *
  • oak_large_beehive: oak with branches and a beehive (1.15+).
  • - *
  • birch
  • - *
  • birch_large: tall variant of birch tree.
  • - *
  • shrub: low bushes that grow in jungles.
  • - *
  • shrub_acacia: low bush but configured with acacia (1.14 only)
  • - *
  • shrub_snowy: low bush with white blocks (1.14 only)
  • - *
  • jungle: a tree
  • - *
  • jungle_large: 2x2 jungle tree
  • - *
  • spruce
  • - *
  • spruce_large: 2x2 spruce tree
  • - *
  • pine: spruce with minimal leafage (1.15+)
  • - *
  • pine_large: 2x2 spruce with minimal leafage (1.15+)
  • - *
  • spruce_matchstick: see 1.15 pine (1.14 only).
  • - *
  • spruce_matchstick_large: see 1.15 pine_large (1.14 only).
  • - *
  • dark_oak
  • - *
  • acacia
  • - *
  • oak_swamp: oak with more leaves and vines.
  • - * - * - *
  • well: desert well
  • - *
  • grass: a few spots of tall grass
  • - *
  • grass_jungle: little bushier grass feature (1.14 only)
  • - *
  • lush_grass: grass with patchy ferns (1.15+)
  • - *
  • tall_grass: 2-high grass patch (1.15+)
  • - *
  • fern: a few random 2-high ferns
  • - *
  • cactus: random cacti
  • - *
  • dead_bush: a few random dead bushi
  • - *
  • fossils: underground fossils, placement little wonky
  • - *
  • mushroom_brown: large brown mushroom.
  • - *
  • mushroom_red: large red mushroom.
  • - *
  • ice_spike: ice spike. Require snow block below to place.
  • - *
  • glowstone: glowstone cluster. Required netherrack above it.
  • - *
  • melon: a patch of melons
  • - *
  • melon_pile: a pile of melons (1.15+)
  • - *
  • pumpkin: a patch of pumpkins
  • - *
  • pumpkin_pile: a pile of pumpkins (1.15+)
  • - *
  • sugarcane
  • - *
  • lilypad
  • - *
  • dungeon: Dungeon. These are hard to place, and fail often.
  • - *
  • iceberg: Iceberg. Generate at sea level.
  • - *
  • iceberg_blue: Blue ice iceberg.
  • - *
  • lake
  • - *
  • lava_lake
  • - *
  • end_island
  • - *
  • chorus: Chorus plant. Require endstone to place.
  • - *
  • sea_grass: a patch of sea grass. Require water.
  • - *
  • sea_grass_river: a variant.
  • - *
  • kelp
  • - *
  • coral_tree, coral_mushroom, coral_claw: various coral types, random color.
  • - *
  • coral: random coral structure. Require water to spawn.
  • - *
  • sea_pickle
  • - *
  • boulder: A rocky, mossy formation from a giant taiga biome. Doesn't update client properly, - * needs relogging.
  • - *
- *
- */ - public void API_AuxiliaryAspects() { this.expr.addLazyFunction("sound", -1, (c, t, lv) -> { @@ -3851,49 +2716,6 @@ public class CarpetExpression API_AuxiliaryAspects(); } - /** - *

/script scan, /script fill and /script outline commands

- *
- *

These commands can be used to evaluate an expression over an area of blocks. They all need to have specified - * the origin of the analyzed area (which is used as referenced (0,0,0), and two corners of an area to analyzed. If - * you would want that the script block coordinates refer to the actual world coordinates, use origin of 0 0 0, - * or if it doesn't matter, duplicating coordinates of one of the corners is the easiest way.

- *

These commands, unlike raw /script run are limited by vanilla fill / clone command - * limit of 32k blocks, which can be altered with carpet mod's own /carpet fillLimit command.

- *

- *

/script scan origin<x y z> corner<x y z> corner<x y z> expr

- *

Evaluates expression for each point in the area and returns number of successes (result was positive). Since - * the command by itself doesn't affect the area, the effects would be in side effects.

- *

/script fill origin<x y z> corner<x y z> corner<x y z> "expr" <block> (? replace <replacement>)

- *

Think of it as a regular fill command, that sets blocks based on whether a result of the command was successful. - * Note that the expression is in quotes. Thankfully string constants in scarpet use single quotes. Can be used - * to fill complex geometric shapes.

- *

/script outline origin<x y z> corner<x y z> corner<x y z> "expr" <block> (? replace <replacement>)

- *

Similar to fill command it evaluates an expression for each block in the area, but in this case setting blocks - * where condition was true and any of the neighbouring blocks were evaluated negatively. This allows to create surface areas, - * like sphere for example, without resorting to various rounding modes and tricks.

- *

Here is an example of seven ways to draw a sphere of radius of 32 blocks around 0 100 0:

- *
-     * /script outline 0 100 0 -40 60 -40 40 140 40 "x*x+y*y+z*z <  32*32" white_stained_glass replace air
-     * /script outline 0 100 0 -40 60 -40 40 140 40 "x*x+y*y+z*z <= 32*32" white_stained_glass replace air
-     * /script outline 0 100 0 -40 60 -40 40 140 40 "x*x+y*y+z*z <  32.5*32.5" white_stained_glass replace air
-     * /script fill    0 100 0 -40 60 -40 40 140 40 "floor(sqrt(x*x+y*y+z*z)) == 32" white_stained_glass replace air
-     * /script fill    0 100 0 -40 60 -40 40 140 40 "round(sqrt(x*x+y*y+z*z)) == 32" white_stained_glass replace air
-     * /script fill    0 100 0 -40 60 -40 40 140 40 "ceil(sqrt(x*x+y*y+z*z)) == 32" white_stained_glass replace air
-     * /draw sphere 0 100 0 32 white_stained_glass replace air
-     *
-     * fluffy ball round(sqrt(x*x+y*y+z*z)-rand(abs(y)))==32
-     *
-     * 
- *

The last method is the one that world edit is using (part of carpet mod). It turns out that the outline method with 32.5 radius, - * fill method with round function and draw command are equivalent

- *
- * @param host . - * @param x . - * @param y . - * @param z . - * @return . - */ public boolean fillAndScanCommand(ScriptHost host, int x, int y, int z) { if (CarpetServer.scriptServer.stopAll) @@ -3928,20 +2750,6 @@ public class CarpetExpression } } - /** - *

/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 the executing entity of the command. - * You will receive tab completion suggestions as you type your code suggesting functions and global variables. - * It is advisable to use /execute in ... at ... as ... run script run ... or similar, to simulate running - * commands in a different scope

- *
- * @param host . - * @param pos . - * @return . - */ public String scriptRunCommand(ScriptHost host, BlockPos pos) { if (CarpetServer.scriptServer.stopAll) @@ -3974,165 +2782,4 @@ public class CarpetExpression throw new CarpetExpressionException("Math doesn't compute... "+ae.getMessage(), null); } } - - /** - *

/script invoke / invokepoint / invokearea, /script globals commands

- *
- *

invoke family of commands provide convenient way to invoke stored procedures (i.e. functions - * that has been defined previously by any running script. To view current stored procedure set, - * run /script globals(or /script globals all to display all functions even hidden ones), - * to define a new stored procedure, just run a /script run function(a,b) -> ( ... ) - * command with your procedure once, and to forget a procedure, use undef function: - * /script run undef('function')

- *

- *

/script invoke <fun> <args?> ...

- *

Equivalent of running /script run fun(args, ...), but you get the benefit of getting the tab completion of the - * command name, and lower permission level required to run these (since player is not capable of running any custom code - * in this case, only this that has been executed before by an operator). Arguments will be checked for validity, and - * you can only pass simple values as arguments (strings, numbers, or null value). Use quotes to include - * whitespaces in argument strings.

- *

Command will check provided arguments with required arguments (count) and fail if not enough or too much arguments - * are provided. Operators defining functions are advised to use descriptive arguments names, as these will be visible - * for invokers and form the base of understanding what each argument does.

- *

invoke family of commands will tab complete any stored function that does not start with '_', - * it will still allow to run procedures starting with '_' but not suggest them, and ban execution of any - * hidden stored procedures, so ones that start with '__'. In case operator needs to use subroutines - * for convenience and don't want to expose them to the invoke callers, they can use this mechanic.

- *
-     * /script run example_function(const, phrase, price) -> print(const+' '+phrase+' '+price)
-     * /script invoke example_function pi costs 5
-     * 
- *

/script invokepoint <fun> <coords x y z> <args?> ...

- *

It is equivalent to invoke except it assumes that the first three arguments are coordinates, and provides - * coordinates tab completion, with looking at... mechanics for convenience. All other arguments are expected - * at the end

- *

/script invokearea <fun> <coords x y z> <coords x y z> <args?> ...

- *

It is equivalent to invoke except it assumes that the first three arguments are one set of ccordinates, - * followed by the second set of coordinates, providing tab completion, with looking at... mechanics for convenience, - * followed by any other required arguments

- *
- */ - - public void invokeGlobalFunctionCommand() - { - - } - - /** - *

/script load / unload <app> (global?), /script in <app> commands

- *
- *

load / unload commands allow for very conventient way of writing your code, providing it to the game - * and distribute with your worlds without the need of use of commandblocks. Just place your scarpet code in the /scripts - * folder of your world files and make sure it ends with .sc extension. The good thing about editing that - * code is that you can no only use normal editing without the need of marking of newlines, but you can also use - * comments in your code.

- *

a comment is anything that starts with a double slash, and continues to the end of the line:

- *
-     * foo = 1;
-     * //This is a comment
-     * bar = 2;
-     * // This never worked, so I commented it out
-     * // baz = foo()
-     * 
- *

- *

/script load/unload <app> (?global)

- *

Loading operation will load that script code from disk and execute it right away. You would probably use it to - * load some stored procedures to be used for later. To reload the module, just type /script load again. - * Reloading removes all the current global state (globals and functions) that were added later by the module.

- *

Loading a module, if it contains a __command() method, will attempt to registed a command with that - * app name, and register all public (no underscore) functions available in the module as subcommands. It will also - * bind specific events to the event system (check Events section for details).

- *

Loaded apps have the ability to store and load external files, especially their persistent tag state. - * For that check load_app_data and store_app_data functions.

- *

If an app defines __config method, and that method returns a map, it will be used to apply custom - * settings for this app. Currently the following options are supported:

- *
    - *
  • scope: default scope for global variables for the app, Default is 'player' which means - * that globals and defined functions will be unique for each player so that apps for each player will run in - * isolation. This is useful in tool-like applications. Another option is 'global' which shares global state for all - * runs on the server - applicable to 'block' like solutions, where custom behaviours are applied to blocks.
  • - *
  • stay_loaded: defaults to false. If true, and /carpet scriptsAutoload is turned on, - * the following apps will stay loaded after startup. Otherwise, after reading a code, and fetching the config, server - * will drop them down. This is to allow to store multiple apps on the server/world and selectively decide which one - * should be running at startup. WARNING: all apps will run once at startup anyways, so be aware that their actions - * that are called statically, will be performed once anyways.
  • - *
- *

Unloading an app removes all of its state from the game, disables commands, and removes bounded events, - * and saves its global state. If more - * cleanup is needed, one can define an __on_close() function which will be executed when the module is unloaded, - * or server is closing, or crashing. However, there is no need to do that explicitly for the things mentioned - * in the previous statement.

- *

Scripts can be loaded in shared(global) and player mode. Default is player, so all globals and stored functions are - * individual for each player, meaning scripts don't need to worry about making sure they store some intermittent data - * for each player independently. In global mode - all global values and stored functions are shared among all players. - * To access specific player data with commandblocks, use /execute as (player) run script in (app) run ... - * To access global/server state, you need to disown the command from any player, so use a commandblock, or any arbitrary - * entity: /execute as @e[type=bat,limit=1] run script in (module) globals for instance. - *

- *

/script in <app> ...

- *

Allows to run normal /script commands in a specific app, like run, invoke,..., globals etc...

- *
- */ - public void loadScriptsFromFilesCommand() - { - - } - - /** - *

Scarpet events system

- *
- *

Provides the ability to execute specific function whenever an event occurs. The functions to be registered - * need to conform with the arguments to the event specification. When loading module functions, each function that - * starts with __on_... and has the required arguments, will be bound automatically. - * In case of player specific modules, all player action events will be directed to the appropriate player space, and - * all tick events will be executed in the global context, so its not a good idea to mix these two, so use either of these, - * or use commands to call tick events directly, or handle player specific data inside a module. In near future using - * global events (on_tick, on_chunk_generated) will be not allowed in player scoped apps.

- *

- *

Event list

- *

Here is a list of events that can be handled by scarpet. This list includes prefixes required by modules to - * autoload them, but you can add any function to any event if it accepts required number of parameters:

- *
-     * __on_tick()         // can access blocks and entities in the overworld
-     * __on_tick_nether()  // can access blocks and entities in the nether
-     * __on_tick_ender()   // can access blocks and entities in the end
-     * __on_chunk_generated(x,z) // called after a chunk is promoted to the full chunk,
-     *                           // prodiving lowest x and z coords in the chunk
-     *                           // event will not work with optifine installed in the game
-     * // player specific callbacks
-     * __on_player_uses_item(player, item_tuple, hand)  // right click action
-     * __on_player_releases_item(player, item_tuple, hand)  // client action (e.g. bow)
-     * __on_player_finishes_using_item(player, item_tuple, hand))  // server action (e.g. food), called item is from before it is used.
-     * __on_player_clicks_block(player, block, face)  // left click attack on a block
-     * __on_player_breaks_block(player, block) // called after block is broken (the caller receives previous blockstate)
-     * __on_player_right_clicks_block(player, item_tuple, hand, block, face, hitvec)  // player right clicks block with anything
-     * __on_player_interacts_with_block(player, hand, block, face, hitvec)  //right click on a block resulted in activation of said block
-     * __on_player_places_block(player, item_tuple, hand, block) // player have just placed the block.
-     * __on_player_interacts_with_entity(player, entity, hand)
-     * __on_player_attacks_entity(player, entity)
-     * __on_player_rides(player, forward, strafe, jumping, sneaking)
-     * __on_player_jumps(player)
-     * __on_player_deploys_elytra(player)
-     * __on_player_wakes_up(player)
-     * __on_player_starts_sneaking(player)
-     * __on_player_stops_sneaking(player)
-     * __on_player_starts_sprinting(player)
-     * __on_player_stops_sprinting(player)
-     * __on_player_drops_item(player)
-     * __on_player_drops_stack(player)
-     *
-     * __on_statistic(player, category, event, value) // player statistic changes
-     * 
- *

/script event command

- *

used to display current events and bounded functions. use add_to ro register new event, or remove_from - * to unbind a specific function from an event. Function to be bounded to an event needs to have the same - * number of parameters as the action is attempting to bind to (see list above). All calls in modules - * loaded via /script load that have functions listed above will be automatically bounded and unbounded when - * script is unloaded.

- *
- */ - public void gameEventsSystem() - { - - } } diff --git a/src/main/java/carpet/script/Expression.java b/src/main/java/carpet/script/Expression.java index 13765769..b5175f4a 100644 --- a/src/main/java/carpet/script/Expression.java +++ b/src/main/java/carpet/script/Expression.java @@ -63,279 +63,6 @@ import static java.lang.Math.abs; import static java.lang.Math.max; import static java.lang.Math.min; - -/** - *

Fundamental components of scarpet programming language (towards version 1.7).

- * - *

Scarpet (a.k.a. Carpet Script, or Script for Carpet) is a programming language designed to provide - * the ability to write custom programs to run within Minecraft and - * interact with the world.

- * - *

The project was initially built based on the EvalEx project, - * however it now diverged so far away from the original implementation, - * it would be hard to tell it without this mention. - * EvalEx is a handy expression evaluator for Java, that - * allows to evaluate simple mathematical and boolean expressions. - * For more information, see: - * EvalEx GitHub - * repository

- * - *

This specification is divided into two sections: this one is agnostic - * to any Minecraft related features and could function on its own, and CarpetExpression for - * Minecraft specific routines and world manipulation functions.

- * - *

Synopsis

- * - *
- * script run print('Hello World!')
- * 
- *

or an OVERLY complex example:

- *
- * /script run
- *     block_check(x1, y1, z1, x2, y2, z2, block_to_check) ->
- *     (
- *         l(minx, maxx) = sort(l(x1, x2));
- *         l(miny, maxy) = sort(l(y1, y2));
- *         l(minz, maxz) = sort(l(z1, z2));
- *         'Need to compute the size of the area of course';
- *         'Cause this language doesn\'t support comments in the command mode';
- *         xsize = maxx - minx + 1;
- *         ysize = maxy - miny + 1;
- *         zsize = maxz - minz + 1;
- *         total_count = 0;
- *         loop(xsize,
- *             xx = minx + _ ;
- *             loop(ysize,
- *                 yy = miny + _ ;
- *                 loop(zsize,
- *                     zz = minz + _ ;
- *                     if ( block(xx,yy,zz) == block_to_check,
- *                         total_count += ceil(rand(1))
- *                     )
- *                 )
- *             )
- *         );
- *         total_count
- *     );
- *     check_area_around_closest_player_for_block(block_to_check) ->
- *     (
- *         closest_player = player();
- *         l(posx, posy, posz) = query(closest_player, 'pos');
- *         total_count = block_check( posx-8,1,posz-8, posx+8,17,posz+8, block_to_check);
- *         print('There is '+total_count+' of '+block_to_check+' around you')
- *     )
- *
- * /script invoke check_area_around_closest_player_for_block 'diamond_ore'
- *
- * 
- *

or simply

- *
- * script run print('There is'+for(rect(x,9,z,8,8,8), _ == 'diamond_ore')+' diamond ore around you')
- * 
- *

It definitely pays to check what higher level scarpet functions have to offer.

- *

Programs

- *

- * You can think of an program like a mathematical expression, like - * "2.4*sin(45)/(2-4)" or "sin(y)>0 & max(z, 3)>3" - * Writing a program, is like writing a 2+3, just a bit longer

- * - *

Basic language components

- *

Programs consist of constants, like 2, 3.14, pi, or 'foo', - * operators like +, /, ->, variables which you can define, like foo - * or special ones that will be defined for you, like _x, or _ , which I specific to - * a each built in function, and functions with name, and arguments in the form of f(a,b,c), where - * f is the function name, and a, b, c are the arguments which can be any other expression. - * And that's all the parts of the language, so all in all - sounds quite simple.

- * - *

Code flow

- *

- * Like any other proper programming language, scarpet needs brackets, basically to identify - * where stuff begins and where it ends. In the languages that uses much more complicated constructs, like Java, - * they tend to use all sort of them, round ones to indicate function calls, curly to indicate section of code, - * square to access lists, pointy for generic types etc... I mean - there is no etc, cause they have exhausted - * all the bracket options... - *

- *

Scarpet is different, since it runs everything based on functions (although its not per se a functional language like lisp) - * only needs the round brackets for everything, and it is up to the programmer to organize its code so its readable, - * as adding more brackets does not have any effect on the performance of the programs as they are compiled before they are executed. - * Look at the following example usage of if() function: - *

- *
- * if(x<y+6,set(x,8+y,z,'air');plop(x,top('surface',x,z),z,'birch'),sin(query(player(),'yaw'))>0.5,plop(0,0,0,'boulder'),particle('fire',x,y,z))
- * 
- *

Would you prefer to read

- *
- * if(   x<y+6,
- *            set(x,8+y,z,'air');
- *            plop(x,top('surface',x,z),z,'birch'),
- *       sin(query(player(),'yaw'))>0.5,
- *            plop(0,0,0,'boulder'),
- *       particle('fire',x,y,z)
- * )
- * 
- *

Or rather:

- *
- * if
- * (
- *     x<y+6,
- *     (
- *         set(x,8+y,z,'air');
- *         plop(x,top('surface',x,z),z,'birch')
- *     ),
- *
- *     sin(query(player(),'yaw'))>0.5,
- *     (
- *         plop(0,0,0,'boulder')
- *     ),
- *
- *     particle('fire',x,y,z)
- * )
- * 
- *

Whichever style you prefer it doesn't matter. It typically depends on the situation and the complexity of the - * subcomponents. No matter how many whitespaces and extra brackets you add - the code will evaluate to exactly the - * same expression, and will run exactly the same, so make sure your programs are nice and clean so others don't - * have problems with them

- * - *

Functions and scoping

- *

- * Users can define functions in the form fun(args....) -> expression and they are compiled and saved - * for further execution in this but also subsequent calls of /script command. Functions can also be assigned to variables, - * passed as arguments, called with call function, but in most cases you would want to call them directly - * by name, in the form of fun(args...). - * This means that once defined functions - * are saved with the world for further use. For variables, there are two types of them, - * global - which are shared anywhere in the code, and those are all which name starts with 'global_', and - * local variables which is everything else and those are only visible inside each function. - * This also means that all the parameters in functions are passed 'by value', not 'by reference'. - *

- * - *

Outer variables

- *

Functions can still 'borrow' variables from the outer scope, - * by adding them to the function signature wrapped around built-in function outer. - * It adds the specified value to the function call stack so they behave exactly like capturing lambdas in Java, but - * unlike java captured variables don't need to be final. Scarpet will just attach their new values at the time of the - * function definition, even if they change later. Most value will be copied, but mutable values, like maps or lists, allow - * to keep the 'state' with the function, allowing them to have memory and act like objects so to speak. - * . Check outer(var) for details.

- * - * - *

Code delivery, line indicators

- *

Note that this should only apply to pasting your code to execute with commandblock. Scarpet recommends placing your - * code in apps (files with .sc extension that can be placed inside "/scripts" folder in the world files - * and loaded as a scarpet app with command /script load [app_name]. Scarpet apps loaded from disk should only - * contain code, no need to start with "/script run" prefix

- *

The following is the code that could be provided in a foo.sc app file located in world /scripts folder

- * - *
- * run_program() -> (
- *   loop( 10,
- *     // looping 10 times
- *     // comments are allowed in scripts located in world files
- *     // since we can tell where that line ends
- *     foo = floor(rand(10));
- *     check_not_zero(foo);
- *     print(_+' - foo: '+foo);
- *     print('  reciprocal: '+  _/foo )
- *   )
- * );
- * check_not_zero(foo) -> (
- *   if (foo==0, foo = 1)
- * )
- * 
- *

Which we then call in-game with:

- *
- *     /script load foo
- *     /script in foo invoke run_program
- * 
- *

However the following code can also be input as a command, or in a command block.

- * - *

Since the maximum command that can be input to the chat is limited in length, you will be probably inserting your - * programs by pasting them to command blocks or reading from world files, however pasting to command blocks will remove some whitespaces and squish - * your newlines making the code not readable. If you are pasting a program that is perfect and will never cause an error, - * I salute you, but for the most part it is quite likely that your program might break, either at compile time, when - * its initially analyzed, or at execute time, when you suddenly attempt to divide something by zero. In these cases - * you would want to get a meaningful error message, but for that you would need to indicate for the compiler where - * did you put these new lines, since command block would squish them. For that, place at the beginning - * of the line to let the compiler know where are you. This makes so that $ is the only character that is - * illegal in programs, since it will be replaced with new lines. As far as I know, $ is not used - * anywhere inside Minecraft identifiers, so this shouldn't hinder the abilities of your programs.

- *

Consider the following program executed as command block command:

- *
- * /script run
- * run_program() -> (
- *   loop( 10,
- *     foo = floor(rand(_));
- *     check_not_zero(foo);
- *     print(_+' - foo: '+foo);
- *     print('  reciprocal: '+  _/foo )
- *   )
- * );
- * check_not_zero(foo) -> (
- *    if (foo==0, foo = 1)
- * )
- *
- * 
- *

Lets say that the intention was to check if the bar is zero and prevent division by zero in print, - * but because the foo is passed as a variable, it never changes the original foo value. - * Because of the inevitable division by zero, we get the following message: - *

- *
- * Your math is wrong, Incorrect number format for NaN at pos 98
- * run_program() -> ( loop( 10, foo = floor(rand(_)); check_not_zero(foo); print(_+' - foo: '+foo);
- * HERE>> print(' reciprocal: '+ _/foo ) ));check_not_zero(foo) -> ( if (foo==0, foo = 1))
- * 
- * - * As we can see, we got our problem where the result of the mathematical operation was not a number (infinity, so not a number), - * however by pasting our program - * into the command made it squish the newlines so while it is clear where the error happened and we still can track the error down, - * the position of the error (98) is not very helpful and wouldn't be useful if the program gets significantly longer. - * To combat this issue we can precede every line of the script with dollar signs $: - *
- * /script run
- * $run_program() -> (
- * $  loop( 10,
- * $    foo = floor(rand(_));
- * $    check_not_zero(foo);
- * $    print(_+' - foo: '+foo);
- * $    print('  reciprocal: '+  _/foo )
- * $  )
- * $);
- * $check_not_zero(foo) -> (
- * $   if (foo==0, foo = 1)
- * $)
- * 
- * - *

Then we get the following error message

- * - *
- * Your math is wrong, Incorrect number format for NaN at line 7, pos 2
- *   print(_+' - foo: '+foo);
- *    HERE>> print(' reciprocal: '+ _/foo )
- *   )
- * 
- * - * - *

As we can note not only we get much more concise snippet, but also information about the line - * number and position, so means its way easier to locate the potential problems problem

- * - *

Obviously that's not the way we intended this program to work. To get it foo modified via - * a function call, we would either return it as a result and assign it to the new variable: - *

- *
- *     foo = check_not_zero(foo);
- *     ...
- *     check_not_zero(foo) -> if(foo == 0, 1, foo)
- * 
- *

.. or convert it to a global variable, which in this case passing as an argument is not required

- *
- *     global_foo = floor(rand(10));
- *     check_foo_not_zero();
- *     ...
- *     check_foo_not_zero() -> if(global_foo == 0, global_foo = 1)
- * 
- *

- */ - public class Expression { private static final Map precedence = new HashMap() {{ @@ -684,181 +411,6 @@ public class Expression return result; } - /** - *

Variables and Constants

- *
- *

scarpet provides a number of constants that can be used literally in scripts

- *
    - *
  • null: nothing, zilch, not even false
  • - *
  • true: pure true, or just 1 (one)
  • - *
  • false: false truth, or true falsth, 0 (zero) actually
  • - *
  • pi: for the fans of perimeters, its a perimeter of an apple pi of diameter 1. About 3.14
  • - *
  • euler: clever guy. Derivative of its exponent is goto 1. About 2.72
  • - *
- *

Apart from that, there is a bunch of system variables, that start with _ that are set by - * scarpet built-ins, like _, typically each consecutive value in loops, - * _i indicating iteration, or _a like an accumulator for reduce - * function. Certain calls to Minecraft specific calls would also set _x, - * _y, _z, indicating block positions. All variables starting with - * _ are read-only, and cannot be declared and modified in client code.

- * - *

Literals

- *

scarpet accepts numeric and string liters constants. - * Numbers look like 1, 2.5, -3e-7, 0xff, and are internally represented as Java's double - * but scarpet will try to trim trailing zeros as much as possible so if you need to use them as intergers, - * you can. Strings using 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 for example, - * or when typing in jsons inside scarpet to feed back into a /data merge command for example. Strings also - * use backslashes \ for quoting special characters, in both plain strings and regular expressions

- *
-     * 'foo'
-     * print('This doesn\'t work')
-     * nbt ~ '\\.foo'   // matching '.' as a '.', not 'any character match'
-     * 
- *
- */ - - public void VariablesAndConstants() // public just to get the Javadocs right - { - // all declared as global variables to save on switching scope cost - } - - /** - *

User-defined functions and program control flow

- *
- *

Writing programs with more than 1 line

- *

Operator ;

- *

To effectively write programs that have more than one line, a programmer needs way to specify a sequence of - * commands that execute one after another. In scarpet this can be achieved with ;. Its an operator, - * and by separating statements with semicolons. And since whitespaces and $ signs are all treats as - * whitespaces, how you layout your code doesn't matter, as long as it is readable to everyone involved.

- *
-     * expr;
-     * expr;
-     * expr;
-     * expr
-     * 
- *

Notice that the last expression is not followed by a semicolon. Since instruction separation is functional - * in scarpet, and not barely an instruction delimiter, terminating the code with a dangling operator - * wouldn't be valid. Having said that, since many programming languages don't care about the number of op terminators - * programmers use, carpet preprocessor will remove all unnecessary semicolons from scripts when compiled.

- *

In general expr; expr; expr; expr is equivalent to - * (((expr ; expr) ; expr) ; expr).

- *

Result of the evaluated expression is the same as the result of the second expression, but first expression is - * also evaluated for sideeffects

- *
-     * expr1 ; expr2 => expr2  // with expr1 as a sideeffect
-     * 
- *

Global variables

- *

All defined functions are compiled, stored persistently, and available globally - - * accessible to all other scripts. Functions can only be undefined via call to undef('fun'), which - * would erase global entry for function fun. Since all variables have local scope inside each function, - * one way to share large objects is via global variables - *

- *

Any variable that is used with a name that starts with 'global_' will be stored and accessible globally, - * not, inside current scope. It will also persist across scripts, so if a procedure needs to use its own construct, it needs to - * define it, or initialize it explicitly, or undefine it via undef

- *
-     * a() -> global_list+=1; global_list = l(1,2,3); a(); a(); global_list  // => [1,2,3,1,1]
-     * 
- *

Operator ->

- *

To organize code better than a flat sequence of operations, one can define functions. Definition is correct if - * has the following form

- *
-     *     fun(args, ...) -> expr
-     * 
- *

Where fun(args, ...) is a function signature indicating function name, number of arguments, - * and their names, and expr is an expression (can be complex) that is evaluated when fun is called. - * Names in the signature don't need to be used anywhere else, other occurrences of these names - * will be masked in this function scope. - * Function call creates new scope for variables inside expr, so all non-global variables are not - * visible from the caller scope. All parameters are passed by value to the new scope, including lists and other - * containers, however their copy will be shallow.

- *

The function returns its name as a string, which means it can be used to call it later with the call function

- *

Using _ as the function name creates anonymous function, so each time _ function is defined, - * it will be given a unique name, which you can pass somewhere else to get this function called.

- *
-     * a(lst) -> lst+=1; list = l(1,2,3); a(list); a(list); list  // => [1,2,3]
-     * 
- *

In case the inner function wants to operate and modify larger objects, lists from the outer - * scope, but not global, it needs to use outer function in function signature

- *

outer(arg)

- *

outer function can only be used in the function signature, and it will - * cause an error everywhere else. It saves the value of that variable from the outer scope and allows - * its use in the inner scope. This is a similar behaviour to using outer variables in lambda function definitions - * from Java, except here you have to specify which variables you want to use, and borrow

- *

This mechanism can be used to use static mutable objects without the need of using global_... variables

- *
-     * list = l(1,2,3); a(outer(list)) -> list+=1;  a(); a(); list  // => [1,2,3,1,1]
-     * 
- *

The return value of a function is the value of the last expression. This as the same effect as using outer - * or global lists, but is more expensive

- *
-     * a(lst) -> lst+=1; list = l(1,2,3); list=a(list); list=a(list); list  // => [1,2,3,1,1]
-     * 
- *

Ability to combine more statements into one expression, with functions, passing parameters, and global and outer - * scoping allow to organize even larger scripts

- *

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, 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. - *

- *

call(function, args.....)

- *

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 function, they can be defined in place as anonymous functions.

- *

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

- *
-     * my_map(list, function) -> map(list, call(function, _));
-     * my_map(l(1,2,3), _(x) -> x*x);    // => [1,4,9]
-     *
-     * profile_expr(my_map(l(1,2,3), _(x) -> x*x));   // => ~32000
-     * sq(x) -> x*x; profile_expr(my_map(l(1,2,3), 'sq'));   // => ~36000
-     * sq = (_(x) -> x*x); profile_expr(my_map(l(1,2,3), sq));   // => ~36000
-     * profile_expr(map(l(1,2,3), _*_));   // => ~80000
-     *
-     * 
- *

Control flow

- *

return(expr?)

- *

Sometimes its convenient to break the organized control flow, or it is not practical to pass - * the final result value of a function to the last statement, in this case a return statement can be used

- *

If no argument is provided - returns null value.

- *
-     * def() -> (
-     *  expr1;
-     *  expr2;
-     *  return(expr3); // function terminates returning expr3
-     *  expr4;     // skipped
-     *  expr5      // skipped
-     * )
-     * 
- *

In general its cheaper to leave the last expression as a return value, rather than calling returns everywhere, - * but it would often lead to a messy code.

- *

exit(expr?)

- *

It terminates entire program passing expr as the result of the program execution, or null if omitted.

- *

try(expr, catch_expr(_)?) ... throw(value?)

- *

try function evaluates expression, and continues further unless throw function is called - * anywhere inside expr. In that case the catch_expr is evaluates with _ set - * to the argument throw was called with. - * This mechanic accepts skipping thrown value - it throws null instead, and catch expression - then try returns null as well - * This mechanism allows to terminate large portion of a convoluted - * call stack and continue program execution. There is only one level of exceptions currently in carpet, so if the inner - * function also defines the try catchment area, it will received the exception first, but it can technically - * rethrow the value its getting for the outer scope. Unhandled throw acts like an exit statement.

- *

if(cond, expr, cond?, expr?, ..., default?)

- *

If statement is a function that takes a number of conditions that are evaluated one after another and if - * any of them turns out true, its expr gets returned, otherwise, if all conditions fail, the return value is - * default expression, or null if default is skipped

- *

if function is equivalent to if (cond) expr; else if (cond) expr; else default; - * from Java, just in a functional form

- *
- */ - public void UserDefinedFunctionsAndControlFlow() // public just to get the javadoc right { // artificial construct to handle user defined functions and function definitions @@ -991,210 +543,6 @@ public class Expression }); } - /** - *

Operators

- *
- * - *

There is a number of operators you can use inside the expressions. Those could be considered - * generic type operators that apply to most data types. They also follow standard operator - * precedence, i.e. 2+2*2 is understood as 2+(2*2), - * not (2+2)*2, otherwise they are applied from left to right, i.e. - * 2+4-3 is interpreted as (2+4)-3, which in case of numbers - * doesn't matter, but since scarpet allows for mixing all value types - * the associativity would matter, and may lead to unintended effects:

- *

Important operator is function definition -> operator. It will be covered - * in {@link carpet.script.Expression#UserDefinedFunctionsAndControlFlow}

- *
-     * '123'+4-2 => ('123'+4)-2 => '1234'-2 => '134'
-     * '123'+(4-2) => '123'+2 => '1232'
-     * 3*'foo' => 'foofoofoo'
-     * 1357-5 => 1352
-     * 1357-'5' => 137
-     * 3*'foo'-'o' => 'fff'
-     * l(1,3,5)+7 => l(8,10,12)
-     * 
- *

As you can see, values can behave differently when mixed with other types - * in the same expression. in case values are of the same types, the result - * tends to be obvious, but Scarpet tries to make sense of whatever it - * has to deal with

- *

Operator Precedence

- *

- * Here is the complete list of operators in scarpet including control flow operators. - * Note, that commas and brackets are not technically operators, but part of the language, - * even if they look like them: - *

- *
    - *
  • Match, Get ~ :
  • - *
  • Unary + - !
  • - *
  • Exponent ^
  • - *
  • Multiplication * / %
  • - *
  • Addition + -
  • - *
  • Comparison == != > >= <= <
  • - *
  • Logical And&&
  • - *
  • Logical Or ||
  • - *
  • Assignment = += <>
  • - *
  • Definition ->
  • - *
  • Next statement;
  • - *
  • Comma ,
  • - *
  • Bracket ( )
  • - *
- * - *

Get, Accessor Operator :

- *

Operator version of the get(...) function to access elements of lists, maps, and potentially other - * containers (i.e. NBTs). It is important to distinguish from ~ operator, which is a matching operator, - * which is expected to perform some extra computations to retrieve the result, while : should be - * straightforward and immediate, and the source object should behave like a container and support full container - * API, meaning get(...), put(...), delete(...), - * and has(...) functions

- *

For certain operators and functions (get, put, delete, has, =, +=) objects can use : annotated - * fields as l-values, meaning construct like foo:0 = 5, would act like put(foo, 0, 5), - * rather than get(foo, 0) = 5, which would result in an error.

- *

TODO: add more information about l-value behaviour.

- * - *

Matching Operator ~

- *

This operator should be understood as 'matches', 'contains', 'is_in', - * or 'find me some stuff about something else. For strings it matches the right operand as a regular - * expression to the left, returning the first match. This can be used to extract information from unparsed nbt's - * in a more convoluted way (use get(...) for more appropriate way of doing it). - * For lists it checks if an element is in the list, and returns the index of that element, - * or null if no such element was found, especially that the use of first(...) function will not - * return the index. Currently it doesn't have any special behaviour for numbers - it checks for existence of characters - * in string representation of the left operand with respect of the regular expression on the right hand side. - * string

- *

in Minecraft API portion entity ~ feature is a shortcode for query(entity,feature) - * for queries that do not take any extra arguments.

- *
-     * l(1,2,3) ~ 2  => 1
-     * l(1,2,3) ~ 4  => null
-     * 'foobar' ~ '.b'  => 'ob'
-     * player('*') ~ 'gnembon'  // null unless player gnembon is logged in (better to use player('gnembon') instead
-     * p ~ 'sneaking' // if p is an entity returns whether p is sneaking
-     * 
- *

Or a longer example of an ineffective way to searching for a squid

- *
-     * entities = entities_area('all',x,y,z,100,10,100);
-     * sid = entities ~ 'Squid';
-     * if(sid != null, run('execute as '+query(get(entities,sid),'id')+' run say I am here '+query(get(entities,sid),'pos') ) )
-     * 
- *

Or an example to find if a player has specific enchantment on a held axe (either hand) and get its level - * (not using proper NBTs query support via get(...)):

- *
-     * global_get_enchantment(p, ench) -> (
-     * $   for(l('main','offhand'),
-     * $      holds = query(p, 'holds', _);
-     * $      if( holds,
-     * $         l(what, count, nbt) = holds;
-     * $         if( what ~ '_axe' && nbt ~ ench,
-     * $            lvl = max(lvl, number(nbt ~ '(?<=lvl:)\\d') )
-     * $         )
-     * $      )
-     * $   );
-     * $   lvl
-     * $);
-     * /script run global_get_enchantment(players(), 'sharpness')
-     * 
- * - *

Basic Arithmetic Operators + - * /

- *

Allows to add the results of two expressions. If the operands resolve to numbers, the result is - * arithmetic operation. - * In case of strings, adding or subtracting from a string results in string concatenation and - * removal of substrings from that string. Multiplication of strings and numbers results in repeating the - * string N times and division results in taking the first k'th part of the string, so that str*n/n ~ str - * In case first operand is a list, either it results in a new list - * with all elements modified one by one with the other operand, or if the operand is a list with the same number of - * items - elementwise addition/subtraction

- *

Examples:

- *
-     * 2+3 => 5
-     * 'foo'+3+2 => 'foo32'
-     * 'foo'+(3+2) => 'foo5'
-     * 3+2+'bar' => '5bar'
-     * 'foo'*3 => 'foofoofoo'
-     * 'foofoofoo' / 3 => 'foo'
-     * 'foofoofoo'-'o' => 'fff'
-     * l(1,2,3)+1  => l(2,3,4)
-     * b = l(100,63,100); b+l(10,0,10)  => l(110,63,110)
-     * 
- * - *

Just Operators % ^

- *

The modulo and exponent (power) operators work only if both operands are numbers

- *
-     * pi^pi%euler  => 1.124....
-     * -9 % 4  => -1
-     * 9 % -4  => 0 ¯\_(ツ)_/¯ Java
-     * -3 ^ 2  => 9
-     * -3 ^ pi => // Error
-     * 
- * - *

Comparison Operators == != < > <= >=

- *

Allows to compare the results of two expressions. - * For numbers it is considers arithmetic order of numbers, for strings - lexicographical, - * nulls are always 'less' than everything else, and lists check their elements - if the sizes - * are different, the size matters, otherwise, pairwise comparisons for each elements are performed. - * The same order rules than with all these operators are used with the default sortographical order as - * used by sort function. All of these are true: - *

- *
-     * null == null
-     * null != false
-     * 0 == false
-     * 1 == true
-     * null < 0
-     * null < -1000
-     * 1000 < 'a'
-     * 'bar' < 'foo'
-     * 3 == 3.0
-     * 
- * - *

Logical Operators && ||

- *

These operator compute respective boolean operation on the operands. What it important is that if calculating - * of the second operand is not necessary, it won't be evaluated, which means one can use them as conditional - * statements. In case of success returns first positive operand (||) or last one (&&).

- *
-     * true || false  => 1
-     * null || false => 0
-     * null != false || run('kill gnembon')  => 1 // gnembon survives
-     * null != false && run('kill gnembon')  => 0 // when cheats not allowed
-     * null != false && run('kill gnembon')  => 1 // gnembon dies, cheats allowed
-     * 
- * - - *

Assignment Operators = <> +=

- *

A set of assignment operators. All require bounded variable on the LHS, <> requires - * bounded arguments on the right hand side as well (bounded, meaning being variables). Additionally they can also - * handle list constructors with all bounded variables, and work then as list assignment operators. - * When += is used on a list, it extends that list of that element, and returns the list (old == new). - * scarpet doesn't support currently removal of items. Removal of items can be obtaine via - * filter command, and reassigning it fo the same variable. Both operations would require rewriting of the - * array anyways.

- *
-     * a = 5  => a == 5
-     * l(a,b,c) = l(3,4,5) => a==3, b==4, c==5
-     * l(minx,maxx) = sort(xi,xj);  // minx assumes min(xi, xj) and maxx, max(xi, xj)
-     * l(a,b,c,d,e,f) = l(range(6)); l(a,b,c) <> l(d,e,f); l(a,b,c,d,e,f)  => [3,4,5,0,1,2]
-     * a = l(1,2,3); a += 4  => [1,2,3,4]
-     * a = l(1,2,3,4); a = filter(a,_!=2)  => [1,3,4]
-     * 
- *

Unary Operators - +

- *

Require a number, flips the sign. One way to assert it's a number is by crashing the script. gg.

- *
-     * -4  => -4
-     * +4  => 4
-     * +'4'  // Error message
-     * 
- * - *

Negation Operator !

- *

flips boolean condition of the expression. Equivalent of bool(expr)==false

- *
-     * !true  => 0
-     * !false  => 1
-     * !null  => 1
-     * !5  => 0
-     * !l() => 1
-     * !l(null) => 0
-     * 
- *
- */ public void Operators() { addBinaryOperator("+", precedence.get("addition+-"), true, Value::add); @@ -1370,84 +718,6 @@ public class Expression } - /** - *

Arithmetic operations

- *
- *

Basic Arithmetic Functions

- *

There is bunch of them - they require a number and spit out a number, - * doing what you would expect them to do.

- *

fact(n)

- *

Factorial of a number, a.k.a n!, just not in scarpet. Gets big... quick...

- *

sqrt(n)

- *

Square root. For other fancy roots, use ^, math and yo noggin. Imagine square roots on a tree...

- *

abs(n)

- *

Absolut value.

- *

round(n)

- *

Closest integer value. Did you know the earth is also round?

- *

floor(n)

- *

Highest integer that is still no larger then n. Insert a floor pun here.

- *

ceil(n)

- *

First lucky integer that is not smalller than n. As you would expect, ceiling is typically - * right above the floor.

- *

ln(n)

- *

Natural logarithm of n. Naturally.

- *

ln1p(n)

- *

Natural logarithm of n+1. Very optimistic.

- *

log10(n)

- *

Decimal logarithm of n. Its ceiling is the length of its floor.

- *

log(n)

- *

Binary logarithm of n. Finally, a proper one, not like the previous 11.

- *

log1p(n)

- *

Binary logarithm of n+1. Also always positive.

- *

mandelbrot(a, b, limit)

- *

Computes the value of the mandelbrot set, for set a and b spot. - * Spot the beetle. Why not.

- *

min(arg, ...), min(list), max(arg, ...), max(list)

- *

Compute minimum or maximum of supplied arguments assuming default sorthoraphical order. In case you are - * missing argmax, just use a ~ max(a), little less efficient, but still fun. - *

- *

- * Interesting bit - min and max don't remove variable associations from arguments, which - * means can be used as LHS of assignments (obvious case), or argument spec in function definitions (far less obvious). - *

- *
-     * a = 1; b = 2; min(a,b) = 3; l(a,b)  => [3, 2]
-     * a = 1; b = 2; fun(x, min(a,b)) -> l(a,b); fun(3,5)  => [5, 0]
-     * 
- *

Absolutely no idea, how the latter might be useful in practice. But since it compiles, can ship it.

- * - *

relu(n)

- *

Linear rectifier of n. 0 below 0, n above. Why not. max(0,n) - * with less moral repercussions.

- *

Trigonometric / Geometric Functions

- *

sin(x)

- *

cos(x)

- *

tan(x)

- *

asin(x)

- *

acos(x)

- *

atan(x)

- *

atan2(x,y)

- *

sinh(x)

- *

cosh(x)

- *

tanh(x)

- *

sec(x)

- *

csc(x)

- *

sech(x)

- *

csch(x)

- *

cot(x)

- *

acot(x)

- *

coth(x)

- *

asinh(x)

- *

acosh(x)

- *

atanh(x)

- *

rad(deg)

- *

deg(rad)

- *

Use as you wish

- * - *
- * - */ - public void ArithmeticOperations() { addLazyFunction("not", 1, (c, t, lv) -> lv.get(0).evalValue(c, Context.BOOLEAN).getBoolean() ? ((cc, tt) -> Value.FALSE) : ((cc, tt) -> Value.TRUE)); @@ -1549,127 +819,6 @@ public class Expression addUnaryFunction("relu", (v) -> v.compareTo(Value.ZERO) < 0 ? Value.ZERO : v); } - /** - *

Loops, and higher order functions

- *
- *

Efficient use of these functions can greatly simplify your programs and speed them up, as these functions - * will internalize most of the operations that need to be applied on multiple values at the same time. Most - * of them take a list argument which can be any iterable structure in scarpet, including - * generators, like rect, or range, and maps, where the iterator returns all the map keys

- *

Loops

- * - *

break(), break(expr), continue(), continue(expr)

- *

These allow to control execution of a loop either skipping current iteration code, using continue, or finishing - * the current loop, using break. break and continue can only be used inside - * for, while, loop, map, filter and reduce functions, while break can be used in first as well. - * Outside of the internal expressions of these functions, calling break or continue will cause an - * error. In case of the nexted loops, and more complex setups, use custom try and throw setup.

- *

Please check corresponding loop function description what continue and break do in their contexts, - * but in general case, passed values to break and continue will be used in place of the return value of the - * internal iteration expression.

- * - * - *

for(list,expr(_,_i))

- *

Evaluates expression over list of items from the list. Supplies - * _(value) and _i(iteration number) to the expr.

- *

Returns the number of times expr was successful. Uses continue and break argument - * in place of the returned value from the expr(if supplied), to determine if the iteration was successful.

- *
-     *     check_prime(n) -> !first( range(2, sqrt(n)+1), !(n % _) );
-     *     for(range(1000000,1100000),check_prime(_))  => 7216
-     * 
- *

From which we can learn that there is 7216 primes between 1M and 1.1M

- * - *

while(cond, limit)

- *

Evaluates expression expr repeatedly until condition cond becomes false, - * but not more than limit times. Returns the result of the last expr evaluation, - * or null if nothing was successful. Both expr and cond will recveived a - * bound variable _ indicating current iteration, so its a number.

- *
-     *  while(a<100,10,a=_*_)  => 81 // loop exhausted via limit
-     *  while(a<100,20,a=_*_)  => 100 // loop stopped at condition, but a has already been assigned
-     *  while(_*_<100,20,a=_*_)  => 81 // loop stopped at condition, before a was assigned a value
-     * 
- * - *

loop(num,expr(_),exit(_)?)

- *

Evaluates expression expr, num number of times. - *expr receives _ system variable indicating the iteration.

- *
-     *     loop(5, game_tick())  => repeat tick 5 times
-     *     list = l(); loop(5, x = _; loop(5, list += l(x, _) ) ); list
-     *       // double loop, produces: [[0, 0], [0, 1], [0, 2], [0, 3], [0, 4], [1, 0], [1, 1], ... , [4, 2], [4, 3], [4, 4]]
-     * 
- *

In this small example we will search for first 10 primes, apparently including 0:

- *
-     *     check_prime(n) -> !first( range(2, sqrt(n)+1), !(n % _) );
-     *     primes = l();
-     *     loop(10000, if(check_prime(_), primes += _ ; if (length(primes) >= 10, break())));
-     *     primes
-     *
-     *     // outputs: [0, 1, 2, 3, 5, 7, 11, 13, 17, 19]
-     * 
- * - *

Higher Order Functions

- *

map(list,expr(_,_i))

- *

Converts a list of values, to another list where each value is result of an expression - * v = expr(_, _i) where _ is passed as each element of the list, and _i is - * the index of such element. If break is called the map returns whatever collected thus far. If - * continue and break are used with supplied argument, it is used in place of the resulting - * map element, otherwise current element is skipped.

- *
-     *     map(range(10), _*_)  => [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
-     *     map(players('*'), _+' is stoopid') [gnembon is stoopid, herobrine is stoopid]
-     * 
- * - *

filter(list,expr(_,_i))

- *

filters list elements returning only these that return positive result of the expr. - * With break and continue statements, the supplied value can be used as a boolean check instead.

- *
-     *     filter(range(100), !(_%5), _*_>1000)  => [0, 5, 10, 15, 20, 25, 30]
-     *     map(filter(entity_list('*'),_=='Witch'), query(_,'pos') )  => [[1082.5, 57, 1243.5]]
-     * 
- * - *

first(list,expr(_,_i))

- *

Finds and returns the first item in the list that satisfies expr. It sets _ for current element value, - * and _i for index of that element. break can be called inside the iteration code, using its argument - * value instead of the current item. continue has no sense and cannot be called inside first call.

- *
-     *     first(range(1000,10000), n=_; !first( range(2, sqrt(n)+1), !(n % _) ) )  => 1009 // first prime after 1000
-     * 
- *

Notice in the example above, that we needed to rename the outer _ to be albe to use in in the inner - * first call

- * - * - *

all(list,expr(_,_i))

- *

Returns true if all elements on the list satisfy the condition. Its roughly equivalent to - * all(list,expr) <=> for(list,expr)==length(list). expr - * also receives bound _ and _i variables. break and continue have no sense - * and cannot be used inside of expr body.

- * - *
-     *     all([1,2,3], check_prime(_))  => 1
-     *     all(neighbours(x,y,z), _=='stone')  => 1 // if all neighbours of [x, y, z] are stone
-     *     map(filter(rect(0,4,0,1000,0,1000), l(x,y,z)=pos(_); all(rect(x,y,z,1,0,1),_=='bedrock') ), pos(_) )
-     *       => [[-298, 4, -703], [-287, 4, -156], [-269, 4, 104], [242, 4, 250], [-159, 4, 335], [-208, 4, 416], [-510, 4, 546], [376, 4, 806]]
-     *         // find all 3x3 bedrock structures in the top bedrock layer
-     *     map( filter( rect(0,4,0,1000,1,1000,1000,0,1000), l(x,y,z)=pos(_);
-     *             all(rect(x,y,z,1,0,1),_=='bedrock') && for(rect(x,y-1,z,1,1,1,1,0,1),_=='bedrock')<8),
-     *        pos(_) )  => [[343, 3, -642], [153, 3, -285], [674, 3, 167], [-710, 3, 398]]
-     *         // ditto, but requiring at most 7 bedrock block in the 18 blocks below them
-     * 
- * - *

reduce(list,expr(_a,_,_i), initial)

- *

Applies expr for each element of the list and saves the result in _a accumulator. - * Consecutive calls to expr can access that value to apply more values. You also need to specify - * the initial value to apply for the accumulator. break can be used to terminate reduction prematurely. - * If a value is provided to break or continue, 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
-     * 
- *
- */ public void LoopsAndHigherOrderFunctions() { // condition and expression will get a bound '_i' @@ -2036,224 +1185,6 @@ public class Expression }); } - /** - *

Lists, Maps and API support for Containers

- *
- *

Scarpet supports basic container types: lists and maps (aka hashmaps, dicts etc..)

- *

Container manipulation

- *

Here is a list of operations that work on all types of containers: lists, maps, as well as other Minecraft - * specific modifyable containers, like NBTs

- * - *

get(container, address, ...), get(lvalue), ':' operator

- *

Returns the value at address element from the value. - * For lists it indicates an index, use negative numbers to reach elements from the end of the list. get - * call will always be able to find the index. In case there is few items, it will loop over

- *

for maps, retrieves the value under the key specified in the address or null otherwise

- *

[Minecraft specific usecase]: In case value is of nbt type, uses address as the - * nbt path to query, - * returning null, if path is not found, one value if there was one match, or list of values if result is a list. - * Returned elements can be of numerical type, string texts, or another compound nbt tags

- *

In case to simplify the access with nested objects, you can add chain of addresses to the arguments of - * get rather than calling it multiple times. get(get(foo,a),b) is equivalent to - * get(foo, a, b), or foo:a:b.

- *
-     *     get(l(range(10)), 5)  => 5
-     *     get(l(range(10)), -1)  => 9
-     *     get(l(range(10)), 10)  => 0
-     *     l(range(10)):93  => 3
-     *
-     *     get(player() ~ 'nbt', 'Health') => 20 // inefficient way to get player health, use player() ~ 'health' instead
-     *
-     *     get(m( l('foo',2), l('bar',3), l('baz',4) ), 'bar')  => 3
-     * 
- * - *

has(container, address, ...), has(lvalue)

- *

Similar to get, but returns boolean value indicating if the given index / key / path is in the - * container. Can be used to determine if get(...)==null means the element doesn't exist, or the stored value - * for this address is null, and is cheaper to run than get.

- *

Like get, it can accept multiple addresses for chains in nested containers. In this case has(foo:a:b) - * is equivalent to has(get(foo,a), b) or has(foo, a, b)

- * - *

delete(container, address, ...), delete(lvalue)

- *

Removes specific entry from the container. For the lists - removes the element and shrinks it. For maps, it - * removes the key from the map, and for nbt - removes content from a given path. For lists and maps returns previous - * entry at the address, for nbt's - number of removed objects, with 0 indicating that the original value was unaffected.

- *

Like with the get and has, delete can accept chained addresses, as well as - * l-value container access, removing the value - * from the leaf of the path provided, so delete(foo, a, b) is the same as delete(get(foo,a),b) - * or delete(foo:a:b)

- *

Returns true, if container was changed, false, if it was left unchanged, and null if operation was invalid.

- * - *

put(container, address, value), put(container, address, value, mode), put(lvalue, value)

- *

Lists

- *

Modifies the container by replacing the value under the address with the supplied value. - * For lists, a valid index is required, but can be negative as well to indicate positions from the end of the list. - * If null is supplied as the address, it always means - add to the end of the list.

- *

There are three modes that lists can have items added to them: - *

    - *
  • replace(default): Replaces item under given index(address). Doesn't change the size of the array - * unless null address is used, which is an exception and then it appends to the end
  • - *
  • insert: Inserts given element at a specified index, shifting the rest of the array to make space - * for the item. Note that index of -1 points to the last element of the list, thus inserting at that position and - * moving the previous last element to the new last element position. To insert at the end, use += - * operator, or null address in put
  • - *
  • extend: treats the supplied value as an iterable set of values to insert at a given index, - * extending the list by this amount of items. Again use null address/index to point to the end of - * the list
  • - *
- *

Due to the extra mode parameter, there is no chaining for put, but you can still use l-value - * container access to indicate container and address, so put(foo, key, value) is the same as - * put(foo:key, value) or foo:key=value

- *

Returns true, if container got modified, false otherwise, and null if operation was invalid.

- * - *

Maps

- *

For maps there are no modes available (yet, seems there is no reason to). It replaces the value under the supplied - * key (address), or sets it if not currently present.

- *

NBT Tags

- *

- * The address for nbt values is a valid nbt path that you would use with /data command, and tag is any - * tag that would be applicable for a given insert operation. Note that to distinguish between proper types (like integer - * types, you need to use command notation, i.e. regular ints is 123, while byte size int would be 123b - * and an explicit string would be "5", so it helps that scarpet uses single quotes in his strings. Unlike - * for lists and maps, it returns the number of affected nodes, or 0 if none were affected. - *

- *

There are three modes that NBT tags can have items added to them: - *

    - *
  • replace(default): Replaces item under given path(address). Removes them first if possible, and then - * adds given element to the supplied position. The target path can indicate compound tag keys, lists, or individual - * elements of the lists.
  • - *
  • <N>: Index for list insertions. Inserts given element at a specified index, inside a list - * specified with the path address. Fails if list is not specified. It behaves like insert mode for lists, - * i.e. it is not removing any of the existing elements. Use replace to remove and replace existing element. - *
  • - *
  • merge: assumes that both path and replacement target are of compound type (dictionaries, maps, - * {} types), and merges keys from value with the compound tag under the path
  • - *
- * - *
-     *     a = l(1, 2, 3); put(a, 1, 4); a  => [1, 4, 3]
-     *     a = l(1, 2, 3); put(a, null, 4); a  => [1, 2, 3, 4]
-     *     a = l(1, 2, 3); put(a, 1, 4, 'insert'); a  => [1, 4, 2, 3]
-     *     a = l(1, 2, 3); put(a, null, l(4, 5, 6), 'extend'); a  => [1, 2, 3, 4, 5, 6]
-     *     a = l(1, 2, 3); put(a, 1, l(4, 5, 6), 'extend'); a  => [1, 4, 5, 6, 2, 3]
-     *     a = l(l(0,0,0),l(0,0,0),l(0,0,0)); put(a.1, 1, 1); a  => [[0, 0, 0], [0, 1, 0], [0, 0, 0]]
-     *     a = m(1,2,3,4); put(a, 5, null); a  => {1: null, 2: null, 3: null, 4: null, 5: null}
-     *     tag = nbt('{}'); put(tag, 'BlockData.Properties', '[1,2,3,4]'); tag  => {BlockData:{Properties:[1,2,3,4]}}
-     *     tag = nbt('{a:[{lvl:3},{lvl:5},{lvl:2}]}'); put(tag, 'a[].lvl', 1); tag  => {a:[{lvl:1},{lvl:1},{lvl:1}]}
-     *     tag = nbt('{a:[{lvl:[1,2,3]},{lvl:[3,2,1]},{lvl:[4,5,6]}]}'); put(tag, 'a[].lvl', 1, 2); tag
-     *          => {a:[{lvl:[1,2,1,3]},{lvl:[3,2,1,1]},{lvl:[4,5,1,6]}]}
-     *     tag = nbt('{a:[{lvl:[1,2,3]},{lvl:[3,2,1]},{lvl:[4,5,6]}]}'); put(tag, 'a[].lvl[1]', 1); tag
-     *          => {a:[{lvl:[1,1,3]},{lvl:[3,1,1]},{lvl:[4,1,6]}]}
-     * 
- * - *

List operations

- *

l(values ...), l(iterator)

- *

Creates a list of values of the expressions passed as parameters. It can be used as an L-value and if all - * elements are variables, you coujld use it to return multiple results from one function call, if that function returns - * a list of results with the same size as the l call uses. In case there is only one argument and it is - * an iterator (vanilla expression specification has range, but Minecraft API implements - * a bunch of them, like diamond), it will convert it to a proper list. Iterators can only be used in - * high order functions, and are treated as empty lists, unless unrolled with l

- *
-     * l(1,2,'foo') => [1, 2, foo]
-     * l() => [] (empty list)
-     * l(range(10)) => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
-     * l(1, 2) = l(3, 4) => Error: l is not a variable
-     * l(foo, bar) = l(3,4); foo==3 && bar==4 => 1
-     * l(foo, bar, baz) = l(2, 4, 6); l(min(foo, bar), baz) = l(3, 5); l(foo, bar, baz)  => [3, 4, 5]
-     * 
- *

In the last example l(min(foo, bar), baz) creates a valid L-value, as min(foo, bar) finds the - * lower of the variables (in this case foo) creating a valid assignable L-list of [foo, baz], and these - * values will be assigned new values

- * - *

join(delim, list), join(delim, values ...)

- *

Returns a string that contains joined elements of the list, iterator, or all values, concatenated with delim delimiter

- *
-     *     join('-',range(10))  => 0-1-2-3-4-5-6-7-8-9
-     *     join('-','foo')  => foo
-     *     join('-', 'foo', 'bar')  => foo-bar
-     * 
- * - *

split(delim, expr)

- *

Splits a string undr expr by delim which can be a regular expression

- *
-     *     split('',foo)  => [f, o, o]
-     *     split('.','foo.bar')  => []
-     *     split('\\.','foo.bar')  => [foo, bar]
-     * 
- * - *

slice(expr, from, to?)

- *

extracts a substring, or sublist (based on the type of the result of the expression under expr with starting index - * of from, and ending at to if provided, or the end, if omitted

- *
-     *     slice(l(0,1,2,3,4,5), 1, 3)  => [1, 2, 3]
-     *     slice('foobar', 0, 1)  => 'f'
-     *     slice('foobar', 3)  => 'bar'
-     *     slice(range(10), 3, 5)  => [3, 4, 5]
-     *     slice(range(10), 5)  => [5, 6, 7, 8, 9]
-     * 
- * - *

sort(list), sort(values ...)

- *

Sorts in the default sortographical order either all arguments, or a list if its the only argument. It returns a new - * sorted list, not affecting the list passed to the argument

- *
-     * sort(3,2,1)  => [1, 2, 3]
-     * sort('a',3,11,1)  => [1, 3, 11, 'a']
-     * list = l(4,3,2,1); sort(list)  => [1, 2, 3, 4]
-     * 
- * - *

sort_key(list, key_expr)

- *

Sorts a copy of the list in the order or keys as defined by the key_expr for each element

- *
-     *     sort_key([1,3,2],_)  => [1, 2, 3]
-     *     sort_key([1,3,2],-_)  => [3, 2, 1]
-     *     sort_key(l(range(10)),rand(1))  => [1, 0, 9, 6, 8, 2, 4, 5, 7, 3]
-     *     sort_key(l(range(20)),str(_))  => [0, 1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 2, 3, 4, 5, 6, 7, 8, 9]
-     * 
- * - *

range(to), range(from, to), range(from, to, step)

- *

Creates a range of numbers from from, no greater/larger than to. - * The step parameter dictates not only the increment size, but also direction (can be negative). - * The returned value is not a proper list, just the iterator - * but if for whatever reason you need a proper list with all items evaluated, use l(range(to)). - * Primarily to be used in higher order functions

- *
-     *     range(10)  => [...]
-     *     l(range(10))  => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
-     *     map(range(10),_*_)  => [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
-     *     reduce(range(10),_a+_, 0)  => 45
-     *     range(5,10)  => [5, 6, 7, 8, 9]
-     *     range(20, 10, -2)  => [20, 18, 16, 14, 12]
-     * 
- * - *

element(list, index)(deprecated)

- *

Legacy support for older method that worked only on lists. Please use get(...) for equivalent - * support, or . operator. Also previous unique behaviours with put on lists has been - * removed to support all type of container types.

- * - *

Map operations

- *

Scarpet supports map structures, aka hashmaps, dicts etc. Map structure can also be used, with null - * values as sets. Apart from container access functions, (. , get, put, has, delete), - * the following functions:

- *

m(values ...), m(iterator), m(key_value_pairs)

- *

creates and initializes a map with supplied keys, and values. If the arguments contains a flat list, - * these are all treated as keys with no value, same goes with the iterator - creates a map that behaves like a set. - * If the arguments is a list of lists, they have to have two elements each, and then first is a key, and second, - * a value

- *
-     * m(1,2,'foo') => {1: null, 2: null, foo: null}
-     * m() => {} (empty map)
-     * m(range(10)) => {0: null, 1: null, 2: null, 3: null, 4: null, 5: null, 6: null, 7: null, 8: null, 9: null}
-     * m(l(1, 2), l(3, 4)) => {1: 2, 3: 4}
-     * reduce(range(10), put(_a, _, _*_); _a, m())
-     *      => {0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81}
-     * 
- *

keys(map), values(map), pairs(map)

- *

Returns full lists of keys, values and key-value pairs (2-element lists) for all the entries in the map

- * - *
- */ - public void BasicDataStructures() { addFunction("l", lv -> @@ -2560,293 +1491,6 @@ public class Expression }); } - /** - * - * - *

System functions

- *
- *

Type conversion functions

- *

copy(expr)

- *

Returns the deep copy of the expression. Can be used to copy mutable objects, like maps and lists

- *

type(expr)

- *

Returns the string value indicating type of the expression. Possible outcomes are - * null, number, string, list, iterator, as well as minecraft related concepts like - * block, entity, nbt

- *

bool(expr)

- *

Returns a boolean context of the expression. Note that there are no true/false values in - * scarpet. true is alias of 1, and false is 0. Bool is also interpreting - * string values as boolean, which is different from other places where boolean context can be used. - * This can be used in places where API functions return string values to represent binary values

- *
-     * bool(pi) => 1
-     * bool(false) => 0
-     * bool('') => 0
-     * bool(l()) => 0
-     * bool(l('')) => 1
-     * bool('foo') => 1
-     * bool('false') => 0
-     * bool('nulL') => 0
-     * if('false',1,0) => 1
-     * 
- * - *

number(expr)

- *

Returns a numeric context of the expression. - * Can be used to read numbers from strings

- *
-     * number(null) => null
-     * number(false) => 0
-     * number('') => null
-     * number('3.14') => 3.14
-     * number(l()) => 0
-     * number(l('')) => 1
-     * number('foo') => null
-     * number('3bar') => null
-     * number('2')+number('2') => 4
-     * 
- * - *

str(expr, params? ... ), str(expr, param_list)

- * - *

Returns a formatted string representing expression. - * Accepts formatting style accepted by String.format. - * Supported types (with "%?" syntax):

- *
    - *
  • d, o, x: integers
  • - *
  • a, e, f, g: floats
  • - *
  • b: booleans
  • - *
  • s: strings
  • - *
- * - *
-     * str(null) => null
-     * str(false) => 0
-     * str('') => null
-     * str('3.14') => 3.14
-     * str(l()) => 0
-     * str(l('')) => 1
-     * str('foo') => null
-     * str('3bar') => null
-     * str(2)+str(2) => 22
-     * str('pi: %.2f',pi) => 'pi: 3.14'
-     * str('player at: %d %d %d',pos(player())) => 'player at: 567, -2423, 124'
-     * 
- * - *
- *

Threading and Parallel Execution

- *

Scarpet allows to run threads of execution in parallel to the main script execution thread. In Minecraft, the main - * thread scripts are executed on the main server thread. Since Minecraft is inherently NOT thread safe, it is not that - * beneficial to parallel execution in order to access world resources faster. Both - * GetBlockState and setBlockState are not thread safe and require the execution to park on the server thread, - * where these requests can be executed in the off-tick time in between ticks that didn't take 50ms. There are however - * benefits of running things in parallel, like fine time control not relying on the tick clock, or running things independent - * on each other. You can still run your actions on tick-by-tick basis, either taking control of the execution using - * game_tick() API function (nasty solution), or scheduling tick using schedule() function (much nicer solution), - * but threading often gives the neatest solution to solve problems in parallel (see scarpet camera). - *

- *

Due to limitations with the game, there are some limits to the threading as well. You cannot for instance join_task() at all - * from the main script and server thread, because any use of Minecraft specific function that require any world access, will - * require to park and join on the main thread to get world access, meaning that calling join on that task would inevitably lead to a - * typical deadlock. You can still join tasks from other threads, just because the only possibility of a deadlock in this case would - * come explicitly from your bad code, not the internal world access behaviour. Some things tough like player or entity manipulation, - * can be effectively parallelized.

- *

task(function, ?args...., ?executor)

- *

Creates and runs a parallel task, returning the handle to the task object. Task will return the return value of the function - * when its completed, or will return null immediately if task is still in progress, so grabbing a value of - * a task object is non-blocking. Function can be either function value, or function lambda, or a name of an existing defined function. - * In case function needs arguments to be called with, they should be supplied after the function name, or value. - * Optional executor identifier is to place the task in a specific queue identified by this value. The default - * thread value is the null thread. There is no limits on number of parallel tasks for any executor, so using different queues is solely - * for synchronization purposes. Also, since task function knows how many extra arguments it requires, the use of - * tailing optional parameter for the custom executor thread pool is not ambiguous. - *

- *
-     * task( _() -> print('Hello Other World') )  => Runs print command on a separate thread
-     * foo(a, b) -> print(a+b); task('foo',2,2)  => Uses existing function definition to start a task
-     * task('foo',3,5,'temp');  => runs function foo with a different thread executor, identified as 'temp'
-     * a = 3; task( _(outer(a), b) -> foo(a,b), 5, 'temp')  => Another example of running the same thing passing arguments using closure over anonymous function as well as passing a parameter.
-     * 
- *

task_count(executor?)

- *

If no argument provided, returns total number of tasks being executed in parallel at this moment using scarpet threading system. - * If the executor is provided, returns number of active tasks for that provider. Use task_count(null) to - * get the task count of the default executor only.

- * - *

task_value(task)

- *

Returns the task return value, or null if task hasn't finished yet. Its a non-blocking operation. - * Unlike join_task, can be called on any task at any point

- * - *

task_join(task)

- *

Waits for the task completion and returns its computed value. If the task has already finished returns it immediately. - * Unless taking the task value directly, i.e. via task_value, this operation is blocking. Since Minecraft has - * a limitation that all world access operations have to be performed on the main game thread in the off-tick time, joining any tasks that use - * Minecraft API from the main thread would mean automatic lock, so joining from the main thread is not allowed. - * Join tasks from other threads, if you really need to, or communicate asynchronously with the task via globals or - * function data / arguments to monitor its progress, communicate, get partial results, or signal termination.

- * - *

task_completed(task)

- *

Returns true if task has completed, or false otherwise.

- * - *

synchronize(lock, expression)

- *

Evaluates expression synchronized with respect to the lock lock. Returns the value of the - * expression.

- * - *
- *

Auxiliary functions

- * - *

lower(expr), upper(expr), title(expr)

- *

Returns lowercase, uppercase or titlecase representation of a string representation of the passed expression

- *
-     * lower('aBc') => 'abc'
-     * upper('aBc') => 'ABC'
-     * title('aBc') => 'Abc'
-     * 
- * - *

replace(string, regex, repl?); replace_first(string, regex, repl?)

- *

Replaces all, or first occurence of a regular expression in the string with repl expression, or - * nothing, if not specified

- *
-     * replace('abbccddebfg','b+','z')  // => azccddezfg
-     * replace_first('abbccddebfg','b+','z')  // => azccddebfg
-     * 
- * - *

length(expr)

- *

Returns length of the expression, the length of the string, - * the length of the integer part of the number, or length of the list

- *
-     * length(pi) => 1
-     * length(pi*pi) => 1
-     * length(pi^pi) => 2
-     * length(l()) => 0
-     * length(l(1,2,3)) => 3
-     * length('') => 0
-     * length('foo') => 3
-     * 
- * - *

rand(expr), rand(expr, seed)

- *

returns a random number from 0.0 - * (inclusive) to expr (exclusive). - * In boolean context (in conditions, boolean functions, or bool), returns - * false if the randomly selected value is less than 1. This means that rand(2) returns true half - * of the time and rand(5) returns true for 80% (4/5) of the time. If seed is not provided, uses a random seed. - * If seed is provided, each consecutive call to rand() will act like 'next' call to the same random object. - * Scarpet keeps track of up to 1024 custom random number generators, so if you exceed this number (per app), then your - * random sequence will revert to the beginning.

- *
-     * map(range(10), floor(rand(10))) => [5, 8, 0, 6, 9, 3, 9, 9, 1, 8]
-     * map(range(10), bool(rand(2))) => [1, 1, 1, 0, 0, 1, 1, 0, 0, 0]
-     * map(range(10), str('%.1f',rand(_))) => [0.0, 0.4, 0.6, 1.9, 2.8, 3.8, 5.3, 2.2, 1.6, 5.6]
-     * 
- * - *

perlin(x), perlin(x, y), perlin(x, y, z), perlin(x, y, z, seed)

- *

returns a noise value from 0.0 to 1.0 (roughly) for 1, 2 or 3 dimensional coordinate. - * The default seed it samples from is 0, but seed can be - * specified as a 4th argument as well. In case you need 1D or 2D noise values with custom seed, use null - * for z, or y and z arguments respectively.

- *

Perlin noise is based on a square grid and generates rougher maps comparing to Simplex, which is creamier. - * Querying for lower-dimensional result, rather than - * affixing unused dimensions to constants has a speed benefit, - *

- *

Thou shall not sample from noise changing seed frequently. Scarpet will keep track of the last 256 perlin seeds used for sampling - * providing similar speed comparing to the default seed of 0. In case the app engine uses more than 256 seeds at the same time, - * switching between them can get much more expensive.

- * - *

simplex(x, y), simplex(x, y, z), simplex(x, y, z, seed)

- *

returns a noise value from 0.0 to 1.0 (roughly) for 2 or 3 dimensional coordinate. - * The default seed it samples from is 0, but seed can be - * specified as a 4th argument as well. In case you need 2D noise values with custom seed, use null - * for z argument.

- *

Simplex noise is based on a triangular grid and generates smoother maps comparing to Perlin. - * To sample 1D simplex noise, affix other coordinate to a constant. - *

- *

Thou shall not sample from noise changing seed frequently. Scarpet will keep track of the last 256 simplex seeds used for sampling - * providing similar speed comparing to the default seed of 0. In case the app engine uses more than 256 seeds at the same time, - * switching between them can get much more expensive.

- * - *

print(expr)

- *

prints the value of the expression to chat. - * Passes the result of the argument to the output unchanged, so print-statements can - * be weaved in code to debug programming issues

- *
-     *     print('foo') => results in foo, prints: foo
-     *     a = 1; print(a = 5) => results in 5, prints: 5
-     *     a = 1; print(a) = 5 => results in 5, prints: 1
-     *     print('pi = '+pi) => prints: pi = 3.141592653589793
-     *     print(str('pi = %.2f',pi)) => prints: pi = 3.14
-     * 
- * - *

sleep(expr)

- *

Halts the execution of the program (and the game itself) for expr milliseconds. - * All in all, its better to use game_tick(expr) to let the game do its job while the program waits

- *
sleep(50)
- *

time()

- *

Returns the number of milliseconds since 'some point', - * like Java's System.nanoTime(). It returns a float, which has 1 microsecond precision - * (0.001 ms)

- *
-     *     start_time = time();
-     *     flip_my_world_upside_down();
-     *     print(str('this took %d milliseconds',time()-start_time))
-     * 
- * - *

profile_expr(expression)

- *

Returns number of times given expression can be run in 50ms time. Useful to profile and optimize your - * code. Note that, even if its only a number, it WILL run these commands, so if they are destructive, - * you need to be careful.

- * - *
- *

Access to variables and stored functions (use with caution)

- * - *

var(expr)

- *

Returns the variable under the name of the string value of the expression. Allows to - * manipulate variables in more programmatic manner, which allows to use local variable set with a - * hash map type key-value access, can also be used with global variables

- *
-     *     a = 1; var('a') = 'foo'; a => a == 'foo'
-     * 
- * - *

undef(expr)

- *

Removes all bindings of a variable with a name of expr. - * Removes also all function definitions with that name. It can affect global variable pool, and local variable set - * for a particular function which will become invalid ........,

- *
-     *     inc(i) -> i+1; foo = 5; inc(foo) => 6
-     *     inc(i) -> i+1; foo = 5; undef('foo'); inc(foo) => 1
-     *     inc(i) -> i+1; foo = 5; undef('inc'); undef('foo'); inc(foo) => Error: Function inc is not defined yet at pos 53
-     *     undef('pi')  => bad idea - removes hidden variable holding the pi value
-     *     undef('true')  => even worse idea, unbinds global true value, all references to true would now refer to the default 0
-     * 
- *

vars(prefix)

- *

It returns all names of variables from local scope (if prefix does not start with 'global') - * or global variables (otherwise). - * Here is a larger example that uses combination of vars and var functions to - * be used for object counting

- *
-     * /script run
-     * $ count_blocks(ent) -> (
-     * $   l(cx, cy, cz) = query(ent, 'pos');
-     * $   scan(cx, cy, cz, 16, 16, 16, var('count_'+_) += 1);
-     * $   for ( sort_key( vars('count_'), -var(_)),
-     * $     print(str( '%s: %d', slice(_,6), var(_) ))
-     * $   )
-     * $ )
-     *
-     * /script run count_blocks(player())
-     * 
- * - *
- *

System key-value storage

- *

Scarpet runs apps in isolation. The can share code via use of shared libraries, but each library that is imported - * to each app is specific to that app. Apps can store and fetch state from disk, but its restricted to specific locations - * meaning apps cannot interact via disk either. To facilitate communication for interappperability, scarpet hosts its own - * key-value storage that is shared between all apps currently running on the host, providing methods for getting - * an associated value with optional setting it if not present, and an operation of modifying a content of a system global - * value.

- *

system_variable_get(key, default_value ?)

- *

Returns the variable from the system shared key-value storage keyed with a key value, optionally - * if value is not present, and default expression is provided, sets a new value to be associated with that key

- *

system_variable_set(key, new_value)

- *

Returns the variable from the system shared key-value storage keyed with a key value, and sets - * a new mapping for the key

- *
- */ public void SystemFunctions() { addUnaryFunction("hash_code", v -> new NumericValue(v.hashCode())); @@ -3312,7 +1956,6 @@ public class Expression public Expression(String expression) { this.expression = expression.trim().replaceAll("\\r\\n?", "\n").replaceAll("\\t"," "); - VariablesAndConstants(); UserDefinedFunctionsAndControlFlow(); Operators(); ArithmeticOperations(); @@ -3689,5 +2332,4 @@ public class Expression throw new ExpressionException(c, this, "Empty expression"); } } - }