11. API - Events

The events module defines the Events class, which provides methods for querying events in the Minecraft world, and the BlockHitEvent, PlayerPosEvent, ChatPostEvent, and IdleEvent classes which represent the various event types.

Note

All items in this module are available from the picraft namespace without having to import picraft.events directly.

The following items are defined in the module:

11.1. Events

class picraft.events.Events(connection, poll_gap=0.1, include_idle=False)[source]

This class implements the events attribute.

There are two ways of responding to picraft’s events: the first is to poll() for them manually, and process each event in the resulting list:

>>> for event in world.events.poll():
...     print(repr(event))
...
<BlockHitEvent pos=1,1,1 face="y+" player=1>,
<PlayerPosEvent old_pos=0.2,1.0,0.7 new_pos=0.3,1.0,0.7 player=1>

The second is to “tag” functions as event handlers with the decorators provided and then call the main_loop() function which will handle polling the server for you, and call all the relevant functions as needed:

@world.events.on_block_hit(pos=Vector(1,1,1))
def hit_block(event):
    print('You hit the block at %s' % event.pos)

world.events.main_loop()

By default, only block hit events will be tracked. This is because it is the only type of event that the Minecraft server provides information about itself, and thus the only type of event that can be processed relatively efficiently. If you wish to track player positions, assign a set of player ids to the track_players attribute. If you wish to include idle events (which fire when nothing else is produced in response to poll()) then set include_idle to True.

Note

If you are using a Raspberry Juice server, chat post events are also tracked by default. Chat post events are only supported with Raspberry Juice servers; Minecraft Pi edition doesn’t support chat post events.

Finally, the poll_gap attribute specifies how long to pause during each iteration of main_loop() to permit event handlers some time to interact with the server. Setting this to 0 will provide the fastest response to events, but will result in event handlers having to fight with event polling for access to the server.

clear()[source]

Forget all pending events that have not yet been retrieved with poll().

This method is used to clear the list of events that have occurred since the last call to poll() without retrieving them. This is useful for ensuring that events subsequently retrieved definitely occurred after the call to clear().

has_handlers(cls)[source]

Decorator for registering a class as containing picraft event handlers.

If you are writing a class which contains methods that you wish to use as event handlers for picraft events, you must decorate the class with @has_handlers. This will ensure that picraft tracks instances of the class and dispatches events to each instance that exists when the event occurs.

For example:

from picraft import World, Block, Vector, X, Y, Z

world = World()

@world.events.has_handlers
class HitMe(object):
    def __init__(self, pos):
        self.pos = pos
        self.been_hit = False
        world.blocks[self.pos] = Block('diamond_block')

    @world.events.on_block_hit()
    def was_i_hit(self, event):
        if event.pos == self.pos:
            self.been_hit = True
            print('Block at %s was hit' % str(self.pos))

p = world.player.tile_pos
block1 = HitMe(p + 2*X)
block2 = HitMe(p + 2*Z)
world.events.main_loop()

Class-based handlers are an advanced feature and have some notable limitations. For instance, in the example above the on_block_hit handler couldn’t be declared with the block’s position because this was only known at instance creation time, not at class creation time (which was when the handler was registered).

Furthermore, class-based handlers must be regular instance methods (those which accept the instance, self, as the first argument); they cannot be class methods or static methods.

Note

The @has_handlers decorator takes no arguments and shouldn’t be called, unlike event handler decorators.

main_loop()[source]

Starts the event polling loop when using the decorator style of event handling (see on_block_hit()).

This method will not return, so be sure that you have specified all your event handlers before calling it. The event loop can only be broken by an unhandled exception, or by closing the world’s connection (in the latter case the resulting ConnectionClosed exception will be suppressed as it is assumed that you want to end the script cleanly).

on_block_hit(thread=False, multi=True, pos=None, face=None)[source]

Decorator for registering a function/method as a block hit handler.

