-
Notifications
You must be signed in to change notification settings - Fork 5
Scenes
This page is meant to serve as a high-level description of the Scene API, which is used to create custom minigames and lobbies.
All scenes, at minimum, implement the Scene
interface. Scenes at their core are just containers of players that can be "shut down", upon which their players are removed (possibly sent to another scene, or kicked from the server).
Scene implementations are maintained by SceneManager
, through which nearly all operations related to scenes must be performed. There is only one SceneManager
instance, which can be obtained using the following code snippet:
SceneManager manager = SceneManager.Global.instance();
Consult SceneManager
's javadoc to see what methods it has and how they must be used.
Critical to the understanding of the Scene API is the PlayerView
. A PlayerView
is a representation of a specific Player
— a Minestom class — who may or may not be online. One can convert a PlayerView
into a Player
using its getPlayer
method, which returns an Optional which will be empty if the player is offline. PlayerView
instances can be stored long-term, and can be obtained from a PlayerViewProvider
.
As with SceneManager
, there is only one instance of PlayerViewProvider
.
PlayerViewProvider provider = PlayerViewProvider.Global.instance();
//get a PlayerView from a UUID
PlayerView player = provider.fromUUID(UUID.randomUUID());
Optional<Player> playerOptional = player.getPlayer();
if (playerOptional.isPresent()) {
Player minestomPlayer = playerOptional.get();
//operations can now be performed on 'player'
minestomPlayer.teleport(new Vec(0, 0, 0)).join();
//you can also get a PlayerView from an already-existing Minestom Player
PlayerView theView = provider.fromPlayer(player);
}
Sometimes, it is necessary to get the current scene that a player belongs to. You can do this as follows:
PlayerViewProvider provider = PlayerViewProvider.Global.instance();
PlayerView player = provider.fromUUID(UUID.randomUUID());
Optional<Scene> sceneOptional = SceneManager.Global.instance().currentScene(player);
if (scene.isPresent()) { //'scene' may be empty if the player is not currently in a scene
Scene current = sceneOptional.get();
//perform actions on 'current'
}
//or, get a scene of a certain type:
Optional<Lobby> lobbyOptional = SceneManager.Global.instance().currentScene(player, Lobby.class);
if (lobbyOptional.isPresent()) {
Lobby lobby = lobbyOptional.get();
//perform actions on the lobby here
}
When a player logs into the server, it is often desirable to immediately send them to a Scene. In Phantazm, this scene is a Lobby instance.
However, in contrast to "regular" joining, this procedure has to occur in two steps. First, the method
Lobby#join(Set, boolean)
is called, with the login
parameter set to true
. When logging in, the join method will still add the player to the scene, but it will not teleport them. Teleporting right away, during the first phase of login, would actually result in an error as the player has not been added to an Instance
(Minecraft 'world') yet.
At some point later, once the player is added to an instance, Lobby#postLogin(Set)
is called. This method will teleport the player to the lobby's spawn point.
Because scenes may be ticked in parallel, on different threads from the rest of Minestom, it is necessary to properly synchronize access to scenes unless you are operating from within the scene itself. For example, methods on a given Scene implementation do not need to sync access to themselves (but would need to synchronize on other instances of Scene).
The earlier examples did not demonstrate how to synchronize on a scene. The following demonstrates what this might look like:
PlayerViewProvider provider = PlayerViewProvider.Global.instance();
PlayerView player = provider.fromUUID(UUID.randomUUID());
Optional<Lobby> lobbyOptional = SceneManager.Global.instance().currentScene(player, Lobby.class);
if (lobbyOptional.isPresent()) {
lobbyOptional.get().getAcquirable().sync(self -> { //'self' is the instance of Lobby returned by lobbyOptional.get()
//anything in this block is synchronized and it is safe to call methods on the scene!
self.sendMessage(Component.text("This message would be sent to all players currently in the lobby"));
});
}
All methods that are annotated with @ApiStatus.Internal
should not be directly called. They are called when appropriate by the SceneManager
.