/* 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 { // --------------- engine functions ------------------------------ void 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 func_quit(std::string const &args) { application()->shutdown(); application()->quit(0); } void func_connect(std::string const &args) { std::istringstream argstream(args); std::string host; if (!(argstream >> host)) host.clear(); application()->connect(host); } void func_disconnect(std::string const &args) { application()->disconnect(); } void 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 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 func_load(std::string const &args) { if (!args.size()) { if (Module::current()) { con_print << " currently loaded: " << Module::current()->label() << " " << Module::current()->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; } std::string name(args); aux::to_label(name); application()->load(name); } // --------------- 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()->shutdown(); application()->quit(0); } else { std::cerr << "Received signal " << signum << ", terminated...\n"; application()->quit(1); } break; #ifdef HAVE_CURSES case SIGWINCH: sys::ConsoleInterface::instance()->resize(); break; #endif default: std::cerr << "Received signal " << signum << ", terminated...\n"; application()->quit(1); break; } } #endif // --------------- 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_time = 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_debug << "Debug messages enabled\n"; con_print << "^BInitializing core...\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"); // 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"); // passwords Cvar::rconpassword = Cvar::get("rconpassword", "", Cvar::Archive); Cvar::rconpassword->set_info("[string] password for remote console access"); #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::ConsoleInterface::instance()->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", func_help); func->set_info("help function"); func = Func::add("quit", func_quit); func->set_info("exit the application"); func = Func::add("load", func_load); func->set_info("[str] load a game module"); func = Func::add("connect", func_connect); func->set_info("[ip] without ip, create a game"); func = Func::add("disconnect", func_disconnect); func->set_info("leave the current game"); func = Func::add("say",func_say); func->set_info("say [text] say something on the public chat"); func = Func::add("msg",func_msg); func->set_info("msg [player] [text] send a private message to another player"); } 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("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) { console()->flush(); sys::quit(status); } Module *Application::load(std::string const &module_name) { if (game()) { con_warn << "Connected. Disconnect first.\n"; return 0; } if (Module::current() && Module::current()->interactive()) { module_interactive = Module::current(); } return Module::load(module_name.c_str()); } void Application::connect(std::string const &host) { if (connected()) { if (!Module::current()->interactive() && module_interactive) { /* if the current running module is non-interactive, disconnect, and load the last interactive module_interactive */ disconnect(); Module::load(module_interactive->label().c_str()); } 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(float seconds) { application_time += seconds; // execute commands in the buffer CommandBuffer::exec(); if (!connected()) return; // run a game interface frame application_game->frame(seconds); 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(Message::Channel const channel, std::string const 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() { } void Application::notify_remove_sound(size_t source) { // the default implementation does nothing. // Dedicated servers don't need sounds } }