/* 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 #include "auxiliary/functions.h" #include "core/application.h" #include "core/cvar.h" #include "core/func.h" #include "core/gameserver.h" #include "core/netserver.h" #include "filesystem/filesystem.h" #include "sys/sys.h" namespace core { Player *console_find_player(std::string const & target) { Player *targetplayer = server()->find_player(target); if (!targetplayer) { con_print << "^BPlayer " + target + "^B not found."; } return targetplayer; } void func_who(std::string const &args) { server()->list_players(); } void func_time(std::string const &args) { 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 << " Local time " << setfill(' ') << setw(2) << syshours << ":" << setfill('0') << setw(2) << sysminutes << ":" << setw(2) << sysseconds << setfill(' ') << std::endl; } void func_mute(std::string const &args) { Player *targetplayer = 0; if (!(targetplayer = console_find_player(args))) return; if (targetplayer->mute()) { con_print << "^BPlayer " + targetplayer->name() + "^B has already been muted."; return; } targetplayer->player_mute = true; server()->broadcast("^B" + targetplayer->name() + "^B has been muted.", targetplayer); server()->send(targetplayer, "^BYou have been muted."); } void func_unmute(std::string const &args) { Player *targetplayer = 0; if (!(targetplayer = console_find_player(args))) return; if (!targetplayer->mute()) { con_print << "^BPlayer " + targetplayer->name() + "^B has not been muted."; return; } targetplayer->player_mute = false; server()->broadcast("^B" +targetplayer->name() + "^B has been unmuted.", targetplayer); server()->send(targetplayer, "^BYou have been unmuted."); } void func_kick(std::string const &args) { std::stringstream str(args); std::string name; if (str >> name) { Player *targetplayer = 0; if (!(targetplayer = console_find_player(name))) return; std::string reason; if (args.size() > name.size()+1) reason.assign(args.substr(name.size()+1)); else reason.assign("forcefully removed."); server()->kick(targetplayer, reason); } } void func_grant_rcon(std::string const &args) { Player *targetplayer = 0; if (!(targetplayer = console_find_player(args))) return;} void func_revoke_rcon(std::string const &args) { Player *targetplayer = 0; if (!(targetplayer = console_find_player(args))) return; } 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::current(); if (!server_module) { con_error << "No module loaded.\n"; abort(); return; } //load_config(); //FIXME interferes with command line because of cmd.exec // set the name of the game core::Cvar::set("g_name", server_module->name().c_str(), core::Cvar::Game | core::Cvar::ReadOnly); 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 (server_module->interactive() && (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 *func = 0; /* -- admin functions -- */ func = Func::add("mute", func_mute); func->set_info("[player] mute a player"); func = Func::add("unmute", func_unmute); func->set_info("[player] unmute a player"); func = Func::add("kick", func_kick); func->set_info("[player] [reason] kick a player from the server"); /* func = Func::add("grant_rcon", func_grant_rcon); func->set_info("[player] grant rcon rights"); func = Func::add("revoke_rcon", func_grant_rcon); func->set_info("[player] revoke rcon rights"); */ /* -- player functions --*/ func = Func::add("time", func_time, Func::Shared); func->set_info("get the server uptime and current server localtime"); func = Func::add("who", func_who, Func::Shared); func->set_info("get a list of connected players"); 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; } save_config(); if (server_module) { if (server_module->running() && !Cvar::sv_dedicated->value()) player_disconnect(localplayer()); server_module->shutdown(); } Func::remove("kick"); Func::remove("mute"); Func::remove("unmute"); /* Func::remove("grant_rcon"); Func::remove("revoke_rcon"); */ Func::remove("time"); Func::remove("who"); server_instance = 0; } void GameServer::abort() { server_running = false; } bool GameServer::interactive() const { if (!server_module) { return false; } else { return server_module->interactive(); } } 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 = game_players.begin(); it != game_players.end(); it++) { if ((*it)->id() == id) { return (*it); } } } if (search.size() <3) return 0; for (std::list:: iterator it = game_players.begin(); it != game_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; if (player->mute()) { send(player, "^BYou have been muted."); return; } std::string notification("^B"); notification.append(player->name()); notification.append("^F:^B "); notification.append(message); broadcast_message(Message::Public, notification); } void GameServer::private_message(Player *player, std::string const &args) { if (!args.size()) return; if (player->mute()) { send(player, "^BYou have been muted."); return; } std::string target; std::stringstream argstr(args); if (!(argstr >> target)) { return; } core::Player *targetplayer = core::server()->find_player(target); if (!targetplayer) { send(player, "^BPlayer " + target + "^B not found."); return; } std::string message(args.substr(target.size())); send_message(Message::Private, player, "^FTo ^B" + targetplayer->name() + "^F:" + message); send_message(Message::Private, targetplayer, "^FFrom ^B" + player->name() + "^F:" + message); } // FIXME kicked by void GameServer::kick(Player *player, std::string const &reason) { if (!server_network) { con_print << "Not running a networked server." << std::endl; return; } NetClient *client = server_network->find_client(player); if (client) { broadcast("^B" + player->name() + "^B has been kicked: " + reason, player); server_network->send_message(client, "info", "^WYou have been kicked: " + reason); server_network->send_disconnect(client); } else { con_print << "Network client not found." << std::endl; } } // broadcast an "info" message to all players void GameServer::broadcast(std::string const message, Player *ignore_player) { if (!message.size()) return; broadcast_message(Message::Info, message, ignore_player); } // send and "info" message to a single player void GameServer::send(Player *player, std::string const message) { send_message(Message::Info, player, message); } // send an rcon message to a single player void GameServer::send_rcon(Player *player, std::string const message) { send_message(Message::RCon, player, message); } void GameServer::send_message(Message::Channel const channel, Player *player, std::string const message) { if (!message.size()) return; if (player == localplayer()) { application()->notify_message(channel, message); return; } else { if (server_network) { std::string msg_channel; switch(channel) { case core::Message::Info: // Info message msg_channel.assign("info"); break; case core::Message::Local: // Chat message in the local zone msg_channel.assign("local"); break; case core::Message::Public: // Public chat message msg_channel.assign("public"); break; case core::Message::Private: // Private chat message msg_channel.assign("private"); break; case core::Message::RCon: // RCon message msg_channel.assign("rcon"); break; default: con_warn << "message on unknown channel " << channel << "!" << std::endl; return; break; } NetClient *client = server_network->find_client(player); if (client) { server_network->send_message(client, msg_channel.c_str(), message); } } } } // broadcast a message on a specified channel to all players void GameServer::broadcast_message(Message::Channel const channel, std::string const message, Player *ignore_player) { if (!message.size()) return; // send to application if (ignore_player != game()->localplayer()) application()->notify_message(channel, message); // broadcast to remote clients if (server_network) { std::string msg_channel; switch(channel) { case core::Message::Info: // Info message msg_channel.assign("info"); break; case core::Message::Local: // Chat message in the local zone msg_channel.assign("local"); break; case core::Message::RCon: // RCon message msg_channel.assign("rcon"); break; case core::Message::Public: // Public chat message msg_channel.assign("public"); break; default: con_warn << "message on unknown channel " << channel << "!" << std::endl; return; break; } server_network->broadcast_message(msg_channel.c_str(), message); } } // broadcast a sound event to all players void GameServer::broadcast_sound(std::string const sound, Player *ignore_player) { if (!sound.size()) return; // send to application if (ignore_player != game()->localplayer()) { application()->notify_sound(sound.c_str()); } // broadcast to remote clients if (server_network) { server_network->broadcast_message("snd", sound, ignore_player); } } // send a sound event to a single player void GameServer::send_sound(Player *player, std::string const sound) { if (!sound.size()) return; // send to application if (player == localplayer()) { application()->notify_sound(sound.c_str()); return; } // send to remote client if (server_network) { NetClient *client = server_network->find_client(player); if (client) { server_network->send_message(client, "snd", sound); } } } // execute a command for a remote player void GameServer::exec(Player *player, std::string const & cmdline) { std::string command; std::stringstream cmdstream; cmdstream.str(cmdline); cmdstream >> command; 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) { function->exec(player, args); return; } else if ((function->flags() & Func::Shared) == Func::Shared) { // enable rcon buffering console()->set_rcon(true); function->exec(args); char line[MAXCMDSIZE]; while(console()->buffer().getline(line, MAXCMDSIZE-1)) { send(player, std::string(line)); } // disable rcon buffering console()->set_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 game_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); // clear all player assets player->clear_assets(); // notify the game module server_module->player_disconnect(player); // manage player list std::list:: iterator it = game_players.begin(); while (((*it)->id() != player->id()) && (it != game_players.end())) { it++; } if (it != game_players.end()) { game_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); } else { game_serverframetime = server_time; game_previousframetime = server_previoustime; } // run a time frame on each entity for (Entity::Registry::iterator 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; } } if (server_network) { // send network updates server_network->frame(server_time, server_previoustime); } // mark all entities as updated for (Entity::Registry::iterator it=Entity::registry().begin(); it != Entity::registry().end(); ) { Entity *entity = (*it).second; if (entity->entity_destroyed) { delete entity; (*it).second = entity = 0; Entity::registry().erase(it++); } else { entity->clear_updates(); ++it; } } if (!Cvar::sv_dedicated->value()) { update_clientstate(0); } server_frametime = 0; server_previoustime = server_time; } void GameServer::save_config() { std::string filename(filesystem::writedir()); filename.append("game.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; ofs << "# game.cfg - " << server_module->name() << " 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) && (((*it).second->flags() & Cvar::Game) == Cvar::Game)){ ofs << "# " << (*it).first << " " << (*it).second->info() << std::endl; ofs << "set " << (*it).first << " " << (*it).second->str() << std::endl; ofs << std::endl; } } ofs.close(); } void GameServer::load_config() { std::string filename(filesystem::writedir()); filename.append("game.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(); } }