Fix bugs in sending paletted chunks
All checks were successful
Build & Test / nix-build (pull_request) Successful in 2m37s
Build & Test / nix-build (push) Successful in 2m39s

This commit is contained in:
Kodi Craft 2024-10-04 11:36:08 +02:00
parent 288ead3e9b
commit b40fee3c3c
Signed by: kodi
GPG Key ID: 69D9EED60B242822
3 changed files with 31 additions and 28 deletions

View File

@ -61,13 +61,12 @@ defmodule Amethyst.ConnectionHandler do
Logger.debug("Switching to version #{newversion} from #{version}") Logger.debug("Switching to version #{newversion} from #{version}")
loop(socket, connstate, newversion, state) loop(socket, connstate, newversion, state)
{:set_position, position} -> {:set_position, position} ->
Logger.debug("Updating client position to #{inspect(position)}")
prev_position = Map.get(state, :position) prev_position = Map.get(state, :position)
state = Map.put(state, :position, position) state = Map.put(state, :position, position)
# If there was no prev position, we consider that we # If there was no prev position, we consider that we
# definitely moved # definitely moved
prev_cp = if prev_position == nil do nil else chunk_pos(elem(prev_position, 0), elem(prev_position, 1)) end prev_cp = if prev_position == nil do nil else chunk_pos(elem(prev_position, 0), elem(prev_position, 2)) end
cp = chunk_pos(elem(position, 0), elem(position, 1)) cp = chunk_pos(elem(position, 0), elem(position, 2))
if prev_cp != cp do if prev_cp != cp do
Logger.debug("Client entered new chunk #{inspect(cp)}") Logger.debug("Client entered new chunk #{inspect(cp)}")
# We changed chunk borders, update center chunk and begin sending new chunks # We changed chunk borders, update center chunk and begin sending new chunks
@ -85,6 +84,7 @@ defmodule Amethyst.ConnectionHandler do
end end
chunks = MapSet.new(visible_chunks_from(elem(cp, 0), elem(cp, 1), Map.get(state, :view_distance, 16))) chunks = MapSet.new(visible_chunks_from(elem(cp, 0), elem(cp, 1), Map.get(state, :view_distance, 16)))
new_chunks = MapSet.difference(chunks, prev_chunks) new_chunks = MapSet.difference(chunks, prev_chunks)
Logger.debug("Sending #{MapSet.size(new_chunks)} chunks...")
# We can process all chunks in parallel # We can process all chunks in parallel
me = self() me = self()
ts = state |> Map.get(:game) |> Map.get(:refs) |> Map.get(:task_supervisor) ts = state |> Map.get(:game) |> Map.get(:refs) |> Map.get(:task_supervisor)
@ -110,13 +110,13 @@ defmodule Amethyst.ConnectionHandler do
end end
defp chunk_pos(x, z) do defp chunk_pos(x, z) do
{div(floor(x), 16), div(floor(z), 16)} {floor(round(x) / 16.0), floor(round(z) / 16.0)}
end end
# x, z here is chunk position
defp visible_chunks_from(x, z, view_distance) do defp visible_chunks_from(x, z, view_distance) do
{cx, cz} = chunk_pos(x, z) (x - view_distance - 3 .. x + view_distance + 3) |> Enum.flat_map(fn ix ->
(cx - view_distance - 3 .. cx + view_distance + 3) |> Enum.flat_map(fn ix -> (z - view_distance - 3 .. z + view_distance + 3) |> Enum.map(fn iz ->
(cz - view_distance - 3 .. cz + view_distance + 3) |> Enum.map(fn iz ->
{ix, iz} {ix, iz}
end) end)
end) end)
@ -154,15 +154,18 @@ defmodule Amethyst.ConnectionHandler do
bpe = max(min_bpe, 4) bpe = max(min_bpe, 4)
palette = MapSet.to_list(unique_blocks) |> palette = MapSet.to_list(unique_blocks) |>
Enum.with_index() |> Enum.with_index() |>
Map.new(fn {i, v} -> {v, i} end) Map.new(fn {i, v} -> {i, v} end)
Logger.debug("Using palette #{inspect(palette)}")
paletted_blocks = blocks |> paletted_blocks = blocks |>
Enum.map(&(Map.get(palette, &1))) Enum.map(&(Map.get(palette, &1)))
Logger.debug("Paletted blocks: #{inspect(paletted_blocks)}")
paletted_data = long_aligned_bit_string_reduce(paletted_blocks, bpe) paletted_data = long_aligned_bit_string_reduce(paletted_blocks, bpe)
Logger.debug("Paletted data: #{inspect(paletted_data)}")
Write.ubyte(bpe) <> Write.ubyte(bpe) <>
Write.varint(map_size(palette)) <> Write.varint(map_size(palette)) <>
Enum.reduce(0..(map_size(palette)-1), "", fn i, acc -> Enum.reduce(palette, "", fn {_k, v}, acc ->
acc <> Write.varint(Map.get(palette, i)) acc <> Write.varint(v)
end) <> end) <>
Write.varint(floor(bit_size(paletted_data) / 64)) <> Write.varint(floor(bit_size(paletted_data) / 64)) <>
paletted_data paletted_data
@ -197,9 +200,9 @@ defmodule Amethyst.ConnectionHandler do
defp long_aligned_bit_string_reduce(values, bpe) do defp long_aligned_bit_string_reduce(values, bpe) do
values |> Enum.reduce(<<>>, fn value, acc -> values |> Enum.reduce(<<>>, fn value, acc ->
next = <<acc::bitstring, value::size(bpe)-big>> next = <<acc::bitstring, value::big-size(bpe)>>
# man i hope they dont suddenly change the size of a long # man i hope they dont suddenly change the size of a long
if rem(bit_size(next), 64) + bpe < 64 do if rem(bit_size(next), 64) + bpe > 64 do
# gotta pad it # gotta pad it
<<next::bitstring, 0::big-size(64 - rem(bit_size(next), 64))>> <<next::bitstring, 0::big-size(64 - rem(bit_size(next), 64))>>
else else

View File

@ -208,7 +208,7 @@ defmodule Amethyst.ConnectionState.Play do
def handle(%{packet_type: :set_player_on_ground, on_ground: _}, 767, _state) do def handle(%{packet_type: :set_player_on_ground, on_ground: _}, 767, _state) do
:ok # Again, don't trust the client for something we can compute :ok # Again, don't trust the client for something we can compute
end end
def handle(%{packet_type: :player_command, eid: _eid, action_id: aid, jump_boost: _horse_jump}, 767, state) do def handle(%{packet_type: :player_command, eid: _eid, action_id: aid, jump_boost: _horse_jump}, 767, _state) do
# TODO: Actually handle these events # TODO: Actually handle these events
case aid do case aid do
0 -> # Start sneaking 0 -> # Start sneaking
@ -243,7 +243,6 @@ defmodule Amethyst.ConnectionState.Play do
def keepalive_loop(player) do def keepalive_loop(player) do
Process.link(player) # Is it fine to do this on loop? Process.link(player) # Is it fine to do this on loop?
<<id::32>> = :rand.bytes(4) <<id::32>> = :rand.bytes(4)
Logger.debug("Sending keepalive...")
send(player, {:send_packet, %{packet_type: :keep_alive, id: id}}) send(player, {:send_packet, %{packet_type: :keep_alive, id: id}})
receive do receive do
{:respond, ^id} -> {:respond, ^id} ->

View File

@ -12,19 +12,20 @@ defmodule Example.Game do
def login(from, cfg, refs) do def login(from, cfg, refs) do
Logger.info("Player logged in from #{inspect(from)}: #{inspect(cfg)}") Logger.info("Player logged in from #{inspect(from)}: #{inspect(cfg)}")
Logger.info("The refs for this game are #{inspect(refs)}") Logger.info("The refs for this game are #{inspect(refs)}")
{:accept, {0.0, 10.0, 0.0}, {0.0, 0.0}} {:accept, {0.0, 270.0, 0.0}, {0.0, 0.0}}
end end
@impl true @impl true
@spec player_position(any(), {any(), any(), any()}, any()) :: :ok @spec player_position(any(), {any(), any(), any()}, any()) :: :ok
def player_position(from, {x, y, z}, _refs) do def player_position(from, {x, y, z}, _refs) do
Logger.info("Player at #{inspect(from)} moved to #{x}, #{y}, #{z}") # Logger.info("Player at #{inspect(from)} moved to #{x}, #{y}, #{z}")
send(from, {:set_position, {x, y, z}})
:ok :ok
end end
@impl true @impl true
def player_rotation(from, {yaw, pitch}, _refs) do def player_rotation(_from, {_yaw, _pitch}, _refs) do
Logger.info("Player at #{inspect(from)} rotated to #{yaw}, #{pitch}") # Logger.info("Player at #{inspect(from)} rotated to #{yaw}, #{pitch}")
:ok :ok
end end
@ -40,18 +41,18 @@ defmodule Example.Game do
end end
@impl true @impl true
def chunk(_from, {_x, _z}, _state_refs) do def chunk(_from, {_cx, _cz}, _state_refs) do
# Logger.info("Player at #{inspect(from)} wants to know chunk #{x}, #{z}") # Logger.info("Player at #{inspect(from)} wants to know chunk #{cx}, #{cz}")
(0..255) |> Enum.map(fn y -> (0..255) |> Enum.map(fn y ->
if y < 5 do (0..15) |> Enum.map(fn z ->
(0..15) |> Enum.map(fn _z -> (0..15) |> Enum.map(fn x ->
(0..15) |> Enum.map(fn _x -> 1 end) if y <= x + z do
3
else
0
end
end) end)
else end)
(0..15) |> Enum.map(fn _z ->
(0..15) |> Enum.map(fn _x -> 1 end)
end)
end
end) end)
end end
end end