feat: casually rewrite in C++

This commit is contained in:
LordMZTE 2022-11-05 18:07:20 +01:00
parent 120894a992
commit 162bd4f953
Signed by: LordMZTE
GPG key ID: B64802DC33A64FF6
25 changed files with 368 additions and 255 deletions

View file

@ -1,4 +0,0 @@
# Used by "mix format"
[
inputs: ["{mix,.formatter}.exs", "{config,lib,test}/**/*.{ex,exs}"]
]

31
.gitignore vendored
View file

@ -1,29 +1,2 @@
# The directory Mix will write compiled artifacts to. build
_build/ .cache
# 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/

12
CMakeLists.txt Normal file
View file

@ -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})

View file

@ -2,11 +2,12 @@
This tool is meant to make porting mods easier for MineteckReloaded. 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 ## Installation
- `MIX_ENV=prod mix release` - `cmake -S. -Bbuild`
- start the server with `_build/prod/rel/portingtools/bin/portingtools start` - `cmake --build build`
- map stuff with `./client.sh map` - start the server with `build/portingtools serve`
- resolve ResourceLocations with `./client.sh resourceloc` - map stuff with `build/portingtools map`
- resolve ResourceLocations with `build/portingtools resourceloc`

View file

@ -1,2 +0,0 @@
#!/usr/bin/sh
exec "$(dirname "$(realpath "$0")")"/_build/prod/rel/portingtools/bin/portingtools rpc "Cli.call(~s<$1>)"

View file

@ -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} -> "<unknown>"
{nil, :resourceloc} -> "<invalid>"
end
|> IO.puts()
end
end

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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<new ResourceLocation("#{mod}", "#{Enum.join(rest, "/")}")>, state}
rescue
MatchError -> {:reply, nil, state}
end
end
end

29
mix.exs
View file

@ -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

View file

@ -10,3 +10,7 @@ getLastAttackingEntity,getLastAttacker
getBlockId,getBlock getBlockId,getBlock
markBlockForRenderUpdate,markBlockRangeForRenderUpdate markBlockForRenderUpdate,markBlockRangeForRenderUpdate
setCompoundTag,setTag setCompoundTag,setTag
getEntityName,getCommandSenderName
textureList,framesTextureData
bucketWater,water_bucket
ingotIron,iron_ingot

1 getBlockTileEntity getTileEntity
10 getBlockId getBlock
11 markBlockForRenderUpdate markBlockRangeForRenderUpdate
12 setCompoundTag setTag
13 getEntityName getCommandSenderName
14 textureList framesTextureData
15 bucketWater water_bucket
16 ingotIron iron_ingot

69
src/client.cpp Normal file
View file

@ -0,0 +1,69 @@
#include "data.hpp"
#include <boost/log/trivial.hpp>
#include <cerrno>
#include <cstring>
#include <iostream>
#include <string>
#include <sys/msg.h>
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

6
src/client.hpp Normal file
View file

@ -0,0 +1,6 @@
#include <sys/types.h>
namespace client {
void map(key_t key);
void resolveResourceLoc(key_t key);
} // namespace client

17
src/data.cpp Normal file
View file

@ -0,0 +1,17 @@
#include "data.hpp"
#include <boost/log/trivial.hpp>
#include <cstdlib>
#include <sys/ipc.h>
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

29
src/data.hpp Normal file
View file

@ -0,0 +1,29 @@
#include <sys/types.h>
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

25
src/main.cpp Normal file
View file

@ -0,0 +1,25 @@
#include "client.hpp"
#include "data.hpp"
#include "server.hpp"
#include <boost/log/trivial.hpp>
#include <cstring>
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;
}

67
src/mappings.cpp Normal file
View file

@ -0,0 +1,67 @@
#include "mappings.hpp"
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/classification.hpp>
#include <boost/algorithm/string/split.hpp>
#include <boost/filesystem.hpp>
#include <boost/filesystem/directory.hpp>
#include <boost/log/trivial.hpp>
#include <fstream>
#include <string>
#include <vector>
namespace fs = boost::filesystem;
namespace mappings {
void loadFile(std::string path, std::map<std::string, std::string> *map) {
std::ifstream file(path);
std::string line;
while (std::getline(file, line)) {
std::vector<std::string> 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<std::string, std::string> mappings;
std::map<std::string, std::string> 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 "<unknown>";
}
auto mapped = this->mappings[inp];
if (this->renames.count(mapped))
return renames[mapped];
return mapped;
}
} // namespace mappings

17
src/mappings.hpp Normal file
View file

@ -0,0 +1,17 @@
#include <map>
#include <string>
namespace mappings {
class Mappings {
std::map<std::string, std::string> mappings;
std::map<std::string, std::string> renames;
public:
Mappings(std::map<std::string, std::string> mappings,
std::map<std::string, std::string> renames) {
this->mappings = mappings;
this->renames = renames;
}
static Mappings load();
std::string map(std::string inp);
};
} // namespace mappings

34
src/resourceloc.cpp Normal file
View file

@ -0,0 +1,34 @@
#include "resourceloc.hpp"
#include <boost/algorithm/string/split.hpp>
#include <boost/algorithm/string/trim.hpp>
#include <boost/format.hpp>
#include <boost/log/trivial.hpp>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
namespace resourceloc {
std::string resolve(std::string inp) {
boost::algorithm::trim_if(inp, boost::is_any_of(" \n\r\""));
std::vector<std::string> 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 "<invalid>";
}
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

4
src/resourceloc.hpp Normal file
View file

@ -0,0 +1,4 @@
#include <string>
namespace resourceloc {
std::string resolve(std::string inp);
}

71
src/server.cpp Normal file
View file

@ -0,0 +1,71 @@
#include "server.hpp"
#include "data.hpp"
#include "mappings.hpp"
#include "resourceloc.hpp"
#include <algorithm>
#include <boost/log/trivial.hpp>
#include <cstdlib>
#include <cstring>
#include <string>
#include <sys/ipc.h>
#include <sys/msg.h>
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

5
src/server.hpp Normal file
View file

@ -0,0 +1,5 @@
#include <sys/types.h>
namespace server {
void run(key_t key);
}

View file

@ -1,8 +0,0 @@
defmodule PortingtoolsTest do
use ExUnit.Case
doctest Portingtools
test "greets the world" do
assert Portingtools.hello() == :world
end
end

View file

@ -1 +0,0 @@
ExUnit.start()