Skip to content

Commit

Permalink
First pass crash dumping and restoring world. Don't always have db tr…
Browse files Browse the repository at this point in the history
…ansaction open.
  • Loading branch information
ethanmoffat committed Aug 14, 2021
1 parent b92d0a1 commit 12302ec
Show file tree
Hide file tree
Showing 8 changed files with 329 additions and 30 deletions.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ target_include_directories(eoserv PUBLIC ${CMAKE_BINARY_DIR}/bcrypt-src/include)
target_include_directories(eoserv_lib PUBLIC ${CMAKE_BINARY_DIR}/bcrypt-src/include)
list(APPEND eoserv_LIBRARIES bcrypt)

target_include_directories(eoserv PUBLIC ${CMAKE_SOURCE_DIR}/json)
target_include_directories(eoserv_lib PUBLIC ${CMAKE_SOURCE_DIR}/json)

target_link_libraries(eoserv eoserv_lib)
target_link_libraries(eoserv_lib ${eoserv_LIBRARIES})
Expand Down
4 changes: 4 additions & 0 deletions config/server.ini
Original file line number Diff line number Diff line change
Expand Up @@ -151,3 +151,7 @@ InitLoginBan = yes
# Number of background threads in the thread pool
# A value of 0 defaults to the number of concurrent threads supported by the implementation
ThreadPoolThreads = 0

## WorldDumpFile (string)
# Path to a file used as a json dump for the world when the server crashes or exits
WorldDumpFile = ./world.bak.json
2 changes: 2 additions & 0 deletions src/character.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,8 @@ std::string SpellSerialize(const std::list<Character_Spell> &list);
*/
std::list<Character_Spell> SpellUnserialize(const std::string& serialized);

std::string QuestSerialize(const std::map<short, std::shared_ptr<Quest_Context>>& list, const std::set<Character_QuestState>& list_inactive);

/**
* One type of item in a Characters inventory
*/
Expand Down
1 change: 1 addition & 0 deletions src/eoserv_config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -273,6 +273,7 @@ void eoserv_config_validate_config(Config& config)
eoserv_config_default(config, "InitLoginBan" , true);
eoserv_config_default(config, "ThreadPoolThreads" , 0);
eoserv_config_default(config, "AutoCreateDatabase" , false);
eoserv_config_default(config, "WorldDumpFile" , "./world.bak.json");
}

void eoserv_config_validate_admin(Config& config)
Expand Down
1 change: 1 addition & 0 deletions src/fwd/character.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class Character;

struct Character_Item;
struct Character_Spell;
struct Character_QuestState;

