Begin work on implementing handshake
All checks were successful
Build & Test / nix-build (push) Successful in 1m5s
All checks were successful
Build & Test / nix-build (push) Successful in 1m5s
This commit is contained in:
parent
92325c05b1
commit
c742fa7c97
@ -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 -> 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)
|
||||
|
70
lib/data.ex
70
lib/data.ex
@ -65,62 +65,64 @@ defmodule Amethyst.Minecraft.Read do
|
||||
values they have read.
|
||||
|
||||
You may use the helper function Amethyst.Minecraft.Read.start/1 to start the chain with a binary buffer.
|
||||
The return value of the chain is a tuple containing the list of values and the remaining binary buffer, note
|
||||
that the values are ordered from last to first, so in the opposite order of the chain.
|
||||
The return value of the chain is a tuple containing the list of values and the remaining binary buffer.
|
||||
|
||||
iex> alias Amethyst.Minecraft.Read
|
||||
iex> {[_, _, _], ""} = Read.start(<<1, 999::16, 64>>) |> Read.bool() |> Read.short() |> Read.byte()
|
||||
{[64, 999, true], ""}
|
||||
iex> {[_, _, _], ""} = Read.start(<<1, 999::16, 64>>) |> Read.bool() |> Read.short() |> Read.byte() |> Read.stop()
|
||||
{[true, 999, 64], ""}
|
||||
"""
|
||||
|
||||
def start(binary) do
|
||||
{[], binary}
|
||||
{[], binary, :reversed}
|
||||
end
|
||||
def stop({acc, rest, :reversed}) do
|
||||
{Enum.reverse(acc), rest}
|
||||
end
|
||||
|
||||
def bool({acc, <<value, rest::binary>>}) do
|
||||
{[value != 0 | acc], rest}
|
||||
def bool({acc, <<value, rest::binary>>, :reversed}) do
|
||||
{[value != 0 | acc], rest, :reversed}
|
||||
end
|
||||
|
||||
def byte({acc, <<value::big-signed, rest::binary>>}) do
|
||||
{[value | acc], rest}
|
||||
def byte({acc, <<value::big-signed, rest::binary>>, :reversed}) do
|
||||
{[value | acc], rest, :reversed}
|
||||
end
|
||||
|
||||
def ubyte({acc, <<value::big-unsigned, rest::binary>>}) do
|
||||
{[value | acc], rest}
|
||||
def ubyte({acc, <<value::big-unsigned, rest::binary>>, :reversed}) do
|
||||
{[value | acc], rest, :reversed}
|
||||
end
|
||||
|
||||
def short({acc, <<value::16-big-signed, rest::binary>>}) do
|
||||
{[value | acc], rest}
|
||||
def short({acc, <<value::16-big-signed, rest::binary>>, :reversed}) do
|
||||
{[value | acc], rest, :reversed}
|
||||
end
|
||||
|
||||
def ushort({acc, <<value::16-big-unsigned, rest::binary>>}) do
|
||||
{[value | acc], rest}
|
||||
def ushort({acc, <<value::16-big-unsigned, rest::binary>>, :reversed}) do
|
||||
{[value | acc], rest, :reversed}
|
||||
end
|
||||
|
||||
def int({acc, <<value::32-big-signed, rest::binary>>}) do
|
||||
{[value | acc], rest}
|
||||
def int({acc, <<value::32-big-signed, rest::binary>>, :reversed}) do
|
||||
{[value | acc], rest, :reversed}
|
||||
end
|
||||
|
||||
def long({acc, <<value::64-big-signed, rest::binary>>}) do
|
||||
{[value | acc], rest}
|
||||
def long({acc, <<value::64-big-signed, rest::binary>>, :reversed}) do
|
||||
{[value | acc], rest, :reversed}
|
||||
end
|
||||
|
||||
def float({acc, <<value::32-float-big, rest::binary>>}) do
|
||||
{[value | acc], rest}
|
||||
def float({acc, <<value::32-float-big, rest::binary>>, :reversed}) do
|
||||
{[value | acc], rest, :reversed}
|
||||
end
|
||||
|
||||
def double({acc, <<value::64-float-big, rest::binary>>}) do
|
||||
{[value | acc], rest}
|
||||
def double({acc, <<value::64-float-big, rest::binary>>, :reversed}) do
|
||||
{[value | acc], rest, :reversed}
|
||||
end
|
||||
|
||||
def varint(tuple, read \\ 0, nacc \\ 0)
|
||||
def varint({acc, <<1::1, value::7, rest::binary>>}, read, nacc) when read < 5 do
|
||||
varint({acc, rest}, read + 1, nacc + (value <<< (7 * read)))
|
||||
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)))
|
||||
end
|
||||
def varint({acc, <<0::1, value::7, rest::binary>>}, read, nacc) do
|
||||
def varint({acc, <<0::1, value::7, rest::binary>>, :reversed}, read, nacc) do
|
||||
total = nacc + (value <<< (7 * read))
|
||||
<<value::32-signed>> = <<total::32-unsigned>>
|
||||
{[value | acc], rest}
|
||||
{[value | acc], rest, :reversed}
|
||||
end
|
||||
def varint(_, read, _) when read >= 5 do
|
||||
raise RuntimeError, "Got a varint which is too big!"
|
||||
@ -130,13 +132,13 @@ defmodule Amethyst.Minecraft.Read do
|
||||
end
|
||||
|
||||
def varlong(tuple, read \\ 0, nacc \\ 0)
|
||||
def varlong({acc, <<1::1, value::7, rest::binary>>}, read, nacc) when read < 10 do
|
||||
varlong({acc, rest}, read + 1, nacc + (value <<< (7 * read)))
|
||||
def varlong({acc, <<1::1, value::7, rest::binary>>, :reversed}, read, nacc) when read < 10 do
|
||||
varlong({acc, rest, :reversed}, read + 1, nacc + (value <<< (7 * read)))
|
||||
end
|
||||
def varlong({acc, <<0::1, value::7, rest::binary>>}, read, nacc) do
|
||||
def varlong({acc, <<0::1, value::7, rest::binary>>, :reversed}, read, nacc) do
|
||||
total = nacc + (value <<< (7 * read))
|
||||
<<value::64-signed>> = <<total::64-unsigned>>
|
||||
{[value | acc], rest}
|
||||
{[value | acc], rest, :reversed}
|
||||
end
|
||||
def varlong(_, read, _) when read >= 10 do
|
||||
raise RuntimeError, "Got a varint which is too big!"
|
||||
@ -145,9 +147,9 @@ defmodule Amethyst.Minecraft.Read do
|
||||
raise RuntimeError, "Got an incomplete varint!"
|
||||
end
|
||||
|
||||
def string({acc, data}) do
|
||||
{[length], rest} = start(data) |> varint()
|
||||
def string({acc, data, :reversed}) do
|
||||
{[length], rest, :reversed} = start(data) |> varint()
|
||||
<<value::binary-size(length), rest::binary>> = rest
|
||||
{[value | acc], rest}
|
||||
{[value | acc], rest, :reversed}
|
||||
end
|
||||
end
|
||||
|
36
lib/servers/generic.ex
Normal file
36
lib/servers/generic.ex
Normal file
@ -0,0 +1,36 @@
|
||||
# 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.Generic do
|
||||
@moduledoc """
|
||||
This module includes generic logic which may be used by all stages of the server, including,
|
||||
for instance, listening for packets.
|
||||
"""
|
||||
|
||||
alias Amethyst.Minecraft.Read
|
||||
|
||||
def get_packet(client) do
|
||||
|
||||
end
|
||||
|
||||
defp get_varint(client, acc) do
|
||||
{:ok, byte} = :gen_tcp.recv(client, 1)
|
||||
case byte do
|
||||
<<0::1, _::7>> -> Read.start(acc <> byte) |> Read.varint() |> Read.stop()
|
||||
<<1::1, _::7>> -> get_varint(client, acc <> byte)
|
||||
end
|
||||
end
|
||||
end
|
@ -18,4 +18,25 @@ defmodule Amethyst.Server.Stage1 do
|
||||
@moduledoc """
|
||||
This module contains the stage 1 (Handshaking) server logic.
|
||||
"""
|
||||
alias Amethyst.Minecraft.Read
|
||||
|
||||
def serve(client) do
|
||||
|
||||
end
|
||||
|
||||
## DESERIALIZATION
|
||||
# Handshake https://wiki.vg/Protocol#Handshake
|
||||
def deserialize(0x00, <<data::binary>>) do
|
||||
{[ver, addr, port, next], ""} = Read.start(data) |> Read.varint() |> Read.string() |> Read.ushort() |> Read.varint() |> Read.stop()
|
||||
next = case next do
|
||||
1 -> :status
|
||||
2 -> :login
|
||||
3 -> :transfer
|
||||
_ -> raise RuntimeError, "Client requested moving to an unknown state!"
|
||||
end
|
||||
%{type: :handshake, version: ver, address: addr, port: port, next: next}
|
||||
end
|
||||
def deserialize(<<type, _::binary>>) do
|
||||
raise RuntimeError, "Got unknown packet type #{type}!"
|
||||
end
|
||||
end
|
||||
|
@ -1,5 +1,5 @@
|
||||
defmodule WriteTest do
|
||||
use ExUnit.Case
|
||||
use ExUnit.Case, async: true
|
||||
|
||||
@moduledoc """
|
||||
This module contains tests for the Amethyst.Minecraft.Write module.
|
||||
@ -34,36 +34,37 @@ defmodule WriteTest do
|
||||
end
|
||||
|
||||
defmodule ReadTest do
|
||||
use ExUnit.Case
|
||||
use ExUnit.Case, async: true
|
||||
|
||||
@moduledoc """
|
||||
This module contains tests for the Amethyst.Minecraft.Read module.
|
||||
"""
|
||||
|
||||
doctest Amethyst.Minecraft.Read
|
||||
alias Amethyst.Minecraft.Read
|
||||
|
||||
test "reading a varint" do
|
||||
assert Amethyst.Minecraft.Read.start(<<0x00>>) |> Amethyst.Minecraft.Read.varint() == {[0], ""}
|
||||
assert Amethyst.Minecraft.Read.start(<<0x7F>>) |> Amethyst.Minecraft.Read.varint() == {[127], ""}
|
||||
assert Amethyst.Minecraft.Read.start(<<0x80, 0x01>>) |> Amethyst.Minecraft.Read.varint() == {[128], ""}
|
||||
assert Amethyst.Minecraft.Read.start(<<0xFF, 0x01>>) |> Amethyst.Minecraft.Read.varint() == {[255], ""}
|
||||
assert Amethyst.Minecraft.Read.start(<<0xFF, 0xFF, 0xFF, 0xFF, 0x07>>) |> Amethyst.Minecraft.Read.varint() == {[2_147_483_647], ""}
|
||||
assert Amethyst.Minecraft.Read.start(<<0xFF, 0xFF, 0xFF, 0xFF, 0x0F>>) |> Amethyst.Minecraft.Read.varint() == {[-1], ""}
|
||||
assert Amethyst.Minecraft.Read.start(<<0x80, 0x80, 0x80, 0x80, 0x08>>) |> Amethyst.Minecraft.Read.varint() == {[-2_147_483_648], ""}
|
||||
assert Read.start(<<0x00>>) |> Read.varint() |> Read.stop() == {[0], ""}
|
||||
assert Read.start(<<0x7F>>) |> Read.varint() |> Read.stop() == {[127], ""}
|
||||
assert Read.start(<<0x80, 0x01>>) |> Read.varint() |> Read.stop() == {[128], ""}
|
||||
assert Read.start(<<0xFF, 0x01>>) |> Read.varint() |> Read.stop() == {[255], ""}
|
||||
assert Read.start(<<0xFF, 0xFF, 0xFF, 0xFF, 0x07>>) |> Read.varint() |> Read.stop() == {[2_147_483_647], ""}
|
||||
assert Read.start(<<0xFF, 0xFF, 0xFF, 0xFF, 0x0F>>) |> Read.varint() |> Read.stop() == {[-1], ""}
|
||||
assert Read.start(<<0x80, 0x80, 0x80, 0x80, 0x08>>) |> Read.varint() |> Read.stop() == {[-2_147_483_648], ""}
|
||||
end
|
||||
|
||||
test "reading a varlong" do
|
||||
assert Amethyst.Minecraft.Read.start(<<0x00>>) |> Amethyst.Minecraft.Read.varlong() == {[0], ""}
|
||||
assert Amethyst.Minecraft.Read.start(<<0x7F>>) |> Amethyst.Minecraft.Read.varlong() == {[127], ""}
|
||||
assert Amethyst.Minecraft.Read.start(<<0x80, 0x01>>) |> Amethyst.Minecraft.Read.varlong() == {[128], ""}
|
||||
assert Amethyst.Minecraft.Read.start(<<0xFF, 0x01>>) |> Amethyst.Minecraft.Read.varlong() == {[255], ""}
|
||||
assert Amethyst.Minecraft.Read.start(<<0xFF, 0xFF, 0xFF, 0xFF, 0x07>>) |> Amethyst.Minecraft.Read.varlong() == {[2_147_483_647], ""}
|
||||
assert Amethyst.Minecraft.Read.start(<<0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F>>) |> Amethyst.Minecraft.Read.varlong() == {[9_223_372_036_854_775_807], ""}
|
||||
assert Amethyst.Minecraft.Read.start(<<0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01>>) |> Amethyst.Minecraft.Read.varlong() == {[-1], ""}
|
||||
assert Read.start(<<0x00>>) |> Read.varlong() |> Read.stop() == {[0], ""}
|
||||
assert Read.start(<<0x7F>>) |> Read.varlong() |> Read.stop() == {[127], ""}
|
||||
assert Read.start(<<0x80, 0x01>>) |> Read.varlong() |> Read.stop() == {[128], ""}
|
||||
assert Read.start(<<0xFF, 0x01>>) |> Read.varlong() |> Read.stop() == {[255], ""}
|
||||
assert Read.start(<<0xFF, 0xFF, 0xFF, 0xFF, 0x07>>) |> Read.varlong() |> Read.stop() == {[2_147_483_647], ""}
|
||||
assert Read.start(<<0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F>>) |> Read.varlong() |> Read.stop() == {[9_223_372_036_854_775_807], ""}
|
||||
assert Read.start(<<0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01>>) |> Read.varlong() |> Read.stop() == {[-1], ""}
|
||||
end
|
||||
|
||||
test "reading a string" do
|
||||
assert Amethyst.Minecraft.Read.start(<<0x0D, "Hello, world!">>) |> Amethyst.Minecraft.Read.string() == {["Hello, world!"], ""}
|
||||
assert Amethyst.Minecraft.Read.start(<<0x00>>) |> Amethyst.Minecraft.Read.string() == {[""], ""}
|
||||
assert Read.start(<<0x0D, "Hello, world!">>) |> Read.string() |> Read.stop() == {["Hello, world!"], ""}
|
||||
assert Read.start(<<0x00>>) |> Read.string() |> Read.stop() == {[""], ""}
|
||||
end
|
||||
end
|
||||
|
Loading…
Reference in New Issue
Block a user