diff --git a/lib/data.ex b/lib/data.ex index 7ffe1db..239e33a 100644 --- a/lib/data.ex +++ b/lib/data.ex @@ -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 + <> + end + + def ubyte(value) when value in 0..255 do + <> + end + + def short(value) when value in -32_768..32_767 do + <> + end + + def ushort(value) when value in 0..65_535 do + <> + end + + def int(value) when value in -2_147_483_648..2_147_483_647 do + <> + end + + def long(value) when value in -9_223_372_036_854_775_808..9_223_372_036_854_775_807 do + <> + end + + def float(value) when is_number(value) do + <> + end + + def double(value) when is_number(value) do + <> + end + def varint(value) when value in -2_147_483_648..2_147_483_647 do <> = <> # 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 diff --git a/lib/servers/play.ex b/lib/servers/play.ex index ed5f28b..03c4d20 100644 --- a/lib/servers/play.ex +++ b/lib/servers/play.ex @@ -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) <> - <> <> 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 <> <> <> <> <> - 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)}" diff --git a/lib/servers/status.ex b/lib/servers/status.ex index 23a272b..d49150a 100644 --- a/lib/servers/status.ex +++ b/lib/servers/status.ex @@ -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) <> <> + Write.varint(0x01) <> Write.long(payload) end def serialize(packet) do raise ArgumentError, "Tried serializing unknown packet #{inspect(packet)}"