You are viewing an older version of the site. Click here to view
the latest version of this page. (This may be a dead link, if so, try the root page of the docs
here.)
Event handling allows users to register MScripts to be triggered in many other places, not just on a command. This
expands the usefulness of CommandHelper to the point where it is roughly equivalent to any other plugin that can be
written for Minecraft.
==main.ms==
In order to initially register an event, you must include a hook inside a "main" function. Upon server startup, any
script in main.ms will be run. This should be used as an opportunity to register your initial hooks. Now, this doesn't
prevent you from registering a hook elsewhere in a script, but for the common case, you'll want to put your hooks in
here. You can also use this opportunity to do other things, perhaps writing out to the console or something, but
typically only event registrations will be in here. This does work as a "server start" event, but it gets re-run
whenever you /reloadaliases also.
==Registering an Event==
To register an event, use the {{function|bind}} function. Let's take a look at the function's signature:
Copy Code
If you note, this looks very similar to the {{function|proc}} function. In essence, it's basically the same, except
CommandHelper is responsible for calling the function, when the specified event occurs, not you. The options object
allows you to set certain options for this event, including setting a custom event id, and event priority. The prefilter
allows you to tell the event handler to pre-filter which events are intercepted (Prefilters are a more complicated
subject, and have their own [[Prefilters|page]]). Both the options parameter and the prefilter
parameter may be null. The information in the @event_object depends on the event. Each event sends different parameters.
Finally, the custom params are discussed below in the scope section. So, let's look at the
Copy Code
So, what's going on here? @event contains the event data. It is an associative array that contains at least the
following information:
* "type" - The event type. Normally you've already got this data, because you registered it, however, wildcard events \
may be possible in the future.
* "macrotype" - This determines what kind of data there will be in the rest of the event
The rest of the data in the event is dependent on the event type, and this information can be looked up in the event
table.
So, what happens? If the player left clicks a block, it will tell them the block id they just clicked. Not terribly
useful by itself, but that should give you an idea of how events work in general.
==Unregistering an Event==
Each event returns some piece of data that uniquely identifies this registration. To unregister an event, you need to
know this data (i.e., store it in a variable). This data is not guaranteed to be unique across reloads of the server
however, so persisting it across reloads doesn't make sense. If you need to always know what the event id is, you can
force a particular id on the event. This id must be unique across all events. To ensure the data is unique compared to
automatically generated event ids, you may not register an id with the following syntax: "string:int". This formatting
is enforced by the function. To unregister the handler, use the {{function|unbind}} function. Let's look at actual code
to more clearly see usage.
Copy Code
In the case where we assign our own id, so that we can persist, or otherwise always know what our event's id is, we can
use the following syntax
Copy Code
In addition, an event may unregister itself from within the handler by running
Copy Code
==Running inside an event handler==
The context of events are sometimes different than when a command is run. For instance, in a mob spawn event, no player
is involved, so {{function|player}} will return null. For player based events however, player() does return the player
that triggered the event.
===Scope===
Events have the same type of scope as functions, i.e. only variables passed in are assigned. This is what the custom
params are for. If you need to have these values passed in to the event, you may do that with these extra parameters.
The values are copied over ''at bind time'', not at event run time. Though the values in the variables may be changed
during the execution of the script, they are reset to the bind time values each time the function is triggered. Using
{{function|import}} and {{function|export}} is a good way to get data in and out of the event handlers, if needed,
though you may consider redesigning if you're doing it this way, because there is likely a simpler way.
===Return===
If {{function|return}} is called from the code, it will cause the event handler to stop running, which can
be a convenient way to stop the handler short. Currently, any values returned are ignored, in the future, it will
be a compile error to return anything other than void.
==Order of events==
Event handlers (the code inside a
Copy Code
Because the event handler is registered at monitor level, it will run last, and it will print out meta information about
the active event, including information about what handlers received, locked, consumed, modified, or cancelled events.
Because logging information for {{function|event_meta}} does use marginally more resources, history is only logged if
debug mode is on (this can be set in preferences.txt).
bind(string eventName, array options, array prefilter, @event_object, [@custom_params...]) {
<code>
}

1 {{keyword|bind}}({{object|string}} eventName, {{object|array}} options, {{object|array}} prefilter, @event_object, [@custom_params...]) {
2 <code>
3 }
player_interact
event, which occurs whenever the player left or right clicks a block or the air, and show a few use cases. First, we
need to know the event's signature though, which can be looked up in the [[Event_API|Event API]].
player_interact:
* @event['action']: One of either: left_click_block, right_click_block, left_click_air, or right_click_air
* @event['block']: The id of the block they clicked, or 0 if they clicked the air. If they clicked the air, neither \
facing or location will be present.
* @event['player']: The player associated with this event
* @event['facing']: The (lowercase) face of the block they clicked.
* @event['location']: The (x, y, z, world) location of the block they clicked
bind(player_interact, null, null, @event) {
if(@event['action'] == 'left_click_block') {
msg('You just left clicked a block with id ' . @event['block']);
}
}

1 {{keyword|bind}}(player_interact, {{keyword|null}}, {{keyword|null}}, @event) {
2 {{keyword|if}}(@event['action'] == 'left_click_block') {
3 {{function|msg}}('You just left clicked a block with id ' . @event['block']);
4 }
5 }
// Automatically generated id
@event_id = bind(player_interact, null, null, @event) {
'' #Handling code for event
};
unbind(@event_id); // This was the id returned by bind()

1 // Automatically generated id
2
3 @event_id = {{keyword|bind}}(player_interact, {{keyword|null}}, {{keyword|null}}, @event) {
4 '' #Handling code for event
5
6 };
7
8 {{function|unbind}}(@event_id); // This was the id returned by bind()
bind(player_interact, array(id: 'interact-123'), null, @event) {
'' // Handling code
}
unbind('interact-123');

1 {{keyword|bind}}(player_interact, {{function|array}}(id: 'interact-123'), {{keyword|null}}, @event) {
2 '' // Handling code
3
4 }
5
6 {{function|unbind}}('interact-123');
unbind()
without any
arguments. This will cause the handler to no longer run, but it will finish running this last time. This is useful for
onetime event handlers, perhaps if the event was registered in a command.
bind(player_interact, null, null, @event) {
// Event handling code goes here
unbind();
}

1 {{keyword|bind}}(player_interact, {{keyword|null}}, {{keyword|null}}, @event) {
2 // Event handling code goes here
3
4 {{function|unbind}}();
5 }
bind
) are fired off in order from highest to lowest, then monitor. All
CommandHelper events are cancellable; that is to say that if an event is cancelled, a flag is set and
{{function|is_cancelled}} will return true, and if the underlying minecraft event is also cancellable, it will also be
cancelled. To cancel an event, call cancel([state])
in your code. The rest of your code will continue
running, so if you need to stop after cancelling, you should return()
.
Handler priorities are handled as such: Suppose we have three events registered, at high, low, and lowest. The handler
at high gets the event first. The handler is free to modify event parameters, which will then be passed to the low
priority handler, which is then allowed to further modify the event as required, and then it is passed on further to the
lowest priority handler, which is also free to edit the event parameters. Finally, the monitor level handlers are
allowed to see a read-only version of the event. This chain can be modified in two ways. First, a handler may call
{{function|lock}} on an event, which will cause calls to modify_event
by lower priority handlers to fail.
Events essentially become read only at that point, however handlers may still react otherwise. In addition, parameters
may be sent to lock()
, which will only lock the specified event parameters, leaving the rest of the
parameters freely editable by lower priority handlers. The second option a higher priority handler has, is to
{{function|consume}} an event. If an event is consumed, it is not even passed to lower priority handlers (except
monitor). This will prevent lower priority events from even seeing the event in the first place.
So, what is the purpose of all this complexity in handler priorities? When handlers fight, things can get messy, and
simply having 5 or 6 priority levels isn't usually enough flexibility to specify the desired behavior. For instance,
if a handler absolutely needs to read the original value of an event parameter and act on the event externally, but only
trigger something else as long as some other parameter is some value upon it actually triggering, this would be
impossible without the callbacks. Or if a handler wants to act on some event, and other events also acting on it would
cause issues, it can consume the event, and not have to worry about undesired behavior. This is why choosing a priority
is important, and the priority you choose should be based on the following guidelines:
* LOWEST: Lowest priority handlers should expect to not be able to edit parameters, or even run, but should instead be \
a "default" occurrence, should nothing else choose to deal with this event.
* LOW: Handlers that don't need to run at all, but would like to be able to edit and see at least some events should \
register at low priority.
* NORMAL: Normal priority handlers intend on being run as they expect, but there would be not be a big loss if they \
weren't able to run as intended.
* HIGH: Handlers that play an important role, but don't need absolute say over an event should register as a high \
priority handler.
* HIGHEST: Highest level handlers receive the event first. Handlers that need to have absolute say about the event \
should register at this level.
* MONITOR: Monitor level handlers receive the event last, and cannot edit the event in any way. This should be used \
for logging type handlers. In general, if the handler hooks in to game functionality (even if it doesn't intend on \
modifying the event) it should register at Lowest or Low. The exception to this is if a handler wants to get the event \
even if it is cancelled or consumed, at which point it is appropriate to use monitor. Monitor level handlers still \
receive cancelled and consumed events, and can check the status of those flags with the is_cancelled()
\
and is_consumed()
functions.
In the event that two or more handlers register at the same priority, other handlers will receive the event even if it
is consumed (however, they can not cause it to stop being consumed) and they can still modify the event parameters
(though they cannot "unlock" the event for lower priority handlers). The order that handlers fire in within priorities
is determined by bind order.
==Debugging==
If you are having trouble getting a script to do what you want, especially if you have many scripts running, it may be
helpful to use some of the debugging tools at your disposal. {{function|dump_events}} will print out all the currently
bound events, including their exact location in your script, so you can easily refer to it. Also, if you need to get
meta information at runtime, you can
use the following code snippet:
bind(event_name, array(priority: 'MONITOR'), null, @event) {
console(event_meta());
}

1 {{keyword|bind}}(event_name, {{function|array}}(priority: 'MONITOR'), {{keyword|null}}, @event) {
2 {{function|console}}({{function|event_meta}}());
3 }
Find a bug in this page? Edit this page yourself, then submit a pull request.