/* core/application.cc This file is part of the Osirion project and is distributed under the terms of the GNU General Public License version 2 */ #include #include #include #include #include #include #include "auxiliary/functions.h" #include "sys/sys.h" #include "math/mathlib.h" #include "filesystem/filesystem.h" #include "core/application.h" #include "core/core.h" #include "core/cvar.h" #include "core/entity.h" #include "core/func.h" #include "core/gameconnection.h" #include "core/gameserver.h" namespace core { /* ---- signal handler --------------------------------------------- */ #ifndef _WIN32 extern "C" void signal_handler(int signum) { switch (signum) { case SIGHUP: case SIGINT: case SIGQUIT: case SIGTERM: if (Application::instance()) { con_warn << "Received signal " << signum << ", shutting down...\n"; Application::instance()->shutdown(); Application::instance()->quit(0); } else { std::cerr << "Received signal " << signum << ", terminated...\n"; Application::instance()->quit(1); } break; #ifdef HAVE_CURSES case SIGWINCH: sys::ConsoleInterface::instance()->resize(); break; #endif default: std::cerr << "Received signal " << signum << ", terminated...\n"; Application::instance()->quit(1); break; } } #endif /* ---- class Application ------------------------------------------ */ Application *Application::application_instance = 0; Application::Application() { if (application_instance) { std::cerr << "multiple core::Application instances!\n"; sys::quit(2); } application_instance = this; application_timestamp = 0; application_game = 0; module_interactive = 0; #ifndef _WIN32 sys::signal(SIGHUP, signal_handler); sys::signal(SIGINT, signal_handler); sys::signal(SIGQUIT, signal_handler); sys::signal(SIGTERM, signal_handler); #ifdef HAVE_CURSES sys::signal(SIGWINCH, signal_handler); #endif #endif } Application::~Application() { application_instance = 0; } void Application::init(int count, char **arguments) { con_print << "^BInitializing core...\n"; con_debug << " debug messages enabled\n"; filesystem::init("base", ""); CommandBuffer::init(); // dedicated server has set this to 1 Cvar::sv_dedicated = Cvar::get("sv_dedicated", "0", Cvar::ReadOnly); Cvar::sv_dedicated->set_info("[bool] indicates this is a dedicated server"); // client can set this to 1 Cvar::sv_private = Cvar::get("sv_private", "0"); Cvar::sv_private->set_info("[bool] enable or disable network server for a client"); Cvar::cl_prediction = Cvar::get("cl_prediction", "0", Cvar::Archive); Cvar::cl_prediction->set_info("[bool] enable or disable client prediction"); // load configuration load_config(); load_autoexec(); // load command line load_commandline(count, arguments); // framerate settings Cvar::sv_framerate = Cvar::get("sv_framerate", "25"); Cvar::sv_framerate->set_info("[int] server framerate in frames/sec"); // server settings Cvar::sv_name = Cvar::get("sv_name", "osirion server", Cvar::Archive); Cvar::sv_name->set_info("[string] server name"); Cvar::sv_description = Cvar::get("sv_description", "default server", Cvar::Archive); Cvar::sv_description->set_info("[string] server description"); Cvar::sv_password = Cvar::get("sv_password", "", Cvar::Archive); Cvar::sv_password->set_info("[string] server rcon password"); // network settings Cvar::net_host = Cvar::get("net_host", "0.0.0.0", Cvar::Archive); Cvar::net_host->set_info("[ip] IP address the network server binds to"); Cvar::net_port = Cvar::get("net_port", "8042", Cvar::Archive); Cvar::net_port->set_info("[int] default network port"); Cvar::net_maxclients = Cvar::get("net_maxclients", "16", Cvar::Archive); Cvar::net_maxclients->set_info("[int] maximum number of network clients"); Cvar::net_timeout = Cvar::get("net_timeout", "20"); Cvar::net_timeout->set_info("[int] network timeout in seconds"); Cvar::net_framerate = Cvar::get("net_framerate", "25"); Cvar::net_framerate->set_info("[int] network framerate in frames/sec"); #ifdef _WIN32 Cvar::con_ansi = Cvar::get("con_ansi", "0", Cvar::Archive); #else Cvar::con_ansi = Cvar::get("con_ansi", "1", Cvar::Archive); #endif Cvar::con_ansi->set_info("[bool] console ANSI colors"); sys::set_ansi(Cvar::con_ansi->value()); #ifdef _WIN32 // Initialize win32 socket library WSADATA wsa_data; WORD wsa_version = MAKEWORD(2, 0); if (WSAStartup(wsa_version, &wsa_data) != 0 ) { con_warn << "Could not initialize socket library!" << std::endl; } #endif // register our engine functions Func *func = 0; func = Func::add("help", Application::func_help); func->set_info("help function"); func = Func::add("quit", Application::func_quit); func->set_info("exit the application"); func = Func::add("load", Application::func_load); func->set_info("[string] load a game module"); func = Func::add("connect", Application::func_connect); func->set_info("[ip] without ip, create a game"); func = Func::add("disconnect", Application::func_disconnect); func->set_info("leave the current game"); func = Func::add("say", Application::func_say); func->set_info("say [text] say something on the public chat"); func = Func::add("msg", Application::func_msg); func->set_info("msg [player] [text] send a private message to another player"); func = Func::add("rcon", Application::func_rcon); func->set_info("[string] send a console command to the server"); func = 0; } void Application::shutdown() { con_print << "^BShutting down core...\n"; if (connected()) disconnect(); if (application_game) { delete application_game; application_game = 0; } save_config(); Module::clear(); // remove our engine functions Func::remove("msg"); Func::remove("say"); Func::remove("help"); Func::remove("quit"); Func::remove("connect"); Func::remove("disconnect"); Func::remove("load"); #ifdef _WIN32 // shutdown win32 socket library WSACleanup(); #endif CommandBuffer::shutdown(); filesystem::shutdown(); } void Application::quit(int status) { sys::quit(status); } Module *Application::load(std::string const &module_label) { if (!module_label.size()) { if (Module::active()) { con_print << " active module: " << Module::active()->label() << " " << Module::active()->name() << std::endl; } if (Module::loaded()) { con_print << " loaded module: " << Module::loaded()->label() << " " << Module::loaded()->name() << std::endl; } if (module_interactive) { con_print << " fallback module: " << module_interactive->label() << " " << module_interactive->name() << std::endl; } std::string helpstr(" available modules:"); for(Module::Registry::iterator it = Module::registry().begin(); it != Module::registry().end(); it++) { helpstr += ' '; helpstr += (*it).first; } con_print << helpstr << std::endl; return 0; } /* if (Module::active() && Module::active()->interactive()) { con_warn << "Connected. Disconnect first.\n"; return 0; } */ if (Module::loaded() && Module::loaded()->interactive()) { module_interactive = Module::loaded(); } return Module::load(module_label.c_str()); } void Application::connect(std::string const &host) { if (connected()) { if (!Module::active()->interactive()) { if ((Module::loaded() == Module::active()) && (module_interactive)) { disconnect(); Module::load(module_interactive->label().c_str()); } else { disconnect(); } } else { con_warn << "Connected. Disconnect first.\n"; return; } } if (application_game) { delete application_game; application_game = 0; } if (host.size()) { application_game = new GameConnection(host); if (!application_game->running()) { delete application_game; application_game = 0; } else { notify_connect(); } } else { application_game = new GameServer(); if (application_game->running()) { con_print << "^BConnected to local game.\n"; notify_connect(); } else { delete application_game; application_game = 0; con_warn << "Could not connect to local game!\n"; } } } void Application::disconnect() { if(application_game) { notify_disconnect(); delete application_game; application_game = 0; con_print << "^BDisconnected.\n"; } } void Application::frame(unsigned long timestamp) { application_timestamp = timestamp; // execute commands in the buffer CommandBuffer::exec(); if (!connected()) return; // run a game interface frame application_game->frame(timestamp); if (!application_game->running()) disconnect(); } void Application::save_config() { std::string filename(filesystem::writedir()); if (!Cvar::sv_dedicated->value()) filename.append("client.cfg"); else filename.append("server.cfg"); std::ofstream ofs(filename.c_str()); if (!ofs.is_open()) { con_warn << "Could not write " << filename << std::endl; return; } con_print << " writing configuration to " << filename << std::endl; if (!Cvar::sv_dedicated->value()) ofs << "# client.cfg - osirion client configuration" << std::endl; else ofs << "# server.cfg - osiriond dedicated server configuration" << std::endl; ofs << "# this file is automaticly generated" << std::endl; ofs << std::endl; for (Cvar::Registry::iterator it = Cvar::registry().begin(); it != Cvar::registry().end(); it++) { if (((*it).second->flags() & Cvar::Archive) == Cvar::Archive) { ofs << "# " << (*it).first << " " << (*it).second->info() << std::endl; ofs << "set " << (*it).first << " " << (*it).second->str() << std::endl; ofs << std::endl; } } ofs.close(); } void Application::load_config() { std::string filename(filesystem::writedir()); if (!Cvar::sv_dedicated->value()) filename.append("client.cfg"); else filename.append("server.cfg"); std::ifstream ifs(filename.c_str(), std::ifstream::in); if (!ifs.is_open()) { con_warn << "Could not read " << filename << std::endl; return; } con_print << " reading configuration from " << filename << std::endl; char line[MAXCMDSIZE]; while (ifs.getline(line, MAXCMDSIZE-1)) { if (line[0] && line[0] != '#' && line[0] != ';') cmd() << line << '\n'; } // execute commands in the buffer CommandBuffer::exec(); } void Application::load_autoexec() { if (Cvar::sv_dedicated->value()) return; std::string filename(filesystem::writedir()); filename.append("autoexec.cfg"); std::ifstream ifs(filename.c_str(), std::ifstream::in); if (!ifs.is_open()) { con_warn << "Could not read " << filename << std::endl; std::ofstream ofs(filename.c_str()); if (!ofs.is_open()) { con_warn << "Could not write " << filename << std::endl; return; } ofs << "# autoexec.cfg - osirion client custom settings" << std::endl; ofs << "# put your custom settings here" << std::endl; ofs.close(); return; } else { con_print << " reading configuration from " << filename << std::endl; char line[MAXCMDSIZE]; while (ifs.getline(line, MAXCMDSIZE-1)) { if (line[0] && line[0] != '#' && line[0] != ';') cmd() << line << '\n'; } CommandBuffer::exec(); } } void Application::load_commandline(int count, char **arguments) { if (count < 2) return; for (int i=1; i < count; i++) { if (arguments[i][0] == '+') { if (i >1) cmd() << '\n'; if (arguments[i][1]) cmd() << &arguments[i][1]; } else { if (i > 1) cmd() << ' '; cmd() << arguments[i]; } } cmd() << '\n'; } void Application::notify_message(const core::Message::Channel channel, const std::string &message) { con_print << message << std::endl; } void Application::notify_sound(const char *name) { // the default implementation does nothing. // Dedicated servers don't need sounds } void Application::notify_zonechange() { // the default implementation does nothing. // The client uses this to clear old zones } void Application::notify_disconnect() { // the default implementation does nothing. // The client uses this to clear game data } void Application::notify_connect() { } /* -- static engine functions -------------------------------------- */ void Application::func_help(std::string const &args) { std::istringstream argstream(args); std::string topic; if (!(argstream >> topic)) topic.assign("help"); topic.append(".txt"); CommandBuffer::print_file("help/" + topic); } void Application::func_quit(std::string const &args) { Application::instance()->shutdown(); Application::instance()->quit(0); } void Application::func_connect(std::string const &args) { std::istringstream argstream(args); std::string host; if (!(argstream >> host)) host.clear(); Application::instance()->connect(host); } void Application::func_disconnect(std::string const &args) { Application::instance()->disconnect(); } void Application::func_say(std::string const &args) { if (connection()) { connection()->say(args); } else if (server()) { server()->say(localplayer(), args); } else { con_print << "Not connected." << std::endl; } } void Application::func_msg(std::string const &args) { if (connection()) { connection()->private_message(args); } else if (server()) { server()->private_message(localplayer(), args); } else { con_print << "Not connected." << std::endl; } } void Application::func_rcon(std::string const &args) { if (connection()) { core::Cvar *rconpassword = core::Cvar::find("rconpassword"); if (rconpassword && rconpassword->str().size()) { connection()->rcon(args); } else { con_warn << "rconpassword not set!" << std::endl; } } else if (server()) { cmd() << args << "\n"; } else { con_print << "Not connected." << std::endl; } } void Application::func_load(std::string const &args) { std::string name(args); aux::to_label(name); Application::instance()->load(name); } }