319 lines
9.9 KiB
C++
319 lines
9.9 KiB
C++
//
|
|
// Created by aurailus on 2020-02-19.
|
|
//
|
|
|
|
#include <fstream>
|
|
#include <json/json.hpp>
|
|
#include <gzip/compress.hpp>
|
|
|
|
#include "../VenusParser.h"
|
|
#include "../../def/ServerGame.h"
|
|
#include "../../util/net/Serializer.h"
|
|
|
|
#include "ServerModHandler.h"
|
|
|
|
void ServerModHandler::loadMods(ServerGame& defs, const std::string &path) {
|
|
auto modDirs = findModDirectories(path);
|
|
mods = initializeLuaMods(modDirs);
|
|
|
|
loadTextures(defs, mods);
|
|
loadModels(defs, mods);
|
|
|
|
organizeDependencies(mods);
|
|
serializeMods(mods);
|
|
}
|
|
|
|
void ServerModHandler::executeMods(std::function<void(std::string)> run) {
|
|
for (LuaMod& mod : mods) {
|
|
if (mod.config.name == "base") {
|
|
run(mod.config.name + "/main");
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (LuaMod& mod : mods) {
|
|
if (mod.config.name != "base") run(mod.config.name + "/main");
|
|
}
|
|
}
|
|
|
|
const std::vector<LuaMod>& ServerModHandler::cGetMods() const {
|
|
return mods;
|
|
}
|
|
|
|
std::list<std::string> ServerModHandler::findModDirectories(const std::string& path) {
|
|
std::list<std::string> modDirs {};
|
|
std::list<std::string> dirsToScan {path};
|
|
|
|
cf_dir_t dir;
|
|
|
|
while (!dirsToScan.empty()) {
|
|
std::string dirStr = *dirsToScan.begin();
|
|
dirsToScan.erase(dirsToScan.begin());
|
|
bool isModFolder = false;
|
|
|
|
cf_dir_open(&dir, dirStr.c_str());
|
|
|
|
std::list<std::string> subDirs;
|
|
|
|
while (dir.has_next) {
|
|
cf_file_t scannedFile;
|
|
cf_read_file(&dir, &scannedFile);
|
|
|
|
if (strncmp(scannedFile.name, ".", 1) == 0) {
|
|
cf_dir_next(&dir);
|
|
continue;
|
|
}
|
|
|
|
if (scannedFile.is_dir) subDirs.emplace_back(scannedFile.path);
|
|
else if (strlen(scannedFile.name) == 9 && strncmp(scannedFile.name, "conf.json", 9) == 0) {
|
|
isModFolder = true;
|
|
break;
|
|
}
|
|
|
|
cf_dir_next(&dir);
|
|
}
|
|
|
|
cf_dir_close(&dir);
|
|
|
|
if (isModFolder) modDirs.push_back(dirStr);
|
|
else for (const std::string& s : subDirs) dirsToScan.push_back(s);
|
|
}
|
|
|
|
return std::move(modDirs);
|
|
}
|
|
|
|
std::vector<LuaMod> ServerModHandler::initializeLuaMods(const std::list<std::string> modDirs) {
|
|
using nlohmann::json;
|
|
|
|
std::vector<LuaMod> mods {};
|
|
cf_dir_t dir;
|
|
|
|
for (const std::string& modDir : modDirs) {
|
|
std::string root = modDir + "/script";
|
|
|
|
std::list<std::string> dirsToScan {root};
|
|
std::list<std::string> luaFiles {};
|
|
|
|
while (!dirsToScan.empty()) {
|
|
std::string dirStr = *dirsToScan.begin();
|
|
dirsToScan.erase(dirsToScan.begin());
|
|
|
|
cf_dir_open(&dir, dirStr.c_str());
|
|
|
|
while (dir.has_next) {
|
|
cf_file_t scannedFile;
|
|
cf_read_file(&dir, &scannedFile);
|
|
|
|
if (strncmp(scannedFile.name, ".", 1) != 0) {
|
|
if (scannedFile.is_dir) dirsToScan.emplace_back(scannedFile.path);
|
|
else {
|
|
char *dot = strrchr(scannedFile.path, '.');
|
|
if (dot && (strncmp(dot, ".lua", 4) == 0 || strncmp(dot, ".venus", 6) == 0)) {
|
|
luaFiles.emplace_back(scannedFile.path);
|
|
}
|
|
}
|
|
}
|
|
|
|
cf_dir_next(&dir);
|
|
}
|
|
|
|
cf_dir_close(&dir);
|
|
}
|
|
|
|
LuaMod mod {};
|
|
mod.modPath = modDir;
|
|
auto& conf = mod.config;
|
|
|
|
std::ifstream i(modDir + "/conf.json");
|
|
json j {};
|
|
i >> j;
|
|
|
|
auto depends = j["depends"];
|
|
if (strncmp(depends.type_name(), "array", 5) == 0) {
|
|
for (auto &it : depends) {
|
|
if (strncmp(it.type_name(), "string", 6) == 0) {
|
|
conf.depends.push_back(static_cast<std::string>(it));
|
|
}
|
|
}
|
|
}
|
|
|
|
conf.name = j["name"];
|
|
conf.description = j["description"];
|
|
conf.version = j["version"];
|
|
|
|
for (std::string& file : luaFiles) {
|
|
size_t rootPos = file.find(root);
|
|
std::string modPath = file;
|
|
assert(rootPos != std::string::npos);
|
|
|
|
std::ifstream t(file);
|
|
std::string fileStr = std::string((std::istreambuf_iterator<char>(t)), std::istreambuf_iterator<char>());
|
|
|
|
modPath.erase(rootPos, root.length());
|
|
modPath.insert(0, conf.name);
|
|
|
|
const static std::string venusSubstr = ".venus";
|
|
if (std::equal(venusSubstr.rbegin(), venusSubstr.rend(), file.rbegin())) {
|
|
modPath.resize(modPath.size() - 6);
|
|
|
|
try {
|
|
fileStr = VenusParser::parse(file, fileStr);
|
|
}
|
|
catch (std::runtime_error e) {
|
|
std::cout << std::endl << e.what() << std::endl;
|
|
exit(1);
|
|
}
|
|
}
|
|
else modPath.resize(modPath.size() - 4);
|
|
|
|
LuaModFile f {modPath, fileStr};
|
|
mod.files.push_back(f);
|
|
}
|
|
|
|
mods.push_back(mod);
|
|
}
|
|
|
|
return mods;
|
|
}
|
|
|
|
void ServerModHandler::loadTextures(ServerGame &defs, const std::vector<LuaMod>& mods) {
|
|
cf_dir_t dir;
|
|
for (const LuaMod& mod : mods) {
|
|
std::string root = mod.modPath + "/textures";
|
|
|
|
std::list<std::string> dirsToScan {root};
|
|
|
|
while (!dirsToScan.empty()) {
|
|
std::string dirStr = *dirsToScan.begin();
|
|
dirsToScan.erase(dirsToScan.begin());
|
|
|
|
if (!cf_file_exists(dirStr.c_str())) continue;
|
|
cf_dir_open(&dir, dirStr.c_str());
|
|
|
|
while (dir.has_next) {
|
|
cf_file_t scannedFile;
|
|
cf_read_file(&dir, &scannedFile);
|
|
|
|
if (strncmp(scannedFile.name, ".", 1) != 0) {
|
|
if (scannedFile.is_dir) dirsToScan.emplace_back(scannedFile.path);
|
|
else {
|
|
char *dot = strrchr(scannedFile.path, '.');
|
|
if (dot && strncmp(dot, ".png", 4) == 0) {
|
|
|
|
std::string name = std::string(scannedFile.name).substr(0, std::string(scannedFile.name).size() - 4);
|
|
name.insert(0, mod.config.name + ":");
|
|
|
|
int width, height;
|
|
unsigned char* data = stbi_load(scannedFile.path, &width, &height, nullptr, 4);
|
|
std::string str(reinterpret_cast<char*>(data), static_cast<unsigned long>(width * height * 4));
|
|
std::string comp = gzip::compress(str.data(), str.length());
|
|
free(data);
|
|
|
|
defs.assets.textures.push_back({std::move(name), comp, static_cast<unsigned int>(width), static_cast<unsigned int>(height)});
|
|
}
|
|
}
|
|
}
|
|
|
|
cf_dir_next(&dir);
|
|
}
|
|
cf_dir_close(&dir);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ServerModHandler::loadModels(ServerGame &defs, const std::vector<LuaMod>& mods) {
|
|
cf_dir_t dir;
|
|
for (const LuaMod& mod : mods) {
|
|
std::string root = mod.modPath + "/models";
|
|
|
|
std::list<std::string> dirsToScan {root};
|
|
|
|
while (!dirsToScan.empty()) {
|
|
std::string dirStr = *dirsToScan.begin();
|
|
dirsToScan.erase(dirsToScan.begin());
|
|
|
|
if (!cf_file_exists(dirStr.c_str())) continue;
|
|
cf_dir_open(&dir, dirStr.c_str());
|
|
|
|
while (dir.has_next) {
|
|
cf_file_t scannedFile;
|
|
cf_read_file(&dir, &scannedFile);
|
|
|
|
if (strncmp(scannedFile.name, ".", 1) != 0) {
|
|
if (scannedFile.is_dir) dirsToScan.emplace_back(scannedFile.path);
|
|
else {
|
|
char *dot = strrchr(scannedFile.path, '.');
|
|
if (dot && strncmp(dot, ".b3d", 4) == 0) {
|
|
|
|
std::string name = std::string(scannedFile.name).substr(0, std::string(scannedFile.name).size() - 4);
|
|
name.insert(0, mod.config.name + ":");
|
|
|
|
std::ifstream t(scannedFile.path);
|
|
std::stringstream buffer;
|
|
buffer << t.rdbuf();
|
|
|
|
defs.assets.models.push_back({std::move(name), buffer.str(), "b3d"});
|
|
}
|
|
}
|
|
}
|
|
|
|
cf_dir_next(&dir);
|
|
}
|
|
cf_dir_close(&dir);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ServerModHandler::organizeDependencies(std::vector<LuaMod>& mods) {
|
|
for (int i = 0; i < mods.size(); i++) {
|
|
LuaMod& mod = mods[i];
|
|
auto& deps = mod.config.depends;
|
|
|
|
bool modifiedList = false;
|
|
|
|
for (std::string& dep : deps) {
|
|
for (int j = 0; j < mods.size(); j++) {
|
|
LuaMod& otherMod = mods[j];
|
|
if (otherMod.config.name == dep) {
|
|
if (j > i) {
|
|
LuaMod copy = otherMod;
|
|
mods.erase(mods.begin() + j);
|
|
mods.insert(mods.begin() + i, copy);
|
|
i++;
|
|
modifiedList = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (modifiedList) i = -1;
|
|
}
|
|
}
|
|
|
|
void ServerModHandler::serializeMods(std::vector<LuaMod>& mods) {
|
|
for (LuaMod& mod : mods) {
|
|
Serializer s = {};
|
|
s.append(mod.config.name)
|
|
.append(mod.config.description)
|
|
.append(mod.config.version);
|
|
|
|
std::string depends;
|
|
bool delimiter = false;
|
|
for (const std::string& dep : mod.config.depends) {
|
|
if (delimiter) depends.append(",");
|
|
else delimiter = true;
|
|
depends.append(dep);
|
|
}
|
|
|
|
s.append(depends);
|
|
|
|
for (LuaModFile& file : mod.files) {
|
|
s.append(file.path).append(file.file);
|
|
}
|
|
|
|
std::string comp = gzip::compress(s.data.c_str(), s.data.length());
|
|
mod.serialized = comp;
|
|
}
|
|
}
|