This commit is contained in:
parent
5413708b29
commit
1ced941440
@ -34,7 +34,7 @@ alias ElixirSense.Log
|
|||||||
def start(socket, connstate, version) do
|
def start(socket, connstate, version) do
|
||||||
{:ok, spawn(fn ->
|
{:ok, spawn(fn ->
|
||||||
Process.set_label("ConnectionHandler for #{inspect(socket)}")
|
Process.set_label("ConnectionHandler for #{inspect(socket)}")
|
||||||
loop(socket, connstate, version)
|
loop(socket, connstate, version, %{})
|
||||||
end)}
|
end)}
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -42,12 +42,12 @@ alias ElixirSense.Log
|
|||||||
def start_link(socket, connstate, version) do
|
def start_link(socket, connstate, version) do
|
||||||
{:ok, spawn_link(fn ->
|
{:ok, spawn_link(fn ->
|
||||||
Process.set_label("ConnectionHandler for #{inspect(socket)}")
|
Process.set_label("ConnectionHandler for #{inspect(socket)}")
|
||||||
loop(socket, connstate, version)
|
loop(socket, connstate, version, %{})
|
||||||
end)}
|
end)}
|
||||||
end
|
end
|
||||||
|
|
||||||
@spec loop(:gen_tcp.socket(), atom(), pos_integer()) :: no_return()
|
@spec loop(:gen_tcp.socket(), atom(), pos_integer(), map()) :: no_return()
|
||||||
defp loop(socket, connstate, version) do
|
defp loop(socket, connstate, version, state) do
|
||||||
receive do
|
receive do
|
||||||
:closed ->
|
:closed ->
|
||||||
Logger.info("Connection #{inspect(socket)} closed.")
|
Logger.info("Connection #{inspect(socket)} closed.")
|
||||||
@ -57,38 +57,45 @@ alias ElixirSense.Log
|
|||||||
Process.exit(self(), :normal)
|
Process.exit(self(), :normal)
|
||||||
{:set_state, newstate} ->
|
{:set_state, newstate} ->
|
||||||
Logger.debug("Switching to state #{newstate} from #{connstate}")
|
Logger.debug("Switching to state #{newstate} from #{connstate}")
|
||||||
loop(socket, newstate, version)
|
loop(socket, newstate, version, state)
|
||||||
{:set_version, newversion} ->
|
{:set_version, newversion} ->
|
||||||
Logger.debug("Switching to version #{newversion} from #{version}")
|
Logger.debug("Switching to version #{newversion} from #{version}")
|
||||||
loop(socket, connstate, newversion)
|
loop(socket, connstate, newversion, state)
|
||||||
{:send_packet, packet} ->
|
{:send_packet, packet} ->
|
||||||
Logger.debug("Sending packet #{inspect(packet)}")
|
Logger.debug("Sending packet #{inspect(packet)}")
|
||||||
send_packet(socket, connstate, packet, version)
|
send_packet(socket, connstate, packet, version)
|
||||||
loop(socket, connstate, version)
|
loop(socket, connstate, version, state)
|
||||||
after 0 ->
|
after 0 ->
|
||||||
receive do
|
receive do
|
||||||
{:packet, id, data} ->
|
{:packet, id, data} ->
|
||||||
handle_packet(id, data, connstate, version)
|
state = handle_packet(id, data, connstate, version, state)
|
||||||
loop(socket, connstate, version)
|
loop(socket, connstate, version, state)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defp handle_packet(id, data, connstate, version) do
|
defp handle_packet(id, data, connstate, version, state) do
|
||||||
try do
|
try do
|
||||||
packet = connstate.deserialize(id, version, data)
|
packet = connstate.deserialize(id, version, data)
|
||||||
case connstate.handle(packet, version) do
|
case connstate.handle(packet, version, state) do
|
||||||
:ok -> :ok
|
:ok -> state
|
||||||
{:error, reason} ->
|
{:error, reason} ->
|
||||||
Logger.error("Error handling packet with ID #{id} in state #{connstate}: #{reason}")
|
Logger.error("Error handling packet with ID #{id} in state #{connstate}: #{reason}")
|
||||||
send(self(), {:disconnect, "Error handling packet #{id}: #{reason}"})
|
send(self(), {:disconnect, "Error handling packet #{id}:\n#{reason}"})
|
||||||
_ ->
|
state
|
||||||
Logger.warn("Unknown return value from handle_packet for packet #{id} in state #{connstate}")
|
newstate ->
|
||||||
|
if is_map(newstate) do
|
||||||
|
newstate
|
||||||
|
else
|
||||||
|
Logger.warning("State change to #{newstate} is not a map! Did you forget to return :ok?")
|
||||||
|
state
|
||||||
|
end
|
||||||
end
|
end
|
||||||
rescue
|
rescue
|
||||||
e ->
|
e ->
|
||||||
Logger.error("Error handling packet with ID #{id} in state #{connstate}: #{Exception.format(:error, e, __STACKTRACE__)}")
|
Logger.error("Error handling packet with ID #{id} in state #{connstate}: #{Exception.format(:error, e, __STACKTRACE__)}")
|
||||||
send(self(), {:disconnect, "Error handling packet #{id}: #{Exception.format(:error, e, __STACKTRACE__)}"})
|
send(self(), {:disconnect, "Error handling packet #{id}:\n#{e.message}"})
|
||||||
|
state
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -135,6 +135,10 @@ defmodule Amethyst.Minecraft.Write do
|
|||||||
v -> bool(true) <> callback.(v)
|
v -> bool(true) <> callback.(v)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def nbt(value) do
|
||||||
|
Amethyst.NBT.Write.write_net(value)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
defmodule Amethyst.Minecraft.Read do
|
defmodule Amethyst.Minecraft.Read do
|
||||||
@ -259,4 +263,7 @@ defmodule Amethyst.Minecraft.Read do
|
|||||||
{[value], rest, :reversed} = string({[], data, :reversed})
|
{[value], rest, :reversed} = string({[], data, :reversed})
|
||||||
{[Jason.decode!(value) | acc], rest, :reversed}
|
{[Jason.decode!(value) | acc], rest, :reversed}
|
||||||
end
|
end
|
||||||
|
def raw({acc, data, :reversed}) do
|
||||||
|
{[data | acc], "", :reversed}
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
115
apps/amethyst/lib/states/configuration.ex
Normal file
115
apps/amethyst/lib/states/configuration.ex
Normal file
@ -0,0 +1,115 @@
|
|||||||
|
# Amethyst - An experimental Minecraft server written in Elixir.
|
||||||
|
# Copyright (C) 2024 KodiCraft
|
||||||
|
#
|
||||||
|
# This program is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU Affero General Public License as
|
||||||
|
# published by the Free Software Foundation, either version 3 of the
|
||||||
|
# License, or (at your option) any later version.
|
||||||
|
#
|
||||||
|
# This program is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU Affero General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Affero General Public License
|
||||||
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
defmodule Amethyst.ConnectionState.Configuration do
|
||||||
|
require Amethyst.ConnectionState.Macros
|
||||||
|
alias Amethyst.ConnectionState.Macros
|
||||||
|
|
||||||
|
require Logger
|
||||||
|
|
||||||
|
@moduledoc """
|
||||||
|
This module contains the packets and logic for the Configuration state.
|
||||||
|
"""
|
||||||
|
Macros.defpacket_clientbound :cookie_request, 0x00, 767, [identifier: :string]
|
||||||
|
Macros.defpacket_clientbound :clientbound_plugin_message, 0x01, 767, [channel: :string, data: :raw]
|
||||||
|
Macros.defpacket_clientbound :disconnect, 0x02, 767, [reason: :json]
|
||||||
|
Macros.defpacket_clientbound :finish_configuration, 0x03, 767, []
|
||||||
|
Macros.defpacket_clientbound :clientbound_keep_alive, 0x04, 767, [id: :long]
|
||||||
|
Macros.defpacket_clientbound :ping, 0x05, 767, [id: :int]
|
||||||
|
Macros.defpacket_clientbound :reset_chat, 0x06, 767, []
|
||||||
|
Macros.defpacket_clientbound :registry_data, 0x07, 767, [
|
||||||
|
id: :string,
|
||||||
|
entries: {:array, [
|
||||||
|
id: :string,
|
||||||
|
data: {:optional, :nbt}
|
||||||
|
]}
|
||||||
|
]
|
||||||
|
Macros.defpacket_clientbound :remove_resource_pack, 0x08, 767, [
|
||||||
|
uuid: {:optional, :uuid}
|
||||||
|
]
|
||||||
|
Macros.defpacket_clientbound :add_resource_pack, 0x09, 767, [
|
||||||
|
uuid: :uuid,
|
||||||
|
url: :string,
|
||||||
|
hash: :string,
|
||||||
|
forced: :bool,
|
||||||
|
prompt_message: {:optional, :string}
|
||||||
|
]
|
||||||
|
Macros.defpacket_clientbound :store_cookie, 0x0A, 767, [identifier: :string, payload: :byte_array]
|
||||||
|
Macros.defpacket_clientbound :transfer, 0x0B, 767, [host: :string, port: :varint]
|
||||||
|
Macros.defpacket_clientbound :feature_flags, 0x0C, 767, [flags: {:array, [flag: :string]}]
|
||||||
|
Macros.defpacket_clientbound :update_tags, 0x0D, 767, [
|
||||||
|
tags: {:array, [
|
||||||
|
registry: :string,
|
||||||
|
tags: {:array, [
|
||||||
|
name: :string,
|
||||||
|
entries: {:array, [id: :varint]}
|
||||||
|
]}
|
||||||
|
]}
|
||||||
|
]
|
||||||
|
Macros.defpacket_clientbound :clientbound_known_packs, 0x0E, 767, [
|
||||||
|
packs: {:array, [
|
||||||
|
nameshape: :string,
|
||||||
|
id: :string,
|
||||||
|
version: :string
|
||||||
|
]}
|
||||||
|
]
|
||||||
|
Macros.defpacket_clientbound :custom_report_details, 0x0F, 767, [
|
||||||
|
details: {:array, [
|
||||||
|
title: :string,
|
||||||
|
desctioption: :string
|
||||||
|
]}
|
||||||
|
]
|
||||||
|
Macros.defpacket_clientbound :server_links, 0x10, 767, [
|
||||||
|
links: {:array, [
|
||||||
|
is_builtin: :bool,
|
||||||
|
label: :string,
|
||||||
|
url: :string
|
||||||
|
]}
|
||||||
|
]
|
||||||
|
|
||||||
|
Macros.defpacket_serverbound :client_information, 0x00, 767, [
|
||||||
|
locale: :string,
|
||||||
|
view_distance: :byte,
|
||||||
|
chat_mode: :varint,
|
||||||
|
chat_colors: :bool,
|
||||||
|
displayed_skin_parts: :byte,
|
||||||
|
main_hand: :varint,
|
||||||
|
text_filtering: :bool,
|
||||||
|
allow_server_listings: :bool
|
||||||
|
]
|
||||||
|
Macros.defpacket_serverbound :cookie_response, 0x01, 767, [
|
||||||
|
key: :string,
|
||||||
|
payload: {:optional, :byte_array}
|
||||||
|
]
|
||||||
|
Macros.defpacket_serverbound :serverbound_plugin_message, 0x02, 767, [channel: :string, data: :raw]
|
||||||
|
Macros.defpacket_serverbound :acknowledge_finish_configuration, 0x03, 767, []
|
||||||
|
Macros.defpacket_serverbound :serverbound_keep_alive, 0x04, 767, [id: :long]
|
||||||
|
Macros.defpacket_serverbound :pong, 0x05, 767, [id: :int]
|
||||||
|
Macros.defpacket_serverbound :resource_pack_response, 0x06, 767, [uuid: :uuid, result: :varint]
|
||||||
|
Macros.defpacket_serverbound :serverbound_known_packs, 0x07, 767, [
|
||||||
|
packs: {:array, [
|
||||||
|
namespace: :string,
|
||||||
|
id: :string,
|
||||||
|
version: :string
|
||||||
|
]}
|
||||||
|
]
|
||||||
|
|
||||||
|
def disconnect(reason) do
|
||||||
|
%{packet_type: :disconnect, reason: %{
|
||||||
|
"text" => reason
|
||||||
|
}}
|
||||||
|
end
|
||||||
|
end
|
@ -31,7 +31,7 @@ defmodule Amethyst.ConnectionState.Handshake do
|
|||||||
next: :varint
|
next: :varint
|
||||||
]
|
]
|
||||||
|
|
||||||
def handle(%{packet_type: :handshake, version: 767, address: address, port: port, next: next}, 0) do
|
def handle(%{packet_type: :handshake, version: 767, address: address, port: port, next: next}, 0, _state) do
|
||||||
Logger.debug("Received handshake for #{address}:#{port} with version 767")
|
Logger.debug("Received handshake for #{address}:#{port} with version 767")
|
||||||
case next do
|
case next do
|
||||||
1 ->
|
1 ->
|
||||||
|
@ -23,7 +23,7 @@ defmodule Amethyst.ConnectionState.Login do
|
|||||||
@moduledoc """
|
@moduledoc """
|
||||||
This module contains the packets and logic for the Login state.
|
This module contains the packets and logic for the Login state.
|
||||||
"""
|
"""
|
||||||
Macros.defpacket_clientbound :disconnect, 0x00, 767, [reason: :json]
|
Macros.defpacket_clientbound :disconnect, 0x00, 767, [reason: :nbt]
|
||||||
Macros.defpacket_clientbound :encryption_request, 0x01, 767, [
|
Macros.defpacket_clientbound :encryption_request, 0x01, 767, [
|
||||||
server_id: :string,
|
server_id: :string,
|
||||||
public_key: :byte_array,
|
public_key: :byte_array,
|
||||||
@ -60,10 +60,33 @@ defmodule Amethyst.ConnectionState.Login do
|
|||||||
Macros.defpacket_serverbound :login_acknowledged, 0x03, 767, []
|
Macros.defpacket_serverbound :login_acknowledged, 0x03, 767, []
|
||||||
Macros.defpacket_serverbound :cookie_response, 0x04, 767, [identifier: :string, payload: {:optional, :byte_array}]
|
Macros.defpacket_serverbound :cookie_response, 0x04, 767, [identifier: :string, payload: {:optional, :byte_array}]
|
||||||
|
|
||||||
|
def handle(%{packet_type: :login_start, name: name, player_uuid: player_uuid}, 767, state) do
|
||||||
|
Logger.debug("Received login start for #{name} with UUID #{player_uuid}")
|
||||||
|
if Application.fetch_env!(:amethyst, :encryption) do
|
||||||
|
raise RuntimeError, "Encryption is not currently supported"
|
||||||
|
else
|
||||||
|
send(self(), {:send_packet, %{
|
||||||
|
packet_type: :login_success,
|
||||||
|
uuid: player_uuid,
|
||||||
|
username: name,
|
||||||
|
properties: [],
|
||||||
|
strict_error_handling: false
|
||||||
|
}})
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def handle(%{packet_type: :login_acknowledged}, 767, _state) do
|
||||||
|
Logger.debug("Received login acknowledged")
|
||||||
|
send(self(), {:set_state, Amethyst.ConnectionState.Configuration})
|
||||||
|
:ok
|
||||||
|
end
|
||||||
|
|
||||||
def disconnect(reason) do
|
def disconnect(reason) do
|
||||||
%{packet_type: :disconnect, reason: %{
|
%{packet_type: :disconnect, reason:
|
||||||
"text" => "You have been disconnected:\n#{reason}",
|
{:compound, %{
|
||||||
"color" => "red"
|
"text" => {:string, reason}
|
||||||
}}
|
}}
|
||||||
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -29,7 +29,7 @@ defmodule Amethyst.ConnectionState.Status do
|
|||||||
Macros.defpacket_serverbound :status_request, 0x00, 767, []
|
Macros.defpacket_serverbound :status_request, 0x00, 767, []
|
||||||
Macros.defpacket_serverbound :ping_request, 0x01, 767, [payload: :long]
|
Macros.defpacket_serverbound :ping_request, 0x01, 767, [payload: :long]
|
||||||
|
|
||||||
def handle(%{packet_type: :status_request}, 767) do
|
def handle(%{packet_type: :status_request}, 767, _state) do
|
||||||
Logger.debug("Received status request")
|
Logger.debug("Received status request")
|
||||||
send(self(), {:send_packet, %{
|
send(self(), {:send_packet, %{
|
||||||
packet_type: :status_response,
|
packet_type: :status_response,
|
||||||
@ -45,7 +45,7 @@ defmodule Amethyst.ConnectionState.Status do
|
|||||||
:ok
|
:ok
|
||||||
end
|
end
|
||||||
|
|
||||||
def handle(%{packet_type: :ping_request, payload: payload}, 767) do
|
def handle(%{packet_type: :ping_request, payload: payload}, 767, _state) do
|
||||||
Logger.debug("Received ping request")
|
Logger.debug("Received ping request")
|
||||||
send(self(), {:send_packet, %{
|
send(self(), {:send_packet, %{
|
||||||
packet_type: :pong_response,
|
packet_type: :pong_response,
|
||||||
|
Loading…
Reference in New Issue
Block a user