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) {