Implement packet type checking and get back up to speed
All checks were successful
Build & Test / nix-build (push) Successful in 1m31s
Build & Test / nix-build (pull_request) Successful in 1m27s

This commit is contained in:
Kodi Craft 2024-09-07 12:34:59 +02:00
parent 4beb083dd6
commit 5063e8af12
Signed by: kodi
GPG Key ID: 69D9EED60B242822
4 changed files with 95 additions and 12 deletions

View File

@ -87,7 +87,7 @@ alias ElixirSense.Log
if is_map(newstate) do if is_map(newstate) do
newstate newstate
else else
Logger.warning("State change to #{newstate} is not a map! Did you forget to return :ok?") Logger.warning("State change to #{inspect(newstate)} is not a map! Did you forget to return :ok?")
state state
end end
end end
@ -100,9 +100,15 @@ alias ElixirSense.Log
end end
defp send_packet(socket, connstate, packet, version) do defp send_packet(socket, connstate, packet, version) do
try do
data = connstate.serialize(packet, version) data = connstate.serialize(packet, version)
length = byte_size(data) |> Amethyst.Minecraft.Write.varint() length = byte_size(data) |> Amethyst.Minecraft.Write.varint()
:gen_tcp.send(socket, length <> data) :gen_tcp.send(socket, length <> data)
rescue
e ->
Logger.error("Error sending packet #{inspect(packet)} in state #{connstate}: #{Exception.format(:error, e, __STACKTRACE__)}")
send(self(), {:disconnect, "§cError sending packet #{inspect(packet)}:\n#{Exception.format(:error, e, __STACKTRACE__)}"})
end
end end
defp disconnect(socket, reason, connstate, version) do defp disconnect(socket, reason, connstate, version) do

View File

@ -27,6 +27,20 @@ defmodule Amethyst.NBT.Write do
<<type_id(type)::size(8), payload(type, value)::binary>> <<type_id(type)::size(8), payload(type, value)::binary>>
end end
def check_type({:byte, value}) when is_integer(value) and value in -128..127, do: true
def check_type({:short, value}) when is_integer(value) and value in -32_768..32_767, do: true
def check_type({:int, value}) when is_integer(value) and value in -2_147_483_648..2_147_483_647, do: true
def check_type({:long, value}) when is_integer(value) and value in -9_223_372_036_854_775_808..9_223_372_036_854_775_807, do: true
def check_type({:float, value}) when is_float(value), do: true
def check_type({:double, value}) when is_float(value), do: true
def check_type({:byte_array, values}) when is_list(values), do: Enum.all?(values, &is_integer/1)
def check_type({:string, value}) when is_binary(value), do: true
def check_type({:list, {type, values}}) when is_list(values), do: Enum.all?(values, &check_type({type, &1}))
def check_type({:compound, values}) when is_map(values), do: Enum.all?(values, fn {name, {type, value}} -> check_type({type, value}) end)
def check_type({:int_array, values}) when is_list(values), do: Enum.all?(values, &is_integer/1)
def check_type({:long_array, values}) when is_list(values), do: Enum.all?(values, &is_integer/1)
def check_type(_), do: false
defp type_id(:end), do: 0 defp type_id(:end), do: 0
defp type_id(:byte), do: 1 defp type_id(:byte), do: 1
defp type_id(:short), do: 2 defp type_id(:short), do: 2

View File

