Kodi Craft 5413708b29
All checks were successful
Build & Test / nix-build (push) Successful in 1m31s
Begin complete rewrite of communication system
2024-09-05 19:06:35 +02:00

82 lines
3.1 KiB
Elixir

# 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.Macros do
defmacro defpacket_serverbound(name, id, version, signature) do
quote do
def deserialize(unquote(id), unquote(version), data) do
{packet, ""} = Amethyst.ConnectionState.Macros.read_signature(data, unquote(signature))
packet |> Map.put(:packet_type, unquote(name))
end
end
end
defmacro defpacket_clientbound(name, id, version, signature) do
quote do
def serialize(%{packet_type: unquote(name)} = packet, unquote(version)) do
Amethyst.Minecraft.Write.varint(unquote(id)) <> Amethyst.ConnectionState.Macros.write_signature(packet, unquote(signature))
end
end
end
alias Amethyst.Minecraft.Read
alias Amethyst.Minecraft.Write
def read_signature(data, signature) do
names = Enum.map(signature, fn {name, _type} -> name end)
{got, rest} = Enum.reduce(signature, Read.start(data), fn {_name, type}, {acc, rest, :reversed} ->
case type do
{:optional, t} ->
{[exists], rest} = Read.start(rest) |> Read.bool() |> Read.stop()
if exists do
apply(Read, t, [{acc, rest, :reversed}])
else
{[nil | acc], rest, :reversed}
end
{:array, signature} ->
{[count], rest} = Read.start(rest) |> Read.varint() |> Read.stop()
{items, rest} = Enum.reduce(1..count, {[], rest}, fn _, {acc, rest} ->
{item, rest} = read_signature(rest, signature)
{[item | acc], rest}
end)
{[Enum.reverse(items) | acc], rest, :reversed}
t -> apply(Read, t, [{acc, rest, :reversed}])
end
end) |> Read.stop()
{Enum.zip(names, got) |> Map.new(), rest}
end
def write_signature(packet, signature) do
data = Enum.reduce(signature, "", fn {name, type}, acc ->
#acc <> apply(Write, type, [Map.get(packet, name)])
case type do
{:optional, t} ->
case Map.get(packet, name) do
nil -> acc <> Write.bool(false)
_ -> acc <> Write.bool(true) <> apply(Write, t, [Map.get(packet, name)])
end
{:array, signature} ->
acc <> Write.varint(Enum.count(Map.get(packet, name))) <>
Enum.reduce(Map.get(packet, name), "", fn item, acc ->
acc <> write_signature(item, signature)
end)
t -> acc <> apply(Write, t, [Map.get(packet, name)])
end
end)
end
end