Flattened writing interface
All checks were successful
Build & Test / nix-build (push) Successful in 1m12s

This commit is contained in:
Kodi Craft 2024-08-01 05:14:05 +02:00
parent 8e57427c99
commit 9f9526f5de
Signed by: kodi
GPG Key ID: 69D9EED60B242822
3 changed files with 63 additions and 18 deletions

View File

@ -18,21 +18,55 @@ defmodule Amethyst.Minecraft.Write do
import Bitwise
@moduledoc """
This module contains functions for writing certain Minecraft data types which are more complex
than simple binary data.
This module contains functions for writing Minecraft data.
Each function in this module takes in an input of the proper type and returns a binary
of the encoded data.
"""
def uuid(uuid) do
def uuid(uuid) when is_binary(uuid) do
UUID.string_to_binary!(uuid)
end
def bool(value) do
def bool(value) when is_boolean(value) do
case value do
true -> <<0x01::8>>
false -> <<0x00::8>>
end
end
def byte(value) when value in -128..127 do
<<value::8-signed-big>>
end
def ubyte(value) when value in 0..255 do
<<value::8-unsigned-big>>
end
def short(value) when value in -32_768..32_767 do
<<value::16-signed-big>>
end
def ushort(value) when value in 0..65_535 do
<<value::16-unsigned-big>>
end
def int(value) when value in -2_147_483_648..2_147_483_647 do
<<value::32-signed-big>>
end
def long(value) when value in -9_223_372_036_854_775_808..9_223_372_036_854_775_807 do
<<value::64-signed-big>>
end
def float(value) when is_number(value) do
<<value::32-float>>
end
def double(value) when is_number(value) do
<<value::64-float>>
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)
@ -73,13 +107,12 @@ defmodule Amethyst.Minecraft.Read do
import Bitwise
@moduledoc """
This module contains functions for reading Minecraft data. Unlike Amethyst.Minecraft.Write, this
includes all supported data types in order to make the interface more consistent.
This module contains functions for reading Minecraft data.
These functions allow you to chain them into eachother, at the end they will produce a list of all the
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.
iex> alias Amethyst.Minecraft.Read
@ -87,9 +120,15 @@ defmodule Amethyst.Minecraft.Read do
{[true, 999, 64], ""}
"""
@doc """
This function structures an input binary to be used by the functions in `Amethyst.Minecraft.Read`.
"""
def start(binary) do
{[], binary, :reversed}
end
@doc """
This function structures the result of the functions in `Amethyst.Minecraft.Read` to be used in the same order they were read.
"""
def stop({acc, rest, :reversed}) do
{Enum.reverse(acc), rest}
end
@ -139,6 +178,9 @@ defmodule Amethyst.Minecraft.Read do
{[data | acc], rest, :reversed}
end
@doc """
Reads a varint. `read` tracks the number of bytes read and `nacc` tracks the number being read.
"""
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)))
@ -155,6 +197,9 @@ defmodule Amethyst.Minecraft.Read do
raise RuntimeError, "Got an incomplete varint!"
end
@doc """
Reads a varlong. `read` tracks the number of bytes read and `nacc` tracks the number being read.
"""
def varlong(tuple, read \\ 0, nacc \\ 0)
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)))
@ -165,10 +210,10 @@ defmodule Amethyst.Minecraft.Read do
{[value | acc], rest, :reversed}
end
def varlong(_, read, _) when read >= 10 do
raise RuntimeError, "Got a varint which is too big!"
raise RuntimeError, "Got a varlong which is too big!"
end
def varlong({_, ""}, _, _) do
raise RuntimeError, "Got an incomplete varint!"
raise RuntimeError, "Got an incomplete varlong!"
end
def string({acc, data, :reversed}) do

View File

@ -44,17 +44,17 @@ defmodule Amethyst.Server.Play do
reduce_debug, enable_respawn_screen, limited_crafting,
dim_type, dim_name, hashed_seed, gamemode, prev_gm,
is_debug, is_flat, death_loc, portal_cooldown, enforce_chat}) do
# TODO: this singlehandedly made me regret not making the write API better, please rework :(
# TODO: This is a big unreadable slab of serialization, it needs a proper rework at some point
Write.varint(0x2B) <>
<<eid::big-signed-32>> <> if(hardcore, do: <<1::big-8>>, else: <<0::big-8>>) <>
Write.int(eid) <> Write.bool(hardcore) <>
Write.varint(length(dimensions)) <> Enum.reduce(dimensions, "", fn dim, acc -> acc <> Write.string(dim) end) <>
Write.varint(max_players) <> Write.varint(view_distance) <> Write.varint(simulation_distance) <> if(reduce_debug, do: <<1::big-8>>, else: <<0::big-8>>) <>
if(enable_respawn_screen, do: <<1::big-8>>, else: <<0::big-8>>)
if(limited_crafting, do: <<1::big-8>>, else: <<0::big-8>>) <> Write.varint(dim_type) <> Write.string(dim_name) <>
hashed_seed <> <<gamemode_id(gamemode)::unsigned-big-8>> <> <<gamemode_id(prev_gm)::signed-big-8>> <>
if(is_debug, do: <<1::big-8>>, else: <<0::big-8>>) <> if(is_flat, do: <<1::big-8>>, else: <<0::big-8>>) <>
Write.varint(max_players) <> Write.varint(view_distance) <> Write.varint(simulation_distance) <> Write.bool(reduce_debug) <>
Write.bool(enable_respawn_screen) <>
Write.bool(limited_crafting) <> Write.varint(dim_type) <> Write.string(dim_name) <>
hashed_seed <> Write.ubyte(gamemode_id(gamemode)) <> Write.byte(gamemode_id(prev_gm)) <>
Write.bool(is_debug) <> Write.bool(is_flat) <>
if(death_loc == nil, do: <<0::big-8>>, else: <<1::big-8>> <> Write.string(elem(death_loc, 0)) <> Write.position(elem(death_loc, 1))) <>
Write.varint(portal_cooldown) <> if(enforce_chat, do: <<1::big-8>>, else: <<0::big-8>>)
Write.varint(portal_cooldown) <> Write.bool(enforce_chat)
end
def serialize(packet) do
raise ArgumentError, "Tried serializing unknown packet #{inspect(packet)}"

View File

@ -51,7 +51,7 @@ defmodule Amethyst.Server.Status do
Write.varint(0x00) <> Write.string(data)
end
def serialize({:ping_response, payload}) do
Write.varint(0x01) <> <<payload::64-big-signed>>
Write.varint(0x01) <> Write.long(payload)
end
def serialize(packet) do
raise ArgumentError, "Tried serializing unknown packet #{inspect(packet)}"