@ -61,7 +61,7 @@ defmodule Amethyst.ConnectionState.Configuration do
] ]
Macros.defpacket_clientbound :clientbound_known_packs, 0x0E, 767, [ Macros.defpacket_clientbound :clientbound_known_packs, 0x0E, 767, [
packs: {:array, [ packs: {:array, [
nameshape: :string, namespace: :string,
id: :string, id: :string,
version: :string version: :string
]} ]}
@ -269,8 +269,9 @@ defmodule Amethyst.ConnectionState.Configuration do
send(self(), {:set_state, Amethyst.ConnectionState.Play}) send(self(), {:set_state, Amethyst.ConnectionState.Play})
game = Application.fetch_env!(:amethyst, :default_game) |> Amethyst.GameCoordinator.find_or_create() game = Application.fetch_env!(:amethyst, :default_game) |> Amethyst.GameCoordinator.find_or_create()
state = state |> Map.put(:game, game) state = state |> Map.put(:game, game)
if Amethyst.Api.Game.login(game, state) == :reject do if Amethyst.API.Game.login(game, state) == :reject do
send(self(), {:disconnect, "Default game rejected connection"}) send(self(), {:disconnect, "Default game rejected connection"})
:ok
else else
send(self(), {:send_packet, %{ send(self(), {:send_packet, %{
packet_type: :login, packet_type: :login,
@ -294,6 +295,7 @@ defmodule Amethyst.ConnectionState.Configuration do
portal_cooldown: 0, portal_cooldown: 0,
enforces_secure_chat: false enforces_secure_chat: false
}}) }})
state
end end
end end

View File

@ -15,6 +15,7 @@
# along with this program. If not, see <https://www.gnu.org/licenses/>. # along with this program. If not, see <https://www.gnu.org/licenses/>.
defmodule Amethyst.ConnectionState.Macros do defmodule Amethyst.ConnectionState.Macros do
require Logger
defmacro defpacket_serverbound(name, id, version, signature) do defmacro defpacket_serverbound(name, id, version, signature) do
quote do quote do
def deserialize(unquote(id), unquote(version), data) do def deserialize(unquote(id), unquote(version), data) do
@ -27,7 +28,11 @@ defmodule Amethyst.ConnectionState.Macros do
defmacro defpacket_clientbound(name, id, version, signature) do defmacro defpacket_clientbound(name, id, version, signature) do
quote do quote do
def serialize(%{packet_type: unquote(name)} = packet, unquote(version)) do def serialize(%{packet_type: unquote(name)} = packet, unquote(version)) do
if Amethyst.ConnectionState.Macros.check_type(packet, unquote(signature)) do
Amethyst.Minecraft.Write.varint(unquote(id)) <> Amethyst.ConnectionState.Macros.write_signature(packet, unquote(signature)) Amethyst.Minecraft.Write.varint(unquote(id)) <> Amethyst.ConnectionState.Macros.write_signature(packet, unquote(signature))
else
raise "Invalid packet type for #{unquote(name)}! Got #{inspect(packet)}"
end
end end
end end
end end
@ -56,11 +61,15 @@ defmodule Amethyst.ConnectionState.Macros do
end end
{:array, signature} -> {:array, signature} ->
{[count], rest} = Read.start(rest) |> Read.varint() |> Read.stop() {[count], rest} = Read.start(rest) |> Read.varint() |> Read.stop()
if count == 0 do
{[[] | acc], rest, :reversed}
else
{items, rest} = Enum.reduce(1..count, {[], rest}, fn _, {acc, rest} -> {items, rest} = Enum.reduce(1..count, {[], rest}, fn _, {acc, rest} ->
{item, rest} = read_signature(rest, signature) {item, rest} = read_signature(rest, signature)
{[item | acc], rest} {[item | acc], rest}
end) end)
{[Enum.reverse(items) | acc], rest, :reversed} {[Enum.reverse(items) | acc], rest, :reversed}
end
t -> apply(Read, t, [{acc, rest, :reversed}]) t -> apply(Read, t, [{acc, rest, :reversed}])
end end
end) |> Read.stop() end) |> Read.stop()
@ -91,4 +100,56 @@ defmodule Amethyst.ConnectionState.Macros do
end end
end) end)
end end
def check_type(packet, signature) do
try do
Enum.all?(signature, fn {name, type} ->
case Map.get(packet, name, :missing) do
:missing -> throw {:missing, name}
value -> case type_matches(value, type) do
true -> true
false -> throw {:mismatch, name, value, type}
end
end
end)
catch
reason ->
Logger.debug("Found invalid packet type: #{inspect(reason)}")
false
end
end
def type_matches(value, :bool) when is_boolean(value), do: true
def type_matches(value, :byte) when is_integer(value) and value in -128..127, do: true
def type_matches(value, :ubyte) when is_integer(value) and value in 0..255, do: true
def type_matches(value, :short) when is_integer(value) and value in -32768..32767, do: true
def type_matches(value, :ushort) when is_integer(value) and value in 0..65535, do: true
def type_matches(value, :int) when is_integer(value) and value in -2147483648..2147483647, do: true
def type_matches(value, :long) when is_integer(value) and value in -9223372036854775808..9223372036854775807, do: true
def type_matches(value, :float) when is_float(value), do: true
def type_matches(value, :double) when is_float(value), do: true
def type_matches(value, :varint) when is_integer(value) and value in -2147483648..2147483647, do: true
def type_matches(value, :varlong) when is_integer(value) and value in -9223372036854775808..9223372036854775807, do: true
def type_matches(value, :uuid) when is_binary(value) and byte_size(value) == 36, do: true
def type_matches(value, :string) when is_binary(value), do: true
def type_matches(value, :raw) when is_binary(value), do: true
def type_matches(value, :byte_array) when is_binary(value), do: true
def type_matches({x, y, z}, :position) when
is_integer(x) and x in -33554432..33554431 and
is_integer(y) and y in -2048..2047 and
is_integer(z) and z in -33554432..33554431, do: true
def type_matches(value, :nbt), do: Amethyst.NBT.Write.check_type(value)
def type_matches(value, :json) do
case Jason.encode(value) do
{:ok, _} -> true
_ -> false
end
end
def type_matches(value, {:optional, type}) when is_nil(value), do: true
def type_matches(value, {:optional, type}), do: type_matches(value, type)
def type_matches(value, {:array, signature}) when is_list(value), do: Enum.all?(value, fn item -> check_type(item, signature) end)
def type_matches(value, {:compound, signature}) when is_map(value), do: check_type(value, signature)
def type_matches(_, _) do
false
end
end end