mirror of
https://github.com/MihailRis/voxelcore.git
synced 2026-10-07 12:01:50 +00:00
Merge branch 'main' into refactor-world-renderer
This commit is contained in:
commit
61b601f95a
41 changed files with 464 additions and 78 deletions
|
|
@ -16,6 +16,9 @@ inline constexpr bool ENGINE_DEBUG_BUILD = true;
|
|||
|
||||
inline const std::string ENGINE_VERSION_STRING = "0.32";
|
||||
|
||||
inline constexpr uint MAX_SUBPROCESSES = 1;
|
||||
inline constexpr uint MAX_SUBPROCESS_DEPTH = 2;
|
||||
|
||||
/// @brief world regions format version
|
||||
inline constexpr uint REGION_FORMAT_VERSION = 3;
|
||||
|
||||
|
|
|
|||
|
|
@ -26,17 +26,21 @@ std::unique_ptr<Content> ContentBuilder::build() {
|
|||
def.rt.tags.insert(tags.add(tag));
|
||||
}
|
||||
|
||||
auto isSolid = [](const Variant& variant) {
|
||||
return variant.model.type == BlockModelType::BLOCK ||
|
||||
variant.culling == CullingMode::OPTIONAL;
|
||||
};
|
||||
|
||||
if (def.variants) {
|
||||
for (auto& variant : def.variants->variants) {
|
||||
variant.rt.solid =
|
||||
variant.model.type == BlockModelType::BLOCK ||
|
||||
def.explictlySolid;
|
||||
variant.rt.solid = isSolid(variant) || def.explictlySolid;
|
||||
}
|
||||
def.defaults = def.variants->variants.at(0);
|
||||
} else {
|
||||
def.defaults.rt.solid =
|
||||
def.defaults.model.type == BlockModelType::BLOCK ||
|
||||
def.explictlySolid;
|
||||
def.defaults.rt.solid = isSolid(def.defaults) || def.explictlySolid;
|
||||
}
|
||||
if (def.material.empty()) {
|
||||
defaults.at("block-material").get(def.material);
|
||||
}
|
||||
|
||||
const float EPSILON = 0.01f;
|
||||
|
|
|
|||
|
|
@ -89,6 +89,7 @@ template<> void ContentUnitLoader<EntityDef>::loadUnit(
|
|||
root.at("skeleton-name").get(def.skeletonName);
|
||||
root.at("material").get(def.material);
|
||||
root.at("blocking").get(def.blocking);
|
||||
root.at("selectable").get(def.selectable);
|
||||
root.at("solid").get(def.solid);
|
||||
root.at("mass").get(def.mass);
|
||||
root.at("elasticity").get(def.elasticity);
|
||||
|
|
|
|||
|
|
@ -8,11 +8,7 @@
|
|||
|
||||
static debug::Logger logger("app-scripts");
|
||||
|
||||
AppScriptsControl::AppScriptsControl(
|
||||
const CoreParameters& params, const Project& project
|
||||
)
|
||||
: project(project) {
|
||||
|
||||
AppScriptsControl::AppScriptsControl(const CoreParameters& params) {
|
||||
io::path scriptFile =
|
||||
params.scriptFile.empty()
|
||||
? "project:start.lua"
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ struct Project;
|
|||
|
||||
class AppScriptsControl {
|
||||
public:
|
||||
AppScriptsControl(const CoreParameters& params, const Project& project);
|
||||
AppScriptsControl(const CoreParameters& params);
|
||||
|
||||
void tick();
|
||||
void loadProjectClientScript();
|
||||
|
|
@ -24,7 +24,6 @@ public:
|
|||
|
||||
bool isFinished() const;
|
||||
private:
|
||||
const Project& project;
|
||||
std::unique_ptr<scripting::IClientProjectScript> clientScript;
|
||||
std::unique_ptr<Process> scriptCoroutine;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "interfaces/Serializable.hpp"
|
||||
#include "io/path.hpp"
|
||||
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
|
@ -12,6 +13,7 @@ struct Permissions {
|
|||
static inline std::string NETWORK = "network";
|
||||
static inline std::string RECORD_AUDIO = "record-audio";
|
||||
static inline std::string WRITE_TO_USER = "write-to-user";
|
||||
static inline std::string SUB_INSTANCES = "sub-instances";
|
||||
|
||||
std::set<std::string> permissions;
|
||||
|
||||
|
|
@ -21,6 +23,7 @@ struct Permissions {
|
|||
struct Project : Serializable {
|
||||
std::string name;
|
||||
std::string title;
|
||||
io::path path;
|
||||
std::vector<std::string> basePacks;
|
||||
Permissions permissions;
|
||||
|
||||
|
|
|
|||
|
|
@ -14,5 +14,6 @@ struct CoreParameters {
|
|||
std::filesystem::path projectFolder;
|
||||
std::string debugServerString;
|
||||
int tps = 20;
|
||||
int subProcessDepth = 0;
|
||||
std::unordered_map<std::string, std::string> projectArgs;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -192,7 +192,7 @@ void Engine::initialize(CoreParameters coreParameters) {
|
|||
audio::set_input_device(name == "auto" ? "" : name);
|
||||
}));
|
||||
|
||||
appScripts = std::make_unique<AppScriptsControl>(params, *project);
|
||||
appScripts = std::make_unique<AppScriptsControl>(params);
|
||||
|
||||
if (params.stdinCommands) {
|
||||
cmd::start_stdin_cmd_reader(*this);
|
||||
|
|
@ -380,6 +380,7 @@ void Engine::loadAssets() {
|
|||
void Engine::loadProject() {
|
||||
io::path projectFile = "project:project.toml";
|
||||
project = std::make_unique<Project>();
|
||||
project->path = "project:";
|
||||
project->deserialize(io::read_object(projectFile));
|
||||
logger.info() << "loaded project " << util::quote(project->name);
|
||||
}
|
||||
|
|
@ -412,6 +413,7 @@ void Engine::onWorldClosed() {
|
|||
}
|
||||
|
||||
void Engine::quit() {
|
||||
logger.info() << "quitSignal set to true";
|
||||
quitSignal = true;
|
||||
if (!isHeadless()) {
|
||||
window->setShouldClose(true);
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ void Mainloop::run() {
|
|||
auto& appScripts = engine.getAppScripts();
|
||||
|
||||
logger.info() << "main loop started";
|
||||
while (!window.isShouldClose()) {
|
||||
while (!window.isShouldClose() && !engine.isQuitSignal()) {
|
||||
time.update(window.time());
|
||||
engine.applicationTick();
|
||||
engine.updateFrontend();
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ void ContentGfxCache::refreshVariant(
|
|||
uint8_t variantIndex,
|
||||
const Atlas& atlas
|
||||
) {
|
||||
std::unordered_map<std::string, std::string> optionalTextures;
|
||||
bool denseRender = settings.denseRender.get();
|
||||
for (uint side = 0; side < 6; side++) {
|
||||
std::string tex = variant.textureFaces[side];
|
||||
|
|
@ -43,6 +44,7 @@ void ContentGfxCache::refreshVariant(
|
|||
texOpaque = tex;
|
||||
} else if (variant.culling == CullingMode::OPTIONAL && !denseRender) {
|
||||
tex = texOpaque;
|
||||
optionalTextures[variant.textureFaces[side]] = texOpaque;
|
||||
}
|
||||
size_t index = getRegionIndex(def.rt.id, variantIndex, side, false);
|
||||
sideregions[index] = atlas.get(tex);
|
||||
|
|
@ -56,7 +58,12 @@ void ContentGfxCache::refreshVariant(
|
|||
if (pos == std::string::npos) {
|
||||
continue;
|
||||
}
|
||||
if (auto region = atlas.getIf(mesh.texture.substr(pos+1))) {
|
||||
std::string texture = mesh.texture.substr(pos+1);
|
||||
const auto& found = optionalTextures.find(texture);
|
||||
if (found != optionalTextures.end()) {
|
||||
texture = found->second;
|
||||
}
|
||||
if (auto region = atlas.getIf(texture)) {
|
||||
for (auto& vertex : mesh.vertices) {
|
||||
vertex.uv = region->apply(vertex.uv);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -514,6 +514,7 @@ void BlocksRenderer::render(
|
|||
int x = i % CHUNK_W;
|
||||
int y = i / (CHUNK_D * CHUNK_W);
|
||||
int z = (i / CHUNK_D) % CHUNK_W;
|
||||
|
||||
switch (def.getModel(state.userbits).type) {
|
||||
case BlockModelType::BLOCK:
|
||||
blockCube({x, y, z}, texfaces, def, vox.state, !def.shadeless,
|
||||
|
|
@ -532,7 +533,6 @@ void BlocksRenderer::render(
|
|||
break;
|
||||
}
|
||||
case BlockModelType::CUSTOM: {
|
||||
if (!denseRender)
|
||||
blockCustomModel(
|
||||
{x, y, z},
|
||||
def,
|
||||
|
|
|
|||
|
|
@ -33,6 +33,8 @@ namespace io {
|
|||
}
|
||||
}
|
||||
|
||||
path(std::string_view str) : path(std::string(str)) {}
|
||||
|
||||
path(const char* str) : path(std::string(str)) {}
|
||||
|
||||
bool operator==(const std::string& other) const {
|
||||
|
|
|
|||
|
|
@ -20,8 +20,14 @@
|
|||
#include "window/Window.hpp"
|
||||
#include "world/Level.hpp"
|
||||
|
||||
#include <array>
|
||||
|
||||
using namespace scripting;
|
||||
|
||||
namespace {
|
||||
static std::array<std::unique_ptr<Process>, MAX_SUBPROCESSES> processes;
|
||||
}
|
||||
|
||||
/// @brief Check if content is loaded
|
||||
static int l_is_content_loaded(lua::State* L) {
|
||||
return lua::pushboolean(L, content != nullptr);
|
||||
|
|
@ -337,6 +343,10 @@ static int l_start_debug_instance(lua::State* L) {
|
|||
if (!engine->getProject().permissions.has(Permissions::DEBUGGING)) {
|
||||
throw std::runtime_error("project has no debugging permission");
|
||||
}
|
||||
const auto& params = engine->getCoreParameters();
|
||||
if (params.subProcessDepth >= MAX_SUBPROCESS_DEPTH) {
|
||||
throw std::runtime_error("max subprocess depth exceeded");
|
||||
}
|
||||
|
||||
int port = lua::tointeger(L, 1);
|
||||
if (port == 0) {
|
||||
|
|
@ -357,6 +367,8 @@ static int l_start_debug_instance(lua::State* L) {
|
|||
"--res", paths.getResourcesFolder().u8string(),
|
||||
"--dir", paths.getUserFilesFolder().u8string(),
|
||||
"--dbg-server", "tcp:" + std::to_string(port),
|
||||
"--sub-depth", std::to_string(engine->getCoreParameters()
|
||||
.subProcessDepth + 1),
|
||||
};
|
||||
if (!projectPath.empty()) {
|
||||
args.emplace_back("--project");
|
||||
|
|
@ -365,11 +377,77 @@ static int l_start_debug_instance(lua::State* L) {
|
|||
|
||||
platform::new_engine_instance(
|
||||
std::move(args),
|
||||
outputPath.empty() ? "" : io::resolve(std::string(outputPath))
|
||||
outputPath.empty() ? "" : io::resolve(std::string(outputPath)),
|
||||
false
|
||||
);
|
||||
return lua::pushinteger(L, port);
|
||||
}
|
||||
|
||||
static int l_start_background_instance(lua::State* L) {
|
||||
if (!engine->getProject().permissions.has(Permissions::SUB_INSTANCES)) {
|
||||
throw std::runtime_error("project has no sub-instances permission");
|
||||
}
|
||||
const auto& params = engine->getCoreParameters();
|
||||
if (params.subProcessDepth >= MAX_SUBPROCESS_DEPTH) {
|
||||
throw std::runtime_error("max subprocess depth exceeded");
|
||||
}
|
||||
|
||||
auto scriptPath = lua::require_lstring(L, 1);
|
||||
io::path outputPath = lua::isstring(L, 2) ? lua::require_lstring(L, 2) : "";
|
||||
const auto& paths = engine->getPaths();
|
||||
|
||||
std::vector<std::string> args {
|
||||
"--headless",
|
||||
"--res", paths.getResourcesFolder().u8string(),
|
||||
"--dir", paths.getUserFilesFolder().u8string(),
|
||||
"--script", io::resolve(scriptPath).u8string(),
|
||||
"--sub-depth", std::to_string(engine->getCoreParameters()
|
||||
.subProcessDepth + 1),
|
||||
};
|
||||
args.emplace_back("--project");
|
||||
args.emplace_back(io::resolve(engine->getProject().path).u8string());
|
||||
|
||||
int handle = -1;
|
||||
for (int i = 0; i < ::processes.size(); i++) {
|
||||
if (!::processes[i] || !::processes[i]->isActive()) {
|
||||
handle = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (handle == -1) {
|
||||
throw std::runtime_error("sub-processes limit exceeded");
|
||||
}
|
||||
::processes[handle] = platform::new_engine_instance(
|
||||
std::move(args),
|
||||
outputPath.empty() ? "" : io::resolve(outputPath),
|
||||
true
|
||||
);
|
||||
return lua::pushinteger(L, handle);
|
||||
}
|
||||
|
||||
static int l_is_instance_alive(lua::State* L) {
|
||||
int handle = lua::tointeger(L, 1);
|
||||
if (handle < 0 || handle >= ::processes.size()) {
|
||||
throw std::runtime_error("invalid process handle");
|
||||
}
|
||||
auto& process = ::processes[handle];
|
||||
return lua::pushboolean(L, process && process->isActive());
|
||||
}
|
||||
|
||||
static int l_terminate_instance(lua::State* L) {
|
||||
int handle = lua::tointeger(L, 1);
|
||||
if (handle < 0 || handle >= ::processes.size()) {
|
||||
throw std::runtime_error("invalid process handle");
|
||||
}
|
||||
auto& process = ::processes[handle];
|
||||
if (process == nullptr) {
|
||||
return lua::pushboolean(L, false);
|
||||
}
|
||||
bool active = process->isActive();
|
||||
process.reset();
|
||||
return lua::pushboolean(L, active);
|
||||
}
|
||||
|
||||
const luaL_Reg applib[] = {
|
||||
/// content
|
||||
{"is_content_loaded", lua::wrap<l_is_content_loaded>},
|
||||
|
|
@ -402,5 +480,8 @@ const luaL_Reg applib[] = {
|
|||
{"get_version", lua::wrap<l_get_version>},
|
||||
{"create_memory_device", lua::wrap<l_create_memory_device>},
|
||||
{"start_debug_instance", lua::wrap<l_start_debug_instance>},
|
||||
{"start_background_instance", lua::wrap<l_start_background_instance>},
|
||||
{"is_instance_alive", lua::wrap<l_is_instance_alive>},
|
||||
{"terminate_instance", lua::wrap<l_terminate_instance>},
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -311,6 +311,10 @@ static int l_world_raycast(lua::State* L) {
|
|||
entityRaycast.entityFilterExcludeMode = lua::toboolean(L, -1);
|
||||
lua::pop(L);
|
||||
}
|
||||
if (lua::getfield(L, "nonselect_entities", 1)) {
|
||||
entityRaycast.includeNonSelectable = lua::toboolean(L, -1);
|
||||
lua::pop(L);
|
||||
}
|
||||
if (lua::getfield(L, "filter_blocks", 1)) {
|
||||
load_blocks_filter(L, -1, filteredBlocks);
|
||||
blocksRaycast.filter = &filteredBlocks;
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
static debug::Logger logger("main");
|
||||
|
||||
static void sigterm_handler(int signum) {
|
||||
logger.info() << (signum == SIGTERM ? "SIGTERM" : "SIGINT") << " received";
|
||||
Engine::getInstance().quit();
|
||||
}
|
||||
|
||||
|
|
@ -25,12 +26,16 @@ int main(int argc, char** argv) {
|
|||
if (!parse_cmdline(argc, argv, coreParameters)) {
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
logger.debug() << "sub-process depth: "
|
||||
<< coreParameters.subProcessDepth;
|
||||
} catch (const std::runtime_error& err) {
|
||||
std::cerr << err.what() << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
std::signal(SIGTERM, sigterm_handler);
|
||||
|
||||
#ifdef NDEBUG
|
||||
std::signal(SIGINT, sigterm_handler);
|
||||
#endif
|
||||
debug::Logger::init(coreParameters.userFolder.string() + "/latest.log");
|
||||
platform::configure_encoding();
|
||||
|
||||
|
|
|
|||
|
|
@ -176,7 +176,8 @@ std::optional<Entities::RaycastResult> Entities::rayCast(
|
|||
|
||||
for (auto [entity, eid, transform, body] : view.each()) {
|
||||
if (eid.uid == settings.ignoredUid || !body.enabled ||
|
||||
(settings.solidEntitiesOnly && !eid.def.solid)) {
|
||||
(settings.solidEntitiesOnly && !eid.def.solid) ||
|
||||
(!eid.def.selectable && !settings.includeNonSelectable)) {
|
||||
continue;
|
||||
}
|
||||
if (settings.entitiesFilter) {
|
||||
|
|
|
|||
|
|
@ -85,6 +85,7 @@ public:
|
|||
bool solidEntitiesOnly = false;
|
||||
bool entityFilterExcludeMode = false;
|
||||
const std::set<entitydefid_t>* entitiesFilter = nullptr;
|
||||
bool includeNonSelectable = false;
|
||||
};
|
||||
|
||||
struct RaycastResult {
|
||||
|
|
|
|||
|
|
@ -42,6 +42,9 @@ struct EntityDef {
|
|||
/// @brief Solid entity material material
|
||||
std::string material;
|
||||
|
||||
/// @brief Can the entity be interacted with cursor (on_attack, on_use)
|
||||
bool selectable = true;
|
||||
|
||||
/// @brief Does entity prevent blocks setup
|
||||
bool blocking = true;
|
||||
|
||||
|
|
|
|||
|
|
@ -13,22 +13,22 @@
|
|||
namespace fs = std::filesystem;
|
||||
|
||||
class ArgC {
|
||||
public:
|
||||
std::string keyword;
|
||||
std::function<bool()> execute;
|
||||
std::string args;
|
||||
std::string help;
|
||||
ArgC(
|
||||
const std::string& keyword,
|
||||
std::function<bool()> execute,
|
||||
const std::string& args,
|
||||
const std::string& help
|
||||
) {
|
||||
this->keyword = keyword;
|
||||
this->execute = execute;
|
||||
this->args = args;
|
||||
this->help = help;
|
||||
}
|
||||
public:
|
||||
std::string keyword;
|
||||
std::function<bool(CoreParameters&, util::ArgsReader&)> execute;
|
||||
std::string args;
|
||||
std::string help;
|
||||
ArgC(
|
||||
const std::string& keyword,
|
||||
std::function<bool(CoreParameters&, util::ArgsReader&)> execute,
|
||||
const std::string& args,
|
||||
const std::string& help
|
||||
) {
|
||||
this->keyword = keyword;
|
||||
this->execute = execute;
|
||||
this->args = args;
|
||||
this->help = help;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
|
@ -36,54 +36,62 @@ static bool perform_keyword(
|
|||
util::ArgsReader& reader, const std::string& keyword, CoreParameters& params
|
||||
) {
|
||||
static const std::vector<ArgC> argumentsCommandline = {
|
||||
ArgC("--res", [¶ms, &reader]() -> bool {
|
||||
ArgC("--res", [](auto& params, auto& reader) -> bool {
|
||||
params.resFolder = reader.next();
|
||||
return true;
|
||||
}, "<path>", "set resources directory."),
|
||||
ArgC("--dir", [¶ms, &reader]() -> bool {
|
||||
ArgC("--dir", [](auto& params, auto& reader) -> bool {
|
||||
params.userFolder = reader.next();
|
||||
return true;
|
||||
}, "<path>", "set userfiles directory."),
|
||||
ArgC("--project", [¶ms, &reader]() -> bool {
|
||||
ArgC("--project", [](auto& params, auto& reader) -> bool {
|
||||
params.projectFolder = reader.next();
|
||||
return true;
|
||||
}, "<path>", "set project directory."),
|
||||
ArgC("--test", [¶ms, &reader]() -> bool {
|
||||
ArgC("--test", [](auto& params, auto& reader) -> bool {
|
||||
params.testMode = true;
|
||||
params.scriptFile = reader.next();
|
||||
return true;
|
||||
}, "<path>", "test script file."),
|
||||
ArgC("--script", [¶ms, &reader]() -> bool {
|
||||
ArgC("--script", [](auto& params, auto& reader) -> bool {
|
||||
params.testMode = false;
|
||||
params.scriptFile = reader.next();
|
||||
return true;
|
||||
}, "<path>", "main script file."),
|
||||
ArgC("--headless", [¶ms]() -> bool {
|
||||
ArgC("--headless", [](auto& params, auto& reader) -> bool {
|
||||
params.headless = true;
|
||||
return true;
|
||||
}, "", "run in headless mode."),
|
||||
ArgC("--stdin-cmd", [¶ms]() -> bool {
|
||||
ArgC("--stdin-cmd", [](auto& params, auto& reader) -> bool {
|
||||
params.stdinCommands = true;
|
||||
return true;
|
||||
}, "", "run commands from stdin."),
|
||||
ArgC("--tps", [¶ms, &reader]() -> bool {
|
||||
ArgC("--tps", [](auto& params, auto& reader) -> bool {
|
||||
params.tps = reader.nextInt();
|
||||
return true;
|
||||
}, "<tps>", "headless mode tick-rate (default - 20)."),
|
||||
ArgC("--version", []() -> bool {
|
||||
ArgC("--version", [](auto&, auto&) -> bool {
|
||||
std::cout << ENGINE_VERSION_STRING << std::endl;
|
||||
return false;
|
||||
}, "", "display the engine version."),
|
||||
ArgC("--dbg-server", [¶ms, &reader]() -> bool {
|
||||
ArgC("--dbg-server", [](auto& params, auto& reader) -> bool {
|
||||
params.debugServerString = reader.next();
|
||||
return true;
|
||||
}, "<serv>", "open debugging server where <serv> is {transport}:{port}"),
|
||||
ArgC("--help", []() -> bool {
|
||||
ArgC("--sub-depth", [](auto& params, auto& reader) -> bool {
|
||||
params.subProcessDepth = reader.nextInt();
|
||||
return true;
|
||||
}, "<depth>", "sub-process depth"),
|
||||
ArgC("--help", [](auto&, auto&) -> bool {
|
||||
std::cout << "VoxelCore v" << ENGINE_VERSION_STRING << "\n\n";
|
||||
std::cout << "Command-line arguments:\n";
|
||||
for (auto& a : argumentsCommandline) {
|
||||
std::cout << std::setw(24) << std::left << (a.keyword + " " + a.args);
|
||||
std::cout << "- " << a.help << std::endl;
|
||||
for (auto& arg : argumentsCommandline) {
|
||||
if (arg.help.empty()) {
|
||||
continue;
|
||||
}
|
||||
std::cout << std::setw(24) << std::left
|
||||
<< (arg.keyword + " " + arg.args);
|
||||
std::cout << "- " << arg.help << std::endl;
|
||||
}
|
||||
std::cout << std::endl;
|
||||
return false;
|
||||
|
|
@ -91,7 +99,7 @@ static bool perform_keyword(
|
|||
};
|
||||
for (auto& a : argumentsCommandline) {
|
||||
if (a.keyword == keyword) {
|
||||
return a.execute();
|
||||
return a.execute(params, reader);
|
||||
}
|
||||
}
|
||||
throw std::runtime_error("unknown argument " + keyword);
|
||||
|
|
|
|||
|
|
@ -19,6 +19,13 @@
|
|||
#else
|
||||
#include <sys/poll.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#endif
|
||||
#ifdef __linux__
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/wait.h>
|
||||
#endif
|
||||
|
||||
namespace platform::internal {
|
||||
|
|
@ -215,8 +222,102 @@ std::filesystem::path platform::get_executable_path() {
|
|||
#endif
|
||||
}
|
||||
|
||||
void platform::new_engine_instance(
|
||||
const std::vector<std::string>& args, std::filesystem::path outputFile
|
||||
class SystemProcess final : public Process {
|
||||
public:
|
||||
#ifdef __linux__
|
||||
SystemProcess(pid_t pid) : pid(pid) {
|
||||
}
|
||||
|
||||
~SystemProcess() {
|
||||
terminate();
|
||||
}
|
||||
#elif defined(_WIN32)
|
||||
SystemProcess(const PROCESS_INFORMATION& pi)
|
||||
: processHandle(pi.hProcess), pid(pi.dwProcessId) {
|
||||
if (pi.hThread != nullptr) {
|
||||
CloseHandle(pi.hThread);
|
||||
}
|
||||
}
|
||||
|
||||
~SystemProcess() {
|
||||
terminate();
|
||||
}
|
||||
#endif
|
||||
SystemProcess(const SystemProcess&) = delete;
|
||||
SystemProcess(SystemProcess&&) noexcept = default;
|
||||
SystemProcess& operator=(const SystemProcess&) = delete;
|
||||
|
||||
SystemProcess& operator=(SystemProcess&& other) noexcept {
|
||||
if (this != &other) {
|
||||
#ifdef __linux__
|
||||
pid = other.pid;
|
||||
#elif defined(_WIN32)
|
||||
if (processHandle != nullptr) {
|
||||
CloseHandle(processHandle);
|
||||
}
|
||||
processHandle = other.processHandle;
|
||||
pid = other.pid;
|
||||
other.processHandle = nullptr;
|
||||
other.pid = 0;
|
||||
#endif
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool isActive() const override {
|
||||
#ifdef __linux__
|
||||
if (kill(pid, 0) == 0) {
|
||||
return true;
|
||||
}
|
||||
return errno != ESRCH;
|
||||
#elif defined(_WIN32)
|
||||
if (processHandle == nullptr) {
|
||||
return false;
|
||||
}
|
||||
DWORD exitCode;
|
||||
if (GetExitCodeProcess(processHandle, &exitCode)) {
|
||||
return exitCode == STILL_ACTIVE;
|
||||
}
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void update() override {}
|
||||
|
||||
void waitForEnd() override {
|
||||
#ifdef __linux__
|
||||
waitpid(pid, nullptr, 0);
|
||||
#elif defined(_WIN32)
|
||||
if (processHandle != nullptr) {
|
||||
WaitForSingleObject(processHandle, INFINITE);
|
||||
}
|
||||
#endif
|
||||
};
|
||||
|
||||
void terminate() final override {
|
||||
#ifdef __linux__
|
||||
logger.info() << "terminating " << pid;
|
||||
kill(pid, SIGTERM);
|
||||
#elif defined(_WIN32)
|
||||
if (processHandle != nullptr) {
|
||||
TerminateProcess(processHandle, 1);
|
||||
WaitForSingleObject(processHandle, INFINITE);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
private:
|
||||
#ifdef __linux__
|
||||
pid_t pid;
|
||||
#elif defined(_WIN32)
|
||||
HANDLE processHandle = nullptr;
|
||||
DWORD pid = 0;
|
||||
#endif
|
||||
};
|
||||
|
||||
std::unique_ptr<Process> platform::new_engine_instance(
|
||||
const std::vector<std::string>& args,
|
||||
std::filesystem::path outputFile,
|
||||
bool subProcess
|
||||
) {
|
||||
auto executable = get_executable_path();
|
||||
|
||||
|
|
@ -285,6 +386,38 @@ void platform::new_engine_instance(
|
|||
&si,
|
||||
&pi
|
||||
);
|
||||
if (subProcess) {
|
||||
HANDLE job = CreateJobObjectW(nullptr, nullptr);
|
||||
|
||||
JOBOBJECT_EXTENDED_LIMIT_INFORMATION info{};
|
||||
info.BasicLimitInformation.LimitFlags =
|
||||
JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
|
||||
|
||||
try {
|
||||
if (!SetInformationJobObject(
|
||||
job,
|
||||
JobObjectExtendedLimitInformation,
|
||||
&info,
|
||||
sizeof(info))) {
|
||||
throw std::runtime_error(
|
||||
"SetInformationJobObject failed with code: " +
|
||||
std::to_string(GetLastError())
|
||||
);
|
||||
}
|
||||
if (!AssignProcessToJobObject(job, pi.hProcess)) {
|
||||
throw std::runtime_error(
|
||||
"SetInformationJobObject failed with code: " +
|
||||
std::to_string(GetLastError())
|
||||
);
|
||||
}
|
||||
return std::make_unique<SystemProcess>(pi);
|
||||
} catch (const std::exception& err) {
|
||||
CloseHandle(job);
|
||||
CloseHandle(pi.hProcess);
|
||||
CloseHandle(pi.hThread);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
if (success) {
|
||||
CloseHandle(pi.hProcess);
|
||||
CloseHandle(pi.hThread);
|
||||
|
|
@ -295,6 +428,46 @@ void platform::new_engine_instance(
|
|||
);
|
||||
}
|
||||
#else
|
||||
|
||||
// TODO: implement
|
||||
#ifndef __APPLE__
|
||||
if (subProcess) {
|
||||
pid_t pid = fork();
|
||||
|
||||
int fd = open(
|
||||
outputFile.string().c_str(), O_WRONLY | O_CREAT | O_TRUNC, 0644
|
||||
);
|
||||
|
||||
if (pid == 0) {
|
||||
prctl(PR_SET_PDEATHSIG, SIGTERM);
|
||||
|
||||
if (getppid() == 1) {
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
dup2(fd, STDOUT_FILENO);
|
||||
dup2(fd, STDERR_FILENO);
|
||||
close(fd);
|
||||
|
||||
std::vector<const char*> argv(args.size() + 2);
|
||||
argv[0] = const_cast<char*>(executable.c_str());
|
||||
for (int i = 0; i < args.size(); i++) {
|
||||
argv[i + 1] = args[i].c_str();
|
||||
}
|
||||
argv[args.size() + 1] = nullptr;
|
||||
|
||||
prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0);
|
||||
execvp(executable.c_str(), const_cast<char**>(argv.data()));
|
||||
|
||||
_exit(127);
|
||||
} else {
|
||||
close(fd);
|
||||
usleep(1000); // waiting for execvp call
|
||||
return std::make_unique<SystemProcess>(pid);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
std::stringstream ss;
|
||||
ss << executable;
|
||||
for (int i = 0; i < args.size(); i++) {
|
||||
|
|
@ -315,6 +488,7 @@ void platform::new_engine_instance(
|
|||
);
|
||||
}
|
||||
#endif
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool platform::stdin_has_data() {
|
||||
|
|
|
|||
|
|
@ -2,8 +2,11 @@
|
|||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include <filesystem>
|
||||
|
||||
#include "interfaces/Process.hpp"
|
||||
|
||||
namespace platform {
|
||||
void configure_encoding();
|
||||
/// @brief Get Environment locale in ISO format ll_CC
|
||||
|
|
@ -18,8 +21,10 @@ namespace platform {
|
|||
/// @brief Get current process running executable path
|
||||
std::filesystem::path get_executable_path();
|
||||
/// @brief Run a separate engine instance with specified arguments
|
||||
void new_engine_instance(
|
||||
const std::vector<std::string>& args, std::filesystem::path outputFile
|
||||
std::unique_ptr<Process> new_engine_instance(
|
||||
const std::vector<std::string>& args,
|
||||
std::filesystem::path outputFile,
|
||||
bool subProcess
|
||||
);
|
||||
/// @brief Open URL in web browser
|
||||
bool open_url(const std::string& url);
|
||||
|
|
|
|||
|
|
@ -37,8 +37,6 @@ inline constexpr size_t MAX_USER_BLOCK_FIELDS_SIZE = 240;
|
|||
|
||||
inline constexpr int BLOCK_MAX_VARIANTS = 16;
|
||||
|
||||
inline std::string DEFAULT_MATERIAL = "base:stone";
|
||||
|
||||
struct BlockFuncsSet {
|
||||
bool init : 1;
|
||||
bool update : 1;
|
||||
|
|
@ -206,7 +204,7 @@ public:
|
|||
dv::value properties = nullptr;
|
||||
|
||||
/// @brief id of used BlockMaterial, may specify non-existing material
|
||||
std::string material = DEFAULT_MATERIAL;
|
||||
std::string material;
|
||||
|
||||
/// @brief Light emission R, G, B, S (sky lights: sun, moon, radioactive
|
||||
/// clouds)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue