Compare commits

..

No commits in common. "9f9abf277dc2fd4527429534e738203ff8e044cf" and "1ce9b7c454c15a42313d2bba58caf185520a8bf5" have entirely different histories.

7 changed files with 6 additions and 126 deletions

View File

@ -29,7 +29,7 @@ defmodule Amethyst.TCPListener do
@spec loop_acceptor(socket :: :gen_tcp.socket()) :: no_return()
defp loop_acceptor(socket) do
{:ok, client} = :gen_tcp.accept(socket)
{:ok, pid} = Task.Supervisor.start_child(Amethyst.ConnectionSupervisor, fn -> Amethyst.Server.Handshake.serve(client) end)
{:ok, pid} = Task.Supervisor.start_child(Amethyst.ConnectionSupervisor, fn -> Amethyst.Server.Stage1.serve(client) end)
:ok = :gen_tcp.controlling_process(client, pid)
Logger.info("Received connection from #{inspect(client)}, assigned to PID #{inspect(pid)}")
loop_acceptor(socket)

View File

@ -22,10 +22,6 @@ defmodule Amethyst.Minecraft.Write do
than simple binary data.
"""
def uuid(uuid) do
UUID.string_to_binary!(uuid)
end
def varint(value) when value in -2_147_483_648..2_147_483_647 do
<<value::32-unsigned>> = <<value::32-signed>> # This is a trick to allow the arithmetic shift to act as a logical shift
varnum("", value)
@ -119,15 +115,6 @@ defmodule Amethyst.Minecraft.Read do
{[value | acc], rest, :reversed}
end
def uuid({acc, <<uuid::16-binary, rest::binary>>, :reversed}) do
{[UUID.binary_to_string!(uuid) | acc], rest, :reversed}
end
def raw({acc, data, :reversed}, amount) do
<<data::binary-size(amount), rest::binary>> = data
{[data | acc], rest, :reversed}
end
def varint(tuple, read \\ 0, nacc \\ 0)
def varint({acc, <<1::1, value::7, rest::binary>>, :reversed}, read, nacc) when read < 5 do
varint({acc, rest, :reversed}, read + 1, nacc + (value <<< (7 * read)))

View File

@ -1,105 +0,0 @@
# 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.Server.Login do
@moduledoc """
This module contains the logic for the Handshake stage of the server.
"""
require Logger
use Amethyst.Server
alias Amethyst.Minecraft.Read
alias Amethyst.Minecraft.Write
## DESERIALIZATION
# Login Start https://wiki.vg/Protocol#Login_Start
def deserialize(0x00, data) do
{[name, uuid], ""} = Read.start(data) |> Read.string() |> Read.uuid() |> Read.stop()
{:login_start, name, uuid}
end
# Encryption Response https://wiki.vg/Protocol#Encryption_Response
def deserialize(0x01, data) do
{[secret_length], rest} = Read.start(data) |> Read.varint() |> Read.stop()
{[secret, verify_token_length], rest} = Read.start(rest) |> Read.raw(secret_length) |> Read.varint() |> Read.stop()
{[verify_token], ""} = Read.start(rest) |> Read.raw(verify_token_length) |> Read.stop()
{:encryption_response, secret, verify_token}
end
# Login Plugin Response https://wiki.vg/Protocol#Login_Plugin_Response
def deserialize(0x02, data) do
{[message_id, success], rest} = Read.start(data) |> Read.varint() |> Read.bool() |> Read.stop()
if success do
{:login_plugin_response, message_id, rest}
else
{:login_plugin_response, message_id, nil}
end
end
# Login Acknowledged https://wiki.vg/Protocol#Login_Acknowledged
def deserialize(0x03, "") do
{:login_acknowledged}
end
# Cookie Response https://wiki.vg/Protocol#Cookie_Response_(login)
def deserialize(0x04, data) do
{[key, exists], rest} = Read.start(data) |> Read.string() |> Read.bool() |> Read.stop()
if exists do
{[length], rest} = Read.start(rest) |> Read.varint() |> Read.stop()
{[data], _} = Read.start(rest) |> Read.raw(length) |> Read.stop()
{:cookie_response, key, data}
else
{:cookie_response, key, nil}
end
end
def deserialize(type, _) do
raise RuntimeError, "Got unknown packet type #{type}!"
end
## SERIALIZATION
# Disconnect (login) https://wiki.vg/Protocol#Disconnect_(login)
def serialize({:disconnect, reason}) do
Write.varint(0x00) <> Write.string(reason)
end
# Encryption Request https://wiki.vg/Protocol#Encryption_Request
def serialize({:encryption_request, id, pubkey, verify_token, auth}) do
Write.varint(0x01) <> Write.string(id) <> Write.varint(byte_size(pubkey)) <> pubkey <> Write.varint(byte_size(verify_token)) <> verify_token <> <<auth::8>>
end
# Login Success https://wiki.vg/Protocol#Login_Success
def serialize({:login_success, uuid, username, props, strict}) do
Write.varint(0x02) <> Write.uuid(uuid) <> Write.string(username) <> Write.varint(length(props)) <>
Enum.reduce(props, "", fn {name, value, signature}, acc -> acc <> Write.string(name) <> Write.string(value) <> case signature do
nil -> <<0x00>>
signature -> <<0x01>> <> Write.string(signature)
end end) <> <<strict::8>>
end
# Set Compression https://wiki.vg/Protocol#Set_Compression
def serialize({:set_compression, threshold}) do
Write.varint(0x03) <> Write.varint(threshold)
end
# Login Plugin Request https://wiki.vg/Protocol#Login_Plugin_Request
def serialize({:login_plugin_request, id, channel, data}) do
Write.varint(0x04) <> Write.varint(id) <> Write.string(channel) <> data
end
# Cookie Request (login) https://wiki.vg/Protocol#Cookie_Request_(login)
def serialize({:cookie_request_login, id}) do
Write.varint(0x05) <> Write.string(id)
end
def serialize(packet) do
raise ArgumentError, "Tried serializing unknown packet #{inspect(packet)}"
end
## HANDLING
def handle(tuple, _) do
Logger.error("Unhandled but known packet #{elem(tuple, 0)}")
end
end

View File

@ -14,9 +14,9 @@
# 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.Server.Handshake do
defmodule Amethyst.Server.Stage1 do
@moduledoc """
This module contains the logic for the Handshake stage of the server.
This module contains the stage 1 (Handshaking) server logic.
"""
require Logger
use Amethyst.Server
@ -53,7 +53,6 @@ defmodule Amethyst.Server.Handshake do
Logger.info("Got handshake, version #{ver} on #{addr}:#{port}. Wants to move to #{next}")
case next do
:status -> Amethyst.Server.Status.serve(client)
:login -> Amethyst.Server.Login.serve(client)
_ -> raise RuntimeError, "Unhandled move to next mode #{next}"
end
end

View File

@ -16,7 +16,8 @@
defmodule Amethyst.Server.Status do
@moduledoc """
This module contains the logic for the Status stage of the server.
This module contains the Status logic, this is not really its own login state since the client
is only asking a bit of information.
"""
require Logger
use Amethyst.Server

View File

@ -22,8 +22,7 @@ defmodule Amethyst.MixProject do
# Run "mix help deps" to learn about dependencies.
defp deps do
[
{:credo, "~> 1.7", only: [:dev, :test], runtime: false},
{:uuid, "~> 1.1"}
{:credo, "~> 1.7", only: [:dev, :test], runtime: false}
]
end
end

View File

@ -3,5 +3,4 @@
"credo": {:hex, :credo, "1.7.7", "771445037228f763f9b2afd612b6aa2fd8e28432a95dbbc60d8e03ce71ba4446", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "8bc87496c9aaacdc3f90f01b7b0582467b69b4bd2441fe8aae3109d843cc2f2e"},
"file_system": {:hex, :file_system, "1.0.0", "b689cc7dcee665f774de94b5a832e578bd7963c8e637ef940cd44327db7de2cd", [:mix], [], "hexpm", "6752092d66aec5a10e662aefeed8ddb9531d79db0bc145bb8c40325ca1d8536d"},
"jason": {:hex, :jason, "1.4.3", "d3f984eeb96fe53b85d20e0b049f03e57d075b5acda3ac8d465c969a2536c17b", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "9a90e868927f7c777689baa16d86f4d0e086d968db5c05d917ccff6d443e58a3"},
"uuid": {:hex, :uuid, "1.1.8", "e22fc04499de0de3ed1116b770c7737779f226ceefa0badb3592e64d5cfb4eb9", [:mix], [], "hexpm", "c790593b4c3b601f5dc2378baae7efaf5b3d73c4c6456ba85759905be792f2ac"},
}