This decorator is used to mark a function as an event handler which will be called for any events indicating a block has been hit while main_loop() is executing. The function will be called with the corresponding BlockHitEvent as the only argument.

The pos parameter can be used to specify a vector or sequence of vectors (including a vector_range); in this case the event handler will only be called for block hits on matching vectors.

The face parameter can be used to specify a face or sequence of faces for which the handler will be called.

For example, to specify that one handler should be called for hits on the top of any blocks, and another should be called only for hits on any face of block at the origin one could use the following code:

from picraft import World, Vector

world = World()

@world.events.on_block_hit(pos=Vector(0, 0, 0))
def origin_hit(event):
    world.say('You hit the block at the origin')

@world.events.on_block_hit(face="y+")
def top_hit(event):
    world.say('You hit the top of a block at %d,%d,%d' % event.pos)

world.events.main_loop()

The thread parameter (which defaults to False) can be used to specify that the handler should be executed in its own background thread, in parallel with other handlers.

Finally, the multi parameter (which only applies when thread is True) specifies whether multi-threaded handlers should be allowed to execute in parallel. When True (the default), threaded handlers execute as many times as activated in parallel. When False, a single instance of a threaded handler is allowed to execute at any given time; simultaneous activations are ignored (but not queued, as with unthreaded handlers).

on_chat_post(thread=False, multi=True, message=None)[source]

Decorator for registering a function/method as a chat event handler.

This decorator is used to mark a function as an event handler which will be called for events indicating a chat message was posted to the world while main_loop() is executing. The function will be called with the corresponding ChatPostEvent as the only argument.

Note

Only the Raspberry Juice server generates chat events; Minecraft Pi Edition does not support this event type.

The message parameter can be used to specify a string or regular expression; in this case the event handler will only be called for chat messages which match this value. For example:

import re
from picraft import World, Vector

world = World()

@world.events.on_chat_post(message="hello world")
def echo(event):
    world.say("Hello player %d!" % event.player.player_id)

@world.events.on_chat_post(message=re.compile(r"teleport_me \d+,\d+,\d+"))
def teleport(event):
    x, y, z = event.message[len("teleport_me "):].split(",")
    event.player.pos = Vector(int(x), int(y), int(z))

world.events.main_loop()

The thread parameter (which defaults to False) can be used to specify that the handler should be executed in its own background thread, in parallel with other handlers.

Finally, the multi parameter (which only applies when thread is True) specifies whether multi-threaded handlers should be allowed to execute in parallel. When True (the default), threaded handlers execute as many times as activated in parallel. When False, a single instance of a threaded handler is allowed to execute at any given time; simultaneous activations are ignored (but not queued, as with unthreaded handlers).

on_idle(thread=False, multi=True)[source]

Decorator for registering a function/method as an idle handler.

This decorator is used to mark a function as an event handler which will be called when no other event handlers have been called in an iteration of main_loop(). The function will be called with the corresponding IdleEvent as the only argument.

Note that idle events will only be generated if include_idle is set to True.

on_player_pos(thread=False, multi=True, old_pos=None, new_pos=None)[source]

Decorator for registering a function/method as a position change handler.

This decorator is used to mark a function as an event handler which will be called for any events indicating that a player’s position has changed while main_loop() is executing. The function will be called with the corresponding PlayerPosEvent as the only argument.

The old_pos and new_pos parameters can be used to specify vectors or sequences of vectors (including a vector_range) that the player position events must match in order to activate the associated handler. For example, to fire a handler every time any player enters or walks over blocks within (-10, 0, -10) to (10, 0, 10):

from picraft import World, Vector, vector_range

world = World()
world.events.track_players = world.players

from_pos = Vector(-10, 0, -10)
to_pos = Vector(10, 0, 10)
@world.events.on_player_pos(new_pos=vector_range(from_pos, to_pos + 1))
def in_box(event):
    world.say('Player %d stepped in the box' % event.player.player_id)

world.events.main_loop()

