/* core/gameserver.h This file is part of the Osirion project and is distributed under the terms of the GNU General Public License version 2 */ #include #include "auxiliary/functions.h" #include "sys/sys.h" #include "core/application.h" #include "core/cvar.h" #include "core/func.h" #include "core/gameserver.h" #include "core/netserver.h" namespace core { void func_who(std::string const &args) { server()->list_players(); } void func_time(std::string const &args) { server()->showtime(); } GameServer *GameServer::server_instance = 0; GameServer::GameServer() : GameInterface() { con_print << "^BInitializing game server...\n"; server_instance = this; server_network = 0; server_time = 0; server_previoustime = 0; server_frametime = 0.0f; server_maxplayerid = 1; server_module = Module::preload(); if (!server_module) { con_error << "No module loaded.\n"; abort(); return; } server_module->init(); if (!server_module->running()) { con_error << "Could not initialize module '" << server_module->name() << "'\n"; abort(); return; } con_print << " module '^B" << server_module->name() << "^N'\n"; if ((Cvar::sv_dedicated->value() || Cvar::sv_private->value())) { server_network = new NetServer(Cvar::net_host->str(), (unsigned int) Cvar::net_port->value()); if (!server_network->valid()) { delete server_network; server_network = 0; con_error << "Could not initialize network server\n"; abort(); return; } } else { con_print << " network server disabled.\n"; server_network = 0; } Func::add("time", func_time, Func::Shared); Func::add("who", func_who, Func::Shared); if (!Cvar::sv_dedicated->value()) { player_connect(localplayer()); } server_running = true; } GameServer::~GameServer() { server_running = false; con_print << "^BShutting down game server...\n"; if (server_network) { delete server_network; server_network = 0; } if (!Cvar::sv_dedicated->value()) player_disconnect(localplayer()); if (server_module) { server_module->shutdown(); if (server_module != Module::preload()) delete server_module; } Func::remove("time"); Func::remove("who"); server_instance = 0; players.clear(); } void GameServer::abort() { server_running = false; } void GameServer::list_players() { using namespace std; stringstream msgstr; int count = 0; for (std::list:: iterator it = players.begin(); it != players.end(); it++) { msgstr.str(""); con_print << setw(3) << (*it)->id() << aux::spaces((*it)->name(), 24) << std::endl; count++; } con_print << count << " connected " << aux::plural("player", count) << std::endl; } void GameServer::showtime() { using namespace std; int minutes = (int) floorf(server_time / 60.0f); int seconds = (int) floorf(server_time - (float) minutes* 60.0f); int syshours = sys::time() / 3600; int sysminutes = (sys::time() - syshours * 3600) / 60; int sysseconds = sys::time() % 60; con_print << "Uptime " << minutes << ":" << setfill('0') << setw(2) << seconds << " Server localtime " << setfill(' ') << setw(2) << syshours << ":" << setfill('0') << setw(2) << sysminutes << ":" << setw(2) << sysseconds << std::endl; } Player *GameServer::find_player(std::string const search) { using aux::lowercase; std::istringstream searchstr(search); int id = 0; if (searchstr >> id) { for (std::list:: iterator it = players.begin(); it != players.end(); it++) { if ((*it)->id() == id) { return (*it); } } } if (search.size() <3) return 0; for (std::list:: iterator it = players.begin(); it != players.end(); it++) { if (aux::text_strip_lowercase((*it)->name()).find(lowercase(search)) != std::string::npos) return (*it); } return 0; } void GameServer::say(Player *player, std::string const &message) { if (!message.size()) return; std::string notification("^B"); notification.append(player->name()); notification.append("^F:^B "); notification.append(message); // send to application application()->notify_message(notification); application()->notify_sound("com/chat"); // broadcast to remote clients if (server_network) { std::string netmessage("msg public "); netmessage.append(notification); netmessage += '\n'; server_network->broadcast(netmessage); } } void GameServer::broadcast(std::string const & message, Player *ignore_player) { // send to application if (ignore_player != game()->localplayer()) application()->notify_message(message); // broadcast to remote clients if (server_network) { std::string netmessage("msg info "); netmessage.append(message); netmessage += '\n'; server_network->broadcast(netmessage, ignore_player); } } void GameServer::broadcast_sound(std::string const & sound, Player *ignore_player) { // send to application if (ignore_player != game()->localplayer()) application()->notify_sound(sound.c_str()); // broadcast to remote clients if (server_network) { std::string netmessage("msg snd "); netmessage.append(sound); netmessage += '\n'; server_network->broadcast(netmessage, ignore_player); } } void GameServer::send(Player *player, std::string message) { // send to application if (player->id() == localplayer()->id()) { application()->notify_message(message); } // send to remote clients if (server_network) { NetClient *client = server_network->find_client(player); if (client) { std::string netmessage("msg info "); netmessage.append(message); netmessage += '\n'; server_network->send(client, netmessage); } } } void GameServer::send_rcon(Player *player, std::string message) { // send to application if (player->id() == localplayer()->id()) { con_print << message << std::endl; } // send to remote clients if (server_network) { NetClient *client = server_network->find_client(player); if (client) { std::string netmessage("msg rcon "); netmessage.append(message); netmessage += '\n'; server_network->send(client, netmessage); } } } void GameServer::send_sound(Player *player, std::string sound) { if (player->id() == localplayer()->id()) { application()->notify_sound(sound.c_str()); } // send to remote clients if (server_network) { NetClient *client = server_network->find_client(player); if (client) { std::string netmessage("msg snd "); netmessage.append(sound); netmessage += '\n'; server_network->send(client, netmessage); } } } void GameServer::exec(Player *player, std::string const & cmdline) { std::string command; std::stringstream cmdstream; cmdstream.str(cmdline); cmdstream >> command; //con_debug << "Executing " << player->name() << ": " << cmdline << "\n"; Func *function = Func::find(command); if (function ) { std::string args; if (cmdline.size() > command.size() +1 ) args.assign(cmdline.substr(command.size()+1)); if ((function ->flags() & Func::Game) == Func::Game) { //con_debug << "About to execute " << function->name() << " " << args << "'\n"; function->exec(player, args); return; } else if ((function->flags() & Func::Shared) == Func::Shared) { // enable rcon buffering sys::ConsoleInterface::instance()->buffer_rcon(true); function->exec(args); char line[MAXCMDSIZE]; while(sys::ConsoleInterface::instance()->buffer().getline(line, MAXCMDSIZE-1)) { send_rcon(player, std::string(line)); } // disable rcon buffering sys::ConsoleInterface::instance()->buffer_rcon(false); return; } } std::string message("Unknown command '"); message.append(command); message.append("^N'"); send(player, message); } void GameServer::player_connect(Player *player) { if (Cvar::sv_dedicated->value() && (player == localplayer())) { return; } player->player_id = server_maxplayerid++; std::string message("^B"); message.append(player->name()); message.append("^B connects."); broadcast(message, player); // notify the game module server_module->player_connect(player); // manage player list players.push_back(player); } void GameServer::player_disconnect(Player *player) { std::string message("^B"); message.append(player->name()); message.append("^B disconnects."); broadcast(message, player); // notify the game module server_module->player_disconnect(player); // manage player list std::list:: iterator it = players.begin(); while (((*it)->id() != player->id()) && (it != players.end())) { it++; } if (it != players.end()) { players.erase(it); } } void GameServer::frame(float seconds) { if (error()) return; server_time += seconds; server_frametime += seconds; // process incoming network messages if (server_network) { server_network->receive(); if (server_network->error()) { abort(); return; } } if (localplayer()->dirty()) localplayer()->update_info(); if (!Cvar::sv_dedicated->value()) { update_clientstate(seconds); } if ((Cvar::sv_dedicated->value() || Cvar::sv_private->value())) { if (core::Cvar::sv_framerate->value()) { float f = 1.0f / core::Cvar::sv_framerate->value(); if (server_frametime < f) { return; } } } // copy the previous entity state to the client state if (!Cvar::sv_dedicated->value()) { reset_clientstate(server_time, server_previoustime); } // run a time frame on each entity std::map::iterator it; for (it=Entity::registry.begin(); it != Entity::registry.end(); it++) { Entity *entity = (*it).second; if ((entity->type() == Entity::Controlable) || (entity->type() == Entity::Dynamic)) { entity->frame(server_frametime); } } // run a frame on the module if (server_module) { server_module->frame(server_frametime); if (server_module->error()) { abort(); return; } } // send updates if (server_network) { // transmit buffered sends server_network->transmit(); // start server frame std::ostringstream framehdr; framehdr.str(""); framehdr << "frame " << server_time << " " << server_previoustime << "\n"; server_network->broadcast(framehdr.str()); std::map::iterator it; for (it=Entity::registry.begin(); it != Entity::registry.end(); it++) { Entity *entity = (*it).second; if (entity->entity_destroyed) { if (!entity->entity_created) { std::ostringstream netmsg; netmsg.str(""); netmsg << "die " << entity->id() << "\n"; server_network->broadcast(netmsg.str()); } core::Entity::remove(entity->id()); } else if (entity->entity_created) { std::ostringstream netmsg; netmsg.str(""); netmsg << "ent "; entity->serialize(netmsg); netmsg << "\n"; server_network->broadcast(netmsg.str()); entity->entity_created = false; } else if (entity->dirty()) { std::ostringstream netmsg; netmsg.str(""); netmsg << "sup " << entity->id() << " "; entity->serialize_server_update(netmsg); netmsg << "\n"; netmsg.flush(); server_network->broadcast(netmsg.str()); } entity->entity_dirty = false; } // update player info for (std::list::iterator it = server_network->clients.begin(); it != server_network->clients.end(); it++) { NetClient *client = *it; if (client->player()->dirty() && (client->state() == NetClient::Connected)) { // send player data std::ostringstream netmsg; netmsg.str(""); netmsg << "pif "; client->player()->serialize_server_update(netmsg); netmsg << "\n"; client->send(netmsg.str()); client->player()->player_dirty = false; } } // transmit buffered sends server_network->transmit(); } else { // local update stub std::map::iterator it; for (it=Entity::registry.begin(); it != Entity::registry.end(); it++) { Entity *entity = (*it).second; if (entity->entity_destroyed) { core::Entity::remove(entity->id()); } else if (entity->entity_created) { entity->entity_created = false; } entity->entity_dirty = false; } } if (!Cvar::sv_dedicated->value()) { update_clientstate(0); } server_frametime = 0; server_previoustime = server_time; } }