diff --git a/apps/amethyst/lib/states/configuration.ex b/apps/amethyst/lib/states/configuration.ex index 6513ac6..c4e99a9 100644 --- a/apps/amethyst/lib/states/configuration.ex +++ b/apps/amethyst/lib/states/configuration.ex @@ -303,6 +303,11 @@ defmodule Amethyst.ConnectionState.Configuration do packet_type: :synchronize_player_position, x: x, y: y, z: z, yaw: yaw, pitch: pitch, teleport_id: 0, flags: 0x00 }}) + send(self(), {:send_packet, Amethyst.ConnectionState.Play.ge_start_waiting_for_level_chunks(767)}) + send(self(), {:send_packet, %{packet_type: :set_center_chunk, + chunk_x: div(x, 16), + chunk_y: div(z, 16) + }}) state end end diff --git a/apps/amethyst/lib/states/macros.ex b/apps/amethyst/lib/states/macros.ex index a6def10..86b3970 100644 --- a/apps/amethyst/lib/states/macros.ex +++ b/apps/amethyst/lib/states/macros.ex @@ -16,18 +16,18 @@ defmodule Amethyst.ConnectionState.Macros do require Logger - defmacro defpacket_serverbound(name, id, version, signature) do + defmacro defpacket_serverbound(name, id, version, signature, where \\ true) do quote do - def deserialize(unquote(id), unquote(version), data) do + def deserialize(unquote(id), unquote(version), data) when unquote(where) 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 + defmacro defpacket_clientbound(name, id, version, signature, where \\ true) do quote do - def serialize(%{packet_type: unquote(name)} = packet, unquote(version)) do + def serialize(%{packet_type: unquote(name)} = packet, unquote(version)) when unquote(where) 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)) else @@ -79,7 +79,6 @@ defmodule Amethyst.ConnectionState.Macros do def write_signature(packet, signature) do Enum.reduce(signature, "", fn {name, type}, acc -> - #acc <> apply(Write, type, [Map.get(packet, name)]) case type do {:optional, {:compound, signature}} -> case Map.get(packet, name) do @@ -126,8 +125,8 @@ defmodule Amethyst.ConnectionState.Macros do 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, :float) when is_number(value), do: true + def type_matches(value, :double) when is_number(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 diff --git a/apps/amethyst/lib/states/play.ex b/apps/amethyst/lib/states/play.ex index b69ead6..c00c81a 100644 --- a/apps/amethyst/lib/states/play.ex +++ b/apps/amethyst/lib/states/play.ex @@ -16,7 +16,6 @@ defmodule Amethyst.ConnectionState.Play do require Amethyst.ConnectionState.Macros - alias Amethyst.Minecraft.Write alias Amethyst.ConnectionState.Macros require Logger @@ -26,6 +25,28 @@ defmodule Amethyst.ConnectionState.Play do """ Macros.defpacket_clientbound :disconnect, 0x1D, 767, [reason: :nbt] + Macros.defpacket_clientbound :chunk_data_and_update_light, 0x27, 767, [ + chunk_x: :int, + chunk_z: :int, + heightmaps: :nbt, + data: :byte_array, + block_entities: {:array, [ + packed_xz: :ubyte, # TODO: This would be interesting to have in a clearer format + y: :short, + type: :varint, + data: :nbt + ]}, + sky_light_mask: :raw, + block_light_mask: :raw, + empty_sky_light_mask: :raw, + empty_block_light_mask: :raw, + sky_light_arrays: {:array, [ + sky_light_array: :byte_array + ]}, + block_light_arrays: {:array, [ + block_light_array: :byte_array + ]} + ] Macros.defpacket_clientbound :login, 0x2B, 767, [ entity_id: :int, is_hardcore: :bool, @@ -59,6 +80,29 @@ defmodule Amethyst.ConnectionState.Play do flags: :byte, teleport_id: :varint ] + Macros.defpacket_clientbound :set_center_chunk, 0x54, 767, [ + chunk_x: :varint, chunk_z: :varint + ] + + Macros.defpacket_clientbound :game_event, 0x22, 767, [ + event: :ubyte, value: :float + ] + # We can use functions to wrap over this packet and make it a bit clearer. + # Taking the protocol version here makes it less portable but whatever, fuck this packet + def ge_no_respawn_block_available(767), do: %{packet_type: :game_event, event: 0, value: 0} + def ge_begin_raining(767), do: %{packet_type: :game_event, event: 1, value: 0} + def ge_end_raining(767), do: %{packet_type: :game_event, event: 2, value: 0} + def ge_change_game_mode(767, gm) when is_integer(gm), do: %{packet_type: :game_event, event: 3, value: gm} + def ge_win_game(767, credits?) when is_integer(credits?), do: %{packet_type: :game_event, event: 4, value: credits?} + def ge_game_event(767, event) when is_integer(event), do: %{packet_type: :game_event, event: 5, value: event} + def ge_arrow_hit_player(767), do: %{packet_type: :game_event, event: 6, value: 0} + def ge_rain_level_change(767, value) when is_number(value), do: %{packet_type: :game_event, event: 7, value: value} + def ge_thunder_level_change(767, value) when is_number(value), do: %{packet_type: :game_event, event: 8, value: value} + def ge_play_pufferfish_sting_sound(767), do: %{packet_type: :game_event, event: 9, value: 0} + def ge_play_elder_guardian_mob_appearance(767), do: %{packet_type: :game_event, event: 10, value: 0} + def ge_enable_respawn_screen(767, enabled?) when is_integer(enabled?), do: %{packet_type: :game_event, event: 11, value: enabled?} + def ge_limited_crafting(767, enabled?) when is_integer(enabled?), do: %{packet_type: :game_event, event: 12, value: enabled?} + def ge_start_waiting_for_level_chunks(767), do: %{packet_type: :game_event, event: 13, value: 0} Macros.defpacket_serverbound :confirm_teleportation, 0x00, 767, [teleport_id: :varint] Macros.defpacket_serverbound :set_player_position, 0x1A, 767, [ @@ -80,19 +124,6 @@ defmodule Amethyst.ConnectionState.Play do pitch: :float, on_ground: :bool # I don't understand their obsession with this... ] - # this packet sucks, thoughtful design is for losers anyway - def serialize(%{packet_type: :game_event, event: :no_respawn_block_available}, 767), do: Write.ubyte(0) - def serialize(%{packet_type: :game_event, event: :begin_raining}, 767), do: Write.ubyte(1) - def serialize(%{packet_type: :game_event, event: :end_raining}, 767), do: Write.ubyte(2) - def serialize(%{packet_type: :game_event, event: :change_gamemode, value: v}, 767), do: Write.ubyte(3) <> Write.float(v) - def serialize(%{packet_type: :game_event, event: :win_game, value: v}, 767), do: Write.ubyte(4) <> Write.float(v) - def serialize(%{packet_type: :game_event, event: :demo_event, value: v}, 767), do: Write.ubyte(5) <> Write.float(v) - def serialize(%{packet_type: :game_event, event: :arrow_hit_player}, 767), do: Write.ubyte(6) - def serialize(%{packet_type: :game_event, event: :rain_level_change, value: v}, 767), do: Write.ubyte(7) <> Write.float(v) - def serialize(%{packet_type: :game_event, event: :thunder_level_change, value: v}, 767), do: Write.ubyte(8) <> Write.float(v) - def serialize(%{packet_type: :game_event, event: :play_pufferfish_sting_sound}, 767), do: Write.ubyte(9) - def serialize(%{packet_type: :game_event, event: :play_elder_guardian_mob_appearance}, 767), do: Write.ubyte(10) - def handle(%{packet_type: :confirm_teleportation, teleport_id: id}, 767, state) do Amethyst.API.Game.accept_teleport(state[:game], id)