From a1b68b050157243a8be4f8b811f70606524a501c Mon Sep 17 00:00:00 2001 From: Nicole Collings <100Toby1@gmail.com> Date: Tue, 11 Feb 2020 14:27:48 -0800 Subject: [PATCH] Add ErrorFormatter, make VenusParser and ServerLuaParser use it. * Better error formatting with ErrorFormatter. * Cleaned up VenusParser, made it use std::runtime_errors * Server logs mods that have been loaded. * Make NetHandler force exit if it fails to initialize a server. --- .gitignore | 1 + .idea/vcs.xml | 1 + src/CMakeLists.txt | 2 +- src/lua/ErrorFormatter.cpp | 40 +++++++++++++++++++++++ src/lua/ErrorFormatter.h | 16 +++++++++ src/lua/VenusParser.cpp | 52 +++++++++++++++++------------- src/lua/VenusParser.h | 2 +- src/lua/client/ServerLuaParser.cpp | 44 +++++++++++++++++++------ src/lua/client/ServerLuaParser.h | 2 +- src/server/Server.cpp | 9 ++++-- src/util/Log.h | 11 +++++++ src/util/net/NetHandler.cpp | 7 ++-- 12 files changed, 146 insertions(+), 41 deletions(-) create mode 100644 src/lua/ErrorFormatter.cpp create mode 100644 src/lua/ErrorFormatter.h diff --git a/.gitignore b/.gitignore index 6761dfff..7de42bf6 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ lib/*/* cmake-build-debug cmake-build-install cmake-build-release +.hidden diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 94a25f7f..33265639 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -2,5 +2,6 @@ + \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index b38a007f..51e7a025 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -279,6 +279,6 @@ set(ZEPHA_SRC util/net/Serializer.h util/net/Deserializer.h lua/api/type/ServerLocalLuaEntity.cpp - lua/api/type/ServerLocalLuaEntity.h lua/api/modules/register_item.h lua/api/modules/register_biome.h lua/api/modules/delay.h lua/api/modules/register_block.h lua/api/modules/register_blockmodel.h lua/api/modules/register_entity.h game/scene/world/World.cpp game/scene/world/World.h lua/api/modules/set_block.h lua/api/modules/get_block.h lua/api/modules/remove_block.h lua/register/RegisterBiomes.h lua/register/RegisterBlocks.h lua/register/RegisterItems.h lua/register/RegisterKeybinds.h lua/api/type/LocalLuaAnimationManager.cpp lua/api/type/LocalLuaAnimationManager.h lua/api/type/cLocalLuaAnimationManager.h game/scene/world/Schematic.cpp game/scene/world/Schematic.h lua/VenusParser.cpp lua/VenusParser.h) + lua/api/type/ServerLocalLuaEntity.h lua/api/modules/register_item.h lua/api/modules/register_biome.h lua/api/modules/delay.h lua/api/modules/register_block.h lua/api/modules/register_blockmodel.h lua/api/modules/register_entity.h game/scene/world/World.cpp game/scene/world/World.h lua/api/modules/set_block.h lua/api/modules/get_block.h lua/api/modules/remove_block.h lua/register/RegisterBiomes.h lua/register/RegisterBlocks.h lua/register/RegisterItems.h lua/register/RegisterKeybinds.h lua/api/type/LocalLuaAnimationManager.cpp lua/api/type/LocalLuaAnimationManager.h lua/api/type/cLocalLuaAnimationManager.h game/scene/world/Schematic.cpp game/scene/world/Schematic.h lua/VenusParser.cpp lua/VenusParser.h lua/ErrorFormatter.cpp lua/ErrorFormatter.h) add_library (Zepha_Core ${ZEPHA_SRC}) \ No newline at end of file diff --git a/src/lua/ErrorFormatter.cpp b/src/lua/ErrorFormatter.cpp new file mode 100644 index 00000000..142a659f --- /dev/null +++ b/src/lua/ErrorFormatter.cpp @@ -0,0 +1,40 @@ +// +// Created by aurailus on 2020-02-11. +// + +#include "ErrorFormatter.h" + +std::string ErrorFormatter::formatError(const std::string &fileName, int line, const std::string &stack, + std::string file, bool ansiColors) { + + const std::string red = (ansiColors ? Log::red : ""); + const std::string unbl = (ansiColors ? Log::unbl : ""); + const std::string endl = (ansiColors ? Log::endl : "\n"); + + std::stringstream out {}; + + // Split the file into lines, and add them to a vector + std::vector fileLines {}; + size_t pos = 0; + std::string token; + + while ((pos = file.find("\n")) != std::string::npos) { + token = file.substr(0, pos); + fileLines.push_back(token); + file.erase(0, pos + 1); + } + fileLines.push_back(file); + + while (fileLines.back() == "") fileLines.pop_back(); + + // Format and add lines to the stringstream + for (unsigned int i = std::max(0, line - 6); i < std::min(static_cast(fileLines.size()), line + 5); i++) { + for (unsigned int j = 0; j < 3 - std::to_string(i + 1).length(); j++) out << " "; + out << red << (i+1 == line ? unbl : "") << (i+1) << (i+1 == line ? " # " : " | ") << fileLines[i] << endl; + } + + // Add the stack trace at the bottom + out << endl << red << stack << endl; + + return out.str(); +} diff --git a/src/lua/ErrorFormatter.h b/src/lua/ErrorFormatter.h new file mode 100644 index 00000000..88b85c0f --- /dev/null +++ b/src/lua/ErrorFormatter.h @@ -0,0 +1,16 @@ +// +// Created by aurailus on 2020-02-11. +// + +#pragma once + +#include +#include +#include +#include "../util/Log.h" + +class ErrorFormatter { +public: + static std::string formatError(const std::string& fileName, int line, + const std::string& stack, std::string file, bool ansiColors = true); +}; diff --git a/src/lua/VenusParser.cpp b/src/lua/VenusParser.cpp index b8ab7de3..7683a614 100644 --- a/src/lua/VenusParser.cpp +++ b/src/lua/VenusParser.cpp @@ -3,38 +3,44 @@ // #include "VenusParser.h" +#include "ErrorFormatter.h" -std::string VenusParser::parse(std::string src) { - #ifdef _WIN32 - const static char* EXECUTABLE_NAME = "zepha-venus-win.exe"; - #elif __APPLE__ - const static char* EXECUTABLE_NAME = "zepha-venus-macos"; - #else - const static char* EXECUTABLE_NAME = "zepha-venus-linux"; - #endif +namespace { +#ifdef _WIN32 + const static char* EXECUTABLE_NAME = "zepha-venus-win.exe"; +#elif __APPLE__ + const static char* EXECUTABLE_NAME = "zepha-venus-macos"; +#else + const static char* EXECUTABLE_NAME = "zepha-venus-linux"; +#endif +} +std::string VenusParser::parse(const std::string& fileName, const std::string& fileContents) { const static std::string path = "../zepha-venus/" + std::string(EXECUTABLE_NAME); + const static bool exists = cf_file_exists(path.data()); + if (!exists) throw std::runtime_error("Trying to compile a venus file when zepha-venus has not been built!"); - if (!exists) throw std::string("Trying to compile a venus file when zepha-venus has not been built!"); - - std::string call = path + " " + src; - - std::array buffer; - std::string result; - - std::unique_ptr pipe(popen(call.data(), "r"), pclose); + std::unique_ptr pipe(popen((path + " " + fileName).data(), "r"), pclose); if (!pipe) throw std::runtime_error("popen() failed!"); - while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) { - result += buffer.data(); - } + std::string result; + std::array buffer; + while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) result += buffer.data(); const static std::string errorHeader = "--ZEPHA_PARSING_ERROR--"; + if (result.length() < errorHeader.length() || result.substr(0, errorHeader.length()) != errorHeader) return result; - if (result.length() > errorHeader.length() && result.substr(0, errorHeader.length()) == errorHeader) { - throw std::string(result.substr(errorHeader.length() + 1, result.length() - errorHeader.length() - 1)); - } + // There was an error in the node app, this is it: + std::string error = result.substr(errorHeader.length() + 1, result.length() - errorHeader.length() - 1); - return result; + std::string::size_type lineNumStart = error.find('['); + assert(lineNumStart != std::string::npos); + std::string::size_type lineNumEnd = error.find_first_of(":]", lineNumStart + 1); + assert(lineNumEnd != std::string::npos); + + int lineNum = std::stoi(error.substr(lineNumStart + 1, lineNumEnd - lineNumStart - 1)); + + throw std::runtime_error(ErrorFormatter::formatError(fileName, lineNum, + "Encountered an error compiling " + fileName + ": \n" + error, fileContents).data()); } diff --git a/src/lua/VenusParser.h b/src/lua/VenusParser.h index ef16598e..ca846449 100644 --- a/src/lua/VenusParser.h +++ b/src/lua/VenusParser.h @@ -11,5 +11,5 @@ class VenusParser { public: - static std::string parse(std::string src); + static std::string parse(const std::string& fileName, const std::string& fileContents); }; diff --git a/src/lua/client/ServerLuaParser.cpp b/src/lua/client/ServerLuaParser.cpp index d598bca6..8a1deeaa 100644 --- a/src/lua/client/ServerLuaParser.cpp +++ b/src/lua/client/ServerLuaParser.cpp @@ -29,6 +29,7 @@ #include "../api/functions/sUpdateEntities.h" #include "../VenusParser.h" +#include "../ErrorFormatter.h" void ServerLuaParser::init(ServerDefs& defs, ServerWorld& world, std::string path) { //Load Base Libraries @@ -227,7 +228,8 @@ std::vector ServerLuaParser::createLuaMods(std::list modDir std::string modPath = file; assert(rootPos != std::string::npos); - std::string fileStr = ""; + std::ifstream t(file); + std::string fileStr = std::string((std::istreambuf_iterator(t)), std::istreambuf_iterator()); modPath.erase(rootPos, root.length()); modPath.insert(0, conf.name); @@ -237,18 +239,15 @@ std::vector ServerLuaParser::createLuaMods(std::list modDir modPath.resize(modPath.size() - 6); try { - fileStr = VenusParser::parse(file); + fileStr = VenusParser::parse(file, fileStr); } - catch (std::string e) { - std::cout << Log::err << "Error compiling Venus file '" << file << "':\n" << e << Log::endl; + catch (std::runtime_error e) { + std::cout << std::endl << e.what() << std::endl; exit(1); } } else { modPath.resize(modPath.size() - 4); - - std::ifstream t(file); - fileStr = std::string((std::istreambuf_iterator(t)), std::istreambuf_iterator()); } LuaModFile f {modPath, fileStr}; @@ -410,8 +409,33 @@ void ServerLuaParser::serializeMods() { sol::protected_function_result ServerLuaParser::errorCallback(lua_State*, sol::protected_function_result errPfr) { sol::error err = errPfr; - std::cout << Log::err << "The Zepha sandbox has encountered an error:" - << std::endl << std::endl << err.what() << std::endl << Log::endl; + std::string errString = err.what(); + + std::string::size_type slash = errString.find('/'); + assert(slash != std::string::npos); + + std::string modString = errString.substr(0, slash); + + std::string::size_type lineNumStart = errString.find(':', slash); + assert(lineNumStart != std::string::npos); + std::string::size_type lineNumEnd = errString.find(':', lineNumStart + 1); + assert(lineNumEnd != std::string::npos); + + std::string fileName = errString.substr(0, lineNumStart); + int lineNum = std::stoi(errString.substr(lineNumStart + 1, lineNumEnd - lineNumStart - 1)); + + for (auto& mod : mods) { + if (mod.config.name == modString) { + for (auto& file : mod.files) { + if (file.path == fileName) { + std::cout << std::endl << ErrorFormatter::formatError(fileName, lineNum, errString, file.file) << std::endl; + break; + } + } + break; + } + } + exit(1); return errPfr; } @@ -430,7 +454,7 @@ sol::protected_function_result ServerLuaParser::DoFileSandboxed(std::string file env["_FILE"] = f.path; env["_MODNAME"] = mod.config.name; - auto pfr = lua.safe_script(f.file, env, &ServerLuaParser::errorCallback, "@" + f.path); + auto pfr = lua.safe_script(f.file, env, std::bind(&ServerLuaParser::errorCallback, this, std::placeholders::_1, std::placeholders::_2), "@" + f.path); return pfr; } } diff --git a/src/lua/client/ServerLuaParser.h b/src/lua/client/ServerLuaParser.h index 141b23c1..e794bb74 100644 --- a/src/lua/client/ServerLuaParser.h +++ b/src/lua/client/ServerLuaParser.h @@ -38,7 +38,7 @@ private: void handleDependencies(); void serializeMods(); - static sol::protected_function_result errorCallback(lua_State*, sol::protected_function_result errPfr); + sol::protected_function_result errorCallback(lua_State*, sol::protected_function_result errPfr); sol::protected_function_result DoFileSandboxed(std::string file); double delta = 0; diff --git a/src/server/Server.cpp b/src/server/Server.cpp index fd501c27..f2bc9d6e 100644 --- a/src/server/Server.cpp +++ b/src/server/Server.cpp @@ -17,8 +17,13 @@ Server::Server(const std::string& path, unsigned short port, const std::string& world.init(); config.init(); - std::cout << Log::info << "Server started successfully." << Log::endl; - std::cout << Log::info << "Listening for clients." << Log::endl; + std::cout << Log::info << "Loaded " << defs.luaApi.mods.size() << " mods: [ "; + for (unsigned int i = 0; i < defs.luaApi.mods.size(); i++) { + std::cout << defs.luaApi.mods[i].config.name << (i < defs.luaApi.mods.size() - 1 ? ", " : " ]"); + } + std::cout << std::endl; + + std::cout << Log::info << "Server started successfully, listening for clients." << Log::endl; while (alive) update(); } diff --git a/src/util/Log.h b/src/util/Log.h index 2e2e017f..4e3da487 100644 --- a/src/util/Log.h +++ b/src/util/Log.h @@ -10,9 +10,15 @@ namespace Log { #ifdef _WIN32 static const char* info = "[INFO] "; + static const char* warn = "[WARN] "; static const char* err = "[ERR!] "; static const char* endl = "\n"; + static const char* undl = ""; + static const char* bold = ""; + static const char* unbl = ""; + static const char* red = ""; + static void clear() { auto s = system("cls"); } #else static const char* info = "\033[36m[INFO] "; @@ -20,6 +26,11 @@ namespace Log { static const char* err = "\033[31m[ERR!] "; static const char* endl = "\033[0m\n"; + static const char* undl = "\033[4m"; + static const char* bold = "\033[1m"; + static const char* unbl = "\033[4m\033[1m"; + static const char* red = "\033[31m"; + static void clear() { auto s = system("clear"); } #endif } \ No newline at end of file diff --git a/src/util/net/NetHandler.cpp b/src/util/net/NetHandler.cpp index 47f03486..ce09d609 100644 --- a/src/util/net/NetHandler.cpp +++ b/src/util/net/NetHandler.cpp @@ -14,13 +14,14 @@ NetHandler::NetHandler(const Address& hostAddress, int attempts, int timeout) { NetHandler::NetHandler(unsigned short port, short max_clients) { initServer(port, max_clients); + if (state == NetState::ENET_ERROR) exit(1); } void NetHandler::initServer(unsigned short port, short max_clients) { state = NetState::HOST; if (enet_initialize() != 0) { - fprintf(stderr, "[FATAL] Failed to Initialize ENet.\n"); + std::cout << Log::err << "Failed to initialize enet." << Log::endl; state = NetState::ENET_ERROR; return; } @@ -32,12 +33,12 @@ void NetHandler::initServer(unsigned short port, short max_clients) { peer = nullptr; if (host == nullptr) { - fprintf(stderr, "[FATAL] Failed to create ENet host.\n"); + std::cout << Log::err << "Failed to create host. Is another application already using port " << address.port << "?" << Log::endl; state = NetState::ENET_ERROR; return; } - std::cout << Log::info << "Starting Zepha Server." << Log::endl; + std::cout << Log::info << "Starting Zepha Server on port " << address.port << "." << Log::endl; } void NetHandler::initClient(Address hostAddress, int attempts, int timeout) {