"Loading terrain..."!
All checks were successful
Build & Test / nix-build (pull_request) Successful in 2m38s
Build & Test / nix-build (push) Successful in 2m42s

This commit is contained in:
Kodi Craft 2024-09-19 15:40:16 +02:00
parent f037f0de02
commit 930a508ad9
Signed by: kodi
GPG Key ID: 69D9EED60B242822
4 changed files with 93 additions and 43 deletions

View File

@ -33,6 +33,9 @@ defmodule Amethyst.API.Game do
You may either :accept or :reject the player for whatever reason, avoid You may either :accept or :reject the player for whatever reason, avoid
rejecting the player in your default game as that will disconnect the player. rejecting the player in your default game as that will disconnect the player.
If you :accept the player, you must return a spawn position ({x, y, z}) and
rotation ({yaw, pitch})
Note that if no new players can join for any reason, your game should return false from `joinable?/1`. Note that if no new players can join for any reason, your game should return false from `joinable?/1`.
The PID received in 'from' will be the one that calls all callbacks The PID received in 'from' will be the one that calls all callbacks
@ -44,7 +47,7 @@ defmodule Amethyst.API.Game do
- 'player_cfg' is a keyword list containing the configuration passed by the game client - 'player_cfg' is a keyword list containing the configuration passed by the game client
- 'state_refs' are your references (see `instantiate/1`) - 'state_refs' are your references (see `instantiate/1`)
""" """
@callback login(from :: pid(), player_cfg :: keyword(), state_refs :: map()) :: :accept | :reject @callback login(from :: pid(), player_cfg :: keyword(), state_refs :: map()) :: {:accept, {x :: float(), y :: float(), z :: float()}, {yaw :: float(), pitch ::float()}} | :reject
def login(%{:mod => mod, :refs => refs}, player_cfg) do def login(%{:mod => mod, :refs => refs}, player_cfg) do
mod.login(self(), player_cfg, refs) mod.login(self(), player_cfg, refs)
end end
@ -61,6 +64,18 @@ defmodule Amethyst.API.Game do
mod.player_position(self(), {x, y, z}, refs) mod.player_position(self(), {x, y, z}, refs)
end end
@doc """ @doc """
`player_rotation/3` is called when a player rotates. This function is called with the absolute angles
that the player client expects. TODO: Teleport Player API.
- 'from' is the PID of the player's connection process (see `login/3`).
- 'yaw' and 'pitch' are the angles the player expects to rotate to. These are in Minecraft's rotation format.
- `state_refs` are your references (see `instantiate/1`)
"""
@callback player_rotation(from :: pid(), {yaw :: float(), pitch :: float()}, state_refs :: map()) :: :ok
def player_rotation(%{:mod => mod, :refs => refs}, {yaw, pitch}) do
mod.player_rotation(self(), {yaw, pitch}, refs)
end
@doc """
`accept_teleport/3` is called when a client accepts a teleportation as sent by the Synchronize Player Position `accept_teleport/3` is called when a client accepts a teleportation as sent by the Synchronize Player Position
packet (TODO: Teleport Player API). This lets you know that the client is now where you expect it to be. packet (TODO: Teleport Player API). This lets you know that the client is now where you expect it to be.

View File

@ -271,10 +271,12 @@ defmodule Amethyst.ConnectionState.Configuration do
send(self(), {:set_state, Amethyst.ConnectionState.Play}) send(self(), {:set_state, Amethyst.ConnectionState.Play})
game = Application.fetch_env!(:amethyst, :default_game) |> Amethyst.GameCoordinator.find_or_create() game = Application.fetch_env!(:amethyst, :default_game) |> Amethyst.GameCoordinator.find_or_create()
state = state |> Map.put(:game, game) state = state |> Map.put(:game, game)
if Amethyst.API.Game.login(game, state) == :reject do login = Amethyst.API.Game.login(game, state)
case login do
:reject ->
send(self(), {:disconnect, "Default game rejected connection"}) send(self(), {:disconnect, "Default game rejected connection"})
:ok :ok
else {:accept, {x, y, z}, {yaw, pitch}} ->
send(self(), {:send_packet, %{ send(self(), {:send_packet, %{
packet_type: :login, packet_type: :login,
entity_id: 0, entity_id: 0,
@ -297,6 +299,10 @@ defmodule Amethyst.ConnectionState.Configuration do
portal_cooldown: 0, portal_cooldown: 0,
enforces_secure_chat: false enforces_secure_chat: false
}}) }})
send(self(), {:send_packet, %{
packet_type: :synchronize_player_position,
x: x, y: y, z: z, yaw: yaw, pitch: pitch, teleport_id: 0, flags: 0x00
}})
state state
end end
end end

