Skip to content
This repository was archived by the owner on Jun 18, 2024. It is now read-only.

Added a newer, easier to explain node explanation #41

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
133 changes: 125 additions & 8 deletions feature/events/README.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,141 @@
# Events

## Overview
## Introduction

Event listening is a fairly hard part to keep easy while having a clear understanding of the execution flow. In Minestom, a tree is used to define inheritance for filtering and extensibility. Each node of the tree contains:
An event is essentiall when "something" just happened in the game such as a player / entity taking damage, opening a chest, placing a block, breaking a block and so on (you can view the full list of all events [by clicking here](https://javadoc.minestom.net/net/minestom/server/event/Event.html)).

* Event class, where only subclasses are allowed to enter (`Event`/`PlayerEvent`/etc...)
* Condition for filtering
* List of listeners
* Name for identification
* Priority
When an event is triggered, that event is passed as a variable which contains relevant data to that event, for example:
```java
var handler = MinecraftServer.getGlobalEventHandler();
handler.addListener(PlayerLoginEvent.class, event -> {
event.getPlayer().setGameMode(GameMode.CREATIVE);
});
```
In this example, the PlayerLoginEvent event which triggers when a player logs in (obviously) will pass on `event` into the function. Using that event, it's possible to get the player that triggered the event using `event.getPlayer()` and then applying a gamemode with `.setGameMode(gameModeHere)`, in this case creative.

The data contained by the `event` will depend on the type of event that was called, make sure to [read the documentation for that specific event](https://javadoc.minestom.net/net/minestom/server/event/Event.html) to see how you can work with it.

## Types of events

There are effectively "two types" of events, the normal events that will listen "globally" to any event regardless of conditions, and a "node event" which allows for special features such as only listening to an event for players in Creative, they will be explained further down.

## Creating an event

In order to create an event, you need to use the `GlobalEventHandler` like so:
```java
var handler = MinecraftServer.getGlobalEventHandler();
```
Using this handler, you can then attach event listeners, whether being normal or node based like this
```java
handler.addListener(PlayerLoginEvent.class, event -> {
event.getPlayer().setGameMode(GameMode.CREATIVE);
});

// Or, if you are using nodes, more on this later
node.addListener(PlayerLoginEvent.class, event -> {
event.getPlayer().setGameMode(GameMode.CREATIVE);
});
handler.addChild(node);
```

Once the event has been added to the GlobalEventHandler, it will start functioning as expected.

## The node events

Unlike other projects such as PaperMC, Spigot, Bukkit etc that only allow "global" events, Minestom has the ability to use a tree-like node system to customize conditions for events to be called.
For example, if you wanted a BlockPlaceEvent to only be called if the player is in creative mode, nodes can be used without checking the player gamemode through the `event` object.

Here's a fancy graph that shows how the same listeners could function differently based on whether a parent node is active

![Event tree with all nodes being executed](../../.gitbook/assets/event-tree.gif)

The tree structure provides us many advantages:
It provides clear advantages such as

* Context-aware listeners due to node filtering
* Clear execution order
* Ability to store the event tree as an image for documentation purpose
* Listener injection into existing nodes

## Nodes in practice

### Node types

There are three types of nodes
```
var allNode = EventNode.all("yourName");
var typeNode = EventNode.type("yourName", EventFilter.ENTITY);
var valueNode = EventNode.value("yourName", EventFilter.PLAYER, Player::isCreative);
```

These nodes can then have listeners added to them in a similar way to the GlobalEventHandler like for example
```java
allNode.addListener(PlayerLoginEvent.class, event -> {
event.getPlayer().setGameMode(GameMode.CREATIVE);
});
```

The `allNode` is essentially no different than just applying a listener directly to the `GlobalEventHandler` without using nodes at all. It would only be useful if combined with other nodes (to be explained further down)

The `typeNode` will only have its listeners work if the "type" of what called the listener matches what you choose.
```java
var typeNode = EventNode.type("yourName", EventFilter.ENTITY);
```
In this example, this node would only activate listeners attached to it if the listener that called it was an ENTITY (as defined by EventFilter)
To see all possible eventfilters, see [this page](https://javadoc.minestom.net/net/minestom/server/event/EventFilter.html)

¨The `valueNode` is where things get interesting, as it now only allows (and requires) a en `EventFilter` type, but also allows a condition
```java
var valueNode = EventNode.value("yourName", EventFilter.PLAYER, Player::isCreative);
```
In this example, any events attached to this node would only trigger if a player in creative mode triggered it.
The `Player::isCreative` in this case is a predicate, meaning in simple terms that it has to be a function that returns either true or false, **but you cannot add a boolean statement to it**, for example
```java
var valueNode = EventNode.value("yourName", EventFilter.PLAYER, isAdmin==1);
```
This would not work (or even compile) since there's no way to know "who" the `isAdmin` applies to when compiling. Instead it has to be a function, for example
```java
var isAdminNode = EventNode.value("yourName", EventFilter.PLAYER, event -> isMyPlayerAdmin(event));
```
Here, the `event` object is passed to the `isMyPlayerAdmin` function so that necessary checks can be made to verify whether this listener should trigger or not. It must return either true or false.

### yourName

As you have seen in the examples above, each node has a string "yourName", this can literally be anything you would like and has 0 purpose or function outside of organizing yourself.

### Node combinations

Imagine a case where you want to have a listener only apply if
1. A player triggered it
2. The player is in creative
3. They are an admin
This would be defined like this:
```java
var baseNode = EventNode.type("isAPlayer", EventFilter.PLAYER);
var creativeNode = EventNode.value("isCreative", EventFilter.PLAYER, Player::isCreative);
var isAdminNode = EventNode.value("isAdmin", EventFilter.PLAYER, event -> isMyPlayerAdmin(event));
```
Ideally, I would like these to run "sequentially", as in the same order as the list above. To do this, I make the `creativeNode` a child of `baseNode` and `isAdminNode` a child of `creativeNode`, like this:
```
baseNode
creativeNode
isAdminNode
```
To do this, I would add them as children like this:
```java
var baseNode = EventNode.type("isAPlayer", EventFilter.PLAYER);
var creativeNode = EventNode.value("isCreative", EventFilter.PLAYER, Player::isCreative);
var isAdminNode = EventNode.value("isAdmin", EventFilter.PLAYER, event -> isMyPlayerAdmin(event));
baseNode.addChild(creativeNode);
creativeNode.addChild(isAdminNode);
```

And once the node structure has been properly created, it can be added to the GlobalEventHandler with
```java
handler.addChild(baseNode);
// assuming that the GlobalEventHandler is stored in a variable called handler
```
**Note that only the base node needs to be added**

## API

### Node
Expand Down