/* 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" #include "core/loader.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_game = 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(arguments[0], "base", ""); Loader::load("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"); // 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"); Cvar::sv_keepalive = Cvar::get("sv_keepalive", "30", core::Cvar::Archive); Cvar::sv_keepalive->set_info("[int] number of seconds to keep dynamic objects alive"); Cvar::sv_collisionmargin = Cvar::get("sv_collisionmargin", 1.0f / 64.0f, core::Cvar::Archive); Cvar::sv_collisionmargin->set_info("[float] margin for mesh collisions"); // network settings Cvar::net_host = Cvar::get("net_host", "0.0.0.0", Cvar::Archive); Cvar::net_host->set_info("[address] 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"); Cvar::net_selecttimeout = Cvar::get("net_selecttimeout", "10000"); Cvar::net_selecttimeout->set_info("[int] network select() call timeout, in microseconds"); Cvar::mem_vertex = core::Cvar::get("mem_vertex", "64" , core::Cvar::Archive); Cvar::mem_vertex->set_info("[int] amount of video memory reserved for model geometry, in megabytes"); #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()); if (Cvar::sv_dedicated->value()) { Cvar::con_timestamps = Cvar::get("con_timestamps", "1", Cvar::Archive); } else { Cvar::con_timestamps = Cvar::get("con_timestamps", "0", Cvar::Archive); } Cvar::con_timestamps->set_info("[bool] enable console timestamps"); sys::set_console_timestamps(Cvar::con_timestamps->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("module", Application::func_module); 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("shout", Application::func_shout); func->set_info("shout [text] shout something on the global chat"); func = Func::add("say", Application::func_say); func->set_info("say [text] say something on the local 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(); Loader::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); } bool Application::load(std::string const &label) { if (!label.size()) { Loader::list(); return false; } if (connected() && game()->interactive()) { con_warn << "Connected. Disconnect first.\n"; return false; } if (!Loader::load(label)) { return false; } return true; } void Application::connect(std::string const &host) { if (connected() && game()->interactive()) { con_warn << "Connected. Disconnect first.\n"; return; } if (application_game) { disconnect(); } if (host.size()) { notify_connect(); application_game = new GameConnection(host); if (!application_game->running()) { delete application_game; application_game = 0; } } else { notify_connect(); application_game = new GameServer(); if (application_game->running()) { con_print << "^BConnected to local game.\n"; } else { delete application_game; application_game = 0; con_warn << "Could not connect to local game!\n"; } } } void Application::disconnect() { if (application_game) { delete application_game; application_game = 0; notify_disconnect(); con_print << "^BDisconnected.\n"; } } void Application::frame() { // execute commands in the buffer CommandBuffer::exec(); if (!connected()) return; // run a game interface frame application_game->frame(application_timer.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] != ';') CommandBuffer::exec(line); } } 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] != ';') CommandBuffer::exec(line); } } } 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) { switch (channel) { case core::Message::Info: // Info message case core::Message::Local: // Chat message in the local zone case core::Message::Public: // Public chat message case core::Message::Private: // Private chat message case core::Message::RCon: // RCon message con_print << message << std::endl; break; case core::Message::Sound: // Sound event break; default: break; } } void Application::messagebox(const char *text, const char *label1, const char *command1, const char *label2, const char *command2) { std::string str_text(text ? text : "" ); std::string str_label1(label1 ? label1 : "" ); std::string str_command1(command1 ? command1 : "" ); std::string str_label2(label2 ? label2 : "" ); std::string str_command2(command2 ? command2 : "" ); notify_messagebox(str_text, str_label1, str_command1, str_label2, str_command2); } void Application::notify_messagebox(const std::string & text, const std::string &label1, const std::string command1, const std::string &label2, const std::string command2) { // the default implementation does nothing // used by the client to show messageboxes } void Application::notify_loader(const std::string &message) { // the default implementation does nothing. // used by the client to update the loader screen } 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_shout(std::string const &args) { if (connection()) { connection()->shout(args); } else if (server()) { server()->shout(localplayer(), args); } else { con_print << "Not connected." << std::endl; } } 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_module(std::string const &args) { std::string name(args); aux::to_label(name); Application::instance()->load(name); } void Application::func_info(std::string const &args) { // FIXME connection info etc } }