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()
|
@spec loop_acceptor(socket :: :gen_tcp.socket()) :: no_return()
|
||||||
defp loop_acceptor(socket) do
|
defp loop_acceptor(socket) do
|
||||||
{:ok, client} = :gen_tcp.accept(socket)
|
{: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)
|
:ok = :gen_tcp.controlling_process(client, pid)
|
||||||
Logger.info("Received connection from #{inspect(client)}, assigned to PID #{inspect(pid)}")
|
Logger.info("Received connection from #{inspect(client)}, assigned to PID #{inspect(pid)}")
|
||||||
loop_acceptor(socket)
|
loop_acceptor(socket)
|
||||||
|
70
lib/data.ex
70
lib/data.ex
@ -65,62 +65,64 @@ defmodule Amethyst.Minecraft.Read do
|
|||||||
values they have read.
|
values they have read.
|
||||||
|
|
||||||
You may use the helper function Amethyst.Minecraft.Read.start/1 to start the chain with a binary buffer.
|
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
|
The return value of the chain is a tuple containing the list of values and the remaining binary buffer.
|
||||||
that the values are ordered from last to first, so in the opposite order of the chain.
|
|
||||||
|
|
||||||
iex> alias Amethyst.Minecraft.Read
|
iex> alias Amethyst.Minecraft.Read
|
||||||
iex> {[_, _, _], ""} = Read.start(<<1, 999::16, 64>>) |> Read.bool() |> Read.short() |> Read.byte()
|
iex> {[_, _, _], ""} = Read.start(<<1, 999::16, 64>>) |> Read.bool() |> Read.short() |> Read.byte() |> Read.stop()
|
||||||
{[64, 999, true], ""}
|
{[true, 999, 64], ""}
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def start(binary) do
|
def start(binary) do
|
||||||
{[], binary}
|
{[], binary, :reversed}
|
||||||
|
end
|
||||||
|
def stop({acc, rest, :reversed}) do
|
||||||
|
{Enum.reverse(acc), rest}
|
||||||
end
|
end
|
||||||
|
|
||||||
def bool({acc, <<value, rest::binary>>}) do
|
def bool({acc, <<value, rest::binary>>, :reversed}) do
|
||||||
{[value != 0 | acc], rest}
|
{[value != 0 | acc], rest, :reversed}
|
||||||
end
|
end
|
||||||
|
|
||||||
def byte({acc, <<value::big-signed, rest::binary>>}) do
|
def byte({acc, <<value::big-signed, rest::binary>>, :reversed}) do
|
||||||
{[value | acc], rest}
|
{[value | acc], rest, :reversed}
|
||||||
end
|
end
|
||||||
|
|
||||||
def ubyte({acc, <<value::big-unsigned, rest::binary>>}) do
|
def ubyte({acc, <<value::big-unsigned, rest::binary>>, :reversed}) do
|
||||||
{[value | acc], rest}
|
{[value | acc], rest, :reversed}
|
||||||
end
|
end
|
||||||
|
|
||||||
def short({acc, <<value::16-big-signed, rest::binary>>}) do
|
def short({acc, <<value::16-big-signed, rest::binary>>, :reversed}) do
|
||||||
{[value | acc], rest}
|
{[value | acc], rest, :reversed}
|
||||||
end
|
end
|
||||||
|
|
||||||
def ushort({acc, <<value::16-big-unsigned, rest::binary>>}) do
|
def ushort({acc, <<value::16-big-unsigned, rest::binary>>, :reversed}) do
|
||||||
{[value | acc], rest}
|
{[value | acc], rest, :reversed}
|
||||||
end
|
end
|
||||||
|
|
||||||
def int({acc, <<value::32-big-signed, rest::binary>>}) do
|
def int({acc, <<value::32-big-signed, rest::binary>>, :reversed}) do
|
||||||
{[value | acc], rest}
|
{[value | acc], rest, :reversed}
|
||||||
end
|
end
|
||||||
|
|
||||||
def long({acc, <<value::64-big-signed, rest::binary>>}) do
|
def long({acc, <<value::64-big-signed, rest::binary>>, :reversed}) do
|
||||||
{[value | acc], rest}
|
{[value | acc], rest, :reversed}
|
||||||
end
|
end
|
||||||
|
|
||||||
def float({acc, <<value::32-float-big, rest::binary>>}) do
|
def float({acc, <<value::32-float-big, rest::binary>>, :reversed}) do
|
||||||
{[value | acc], rest}
|
{[value | acc], rest, :reversed}
|
||||||
end
|
end
|
||||||
|
|
||||||
def double({acc, <<value::64-float-big, rest::binary>>}) do
|
def double({acc, <<value::64-float-big, rest::binary>>, :reversed}) do
|
||||||
{[value | acc], rest}
|
{[value | acc], rest, :reversed}
|
||||||
end
|
end
|
||||||
|
|
||||||
def varint(tuple, read \\ 0, nacc \\ 0)
|
def varint(tuple, read \\ 0, nacc \\ 0)
|
||||||
def varint({acc, <<1::1, value::7, rest::binary>>}, read, nacc) when read < 5 do
|
def varint({acc, <<1::1, value::7, rest::binary>>, :reversed}, read, nacc) when read < 5 do
|
||||||
varint({acc, rest}, read + 1, nacc + (value <<< (7 * read)))
|
varint({acc, rest, :reversed}, read + 1, nacc + (value <<< (7 * read)))
|
||||||
end
|
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))
|
total = nacc + (value <<< (7 * read))
|
||||||
<<value::32-signed>> = <<total::32-unsigned>>
|
<<value::32-signed>> = <<total::32-unsigned>>
|
||||||
{[value | acc], rest}
|
{[value | acc], rest, :reversed}
|
||||||
end
|
end
|
||||||
def varint(_, read, _) when read >= 5 do
|
def varint(_, read, _) when read >= 5 do
|
||||||
raise RuntimeError, "Got a varint which is too big!"
|
raise RuntimeError, "Got a varint which is too big!"
|
||||||
@ -130,13 +132,13 @@ defmodule Amethyst.Minecraft.Read do
|
|||||||
end
|
end
|
||||||
|
|
||||||
def varlong(tuple, read \\ 0, nacc \\ 0)
|
def varlong(tuple, read \\ 0, nacc \\ 0)
|
||||||
def varlong({acc, <<1::1, value::7, rest::binary>>}, read, nacc) when read < 10 do
|
def varlong({acc, <<1::1, value::7, rest::binary>>, :reversed}, read, nacc) when read < 10 do
|
||||||
varlong({acc, rest}, read + 1, nacc + (value <<< (7 * read)))
|
varlong({acc, rest, :reversed}, read + 1, nacc + (value <<< (7 * read)))
|
||||||
end
|
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))
|
total = nacc + (value <<< (7 * read))
|
||||||
<<value::64-signed>> = <<total::64-unsigned>>
|
<<value::64-signed>> = <<total::64-unsigned>>
|
||||||
{[value | acc], rest}
|
{[value | acc], rest, :reversed}
|
||||||
end
|
end
|
||||||
def varlong(_, read, _) when read >= 10 do
|
def varlong(_, read, _) when read >= 10 do
|
||||||
raise RuntimeError, "Got a varint which is too big!"
|
raise RuntimeError, "Got a varint which is too big!"
|
||||||
@ -145,9 +147,9 @@ defmodule Amethyst.Minecraft.Read do
|
|||||||
raise RuntimeError, "Got an incomplete varint!"
|
raise RuntimeError, "Got an incomplete varint!"
|
||||||
end
|
end
|
||||||
|
|
||||||
def string({acc, data}) do
|
def string({acc, data, :reversed}) do
|
||||||
{[length], rest} = start(data) |> varint()
|
{[length], rest, :reversed} = start(data) |> varint()
|
||||||
<<value::binary-size(length), rest::binary>> = rest
|
<<value::binary-size(length), rest::binary>> = rest
|
||||||
{[value | acc], rest}
|
{[value | acc], rest, :reversed}
|
||||||
end
|
end
|
||||||
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 """
|
@moduledoc """
|
||||||
This module contains the stage 1 (Handshaking) server logic.
|
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
|
end
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
defmodule WriteTest do
|
defmodule WriteTest do
|
||||||
use ExUnit.Case
|
use ExUnit.Case, async: true
|
||||||
|
|
||||||
@moduledoc """
|
@moduledoc """
|
||||||
This module contains tests for the Amethyst.Minecraft.Write module.
|
This module contains tests for the Amethyst.Minecraft.Write module.
|
||||||
@ -34,36 +34,37 @@ defmodule WriteTest do
|
|||||||
end
|
end
|
||||||
|
|
||||||
defmodule ReadTest do
|
defmodule ReadTest do
|
||||||
use ExUnit.Case
|
use ExUnit.Case, async: true
|
||||||
|
|
||||||
@moduledoc """
|
@moduledoc """
|
||||||
This module contains tests for the Amethyst.Minecraft.Read module.
|
This module contains tests for the Amethyst.Minecraft.Read module.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
doctest Amethyst.Minecraft.Read
|
doctest Amethyst.Minecraft.Read
|
||||||
|
alias Amethyst.Minecraft.Read
|
||||||
|
|
||||||
test "reading a varint" do
|
test "reading a varint" do
|
||||||
assert Amethyst.Minecraft.Read.start(<<0x00>>) |> Amethyst.Minecraft.Read.varint() == {[0], ""}
|
assert Read.start(<<0x00>>) |> Read.varint() |> Read.stop() == {[0], ""}
|
||||||
assert Amethyst.Minecraft.Read.start(<<0x7F>>) |> Amethyst.Minecraft.Read.varint() == {[127], ""}
|
assert Read.start(<<0x7F>>) |> Read.varint() |> Read.stop() == {[127], ""}
|
||||||
assert Amethyst.Minecraft.Read.start(<<0x80, 0x01>>) |> Amethyst.Minecraft.Read.varint() == {[128], ""}
|
assert Read.start(<<0x80, 0x01>>) |> Read.varint() |> Read.stop() == {[128], ""}
|
||||||
assert Amethyst.Minecraft.Read.start(<<0xFF, 0x01>>) |> Amethyst.Minecraft.Read.varint() == {[255], ""}
|
assert Read.start(<<0xFF, 0x01>>) |> Read.varint() |> Read.stop() == {[255], ""}
|
||||||
assert Amethyst.Minecraft.Read.start(<<0xFF, 0xFF, 0xFF, 0xFF, 0x07>>) |> Amethyst.Minecraft.Read.varint() == {[2_147_483_647], ""}
|
assert Read.start(<<0xFF, 0xFF, 0xFF, 0xFF, 0x07>>) |> Read.varint() |> Read.stop() == {[2_147_483_647], ""}
|
||||||
assert Amethyst.Minecraft.Read.start(<<0xFF, 0xFF, 0xFF, 0xFF, 0x0F>>) |> Amethyst.Minecraft.Read.varint() == {[-1], ""}
|
assert Read.start(<<0xFF, 0xFF, 0xFF, 0xFF, 0x0F>>) |> Read.varint() |> Read.stop() == {[-1], ""}
|
||||||
assert Amethyst.Minecraft.Read.start(<<0x80, 0x80, 0x80, 0x80, 0x08>>) |> Amethyst.Minecraft.Read.varint() == {[-2_147_483_648], ""}
|
assert Read.start(<<0x80, 0x80, 0x80, 0x80, 0x08>>) |> Read.varint() |> Read.stop() == {[-2_147_483_648], ""}
|
||||||
end
|
end
|
||||||
|
|
||||||
test "reading a varlong" do
|
test "reading a varlong" do
|
||||||
assert Amethyst.Minecraft.Read.start(<<0x00>>) |> Amethyst.Minecraft.Read.varlong() == {[0], ""}
|
assert Read.start(<<0x00>>) |> Read.varlong() |> Read.stop() == {[0], ""}
|
||||||
assert Amethyst.Minecraft.Read.start(<<0x7F>>) |> Amethyst.Minecraft.Read.varlong() == {[127], ""}
|
assert Read.start(<<0x7F>>) |> Read.varlong() |> Read.stop() == {[127], ""}
|
||||||
assert Amethyst.Minecraft.Read.start(<<0x80, 0x01>>) |> Amethyst.Minecraft.Read.varlong() == {[128], ""}
|
assert Read.start(<<0x80, 0x01>>) |> Read.varlong() |> Read.stop() == {[128], ""}
|
||||||
assert Amethyst.Minecraft.Read.start(<<0xFF, 0x01>>) |> Amethyst.Minecraft.Read.varlong() == {[255], ""}
|
assert Read.start(<<0xFF, 0x01>>) |> Read.varlong() |> Read.stop() == {[255], ""}
|
||||||
assert Amethyst.Minecraft.Read.start(<<0xFF, 0xFF, 0xFF, 0xFF, 0x07>>) |> Amethyst.Minecraft.Read.varlong() == {[2_147_483_647], ""}
|
assert Read.start(<<0xFF, 0xFF, 0xFF, 0xFF, 0x07>>) |> Read.varlong() |> Read.stop() == {[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 Read.start(<<0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F>>) |> Read.varlong() |> Read.stop() == {[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(<<0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x01>>) |> Read.varlong() |> Read.stop() == {[-1], ""}
|
||||||
end
|
end
|
||||||
|
|
||||||
test "reading a string" do
|
test "reading a string" do
|
||||||
assert Amethyst.Minecraft.Read.start(<<0x0D, "Hello, world!">>) |> Amethyst.Minecraft.Read.string() == {["Hello, world!"], ""}
|
assert Read.start(<<0x0D, "Hello, world!">>) |> Read.string() |> Read.stop() == {["Hello, world!"], ""}
|
||||||
assert Amethyst.Minecraft.Read.start(<<0x00>>) |> Amethyst.Minecraft.Read.string() == {[""], ""}
|
assert Read.start(<<0x00>>) |> Read.string() |> Read.stop() == {[""], ""}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
Loading…
Reference in New Issue
Block a user