From 4a387ff296227045a05c91a6015149a567922eec Mon Sep 17 00:00:00 2001 From: Kodi Craft Date: Wed, 16 Oct 2024 09:58:24 +0200 Subject: [PATCH] Attempt at implementing versioned block registry --- apps/amethyst/lib/amethyst.ex | 13 +----- .../lib/{blockregistry.ex => blockstates.ex} | 44 +++++++++++++++---- apps/amethyst/lib/datagen.ex | 20 ++++++--- 3 files changed, 51 insertions(+), 26 deletions(-) rename apps/amethyst/lib/{blockregistry.ex => blockstates.ex} (51%) diff --git a/apps/amethyst/lib/amethyst.ex b/apps/amethyst/lib/amethyst.ex index 0344dcb..f8cf285 100644 --- a/apps/amethyst/lib/amethyst.ex +++ b/apps/amethyst/lib/amethyst.ex @@ -31,18 +31,7 @@ defmodule Amethyst.Application do child_spec: DynamicSupervisor.child_spec([]), name: Amethyst.GameMetaSupervisor }, - {PartitionSupervisor, - child_spec: Amethyst.BlockRegistry.child_spec(%{}), - name: Amethyst.BlockRegistry}, - Supervisor.child_spec( - {Task, fn -> - # TODO: not this - Amethyst.DataGenerator.generate_and_populate_data( - :latest, - "/tmp/server.jar", - "/tmp/amethyst-generated") - end}, - id: Amethyst.DataGenerator) + {Registry, keys: :unique, name: Amethyst.BlockStateRegistry}, ] children = case Application.fetch_env!(:amethyst, :port) do diff --git a/apps/amethyst/lib/blockregistry.ex b/apps/amethyst/lib/blockstates.ex similarity index 51% rename from apps/amethyst/lib/blockregistry.ex rename to apps/amethyst/lib/blockstates.ex index 0b23117..492ef9d 100644 --- a/apps/amethyst/lib/blockregistry.ex +++ b/apps/amethyst/lib/blockstates.ex @@ -14,7 +14,7 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . -defmodule Amethyst.BlockRegistry do +defmodule Amethyst.BlockStates do use GenServer @moduledoc """ GenServer which can be populated with block states and their corresponding IDs. @@ -22,8 +22,8 @@ defmodule Amethyst.BlockRegistry do """ require Logger - def start_link(map) do - GenServer.start_link(__MODULE__, map) + def start_link(map, name) do + GenServer.start_link(__MODULE__, map, name: name) end def init(map) do @@ -52,22 +52,48 @@ defmodule Amethyst.BlockRegistry do Adds a block state to the registry. ## Parameters + - `version` - The Minecraft version - `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({:via, PartitionSupervisor, {__MODULE__, id}}, {:add, id, bs, bsi}) + @spec add(String.t(), String.t(), map(), integer()) :: :ok + def add(version, id, bs, bsi) do + ps = {:via, Registry, {Amethyst.BlockStateRegistry, version}} + GenServer.cast({:via, PartitionSupervisor, {ps, id}}, {:add, id, bs, bsi}) end @doc """ Gets the block state ID for a given block identifier and block state. + If no data is known for this version, errors. Prefer using `try_get/3` + if you can wait for the data to be loaded. + - `version` - The Minecraft version - `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({:via, PartitionSupervisor, {__MODULE__, id}}, {:get, id, bs}) + @spec get(String.t(), String.t(), map() | nil) :: integer() | nil + def get(version, id, bs \\ %{}) do + ps = {:via, Registry, {Amethyst.BlockStateRegistry, version}} + GenServer.call({:via, PartitionSupervisor, {ps, id}}, {:get, id, bs}) + end + + @doc """ + Tries to get the block state ID for a given block identifier and block state. + If no data is known for this version, it will block until the data is loaded. + - `version` - The Minecraft version + - `id` - The block identifier + - `bs` - The block state, nil if the block has no states + """ + @spec try_get(String.t(), String.t(), map() | nil) :: integer() | nil + def try_get(version, id, bs \\ %{}) do + case Registry.lookup(Amethyst.BlockStateRegistry, version) do + [{_pid, _}] -> + ps = {:via, Registry, {Amethyst.BlockStateRegistry, version}} + GenServer.call({:via, PartitionSupervisor, {ps, id}}, {:get, id, bs}) + [] -> + Logger.warning("BlockStateRegistry doesn't exist for version #{version}, generating it now!") + Amethyst.DataGenerator.generate_and_populate_data(version, "/tmp/minecraft_server_#{version}.jar", "/tmp/minecraft_data_#{version}") + try_get(version, id, bs) + end end end diff --git a/apps/amethyst/lib/datagen.ex b/apps/amethyst/lib/datagen.ex index 90948f5..90f228d 100644 --- a/apps/amethyst/lib/datagen.ex +++ b/apps/amethyst/lib/datagen.ex @@ -75,8 +75,8 @@ defmodule Amethyst.DataGenerator do end end - @spec populate_blockregistry(String.t()) :: :ok | {:error, term} - def populate_blockregistry(data_path) do + @spec populate_blockstates(String.t(), String.t()) :: :ok | {:error, term} + def populate_blockstates(data_path, version) do block_file = Path.join([data_path, "reports", "blocks.json"]) case File.read(block_file) do {:ok, data} -> @@ -84,8 +84,8 @@ defmodule Amethyst.DataGenerator 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) + %{"id" => bsi, "properties" => props} -> Amethyst.BlockStates.add(version, id, props, bsi) + %{"id" => bsi, "default" => true} -> Amethyst.BlockStates.add(version, id, %{}, bsi) end) end) :ok @@ -95,15 +95,25 @@ defmodule Amethyst.DataGenerator do end end + defp create_blockregistry(version) do + # TODO: unsupervised + Amethyst.BlockStates.start_link(%{}, {:via, Registry, {Amethyst.BlockStateRegistry, version}}) + 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() + {:ok, version} = case version do + :latest -> get_latest_version() + version -> {:ok, version} + end 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} -> Logger.info("Finished generating data in #{Time.diff(Time.utc_now(), start_time, :millisecond)}ms") - populate_blockregistry(data_path) + create_blockregistry(version) + populate_blockstates(version, data_path) Logger.info("Finished generating and populating data in #{Time.diff(Time.utc_now(), start_time, :millisecond)}ms") {:error, code} -> {:error, code} end