Implement block registry
All checks were successful
Build & Test / nix-build (push) Successful in 2m41s

This commit is contained in:
Kodi Craft 2024-10-15 11:10:33 +02:00
parent 9a926d524e
commit ceb4daaf57
Signed by: kodi
GPG Key ID: 69D9EED60B242822
3 changed files with 111 additions and 4 deletions

View File

@ -30,12 +30,15 @@ defmodule Amethyst.Application do
{PartitionSupervisor,
child_spec: DynamicSupervisor.child_spec([]),
name: Amethyst.GameMetaSupervisor
}
},
{Amethyst.BlockRegistry, %{}},
Supervisor.child_spec(
{Task, fn -> Amethyst.DataGenerator.generate_and_populate_data(:latest, "/tmp/server.jar", "/tmp/amethyst-generated") end}, id: Amethyst.DataGenerator)
]
children = case Application.fetch_env!(:amethyst, :port) do
:no_listen -> children
port -> [Supervisor.child_spec({Task, fn -> Amethyst.TCPListener.accept(port) end}, restart: :permanent) | children]
port -> [Supervisor.child_spec({Task, fn -> Amethyst.TCPListener.accept(port) end}, restart: :permanent, id: Amethyst.TCPListener) | children]
end
# See https://hexdocs.pm/elixir/Supervisor.html

View File

@ -0,0 +1,69 @@
# Amethyst - An experimental Minecraft server written in Elixir.
# Copyright (C) 2024 KodiCraft
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
defmodule Amethyst.BlockRegistry do
use GenServer
@moduledoc """
GenServer which can be populated with block states and their corresponding IDs.
It can be queried by block identifier and state to get the corresponding ID.
"""
require Logger
def start_link(initial) do
Logger.info("Starting BlockRegistry")
GenServer.start_link(__MODULE__, initial, name: __MODULE__)
end
def init(map) do
{:ok, map}
end
def handle_cast({:add, id, bs, bsi}, state) do
state = Map.put_new(state, id, %{})
state = Map.put(state, id, Map.put(state[id], bs, bsi))
{:noreply, state}
end
def handle_call({:get, id, bs}, _from, state) do
case Map.get(state, id) do
nil -> {:reply, nil, state}
block_states -> {:reply, Map.get(block_states, bs), state}
end
end
@doc """
Adds a block state to the registry.
## Parameters
- `id` - The block identifier
- `bs` - The block state
- `bsi` - The block state ID
"""
@spec add(String.t(), map(), integer()) :: :ok
def add(id, bs, bsi) do
GenServer.cast(__MODULE__, {:add, id, bs, bsi})
end
@doc """
Gets the block state ID for a given block identifier and block state.
- `id` - The block identifier
- `bs` - The block state, nil if the block has no states
"""
@spec get(String.t(), map() | nil) :: integer() | nil
def get(id, bs \\ %{}) do
GenServer.call(__MODULE__, {:get, id, bs})
end
end

View File

@ -66,7 +66,7 @@ defmodule Amethyst.DataGenerator do
@spec generate_data_files(String.t(), out_path, nil | String.t()) :: {:error, pos_integer()} | {:ok, out_path} when out_path: String.t()
def generate_data_files(jar_path, out_path, java_bin \\ "java") do
Logger.debug("Generating data files from server jar at #{jar_path}")
Logger.info("Generating data files")
# mkdir the output directory since we cd into it
File.mkdir_p!(out_path)
case System.cmd(java_bin, ["-DbundlerMainClass=net.minecraft.data.Main", "-jar", jar_path, "--all", "--output", out_path], cd: out_path) do
@ -75,8 +75,43 @@ defmodule Amethyst.DataGenerator do
end
end
@spec populate_blockregistry(String.t()) :: :ok | {:error, term}
def populate_blockregistry(data_path) do
block_file = Path.join([data_path, "reports", "blocks.json"])
case File.read(block_file) do
{:ok, data} ->
case Jason.decode(data) do
{:ok, blocks} ->
Enum.each(blocks, fn {id, %{"states" => states}} ->
Enum.each(states, fn
%{"id" => bsi, "properties" => props} -> Amethyst.BlockRegistry.add(id, props, bsi)
%{"id" => bsi, "default" => true} -> Amethyst.BlockRegistry.add(id, %{}, bsi)
end)
end)
:ok
{:error, err} -> {:error, err}
end
{:error, err} -> {:error, err}
end
end
@spec generate_and_populate_data(String.t() | :latest, String.t(), String.t(), String.t() | nil) :: :ok | {:error, term}
def generate_and_populate_data(version, jar_path, data_dir, java_bin \\ "java") do
start_time = Time.utc_now()
case get_server_jar(version, jar_path) do
{:ok, jar_path} ->
case generate_data_files(jar_path, data_dir, java_bin) do
{:ok, data_path} ->
populate_blockregistry(data_path)
Logger.info("Finished generating and populating data in #{Time.diff(Time.utc_now(), start_time, :millisecond)}ms")
{:error, code} -> {:error, code}
end
{:error, err} -> {:error, err}
end
end
defp download_jar(url, path) do
Logger.debug("Downloading server jar from #{url}")
Logger.info("Downloading server jar from #{url} to #{path}")
case Req.get(url, into: File.stream!(path)) do
{:ok, _} -> {:ok, path}
{:error, err} -> {:error, err}