View File

@ -60,21 +60,48 @@ defmodule Amethyst.ConnectionState.Play do
] ]
Macros.defpacket_serverbound :confirm_teleportation, 0x00, 767, [teleport_id: :varint] Macros.defpacket_serverbound :confirm_teleportation, 0x00, 767, [teleport_id: :varint]
Macros.defpacket_serverbound :set_player_position, 0x1A, 767, [
x: :double,
feet_y: :double,
z: :double,
on_ground: :bool
]
Macros.defpacket_serverbound :set_player_position_and_rotation, 0x1B, 767, [
x: :double,
feet_y: :double,
z: :double,
yaw: :float,
pitch: :float,
on_ground: :bool
]
Macros.defpacket_serverbound :set_player_rotation, 0x1C, 767, [
yaw: :float,
pitch: :float,
on_ground: :bool # I don't understand their obsession with this...
]
@spec handle(
%{
:packet_type => :confirm_teleportation,
:teleport_id => any(),
optional(any()) => any()
},
767,
nil | maybe_improper_list() | map()
) :: :ok
def handle(%{packet_type: :confirm_teleportation, teleport_id: id}, 767, state) do def handle(%{packet_type: :confirm_teleportation, teleport_id: id}, 767, state) do
Amethyst.API.Game.accept_teleport(state[:game], id) Amethyst.API.Game.accept_teleport(state[:game], id)
:ok :ok
end end
def handle(%{packet_type: :set_player_position_and_rotation, x: x, feet_y: y, z: z, yaw: yaw, pitch: pitch, on_ground: _ground}, 767, state) do
# I don't know why we would ever trust on_ground here... the server computes that
Amethyst.API.Game.player_position(state[:game], {x, y, z})
Amethyst.API.Game.player_rotation(state[:game], {yaw, pitch})
:ok
end
def handle(%{packet_type: :set_player_position, x: x, feet_y: y, z: z, on_ground: _ground}, 767, state) do
# I don't know why we would ever trust on_ground here... the server computes that
Amethyst.API.Game.player_position(state[:game], {x, y, z})
:ok
end
def handle(%{packet_type: :set_player_rotation, yaw: yaw, pitch: pitch, on_ground: _ground}, 767, state) do
# I don't know why we would ever trust on_ground here... the server computes that
Amethyst.API.Game.player_rotation(state[:game], {yaw, pitch})
:ok
end
def disconnect(reason) do def disconnect(reason) do
%{ %{
packet_type: :disconnect, packet_type: :disconnect,

View File

@ -12,20 +12,22 @@ defmodule Example.Game do
def login(from, cfg, refs) do def login(from, cfg, refs) do
Logger.info("Player logged in from #{inspect(from)}: #{inspect(cfg)}") Logger.info("Player logged in from #{inspect(from)}: #{inspect(cfg)}")
Logger.info("The refs for this game are #{inspect(refs)}") Logger.info("The refs for this game are #{inspect(refs)}")
#send(from, {:send_packet, %{ {:accept, {0.0, 0.0, 0.0}, {0.0, 0.0}}
# packet_type: :synchronize_player_position,
# x: 0.0, y: 0.0, z: 0.0, yaw: 0.0, pitch: 0.0, flags: 0x00,
# teleport_id: 0
#}})
:accept
end end
@impl true @impl true
@spec player_position(any(), {any(), any(), any()}, any()) :: :ok
def player_position(from, {x, y, z}, _refs) do def player_position(from, {x, y, z}, _refs) do
Logger.info("Player at #{inspect(from)} moved to #{x}, #{y}, #{z}") Logger.info("Player at #{inspect(from)} moved to #{x}, #{y}, #{z}")
:ok :ok
end end
@impl true
def player_rotation(from, {yaw, pitch}, _refs) do
Logger.info("Player at #{inspect(from)} rotated to #{yaw}, #{pitch}")
:ok
end
@impl true @impl true
def accept_teleport(from, id, _state_refs) do def accept_teleport(from, id, _state_refs) do
Logger.info("Player at #{inspect(from)} accepted teleport #{inspect(id)}") Logger.info("Player at #{inspect(from)} accepted teleport #{inspect(id)}")