#include #include #include "Player.hpp" #include "Networking.hpp" #include "MasterClient.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef ENABLE_BREAKPAD #include #endif using namespace std; using namespace mwmp; void printVersion(string version, int protocol) { cout << "TES3:MP dedicated server " << version; cout << " ("; #ifdef _WIN32 cout << "Windows"; #elif __linux cout << "Linux"; #elif __APPLE__ cout << "OS X"; #else cout << "Unknown OS"; #endif cout << " "; #ifdef __x86_64__ cout << "64-bit"; #elif defined __i386__ || defined _M_I86 cout << "32-bit"; #else cout << "Unknown architecture"; #endif cout << ")" << endl; cout << "Protocol version: " << protocol << endl; cout << "------------------------------------------------------------" << endl; } #ifdef ENABLE_BREAKPAD google_breakpad::ExceptionHandler *pHandler = 0; #if defined(_WIN32) bool DumpCallback(const char* _dump_dir, const char* _minidump_id, void* context, EXCEPTION_POINTERS* exinfo, MDRawAssertionInfo* assertion, bool success) #elif defined(__linux) bool DumpCallback(const google_breakpad::MinidumpDescriptor &md, void *context, bool success) #endif { // NO STACK USE, NO HEAP USE THERE !!! return success; } void breakpad(std::string pathToDump) { #ifdef _WIN32 pHandler = new google_breakpad::ExceptionHandler( pathToDump, /*FilterCallback*/ 0, DumpCallback, /*context*/ 0, true ); #else google_breakpad::MinidumpDescriptor md(pathToDump); pHandler = new google_breakpad::ExceptionHandler( md, /*FilterCallback*/ 0, DumpCallback, /*context*/ 0, true, -1 ); #endif } void breakpad_close() { delete pHandler; } #else void breakpad(std::string pathToDump){} void breakpad_close(){} #endif std::string loadSettings (Settings::Manager & settings) { Files::ConfigurationManager mCfgMgr; // Create the settings manager and load default settings file const std::string localdefault = (mCfgMgr.getLocalPath() / "tes3mp-server-default.cfg").string(); const std::string globaldefault = (mCfgMgr.getGlobalPath() / "tes3mp-server-default.cfg").string(); // prefer local if (boost::filesystem::exists(localdefault)) settings.loadDefault(localdefault); else if (boost::filesystem::exists(globaldefault)) settings.loadDefault(globaldefault); else throw std::runtime_error ("No default settings file found! Make sure the file \"tes3mp-server-default.cfg\" was properly installed."); // load user settings if they exist const std::string settingspath = (mCfgMgr.getUserConfigPath() / "tes3mp-server.cfg").string(); if (boost::filesystem::exists(settingspath)) settings.loadUser(settingspath); return settingspath; } void queryThread(MasterClient *mclient) { mclient->Update(); } class Tee : public boost::iostreams::sink { public: Tee(std::ostream &stream, std::ostream &stream2) : out(stream), out2(stream2) { } std::streamsize write(const char *str, std::streamsize size) { out.write (str, size); out.flush(); out2.write (str, size); out2.flush(); return size; } private: std::ostream &out; std::ostream &out2; }; int main(int argc, char *argv[]) { Settings::Manager mgr; Files::ConfigurationManager cfgMgr; breakpad(boost::filesystem::path(cfgMgr.getLogPath()).c_str()); loadSettings(mgr); int logLevel = mgr.getInt("loglevel", "General"); if (logLevel < Log::LOG_VERBOSE || logLevel > Log::LOG_FATAL) logLevel = Log::LOG_VERBOSE; // Some objects used to redirect cout and cerr // Scope must be here, so this still works inside the catch block for logging exceptions std::streambuf* cout_rdbuf = std::cout.rdbuf (); std::streambuf* cerr_rdbuf = std::cerr.rdbuf (); boost::iostreams::stream_buffer coutsb; boost::iostreams::stream_buffer cerrsb; std::ostream oldcout(cout_rdbuf); std::ostream oldcerr(cerr_rdbuf); boost::filesystem::ofstream logfile; // Redirect cout and cerr to openmw.log logfile.open (boost::filesystem::path(cfgMgr.getLogPath() / "/server.log")); coutsb.open (Tee(logfile, oldcout)); cerrsb.open (Tee(logfile, oldcerr)); std::cout.rdbuf (&coutsb); std::cerr.rdbuf (&cerrsb); LOG_INIT(logLevel); int players = mgr.getInt("players", "General"); string addr = mgr.getString("address", "General"); int port = mgr.getInt("port", "General"); string plugin_home = mgr.getString("home", "Plugins"); string moddir = Utils::convertPath(plugin_home + "/data"); vector plugins (Utils::split(mgr.getString("plugins", "Plugins"), ',')); printVersion(TES3MP_VERSION, TES3MP_PROTO_VERSION); setenv("AMXFILE", moddir.c_str(), 1); setenv("MOD_DIR", moddir.c_str(), 1); // hack for lua setenv("LUA_PATH", Utils::convertPath(plugin_home + "/scripts/?.lua" + ";" + plugin_home + "/scripts/?.t").c_str(), 1); for (auto plugin : plugins) Script::LoadScript(plugin.c_str(), plugin_home.c_str()); RakNet::RakPeerInterface *peer = RakNet::RakPeerInterface::GetInstance(); stringstream sstr(TES3MP_VERSION); sstr << TES3MP_PROTO_VERSION; peer->SetIncomingPassword(sstr.str().c_str(), (int)sstr.str().size()); if (RakNet::NonNumericHostString(addr.c_str())) { LOG_MESSAGE_SIMPLE(Log::LOG_ERROR, "%s", "You cannot use non-numeric addresses for the server."); return 1; } RakNet::SocketDescriptor sd((unsigned short)port, addr.c_str()); if (peer->Startup((unsigned)players, &sd, 1) != RakNet::RAKNET_STARTED) return 1; peer->SetMaximumIncomingConnections((unsigned short)(players)); Networking networking(peer); bool masterEnabled = mgr.getBool("enabled", "MasterServer"); thread thrQuery; MasterClient *mclient; if (masterEnabled) { LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "%s", "Sharing server query info to master enabled."); string masterAddr = mgr.getString("address", "MasterServer"); int masterPort = mgr.getInt("port", "MasterServer"); mclient = new MasterClient(masterAddr, (unsigned short) masterPort, addr, (unsigned short) port); mclient->SetMaxPlayers((unsigned)players); string motd = mgr.getString("motd", "General"); mclient->SetMOTD(motd); thrQuery = thread(queryThread, mclient); } int code = networking.MainLoop(); RakNet::RakPeerInterface::DestroyInstance(peer); if (thrQuery.joinable()) { mclient->Stop(); thrQuery.join(); } if (code == 0) LOG_MESSAGE_SIMPLE(Log::LOG_INFO, "%s", "Quitting peacefully."); // Restore cout and cerr std::cout.rdbuf(cout_rdbuf); std::cerr.rdbuf(cerr_rdbuf); breakpad_close(); return code; }