enum AdminLevel : unsigned char
{
Expand Down
53 changes: 38 additions & 15 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ extern volatile std::sig_atomic_t eoserv_sig_abort;
volatile std::sig_atomic_t eoserv_sig_rehash = false;
volatile bool eoserv_running = true;

std::unique_ptr<EOServer> server;
void DumpWorld(std::unique_ptr<EOServer>& server);

#ifdef SIGHUP
static void eoserv_rehash(int signal)
{
Expand Down Expand Up @@ -68,6 +71,9 @@ static void eoserv_crash(int signal)

Console::Err("EOSERV is dying! %s", extype);

// todo: doing this from a crash signal handler is potentially pretty dangerous
DumpWorld(server);

#ifdef DEBUG
std::signal(signal, SIG_DFL);
std::raise(signal);
Expand Down Expand Up @@ -340,8 +346,8 @@ int eoserv_main(int argc, char *argv[])

const auto databaseFactory = std::make_shared<DatabaseFactory>(DatabaseFactory());

EOServer server(static_cast<std::string>(config["Host"]), static_cast<int>(config["Port"]), databaseFactory, config, aconfig);
server.Listen(int(config["MaxConnections"]), int(config["ListenBacklog"]));
server = std::make_unique<EOServer>(static_cast<std::string>(config["Host"]), static_cast<int>(config["Port"]), databaseFactory, config, aconfig);
server->Listen(int(config["MaxConnections"]), int(config["ListenBacklog"]));
Console::Out("Listening on %s:%i (0/%i connections)", std::string(config["Host"]).c_str(), int(config["Port"]), int(config["MaxConnections"]));

bool tables_exist = false;
Expand All @@ -353,21 +359,21 @@ int eoserv_main(int argc, char *argv[])

try
{
Database_Result acc_count = server.world->db->Query("SELECT COUNT(1) AS `count` FROM `accounts`");
Database_Result character_count = server.world->db->Query("SELECT COUNT(1) AS `count` FROM `characters`");
Database_Result admin_character_count = server.world->db->Query("SELECT COUNT(1) AS `count` FROM `characters` WHERE `admin` > 0");
Database_Result guild_count = server.world->db->Query("SELECT COUNT(1) AS `count` FROM `guilds`");
Database_Result ban_count = server.world->db->Query("SELECT COUNT(1) AS `count` FROM `bans`");
Database_Result ban_active_count = server.world->db->Query("SELECT COUNT(1) AS `count` FROM `bans` WHERE `expires` <= # AND `expires` <> 0", int(std::time(0)));
Database_Result ban_perm_count = server.world->db->Query("SELECT COUNT(1) AS `count` FROM `bans` WHERE `expires` = 0");
Database_Result acc_count = server->world->db->Query("SELECT COUNT(1) AS `count` FROM `accounts`");
Database_Result character_count = server->world->db->Query("SELECT COUNT(1) AS `count` FROM `characters`");
Database_Result admin_character_count = server->world->db->Query("SELECT COUNT(1) AS `count` FROM `characters` WHERE `admin` > 0");
Database_Result guild_count = server->world->db->Query("SELECT COUNT(1) AS `count` FROM `guilds`");
Database_Result ban_count = server->world->db->Query("SELECT COUNT(1) AS `count` FROM `bans`");
Database_Result ban_active_count = server->world->db->Query("SELECT COUNT(1) AS `count` FROM `bans` WHERE `expires` <= # AND `expires` <> 0", int(std::time(0)));
Database_Result ban_perm_count = server->world->db->Query("SELECT COUNT(1) AS `count` FROM `bans` WHERE `expires` = 0");

Console::Out("Database info:");
Console::Out(" Accounts: %i", int(acc_count.front()["count"]));
Console::Out(" Characters: %i (%i staff)", int(character_count.front()["count"]), int(admin_character_count.front()["count"]));
Console::Out(" Guilds: %i", int(guild_count.front()["count"]));
Console::Out(" Bans: %i (%i expired, %i permanent)", int(ban_count.front()["count"]), int(ban_active_count.front()["count"]), int(ban_perm_count.front()["count"]));

server.world->UpdateAdminCount(int(admin_character_count.front()["count"]));
server->world->UpdateAdminCount(int(admin_character_count.front()["count"]));

tables_exist = true;
}
Expand Down Expand Up @@ -395,9 +401,7 @@ int eoserv_main(int argc, char *argv[])

try
{
server.world->CommitDB();
server.world->db->ExecuteFile(install_script);
server.world->BeginDB();
server->world->db->ExecuteFile(install_script);
}
catch (Database_Exception& e)
{
Expand All @@ -408,11 +412,14 @@ int eoserv_main(int argc, char *argv[])
}
}

server->world->RestoreFromDump(server->world->config["WorldDumpFile"]);

while (eoserv_running)
{
if (eoserv_sig_abort)
{
Console::Out("Exiting EOSERV");
DumpWorld(server);
eoserv_sig_abort = false;
break;
}
Expand All @@ -425,7 +432,7 @@ int eoserv_main(int argc, char *argv[])
std::string old_logout = config["LogOut"];

eoserv_sig_rehash = false;
server.world->Rehash();
server->world->Rehash();

// Does not support changing from file logging back to '-'
{
Expand Down Expand Up @@ -482,45 +489,61 @@ int eoserv_main(int argc, char *argv[])
Console::Out("Config reloaded");
}

server.Tick();
server->Tick();
}
#ifndef DEBUG_EXCEPTIONS
}
catch (Socket_Exception &e)
{
DumpWorld(server);
Console::Err("%s: %s", e.what(), e.error());
return 1;
}
catch (Database_Exception &e)
{
DumpWorld(server);
Console::Err("%s: %s", e.what(), e.error());
return 1;
}
catch (std::runtime_error &e)
{
DumpWorld(server);
Console::Err("Runtime Error: %s", e.what());
return 1;
}
catch (std::logic_error &e)
{
DumpWorld(server);
Console::Err("Logic Error: %s", e.what());
return 1;
}
catch (std::exception &e)
{
DumpWorld(server);
Console::Err("Uncaught Exception: %s", e.what());
return 1;
}
catch (...)
{
DumpWorld(server);
Console::Err("Uncaught Exception");
return 1;
}
#endif // DEBUG_EXCEPTIONS

DumpWorld(server);

#ifdef WIN32
::SetEvent(eoserv_close_event);
#endif // WIN32

return 0;
}

void DumpWorld(std::unique_ptr<EOServer>& server)
{
if (!server || !server->world)
return;

server->world->DumpToFile(server->world->config["WorldDumpFile"]);
}
Loading

0 comments on commit 12302ec

Please sign in to comment.