diff --git a/.formatter.exs b/.formatter.exs deleted file mode 100644 index d2cda26..0000000 --- a/.formatter.exs +++ /dev/null @@ -1,4 +0,0 @@ -# Used by "mix format" -[ - inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"] -] diff --git a/.gitignore b/.gitignore index 17e7656..9785597 100644 --- a/.gitignore +++ b/.gitignore @@ -1,29 +1,2 @@ -# The directory Mix will write compiled artifacts to. -_build/ - -# If you run "mix test --cover", coverage assets end up here. -cover/ - -# The directory Mix downloads your dependencies sources to. -deps/ - -# Where third-party dependencies like ExDoc output generated docs. -doc/ - -# Ignore .fetch files in case you like to edit your project deps locally. -.fetch - -# If the VM crashes, it generates a dump, let's ignore it too. -erl_crash.dump - -# Also ignore archive artifacts (built via "mix archive.build"). -*.ez - -# Ignore package tarball (built via "mix hex.build"). -portingtools-*.tar - -# Temporary files, for example, from tests. -tmp/ - -# Language Server -.elixir_ls/ +build +.cache diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..2dc6259 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,12 @@ +cmake_minimum_required(VERSION 2.8.11) +project(PORTINGTOOLS) + +file(GLOB_RECURSE sources src/*) + +find_package(Boost 1.80 COMPONENTS log filesystem REQUIRED) + +include_directories(${Boost_INCLUDE_DIR}) + +add_executable(portingtools ${sources}) + +target_link_libraries(portingtools LINK_PUBLIC ${Boost_LIBRARIES}) diff --git a/README.md b/README.md index 08e72d9..6f276b9 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,12 @@ This tool is meant to make porting mods easier for MineteckReloaded. -Also, it helped me learn some elixir, so don't expect good code. +Also, it helped me learn some c++, so don't expect good code. ## Installation -- `MIX_ENV=prod mix release` -- start the server with `_build/prod/rel/portingtools/bin/portingtools start` -- map stuff with `./client.sh map` -- resolve ResourceLocations with `./client.sh resourceloc` +- `cmake -S. -Bbuild` +- `cmake --build build` +- start the server with `build/portingtools serve` +- map stuff with `build/portingtools map` +- resolve ResourceLocations with `build/portingtools resourceloc` diff --git a/client.sh b/client.sh deleted file mode 100755 index 07e5a5e..0000000 --- a/client.sh +++ /dev/null @@ -1,2 +0,0 @@ -#!/usr/bin/sh -exec "$(dirname "$(realpath "$0")")"/_build/prod/rel/portingtools/bin/portingtools rpc "Cli.call(~s<$1>)" diff --git a/lib/cli.ex b/lib/cli.ex deleted file mode 100644 index d9e2ba8..0000000 --- a/lib/cli.ex +++ /dev/null @@ -1,31 +0,0 @@ -defmodule Cli do - def call(command) do - case command do - "map" -> - input() - |> PortingTools.Mappings.map() - |> output(:map) - - "resourceloc" -> - input() - |> PortingTools.ResourceLoc.resolve() - |> output(:map) - - _ -> - raise "Invalid command!" - end - end - - def input() do - IO.gets(nil) |> String.trim() - end - - def output(response, type) do - case {response, type} do - {s, _} when is_binary(s) -> s - {nil, :map} -> "" - {nil, :resourceloc} -> "" - end - |> IO.puts() - end -end diff --git a/lib/portingtools/application.ex b/lib/portingtools/application.ex deleted file mode 100644 index 930e775..0000000 --- a/lib/portingtools/application.ex +++ /dev/null @@ -1,18 +0,0 @@ -defmodule PortingTools.Application do - use Application - - @impl true - def start(_type, _args) do - #{:ok, _} = Node.start(:"portingtools@localhost", :shortnames) - #Node.set_cookie(Node.self(), :ptools) - - children = [ - PortingTools.ResourceLoc, - PortingTools.Mappings, - PortingTools.Mappings.Agent, - ] - - opts = [strategy: :one_for_one, name: PortingTools.Supervisor] - Supervisor.start_link(children, opts) - end -end diff --git a/lib/portingtools/mappings.ex b/lib/portingtools/mappings.ex deleted file mode 100644 index 43a9a22..0000000 --- a/lib/portingtools/mappings.ex +++ /dev/null @@ -1,22 +0,0 @@ -defmodule PortingTools.Mappings do - use GenServer, restart: :transient - require Logger - - def start_link(_) do - GenServer.start_link(__MODULE__, nil, name: __MODULE__) - end - - def init(_init_arg) do - Logger.info("Initilizing mappings server @ #{__MODULE__}") - {:ok, nil} - end - - def map(str) do - GenServer.call(__MODULE__, {:map, str}) - end - - def handle_call({:map, str}, _from, state) do - Logger.info("Got map request to map #{str}") - {:reply, PortingTools.Mappings.Agent.map(str), state} - end -end diff --git a/lib/portingtools/mappings/agent.ex b/lib/portingtools/mappings/agent.ex deleted file mode 100644 index ed53e51..0000000 --- a/lib/portingtools/mappings/agent.ex +++ /dev/null @@ -1,74 +0,0 @@ -defmodule PortingTools.Mappings.Agent do - @moduledoc """ - This module manages and loads the mappings - and renames. - """ - - use Agent, restart: :transient - require Logger - - def start_link(_) do - Agent.start_link(&init/0, name: __MODULE__) - end - - defmodule PortingTools.Mappings.Agent.State do - defstruct [:mappings, :renames] - end - - def init() do - %PortingTools.Mappings.Agent.State{ - mappings: load_mappings(), - renames: load_renames() - } - end - - def load_mappings() do - Logger.info("reading mappings") - - mappings = - File.ls!("mappings") - |> Stream.flat_map(&File.stream!("mappings/#{&1}")) - |> Stream.map(&String.split(&1, ",")) - |> Enum.reduce(%{}, fn [remapped, orig | _], map -> - Map.put(map, String.trim(remapped), String.trim(orig)) - end) - - Logger.info("read #{map_size(mappings)} mappings") - mappings - end - - def load_renames() do - Logger.info("reading renames") - - renames = - File.stream!("renames.csv") - |> Stream.map(&String.split(&1, ",")) - |> Enum.reduce(%{}, fn [old, new | _], map -> - Map.put(map, String.trim(old), String.trim(new)) - end) - - Logger.info("read #{map_size(renames)} renames") - - renames - end - - def map(key) do - mapping = Agent.get(__MODULE__, &Map.get(&1.mappings, key)) - - case mapping do - nil -> - Logger.warn("tried to map unknown symbol") - nil - - m -> - case Agent.get(__MODULE__, &Map.get(&1.renames, m)) do - nil -> - m - - r -> - Logger.info("found rename for #{m} -> #{r}") - r - end - end - end -end diff --git a/lib/portingtools/resourceloc.ex b/lib/portingtools/resourceloc.ex deleted file mode 100644 index 3a8f4f2..0000000 --- a/lib/portingtools/resourceloc.ex +++ /dev/null @@ -1,32 +0,0 @@ -defmodule PortingTools.ResourceLoc do - use GenServer, restart: :transient - require Logger - - def start_link(_) do - GenServer.start_link(__MODULE__, nil, name: __MODULE__) - end - - def init(_init_arg) do - Logger.info("initializing Resourceloc server @ #{__MODULE__}") - {:ok, nil} - end - - def resolve(str) do - GenServer.call(__MODULE__, {:resolve, str}) - end - - def handle_call({:resolve, str}, _from, state) do - Logger.info("got ResourceLoc resolve request") - - try do - ["mods", mod | rest] = - String.trim(str) - |> String.trim(<>) - |> String.split("/", trim: true) - - {:reply, ~s, state} - rescue - MatchError -> {:reply, nil, state} - end - end -end diff --git a/mix.exs b/mix.exs deleted file mode 100644 index 39352c8..0000000 --- a/mix.exs +++ /dev/null @@ -1,29 +0,0 @@ -defmodule PortingTools.MixProject do - use Mix.Project - - def project do - [ - app: :portingtools, - version: "0.1.0", - elixir: "~> 1.14", - start_permanent: Mix.env() == :prod, - deps: deps() - ] - end - - # Run "mix help compile.app" to learn about applications. - def application do - [ - extra_applications: [:logger], - mod: {PortingTools.Application, []} - ] - end - - # Run "mix help deps" to learn about dependencies. - defp deps do - [ - # {:dep_from_hexpm, "~> 0.3.0"}, - # {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"} - ] - end -end diff --git a/renames.csv b/renames.csv index c307d2d..cee1b09 100644 --- a/renames.csv +++ b/renames.csv @@ -10,3 +10,7 @@ getLastAttackingEntity,getLastAttacker getBlockId,getBlock markBlockForRenderUpdate,markBlockRangeForRenderUpdate setCompoundTag,setTag +getEntityName,getCommandSenderName +textureList,framesTextureData +bucketWater,water_bucket +ingotIron,iron_ingot diff --git a/src/client.cpp b/src/client.cpp new file mode 100644 index 0000000..1d2278f --- /dev/null +++ b/src/client.cpp @@ -0,0 +1,69 @@ +#include "data.hpp" +#include +#include +#include +#include +#include +#include + +namespace client { +void sendMsg(int msqid, data::RequestMsg *msg) { + if (msgsnd(msqid, msg, sizeof(data::RequestMsg) - sizeof(long), 0) < 0) { + BOOST_LOG_TRIVIAL(fatal) << "send error " << strerror(errno); + exit(1); + } +} + +std::string recvMsg(int msqid) { + data::ResponseMsg msg; + if (msgrcv(msqid, &msg, sizeof(data::ResponseMsg) - sizeof(long), + data::clientbound_msg, 0) < 0) { + BOOST_LOG_TRIVIAL(fatal) << "receive error " << strerror(errno); + exit(1); + } + + return std::string(&msg.data[0], msg.datalen); +} + +void map(key_t key) { + std::string inp; + std::cin >> inp; + + auto msqid = msgget(key, 0664); + + if (msqid < 0) { + BOOST_LOG_TRIVIAL(fatal) << "msgget fail"; + exit(0); + } + + data::RequestMsg req; + req.msgtype = data::serverbound_msg; + req.type = data::map; + req.datalen = inp.size(); + std::copy(inp.begin(), inp.end(), req.data); + + sendMsg(msqid, &req); + std::cout << recvMsg(msqid) << std::endl; +} + +void resolveResourceLoc(key_t key) { + std::string inp; + std::cin >> inp; + + auto msqid = msgget(key, 0664); + + if (msqid < 0) { + BOOST_LOG_TRIVIAL(fatal) << "msgget fail"; + exit(0); + } + + data::RequestMsg req; + req.msgtype = data::serverbound_msg; + req.type = data::resolve_resource_loc; + req.datalen = inp.size(); + std::copy(inp.begin(), inp.end(), req.data); + + sendMsg(msqid, &req); + std::cout << recvMsg(msqid) << std::endl; +} +} // namespace client diff --git a/src/client.hpp b/src/client.hpp new file mode 100644 index 0000000..98caf3d --- /dev/null +++ b/src/client.hpp @@ -0,0 +1,6 @@ +#include + +namespace client { +void map(key_t key); +void resolveResourceLoc(key_t key); +} // namespace client diff --git a/src/data.cpp b/src/data.cpp new file mode 100644 index 0000000..fa366b3 --- /dev/null +++ b/src/data.cpp @@ -0,0 +1,17 @@ +#include "data.hpp" +#include +#include +#include + +namespace data { +key_t getIpcKeyFromExeName(char *argv0) { + auto k = ftok(argv0, 'X'); + + if (k < 0) { + BOOST_LOG_TRIVIAL(fatal) << "failed to get IPC key"; + exit(1); + } + + return k; +} +} // namespace data diff --git a/src/data.hpp b/src/data.hpp new file mode 100644 index 0000000..6c099d7 --- /dev/null +++ b/src/data.hpp @@ -0,0 +1,29 @@ +#include + +namespace data { +const int serverbound_msg = 1; +const int clientbound_msg = 2; + +key_t getIpcKeyFromExeName(char *argv0); + +enum RequestType { + map, + resolve_resource_loc, +}; + +struct RequestMsg { + long msgtype; + + RequestType type; + + unsigned int datalen; + char data[128]; +}; + +struct ResponseMsg { + long msgtype; + + unsigned int datalen; + char data[128]; +}; +} // namespace data diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..540782d --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,25 @@ +#include "client.hpp" +#include "data.hpp" +#include "server.hpp" +#include +#include + +int main(int argc, char *argv[]) { + if (argc < 2) { + BOOST_LOG_TRIVIAL(fatal) << "not enough arguments!"; + return 1; + } + + if (strcmp(argv[1], "serve") == 0) { + server::run(data::getIpcKeyFromExeName(argv[0])); + } else if (strcmp(argv[1], "map") == 0) { + client::map(data::getIpcKeyFromExeName(argv[0])); + } else if (strcmp(argv[1], "resourceloc") == 0) { + client::resolveResourceLoc(data::getIpcKeyFromExeName(argv[0])); + } else { + BOOST_LOG_TRIVIAL(fatal) << "unknown command!"; + return 1; + } + + return 0; +} diff --git a/src/mappings.cpp b/src/mappings.cpp new file mode 100644 index 0000000..073e163 --- /dev/null +++ b/src/mappings.cpp @@ -0,0 +1,67 @@ +#include "mappings.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace fs = boost::filesystem; + +namespace mappings { +void loadFile(std::string path, std::map *map) { + std::ifstream file(path); + std::string line; + while (std::getline(file, line)) { + std::vector splits; + boost::algorithm::split(splits, line, boost::is_any_of(",\t|")); + + if (splits.size() < 2) { + BOOST_LOG_TRIVIAL(warning) << "found invalid mapping '" << line << "'"; + continue; + } + + (*map)[splits[0]] = splits[1]; + } +} + +Mappings Mappings::load() { + std::map mappings; + std::map renames; + + // load mappings + fs::directory_iterator end_itr; + for (fs::directory_iterator itr("mappings"); itr != end_itr; ++itr) { + if (fs::is_directory(itr->status()) || + !boost::algorithm::ends_with(itr->path().string(), ".csv")) + continue; + + loadFile(itr->path().string(), &mappings); + } + + if (fs::is_regular_file("renames.csv")) { + loadFile("renames.csv", &renames); + } + + BOOST_LOG_TRIVIAL(info) << "loaded " << mappings.size() << " mappings and " + << renames.size() << " renames"; + return {mappings, renames}; +} + +std::string Mappings::map(std::string inp) { + if (!this->mappings.count(inp)) { + BOOST_LOG_TRIVIAL(warning) << "unknown mapping '" << inp << "'"; + return ""; + } + + auto mapped = this->mappings[inp]; + + if (this->renames.count(mapped)) + return renames[mapped]; + + return mapped; +} +} // namespace mappings diff --git a/src/mappings.hpp b/src/mappings.hpp new file mode 100644 index 0000000..2284814 --- /dev/null +++ b/src/mappings.hpp @@ -0,0 +1,17 @@ +#include +#include +namespace mappings { +class Mappings { + std::map mappings; + std::map renames; + +public: + Mappings(std::map mappings, + std::map renames) { + this->mappings = mappings; + this->renames = renames; + } + static Mappings load(); + std::string map(std::string inp); +}; +} // namespace mappings diff --git a/src/resourceloc.cpp b/src/resourceloc.cpp new file mode 100644 index 0000000..f9dc416 --- /dev/null +++ b/src/resourceloc.cpp @@ -0,0 +1,34 @@ +#include "resourceloc.hpp" +#include +#include +#include +#include +#include +#include +#include +#include + +namespace resourceloc { +std::string resolve(std::string inp) { + boost::algorithm::trim_if(inp, boost::is_any_of(" \n\r\"")); + + std::vector components; + boost::algorithm::split(components, inp, boost::is_any_of("/")); + + if (components.size() < 3 || components[0] != "" || components[1] != "mods") { + BOOST_LOG_TRIVIAL(warning) << "invalid resourceloc"; + return ""; + } + + std::string path_component; + for (unsigned int i = 3; i < components.size(); i++) { + path_component += components[i]; + if (i != components.size() - 1) { + path_component += "/"; + } + } + + return boost::str(boost::format("new ResourceLocation(\"%1%\", \"%2%\")") % + components[2] % path_component); +} +} // namespace resourceloc diff --git a/src/resourceloc.hpp b/src/resourceloc.hpp new file mode 100644 index 0000000..ee0ceec --- /dev/null +++ b/src/resourceloc.hpp @@ -0,0 +1,4 @@ +#include +namespace resourceloc { +std::string resolve(std::string inp); +} diff --git a/src/server.cpp b/src/server.cpp new file mode 100644 index 0000000..137b2e3 --- /dev/null +++ b/src/server.cpp @@ -0,0 +1,71 @@ +#include "server.hpp" +#include "data.hpp" +#include "mappings.hpp" +#include "resourceloc.hpp" +#include +#include +#include +#include +#include +#include +#include + +namespace server { +void sendToClient(int msqid, std::string msg) { + data::ResponseMsg pkt; + pkt.msgtype = data::clientbound_msg; + pkt.datalen = msg.size(); + std::copy(msg.begin(), msg.end(), pkt.data); + + if (msgsnd(msqid, &pkt, sizeof(data::ResponseMsg) - sizeof(long), 0) < 0) { + BOOST_LOG_TRIVIAL(error) << "failed to send response " << strerror(errno); + } +} + +void handleResolveResourceLoc(int msqid, data::RequestMsg *msg) { + std::string s(&msg->data[0], msg->datalen); + BOOST_LOG_TRIVIAL(info) << "got request to resolve resourceloc '" << s << "'"; + auto res = resourceloc::resolve(s); + + sendToClient(msqid, res); +} + +void handleMap(int msqid, mappings::Mappings *mappings, data::RequestMsg *msg) { + std::string s(&msg->data[0], msg->datalen); + BOOST_LOG_TRIVIAL(info) << "got request to map '" << s << "'"; + sendToClient(msqid, mappings->map(s)); +} + +void run(key_t key) { + BOOST_LOG_TRIVIAL(info) << "starting server"; + + auto msqid = msgget(key, 0664 | IPC_CREAT); + + if (msqid < 0) { + BOOST_LOG_TRIVIAL(fatal) << "msgget fail"; + exit(1); + } + + BOOST_LOG_TRIVIAL(info) << "msqid " << msqid; + + auto mappings = mappings::Mappings::load(); + + data::RequestMsg msg; + for (;;) { + if (msgrcv(msqid, &msg, sizeof(data::RequestMsg) - sizeof(long), + data::serverbound_msg, 0) < 0) { + BOOST_LOG_TRIVIAL(error) << "receive error " << strerror(errno); + continue; + } + + switch (msg.type) { + case data::map: + handleMap(msqid, &mappings, &msg); + break; + case data::resolve_resource_loc: + handleResolveResourceLoc(msqid, &msg); + break; + } + } +} +} // namespace server diff --git a/src/server.hpp b/src/server.hpp new file mode 100644 index 0000000..57f3e4d --- /dev/null +++ b/src/server.hpp @@ -0,0 +1,5 @@ +#include + +namespace server { +void run(key_t key); +} diff --git a/test/portingtools_test.exs b/test/portingtools_test.exs deleted file mode 100644 index 7b3f2b7..0000000 --- a/test/portingtools_test.exs +++ /dev/null @@ -1,8 +0,0 @@ -defmodule PortingtoolsTest do - use ExUnit.Case - doctest Portingtools - - test "greets the world" do - assert Portingtools.hello() == :world - end -end diff --git a/test/test_helper.exs b/test/test_helper.exs deleted file mode 100644 index 869559e..0000000 --- a/test/test_helper.exs +++ /dev/null @@ -1 +0,0 @@ -ExUnit.start()