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.
master
Nicole Collings 2020-02-11 14:27:48 -08:00
parent d7aea41234
commit a1b68b0501
12 changed files with 146 additions and 41 deletions

1
.gitignore vendored
View File

@ -2,3 +2,4 @@ lib/*/*
cmake-build-debug
cmake-build-install
cmake-build-release
.hidden

View File

@ -2,5 +2,6 @@
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
<mapping directory="$PROJECT_DIR$/zepha-venus" vcs="Git" />
</component>
</project>

View File

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

View File

@ -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<std::string> 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<int>(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();
}

16
src/lua/ErrorFormatter.h Normal file
View File

@ -0,0 +1,16 @@
//
// Created by aurailus on 2020-02-11.
//
#pragma once
#include <string>
#include <vector>
#include <sstream>
#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);
};

View File

@ -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<char, 128> buffer;
std::string result;
std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(call.data(), "r"), pclose);
std::unique_ptr<FILE, decltype(&pclose)> 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<char, 128> 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());
}

View File

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

View File

@ -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<LuaMod> ServerLuaParser::createLuaMods(std::list<std::string> 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<char>(t)), std::istreambuf_iterator<char>());
modPath.erase(rootPos, root.length());
modPath.insert(0, conf.name);
@ -237,18 +239,15 @@ std::vector<LuaMod> ServerLuaParser::createLuaMods(std::list<std::string> 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<char>(t)), std::istreambuf_iterator<char>());
}
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;
}
}

View File

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

View File

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

View File

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

View File

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