Various effects can be achieved by combining old_pos and new_pos filters. For example, one could detect when a player crosses a boundary in a particular direction, or decide when a player enters or leaves a particular area.

Note that only players specified in track_players will generate player position events.

poll()[source]

Return a list of all events that have occurred since the last call to poll().

For example:

>>> w = World()
>>> w.events.track_players = w.players
>>> w.events.include_idle = True
>>> w.events.poll()
[<PlayerPosEvent old_pos=0.2,1.0,0.7 new_pos=0.3,1.0,0.7 player=1>,
 <BlockHitEvent pos=1,1,1 face="x+" player=1>,
 <BlockHitEvent pos=1,1,1 face="x+" player=1>]
>>> w.events.poll()
[<IdleEvent>]
process()[source]

Poll the server for events and call any relevant event handlers registered with on_block_hit().

This method is called repeatedly the event handler loop implemented by main_loop(); developers should only call this method when implementing their own event loop manually, or when their (presumably non-threaded) event handler is engaged in a long operation and they wish to permit events to be processed in the meantime.

include_idle

If True, generate an idle event when no other events would be generated by poll(). This attribute defaults to False.

poll_gap

The length of time (in seconds) to pause during main_loop().

This property specifies the length of time to wait at the end of each iteration of main_loop(). By default this is 0.1 seconds.

The purpose of the pause is to give event handlers executing in the background time to communicate with the Minecraft server. Setting this to 0.0 will result in faster response to events, but also starves threaded event handlers of time to communicate with the server, resulting in “choppy” performance.

track_players

The set of player ids for which movement should be tracked.

By default the poll() method will not produce player position events (PlayerPosEvent). Producing these events requires extra interactions with the Minecraft server (one for each player tracked) which slow down response to block hit events.

If you wish to track player positions, set this attribute to the set of player ids you wish to track and their positions will be stored. The next time poll() is called it will query the positions for all specified players and fire player position events if they have changed.

Given that the players attribute represents a dictionary mapping player ids to players, if you wish to track all players you can simply do:

>>> world.events.track_players = world.players

11.2. BlockHitEvent

class picraft.events.BlockHitEvent(pos, face, player)[source]

Event representing a block being hit by a player.

This tuple derivative represents the event resulting from a player striking a block with their sword in the Minecraft world. Users will not normally need to construct instances of this class, rather they are constructed and returned by calls to poll().

Note

Please note that the block hit event only registers when the player right clicks with the sword. For some reason, left clicks do not count.

pos

A Vector indicating the position of the block which was struck.

face

A string indicating which side of the block was struck. This can be one of six values: ‘x+’, ‘x-‘, ‘y+’, ‘y-‘, ‘z+’, or ‘z-‘. The value indicates the axis, and direction along that axis, that the side faces:

player

A Player instance representing the player that hit the block.

11.3. PlayerPosEvent

class picraft.events.PlayerPosEvent(old_pos, new_pos, player)[source]

Event representing a player moving.

This tuple derivative represents the event resulting from a player moving within the Minecraft world. Users will not normally need to construct instances of this class, rather they are constructed and returned by calls to poll().

old_pos

A Vector indicating the location of the player prior to this event. The location includes decimal places (it is not the tile-position, but the actual position).

new_pos

A Vector indicating the location of the player as of this event. The location includes decimal places (it is not the tile-position, but the actual position).

player

A Player instance representing the player that moved.

11.4. ChatPostEvent

class picraft.events.ChatPostEvent(message, player)[source]

Event representing a chat post.

This tuple derivative represents the event resulting from a chat message being posted in the Minecraft world. Users will not normally need to construct instances of this class, rather they are constructed and returned by calls to poll().

Note

Chat events are only generated by the Raspberry Juice server, not by Minecraft Pi edition.

message

The message that was posted to the world.

player

A Player instance representing the player that moved.

11.5. IdleEvent

class picraft.events.IdleEvent[source]

Event that fires in the event that no other events have occurred since the last poll. This is only used if Events.include_idle is True.