All checks were successful
Build & Test / nix-build (push) Successful in 1m31s
82 lines
3.1 KiB
Elixir
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
|