diff --git a/src/main/java/carpet/fakes/StatTypeInterface.java b/src/main/java/carpet/fakes/StatTypeInterface.java
new file mode 100644
index 00000000..e4196879
--- /dev/null
+++ b/src/main/java/carpet/fakes/StatTypeInterface.java
@@ -0,0 +1,6 @@
+package carpet.fakes;
+
+public interface StatTypeInterface String with gamemode, or Good'ol gamemode id, or null if not a player. Player's permission level, or 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.
@@ -1834,6 +1841,12 @@ public class CarpetExpression
* Make noises. If called with Sets if the entity obeys any collisions, including collisions with the terrain and basic physics.
+ * Not affecting players, since they are controlled client side Applies status effect to the living entity. Takes several optional parameters, which default to Sets AI to stay around the home position, within 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
- * floder.null if not a player.
* query(e,'gamemode_id')
+ * query(e,'permission_level')null if not applicable for this entity.
* query(e,'effect',name?)
* modify(e, 'ai', boolean)false value, it will disable AI in the mob. true will enable it again.
+ * modify(e, 'no_clip', boolean)
+ * modify(e, 'effect', name, duration, amplifier?, show_particles?, show_icon?)0,
+ * true and true.
* modify(e, 'home', null), modify(e, 'home', block, distance?), modify(e, 'home', x, y, z, distance?)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
@@ -2638,7 +2651,7 @@ public class CarpetExpression
*
* load_app_data(), load_app_data(file)
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
@@ -2663,6 +2676,25 @@ public class CarpetExpression
*
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 minedcrafted: items craftedused: items usedbroken: items brokenpicked_up: items picked updropped: items droppedkilled: mobs killedkilled_by: blocks minedcustom: various random statsFor 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 statisitic, 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 wrone, or statistic has effectively
+ * a value of 0.
plop(pos, what)Plops a structure or a feature at a given Unloading an app removes all of its state from the game, disables commands, and removes bounded events 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 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.
@@ -3486,8 +3557,10 @@ public class CarpetExpression
* __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_right_clicks_block(player, item_tuple, hand, block, face, hitvec)
* __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)
@@ -3500,6 +3573,8 @@ public class CarpetExpression
* __on_player_stops_sprinting(player)
* __on_player_drops_item(player)
* __on_player_drops_stack(player)
+ *
+ * __on_statistic(player, category, event, value) // player statistic changes
*
* used to display current events and bounded functions. use 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
@@ -2613,6 +2613,59 @@ public class Expression implements Cloneable
*
*
* 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
+ * Due to limitations with the game, there are some limits to the threading as well. You cannot for instance 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 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 Returns the task return value, or 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 Returns true if task has completed, or false otherwise.pos, so block, triple position coordinates
* or a list of coordinates. To what gets plopped and exactly where it often depends on the
@@ -3008,12 +3040,12 @@ public class CarpetExpression
});
//"overidden" native call to cancel if on main thread
- this.expr.addLazyFunction("join_task", 1, (c, t, lv) -> {
+ this.expr.addLazyFunction("task_join", 1, (c, t, lv) -> {
if (((CarpetContext)c).s.getMinecraftServer().isOnThread())
- throw new InternalExpressionException("'join_task' cannot be called from main thread to avoid deadlocks");
+ throw new InternalExpressionException("'task_join' cannot be called from main thread to avoid deadlocks");
Value v = lv.get(0).evalValue(c);
if (!(v instanceof ThreadValue))
- throw new InternalExpressionException("'join_task' could only be used with a task value");
+ throw new InternalExpressionException("'task_join' could only be used with a task value");
Value ret = ((ThreadValue) v).join();
return (_c, _t) -> ret;
});
@@ -3219,8 +3251,43 @@ public class CarpetExpression
((CarpetScriptHost)((CarpetContext)c).host).setGlobalState(tag, file);
return (cc, tt) -> Value.NULL;
});
+
+ this.expr.addLazyFunction("statistic", 3, (c, t, lv) ->
+ {
+ Value playerValue = lv.get(0).evalValue(c);
+ CarpetContext cc = (CarpetContext)c;
+ ServerPlayerEntity player = EntityValue.getPlayerByValue(cc.s.getMinecraftServer(), playerValue);
+ if (player == null) return LazyValue.NULL;
+ Identifier category;
+ Identifier statName;
+ try
+ {
+ category = new Identifier(lv.get(1).evalValue(c).getString());
+ statName = new Identifier(lv.get(2).evalValue(c).getString());
+ }
+ catch (InvalidIdentifierException e)
+ {
+ return LazyValue.NULL;
+ }
+ StatType> type = Registry.STAT_TYPE.get(category);
+ if (type == null)
+ return LazyValue.NULL;
+ Stat> stat = getStat(type, statName);
+ if (stat == null)
+ return LazyValue.NULL;
+ return (_c, _t) -> new NumericValue(player.getStatHandler().getStat(stat));
+ });
}
+ private .
* @param expression expression
@@ -3446,7 +3513,11 @@ public class CarpetExpression
* 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.
*
- * __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.
* /script event commandadd_to ro register new event, or remove_from
diff --git a/src/main/java/carpet/script/CarpetScriptServer.java b/src/main/java/carpet/script/CarpetScriptServer.java
index 75968456..0f8639a8 100644
--- a/src/main/java/carpet/script/CarpetScriptServer.java
+++ b/src/main/java/carpet/script/CarpetScriptServer.java
@@ -46,6 +46,7 @@ public class CarpetScriptServer
private static ListFundamental components of
+ * scarpet programming language (version 1.6).Fundamental components of
*
* scarpet programming language (towards version 1.7).
+ * Threading and Parallel Execution
+ * 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).
+ * 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)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?)task_count(null) to
+ * get the task count of the default executor only.
+ * task_value(task)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)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)Auxiliary functions
*
*
@@ -3039,9 +3092,15 @@ public class Expression implements Cloneable
return new NumericValue(ThreadValue.taskCount());
});
- addUnaryFunction("join_task", (v) -> {
+ addUnaryFunction("task_value", (v) -> {
if (!(v instanceof ThreadValue))
- throw new InternalExpressionException("'join_task' could only be used with a task value");
+ throw new InternalExpressionException("'task_value' could only be used with a task value");
+ return ((ThreadValue) v).getValue();
+ });
+
+ addUnaryFunction("task_join", (v) -> {
+ if (!(v instanceof ThreadValue))
+ throw new InternalExpressionException("'task_join' could only be used with a task value");
return ((ThreadValue) v).join();
});
diff --git a/src/main/java/carpet/script/value/EntityValue.java b/src/main/java/carpet/script/value/EntityValue.java
index 52487b01..25c35898 100644
--- a/src/main/java/carpet/script/value/EntityValue.java
+++ b/src/main/java/carpet/script/value/EntityValue.java
@@ -29,6 +29,7 @@ import net.minecraft.entity.mob.MobEntityWithAi;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.predicate.entity.EntityPredicates;
+import net.minecraft.server.MinecraftServer;
import net.minecraft.server.command.ServerCommandSource;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.server.world.ServerWorld;
@@ -93,6 +94,25 @@ public class EntityValue extends Value
return entity;
}
+ public static ServerPlayerEntity getPlayerByValue(MinecraftServer server, Value value)
+ {
+ ServerPlayerEntity player = null;
+ if (value instanceof EntityValue)
+ {
+ Entity e = ((EntityValue) value).getEntity();
+ if (e instanceof ServerPlayerEntity)
+ {
+ player = (ServerPlayerEntity) e;
+ }
+ }
+ else
+ {
+ String playerName = value.getString();
+ player = server.getPlayerManager().getPlayer(playerName);
+ }
+ return player;
+ }
+
@Override
public String getString()
{
diff --git a/src/main/java/carpet/script/value/ThreadValue.java b/src/main/java/carpet/script/value/ThreadValue.java
index 40500011..42682197 100644
--- a/src/main/java/carpet/script/value/ThreadValue.java
+++ b/src/main/java/carpet/script/value/ThreadValue.java
@@ -37,6 +37,11 @@ public class ThreadValue extends Value
return taskFuture.getNow(Value.NULL).getString();
}
+ public Value getValue()
+ {
+ return taskFuture.getNow(Value.NULL);
+ }
+
@Override
public boolean getBoolean()
{
diff --git a/src/main/resources/assets/carpet/scripts/event_test.sc b/src/main/resources/assets/carpet/scripts/event_test.sc
index 4ac8d59e..80e2346e 100644
--- a/src/main/resources/assets/carpet/scripts/event_test.sc
+++ b/src/main/resources/assets/carpet/scripts/event_test.sc
@@ -160,7 +160,28 @@ __on_player_drops_stack(player) -> (
print('Player '+player+' drops current stack.')
);
-__on_statistic(player, category, event, value) ->
+__on_player_interacts_with_block(player, hand, block, face, hitvec) ->
(
- print('__on_statistic(player, category, event, value) -> ['+join(', ',l(player, category, event, value))+']');
-);
\ No newline at end of file
+ print('');
+ print('__on_player_interacts_with_block(player, hand, block, face, hitvec) ');
+ print('block right clicked by player:');
+ print(' - player: '+player);
+ print(' - hand: '+hand);
+ print(' - block: '+block+' at '+map(pos(block), str('%.2f',_)));
+ print(' - face: '+face);
+ print(' - hitvec: '+map(hitvec, str('%.2f',_)))
+);
+__on_player_places_block(player, item_tuple, hand, block) ->
+(
+ l(item, count, nbt) = item_tuple || l('None', 0, null);
+ print('');
+ print('__on_player_places_block(player, item_tuple, hand, block)');
+ print('player places a block:');
+ print(' - player: '+player);
+ print(' - block: '+block+' at '+map(pos(block), str('%.2f',_)));
+ print(' - hand: '+hand);
+ print(' - item:');
+ print(' > name: '+item);
+ print(' > count: '+count);
+ print(' > nbt: '+nbt);
+);
diff --git a/src/main/resources/assets/carpet/scripts/stats_test.sc b/src/main/resources/assets/carpet/scripts/stats_test.sc
new file mode 100644
index 00000000..076e8eae
--- /dev/null
+++ b/src/main/resources/assets/carpet/scripts/stats_test.sc
@@ -0,0 +1,5 @@
+__on_statistic(player, category, event, value) ->
+(
+ print('__on_statistic(player, category, event, value) -> ');
+ print(' ['+join(', ',l(player, category, event, value))+']');
+);
\ No newline at end of file
diff --git a/src/main/resources/carpet.mixins.json b/src/main/resources/carpet.mixins.json
index 8e58ceb1..ffae0208 100644
--- a/src/main/resources/carpet.mixins.json
+++ b/src/main/resources/carpet.mixins.json
@@ -132,9 +132,13 @@
"DamageTracker_scarpetEventMixin",
"ServerPlayerEntity_scarpetEventMixin",
"FallingBlockEntity_scarpetEventsMixin",
+ "BlockItem_scarpetEventMixin",
"ChunkTicketManager_scarpetMixin",
"ServerChunkManager_scarpetMixin",
+ "StatType_scarpetMixin",
+
+
"PistonBlock_movableTEMixin",
"PistonBlockEntity_movableTEMixin",
"World_movableTEMixin",
lower(expr), upper(expr), title(expr)