Continue rewrite
All checks were successful
Build & Test / nix-build (push) Successful in 1m34s

This commit is contained in:
Kodi Craft 2024-09-06 00:42:09 +02:00
parent 5413708b29
commit 1ced941440
Signed by: kodi
GPG Key ID: 69D9EED60B242822
6 changed files with 176 additions and 24 deletions

View File

@ -34,7 +34,7 @@ alias ElixirSense.Log
def start(socket, connstate, version) do
{:ok, spawn(fn ->
Process.set_label("ConnectionHandler for #{inspect(socket)}")
loop(socket, connstate, version)
loop(socket, connstate, version, %{})
end)}
end
@ -42,12 +42,12 @@ alias ElixirSense.Log
def start_link(socket, connstate, version) do
{:ok, spawn_link(fn ->
Process.set_label("ConnectionHandler for #{inspect(socket)}")
loop(socket, connstate, version)
loop(socket, connstate, version, %{})
end)}
end
@spec loop(:gen_tcp.socket(), atom(), pos_integer()) :: no_return()
defp loop(socket, connstate, version) do
@spec loop(:gen_tcp.socket(), atom(), pos_integer(), map()) :: no_return()
defp loop(socket, connstate, version, state) do
receive do
:closed ->
Logger.info("Connection #{inspect(socket)} closed.")
@ -57,38 +57,45 @@ alias ElixirSense.Log
Process.exit(self(), :normal)
{:set_state, newstate} ->
Logger.debug("Switching to state #{newstate} from #{connstate}")
loop(socket, newstate, version)
loop(socket, newstate, version, state)
{:set_version, newversion} ->
Logger.debug("Switching to version #{newversion} from #{version}")
loop(socket, connstate, newversion)
loop(socket, connstate, newversion, state)
{:send_packet, packet} ->
Logger.debug("Sending packet #{inspect(packet)}")
send_packet(socket, connstate, packet, version)
loop(socket, connstate, version)
loop(socket, connstate, version, state)
after 0 ->
receive do
{:packet, id, data} ->
handle_packet(id, data, connstate, version)
loop(socket, connstate, version)
state = handle_packet(id, data, connstate, version, state)
loop(socket, connstate, version, state)
end
end
end
defp handle_packet(id, data, connstate, version) do
defp handle_packet(id, data, connstate, version, state) do
try do
packet = connstate.deserialize(id, version, data)
case connstate.handle(packet, version) do
:ok -> :ok
case connstate.handle(packet, version, state) do
:ok -> state
{:error, reason} ->
Logger.error("Error handling packet with ID #{id} in state #{connstate}: #{reason}")
send(self(), {:disconnect, "Error handling packet #{id}: #{reason}"})
_ ->
Logger.warn("Unknown return value from handle_packet for packet #{id} in state #{connstate}")
send(self(), {:disconnect, "Error handling packet #{id}:\n#{reason}"})
state
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
rescue
e ->
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

View File

@ -135,6 +135,10 @@ defmodule Amethyst.Minecraft.Write do
v -> bool(true) <> callback.(v)
end
end
def nbt(value) do
Amethyst.NBT.Write.write_net(value)
end
end
defmodule Amethyst.Minecraft.Read do
@ -259,4 +263,7 @@ defmodule Amethyst.Minecraft.Read do
{[value], rest, :reversed} = string({[], data, :reversed})
{[Jason.decode!(value) | acc], rest, :reversed}
end
def raw({acc, data, :reversed}) do
{[data | acc], "", :reversed}
end
end

View 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

View File

@ -31,7 +31,7 @@ defmodule Amethyst.ConnectionState.Handshake do
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")
case next do
1 ->

View File

@ -23,7 +23,7 @@ defmodule Amethyst.ConnectionState.Login do
@moduledoc """
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, [
server_id: :string,
public_key: :byte_array,
@ -60,10 +60,33 @@ defmodule Amethyst.ConnectionState.Login do
Macros.defpacket_serverbound :login_acknowledged, 0x03, 767, []
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
%{packet_type: :disconnect, reason: %{
"text" => "You have been disconnected:\n#{reason}",
"color" => "red"
%{packet_type: :disconnect, reason:
{:compound, %{
"text" => {:string, reason}
}}
}
end
end

View File

@ -29,7 +29,7 @@ defmodule Amethyst.ConnectionState.Status do
Macros.defpacket_serverbound :status_request, 0x00, 767, []
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")
send(self(), {:send_packet, %{
packet_type: :status_response,
@ -45,7 +45,7 @@ defmodule Amethyst.ConnectionState.Status do
:ok
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")
send(self(), {:send_packet, %{
packet_type: :pong_response,