Attempt implementing light
All checks were successful
Build & Test / nix-build (push) Successful in 1m49s

This commit is contained in:
Kodi Craft 2024-10-07 21:05:37 +02:00
parent 5743ae55e0
commit b44784e9ef
Signed by: kodi
GPG Key ID: 69D9EED60B242822
6 changed files with 104 additions and 65 deletions

View File

@ -154,39 +154,42 @@ defmodule Amethyst.ConnectionHandler do
data = Enum.chunk_every(chunk_array, 16, 16, 0) # 0 -> air data = Enum.chunk_every(chunk_array, 16, 16, 0) # 0 -> air
|> Enum.reduce("", fn chunk_section, acc -> |> Enum.reduce("", fn chunk_section, acc ->
blocks = chunk_section |> List.flatten() blocks_and_lights = chunk_section |> List.flatten()
block_count = blocks |> Enum.filter(&(&1 != 0)) |> length block_count = blocks_and_lights |> Enum.filter(fn {bs, _, _} -> bs != 0 end) |> length
# Put together the palette # Put together the palette
unique_blocks = MapSet.new(blocks) unique_blocks = MapSet.new(blocks_and_lights |> Enum.map(fn {bs, _, _} -> bs end))
min_bpe = MapSet.size(unique_blocks) |> :math.log2() |> ceil() min_bpe = MapSet.size(unique_blocks) |> :math.log2() |> ceil()
paletted_container_data = case min_bpe do paletted_container_data = case min_bpe do
0 -> 0 ->
# SINGLE VALUED # SINGLE VALUED
Write.ubyte(0) <> Write.ubyte(0) <>
Write.varint(MapSet.to_list(unique_blocks) |> List.first()) <> Write.varint(MapSet.to_list(unique_blocks) |> List.first()) <>
Write.varint(0) # No data, empty pallette Write.varint(0) # No data, empty pallette
min_bpe when min_bpe in 1..8 -> min_bpe when min_bpe in 1..8 ->
# INDIRECT # INDIRECT
# Minimum bpe accepted by minecraft is 4 # Minimum bpe accepted by minecraft is 4
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} -> {i, v} end) Map.new(fn {v, i} -> {v, i} end)
paletted_blocks = blocks |> paletted_blocks = blocks_and_lights |>
Enum.map(&(Map.get(palette, &1))) Enum.map(fn {bs, _, _} -> Map.get(palette, bs) end)
paletted_data = long_aligned_bit_string_reduce(paletted_blocks, bpe) paletted_data = long_aligned_bit_string_reduce(paletted_blocks, bpe,
fn bs -> <<bs::signed-integer-big-size(bpe)>> end)
Write.ubyte(bpe) <> Write.ubyte(bpe) <>
Write.varint(map_size(palette)) <> Write.varint(map_size(palette)) <>
Enum.reduce(palette, "", fn {_k, v}, acc -> (Enum.sort(palette, fn {_k1, v1}, {_k2, v2} -> v1 < v2 end) |>
acc <> Write.varint(v) Enum.reduce("", fn {k, _v}, acc -> acc <> Write.varint(k) end)) <>
end) <>
Write.varint(floor(bit_size(paletted_data) / 64)) <> Write.varint(floor(bit_size(paletted_data) / 64)) <>
paletted_data paletted_data
_ -> _ ->
# DIRECT # DIRECT
data = long_aligned_bit_string_reduce(blocks, 15) data = long_aligned_bit_string_reduce(blocks_and_lights, 15,
fn {bs, _sl, _bl} -> <<bs::signed-integer-big-size(15)>> end)
Write.ubyte(15) <> Write.ubyte(15) <>
Write.varint(floor(bit_size(data) / 64)) <> Write.varint(floor(bit_size(data) / 64)) <>
data data
@ -196,26 +199,48 @@ defmodule Amethyst.ConnectionHandler do
<<0::8, 0::8, 0::8>> # TODO: This should be biome data <<0::8, 0::8, 0::8>> # TODO: This should be biome data
end) end)
sky_light_sections = Enum.chunk_every(chunk_array, 16, 16, 0)
|> Enum.map(fn chunk_section ->
chunk_section |> List.flatten() |> Enum.map(fn {_bs, sl, _bl} -> sl end)
end)
non_empty_sky_light_sections = sky_light_sections |> Enum.filter(fn section -> !Enum.all?(section, &(&1 == 0)) end)
has_sky_light = [false] ++ (sky_light_sections |> Enum.map(fn section -> !Enum.all?(section, &(&1 == 0)) end)) ++ [false]
empty_sky_light = has_sky_light |> Enum.map(&not/1)
sky_light_array = non_empty_sky_light_sections |> Enum.map(&(Enum.reduce(&1, <<>>, fn light, acc ->
<<acc::bitstring, light::size(4)>>
end)))
block_light_sections = Enum.chunk_every(chunk_array, 16, 16, 0)
|> Enum.map(fn chunk_section ->
chunk_section |> List.flatten() |> Enum.map(fn {_bs, _sl, bl} -> bl end)
end)
non_empty_block_light_sections = block_light_sections |> Enum.filter(fn section -> !Enum.all?(section, &(&1 == 0)) end)
has_block_light = [false] ++ (block_light_sections |> Enum.map(fn section -> !Enum.all?(section, &(&1 == 0)) end)) ++ [false]
empty_block_light = has_block_light |> Enum.map(&not/1)
block_light_array = non_empty_block_light_sections |> Enum.map(&(Enum.reduce(&1, <<>>, fn light, acc ->
<<acc::bitstring, light::size(4)>>
end)))
send(to, {:send_packet, %{ send(to, {:send_packet, %{
packet_type: :chunk_data_and_update_light, packet_type: :chunk_data_and_update_light,
chunk_x: cx, chunk_z: cz, chunk_x: cx, chunk_z: cz,
heightmaps: heightmaps, heightmaps: heightmaps,
data: data, data: data,
block_entities: [], block_entities: [],
# TODO: Light sky_light_mask: has_sky_light,
sky_light_mask: Write.varint(0), block_light_mask: has_block_light,
block_light_mask: Write.varint(0), empty_sky_light_mask: empty_sky_light,
empty_sky_light_mask: Write.varint(0), empty_block_light_mask: empty_block_light,
empty_block_light_mask: Write.varint(0), sky_light_arrays: sky_light_array |> Enum.map(&(%{sky_light_array: &1})),
sky_light_arrays: [], block_light_arrays: block_light_array |> Enum.map(&(%{block_light_array: &1}))
block_light_arrays: []
}}) }})
:ok :ok
end end
defp long_aligned_bit_string_reduce(values, bpe) do defp long_aligned_bit_string_reduce(values, bpe, func) do
values |> Enum.reduce(<<>>, fn value, acc -> values |> Enum.reduce(<<>>, fn value, acc ->
next = <<acc::bitstring, value::big-size(bpe)>> ret = func.(value)
next = <<acc::bitstring, ret::bitstring-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

View File

@ -16,6 +16,7 @@
defmodule Amethyst.Minecraft.Write do defmodule Amethyst.Minecraft.Write do
import Bitwise import Bitwise
require Logger
@moduledoc """ @moduledoc """
This module contains functions for writing Minecraft data. This module contains functions for writing Minecraft data.
@ -139,6 +140,18 @@ defmodule Amethyst.Minecraft.Write do
def nbt(value) do def nbt(value) do
Amethyst.NBT.Write.write_net(value) Amethyst.NBT.Write.write_net(value)
end end
def bitset(list) do
Logger.debug("Writing bitset #{inspect(list)}")
unaligned = Enum.reduce(list, <<>>, &(if &1 do <<&2::bitstring, 1::1>> else <<&2::bitstring, 0::1>> end))
aligned = if rem(bit_size(unaligned), 64) == 0 do
unaligned
else
<<unaligned::bitstring, 0::size(64 - rem(bit_size(unaligned), 64))>>
end
Logger.debug("Writing as #{inspect(aligned)}")
varint(div(byte_size(aligned), 8)) <> aligned
end
end end
defmodule Amethyst.Minecraft.Read do defmodule Amethyst.Minecraft.Read do

View File

@ -92,7 +92,7 @@ defmodule Amethyst.Game do
For now, this data must be formatted as a 3D list, indexed as [y][z][x]. For now, this data must be formatted as a 3D list, indexed as [y][z][x].
""" """
@callback chunk(from :: pid(), {x :: integer(), z :: integer()}, state_refs :: map()) :: [[[pos_integer()]]] @callback chunk(from :: pid(), {x :: integer(), z :: integer()}, state_refs :: map()) :: [[[{block_state :: pos_integer(), sky_light :: 0..15, block_light :: 0..15}]]]
def chunk(%{:mod => mod, :refs => refs}, pos) do def chunk(%{:mod => mod, :refs => refs}, pos) do
mod.chunk(self(), pos, refs) mod.chunk(self(), pos, refs)
end end

View File

@ -154,6 +154,7 @@ defmodule Amethyst.ConnectionState.Macros do
def type_matches(value, {:optional, _type}) when is_nil(value), do: true 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, {: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, {:array, signature}) when is_list(value), do: Enum.all?(value, fn item -> check_type(item, signature) end)
def type_matches(value, :bitset) when is_list(value), do: Enum.all?(value, fn item -> is_boolean(item) end)
def type_matches(value, {:compound, signature}) when is_map(value), do: check_type(value, signature) def type_matches(value, {:compound, signature}) when is_map(value), do: check_type(value, signature)
def type_matches(_, _) do def type_matches(_, _) do
false false

View File

@ -36,10 +36,10 @@ defmodule Amethyst.ConnectionState.Play do
type: :varint, type: :varint,
data: :nbt data: :nbt
]}, ]},
sky_light_mask: :raw, sky_light_mask: :bitset,
block_light_mask: :raw, block_light_mask: :bitset,
empty_sky_light_mask: :raw, empty_sky_light_mask: :bitset,
empty_block_light_mask: :raw, empty_block_light_mask: :bitset,
sky_light_arrays: {:array, [ sky_light_arrays: {:array, [
sky_light_array: :byte_array sky_light_array: :byte_array
]}, ]},

View File

@ -50,9 +50,9 @@ defmodule Example.Game do
gz = cz * 16 + z gz = cz * 16 + z
gy = y gy = y
if rem(gx, 4) == 0 && rem(gy, 4) == 0 && rem(gz, 4) == 0 do if rem(gx, 4) == 0 && rem(gy, 4) == 0 && rem(gz, 4) == 0 do
1 {abs(rem(div(gx + gy + gz, 4), 1000)), 15, 0}
else else
0 {0, 15, 0}
end end
end) end)
end) end)