110 lines
3.1 KiB
Elixir
110 lines
3.1 KiB
Elixir
|
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.
|
||
|
"""
|
||
|
|
||
|
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)
|
||
|
end
|
||
|
|
||
|
def varint(_) do
|
||
|
raise ArgumentError, "Value is out of range for a varint"
|
||
|
end
|
||
|
|
||
|
def varlong(value) when value in -9_223_372_036_854_775_808..9_223_372_036_854_775_807
|
||
|
do
|
||
|
<<value::64-unsigned>> = <<value::64-signed>>
|
||
|
varnum("", value)
|
||
|
end
|
||
|
|
||
|
def varlong(_) do
|
||
|
raise ArgumentError, "Value is out of range for a varlong"
|
||
|
end
|
||
|
|
||
|
defp varnum(acc, value) when value in 0..127 do
|
||
|
acc <> <<0::1, value::7-big>>
|
||
|
end
|
||
|
|
||
|
defp varnum(acc, value) do
|
||
|
acc <> <<1::1, value::7-big>> |> varnum(value >>> 7)
|
||
|
end
|
||
|
|
||
|
def string(value) do
|
||
|
<<varint(byte_size(value))::binary, value::binary>>
|
||
|
end
|
||
|
end
|
||
|
|
||
|
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.
|
||
|
|
||
|
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 and Amethyst.Minecraft.Read.stop/1
|
||
|
to get the data out of the chain. If you pass the option `reverse: true` to stop/1, the list will be reversed, this may
|
||
|
provide better performance if you do not mind the inconvenience of the reversed list.
|
||
|
|
||
|
iex> alias Amethyst.Minecraft.Read
|
||
|
iex> [_, _, _] = Read.start(<<1, 999::16, 64>>) |> Read.bool() |> Read.short() |> Read.byte() |> Read.stop()
|
||
|
[true, 999, 64]
|
||
|
"""
|
||
|
|
||
|
def start(binary) do
|
||
|
{[], binary}
|
||
|
end
|
||
|
|
||
|
def stop({acc, rest}, opts \\ []) do
|
||
|
if Keyword.get(opts, :force_empty, false) and bit_size(rest) != 0 do
|
||
|
raise ArgumentError, "Expected no more data, but there is still #{bit_size(rest)} bits left"
|
||
|
end
|
||
|
case Keyword.get(opts, :reverse, false) do
|
||
|
true -> acc
|
||
|
false -> Enum.reverse(acc)
|
||
|
end
|
||
|
end
|
||
|
|
||
|
def bool({acc, <<value, rest::bitstring>>}) do
|
||
|
{[value != 0 | acc], rest}
|
||
|
end
|
||
|
|
||
|
def byte({acc, <<value::big-signed, rest::binary>>}) do
|
||
|
{[value | acc], rest}
|
||
|
end
|
||
|
|
||
|
def ubyte({acc, <<value::big-unsigned, rest::binary>>}) do
|
||
|
{[value | acc], rest}
|
||
|
end
|
||
|
|
||
|
def short({acc, <<value::16-big-signed, rest::binary>>}) do
|
||
|
{[value | acc], rest}
|
||
|
end
|
||
|
|
||
|
def ushort({acc, <<value::16-big-unsigned, rest::binary>>}) do
|
||
|
{[value | acc], rest}
|
||
|
end
|
||
|
|
||
|
def int({acc, <<value::32-big-signed, rest::binary>>}) do
|
||
|
{[value | acc], rest}
|
||
|
end
|
||
|
|
||
|
def long({acc, <<value::64-big-signed, rest::binary>>}) do
|
||
|
{[value | acc], rest}
|
||
|
end
|
||
|
|
||
|
def float({acc, <<value::32-float-big, rest::binary>>}) do
|
||
|
{[value | acc], rest}
|
||
|
end
|
||
|
|
||
|
def double({acc, <<value::64-float-big, rest::binary>>}) do
|
||
|
{[value | acc], rest}
|
||
|
end
|
||
|
end
|