X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Fserver.cpp;h=b257448a1c4e2de8b1b7091ffd74e103352e4c2a;hb=676f34a02be17d898ea45b1b05be72bfcfcd588e;hp=2d00cf4aca406c57c484238b5013f9cd634a704b;hpb=979ca23f1eae1adeb8b0083dffe7203c54d87395;p=dragonfireclient.git diff --git a/src/server.cpp b/src/server.cpp index 2d00cf4ac..b257448a1 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -20,21 +20,25 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "server.h" #include #include +#include #include "clientserver.h" +#include "ban.h" +#include "environment.h" #include "map.h" -#include "jmutexautolock.h" +#include "jthread/jmutexautolock.h" #include "main.h" #include "constants.h" #include "voxel.h" #include "config.h" +#include "version.h" #include "filesys.h" #include "mapblock.h" #include "serverobject.h" +#include "genericobject.h" #include "settings.h" #include "profiler.h" #include "log.h" -#include "script.h" -#include "scriptapi.h" +#include "scripting_game.h" #include "nodedef.h" #include "itemdef.h" #include "craftdef.h" @@ -58,18 +62,44 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/mathconstants.h" #include "rollback.h" #include "util/serialize.h" +#include "util/thread.h" +#include "defaultsettings.h" -void * ServerThread::Thread() +class ClientNotFoundException : public BaseException { - ThreadStarted(); +public: + ClientNotFoundException(const char *s): + BaseException(s) + {} +}; + +class ServerThread : public JThread +{ + Server *m_server; + +public: + + ServerThread(Server *server): + JThread(), + m_server(server) + { + } + void * Thread(); +}; + +void * ServerThread::Thread() +{ log_register_thread("ServerThread"); DSTACK(__FUNCTION_NAME); - BEGIN_DEBUG_EXCEPTION_HANDLER - while(getRun()) + m_server->AsyncRunStep(true); + + ThreadStarted(); + + while(!StopRequested()) { try{ //TimeTaker timer("AsyncRunStep() + Receive()"); @@ -89,6 +119,9 @@ void * ServerThread::Thread() { infostream<<"Server: PeerNotFoundException"<setAsyncFatalError(e.what()); @@ -126,7 +159,7 @@ v3f ServerSoundParams::getPos(ServerEnvironment *env, bool *pos_exists) const } void RemoteClient::GetNextBlocks(Server *server, float dtime, - core::array &dest) + std::vector &dest) { DSTACK(__FUNCTION_NAME); @@ -274,11 +307,11 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, Get the border/face dot coordinates of a "d-radiused" box */ - core::list list; + std::list list; getFacePositions(list, d); - core::list::Iterator li; - for(li=list.begin(); li!=list.end(); li++) + std::list::iterator li; + for(li=list.begin(); li!=list.end(); ++li) { v3s16 p = *li + center; @@ -305,7 +338,7 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, } // Don't send blocks that are currently being transferred - if(m_blocks_sending.find(p) != NULL) + if(m_blocks_sending.find(p) != m_blocks_sending.end()) continue; /* @@ -382,7 +415,7 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, Don't send already sent blocks */ { - if(m_blocks_sent.find(p) != NULL) + if(m_blocks_sent.find(p) != m_blocks_sent.end()) { continue; } @@ -554,21 +587,21 @@ void RemoteClient::GetNextBlocks(Server *server, float dtime, void RemoteClient::GotBlock(v3s16 p) { - if(m_blocks_sending.find(p) != NULL) - m_blocks_sending.remove(p); + if(m_blocks_sending.find(p) != m_blocks_sending.end()) + m_blocks_sending.erase(p); else { /*infostream<<"RemoteClient::GotBlock(): Didn't find in" " m_blocks_sending"< &blocks) +void RemoteClient::SetBlocksNotSent(std::map &blocks) { m_nearest_unsent_d = 0; - for(core::map::Iterator - i = blocks.getIterator(); - i.atEnd()==false; i++) + for(std::map::iterator + i = blocks.begin(); + i != blocks.end(); ++i) { - v3s16 p = i.getNode()->getKey(); + v3s16 p = i->first; - if(m_blocks_sending.find(p) != NULL) - m_blocks_sending.remove(p); - if(m_blocks_sent.find(p) != NULL) - m_blocks_sent.remove(p); + if(m_blocks_sending.find(p) != m_blocks_sending.end()) + m_blocks_sending.erase(p); + if(m_blocks_sent.find(p) != m_blocks_sent.end()) + m_blocks_sent.erase(p); } } -/* - PlayerInfo -*/ - -PlayerInfo::PlayerInfo() -{ - name[0] = 0; - avg_rtt = 0; -} - -void PlayerInfo::PrintLine(std::ostream *s) -{ - (*s)<getBool("enable_ipv6") && g_settings->getBool("ipv6_server"), this), + m_banmanager(NULL), m_rollback(NULL), m_rollback_sink_enabled(true), m_enable_rollback_recording(false), m_emerge(NULL), - m_biomedef(NULL), - m_lua(NULL), + m_script(NULL), m_itemdef(createItemDefManager()), m_nodedef(createNodeDefManager()), m_craftdef(createCraftDefManager()), m_event(new EventManager()), - m_thread(this), - //m_emergethread(this), + m_thread(NULL), m_time_of_day_send_timer(0), m_uptime(0), m_shutdown_requested(false), @@ -665,12 +674,9 @@ Server::Server( m_objectdata_timer = 0.0; m_emergethread_trigger_timer = 0.0; m_savemap_timer = 0.0; - m_clients_number = 0; - m_env_mutex.Init(); - m_con_mutex.Init(); - m_step_dtime_mutex.Init(); m_step_dtime = 0.0; + m_lag = g_settings->getFloat("dedicated_server_step"); if(path_world == "") throw ServerError("Supplied empty world path"); @@ -684,27 +690,40 @@ Server::Server( else infostream< unsatisfied_mods = modconf.getUnsatisfiedMods(); + std::vector unsatisfied_mods = modconf.getUnsatisfiedMods(); // complain about mods with unsatisfied dependencies - if(!modconf.isConsistent()) + if(!modconf.isConsistent()) { - for(std::list::iterator it = unsatisfied_mods.begin(); + for(std::vector::iterator it = unsatisfied_mods.begin(); it != unsatisfied_mods.end(); ++it) { ModSpec mod = *it; @@ -720,29 +739,23 @@ Server::Server( std::string worldmt = m_path_world + DIR_DELIM + "world.mt"; worldmt_settings.readConfigFile(worldmt.c_str()); std::vector names = worldmt_settings.getNames(); - std::set exclude_mod_names; std::set load_mod_names; - for(std::vector::iterator it = names.begin(); + for(std::vector::iterator it = names.begin(); it != names.end(); ++it) - { - std::string name = *it; - if (name.compare(0,9,"load_mod_")==0) - { - if(worldmt_settings.getBool(name)) - load_mod_names.insert(name.substr(9)); - else - exclude_mod_names.insert(name.substr(9)); - } + { + std::string name = *it; + if(name.compare(0,9,"load_mod_")==0 && worldmt_settings.getBool(name)) + load_mod_names.insert(name.substr(9)); } // complain about mods declared to be loaded, but not found for(std::vector::iterator it = m_mods.begin(); - it != m_mods.end(); ++it) + it != m_mods.end(); ++it) load_mod_names.erase((*it).name); - for(std::list::iterator it = unsatisfied_mods.begin(); - it != unsatisfied_mods.end(); ++it) + for(std::vector::iterator it = unsatisfied_mods.begin(); + it != unsatisfied_mods.end(); ++it) load_mod_names.erase((*it).name); if(!load_mod_names.empty()) - { + { errorstream << "The following mods could not be found:"; for(std::set::iterator it = load_mod_names.begin(); it != load_mod_names.end(); ++it) @@ -760,14 +773,14 @@ Server::Server( // Initialize scripting infostream<<"Server: Initializing Lua"<loadMod(builtinpath, "__builtin"); if(!success){ errorstream<<"Server: Failed to load and run " <loadMod(scriptpath, mod.name); if(!success){ errorstream<<"Server: Failed to load and run " <updateAliases(m_itemdef); - // Add default biomes after nodedef had its aliases added - m_biomedef->addDefaultBiomes(); - - // Create emerge manager - m_emerge = new EmergeManager(this, m_biomedef); - // Initialize Environment ServerMap *servermap = new ServerMap(path_world, this, m_emerge); - m_env = new ServerEnvironment(servermap, m_lua, this, this); + m_env = new ServerEnvironment(servermap, m_script, this, m_emerge); - m_emerge->initMapgens(servermap->getMapgenParams()); + // Run some callbacks after the MG params have been set up but before activation + MapgenParams *mgparams = servermap->getMapgenParams(); + m_script->environment_OnMapgenInit(mgparams); + + // Initialize mapgens + m_emerge->initMapgens(mgparams); + servermap->setMapgenParams(m_emerge->params); // Give environment reference to scripting api - scriptapi_add_environment(m_lua, m_env); + m_script->initializeEnvironment(m_env); // Register us to receive map edit events servermap->addEventReceiver(this); @@ -854,13 +867,13 @@ Server::~Server() /* Send the message to clients */ - for(core::map::Iterator - i = m_clients.getIterator(); - i.atEnd() == false; i++) + for(std::map::iterator + i = m_clients.begin(); + i != m_clients.end(); ++i) { // Get client and check that it is valid - RemoteClient *client = i.getNode()->getValue(); - assert(client->peer_id == i.getNode()->getKey()); + RemoteClient *client = i->second; + assert(client->peer_id == i->first); if(client->serialization_version == SER_FMT_VER_INVALID) continue; @@ -879,7 +892,7 @@ Server::~Server() /* Execute script shutdown hooks */ - scriptapi_on_shutdown(m_lua); + m_script->on_shutdown(); } { @@ -902,6 +915,10 @@ Server::~Server() Stop threads */ stop(); + delete m_thread; + + //shutdown all emerge threads first! + delete m_emerge; /* Delete clients @@ -909,20 +926,20 @@ Server::~Server() { JMutexAutoLock clientslock(m_con_mutex); - for(core::map::Iterator - i = m_clients.getIterator(); - i.atEnd() == false; i++) + for(std::map::iterator + i = m_clients.begin(); + i != m_clients.end(); ++i) { // Delete client - delete i.getNode()->getValue(); + delete i->second; } } // Delete things in the reverse order of creation delete m_env; delete m_rollback; - delete m_emerge; + delete m_banmanager; delete m_event; delete m_itemdef; delete m_nodedef; @@ -930,7 +947,7 @@ Server::~Server() // Deinitialize scripting infostream<<"Server: Deinitializing scripting"<Stop(); // Initialize connection m_con.SetTimeoutMs(30); m_con.Serve(port); // Start thread - m_thread.setRun(true); - m_thread.Start(); + m_thread->Start(); // ASCII art for the win! actionstream @@ -978,9 +994,9 @@ void Server::stop() infostream<<"Server: Stopping and waiting threads"<Stop(); //m_emergethread.setRun(false); - m_thread.stop(); + m_thread->Wait(); //m_emergethread.stop(); infostream<<"Server: Threads stopped"<add("Server::AsyncRunStep with dtime (num)", 1); @@ -1073,21 +1089,31 @@ void Server::AsyncRunStep() //JMutexAutoLock envlock(m_env_mutex); JMutexAutoLock conlock(m_con_mutex); - for(core::map::Iterator - i = m_clients.getIterator(); - i.atEnd() == false; i++) + u16 time = m_env->getTimeOfDay(); + float time_speed = g_settings->getFloat("time_speed"); + + for(std::map::iterator + i = m_clients.begin(); + i != m_clients.end(); ++i) { - RemoteClient *client = i.getNode()->getValue(); - SharedBuffer data = makePacket_TOCLIENT_TIME_OF_DAY( - m_env->getTimeOfDay(), g_settings->getFloat("time_speed")); - // Send as reliable - m_con.Send(client->peer_id, 0, data, true); + RemoteClient *client = i->second; + SendTimeOfDay(client->peer_id, time, time_speed); } } } { JMutexAutoLock lock(m_env_mutex); + // Figure out and report maximum lag to environment + float max_lag = m_env->getMaxLagEstimate(); + max_lag *= 0.9998; // Decrease slowly (about half per 5 minutes) + if(dtime > max_lag){ + if(dtime > 0.1 && dtime > max_lag * 2.0) + infostream<<"Server: Maximum lag peaked to "<reportMaxLagEstimate(max_lag); // Step environment ScopeProfiler sp(g_profiler, "SEnv step"); ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG); @@ -1117,11 +1143,11 @@ void Server::AsyncRunStep() ScopeProfiler sp(g_profiler, "Server: handle players"); - for(core::map::Iterator - i = m_clients.getIterator(); - i.atEnd() == false; i++) + for(std::map::iterator + i = m_clients.begin(); + i != m_clients.end(); ++i) { - RemoteClient *client = i.getNode()->getValue(); + RemoteClient *client = i->second; PlayerSAO *playersao = getPlayerSAO(client->peer_id); if(playersao == NULL) continue; @@ -1137,6 +1163,13 @@ void Server::AsyncRunStep() SendPlayerHP(client->peer_id); } + /* + Send player breath if changed + */ + if(playersao->m_breath_not_sent){ + SendPlayerBreath(client->peer_id); + } + /* Send player inventories if necessary */ @@ -1161,7 +1194,7 @@ void Server::AsyncRunStep() ScopeProfiler sp(g_profiler, "Server: liquid transform"); - core::map modified_blocks; + std::map modified_blocks; m_env->getMap().transformLiquids(modified_blocks); #if 0 /* @@ -1186,11 +1219,11 @@ void Server::AsyncRunStep() JMutexAutoLock lock2(m_con_mutex); - for(core::map::Iterator - i = m_clients.getIterator(); - i.atEnd() == false; i++) + for(std::map::iterator + i = m_clients.begin(); + i != m_clients.end(); ++i) { - RemoteClient *client = i.getNode()->getValue(); + RemoteClient *client = i->second; if(modified_blocks.size() > 0) { @@ -1209,33 +1242,34 @@ void Server::AsyncRunStep() counter = 0.0; JMutexAutoLock lock2(m_con_mutex); - m_clients_number = 0; + m_clients_names.clear(); if(m_clients.size() != 0) infostream<<"Players:"<::Iterator - i = m_clients.getIterator(); - i.atEnd() == false; i++) + for(std::map::iterator + i = m_clients.begin(); + i != m_clients.end(); ++i) { //u16 peer_id = i.getNode()->getKey(); - RemoteClient *client = i.getNode()->getValue(); + RemoteClient *client = i->second; Player *player = m_env->getPlayer(client->peer_id); if(player==NULL) continue; infostream<<"* "<getName()<<"\t"; client->PrintInfo(infostream); - ++m_clients_number; + m_clients_names.push_back(player->getName()); } } } + m_lag += (m_lag > dtime ? -1 : 1) * dtime/100; #if USE_CURL // send masterserver announce { float &counter = m_masterserver_timer; - if((!counter || counter >= 300.0) && g_settings->getBool("server_announce") == true) + if(!isSingleplayer() && (!counter || counter >= 300.0) && g_settings->getBool("server_announce") == true) { - ServerList::sendAnnounce(!counter ? "start" : "update", m_clients_number); + ServerList::sendAnnounce(!counter ? "start" : "update", m_clients_names, m_uptime.get(), m_env->getGameTime(), m_lag, m_gamespec.id, m_mods); counter = 0.01; } counter += dtime; @@ -1259,11 +1293,11 @@ void Server::AsyncRunStep() s16 radius = g_settings->getS16("active_object_send_range_blocks"); radius *= MAP_BLOCKSIZE; - for(core::map::Iterator - i = m_clients.getIterator(); - i.atEnd() == false; i++) + for(std::map::iterator + i = m_clients.begin(); + i != m_clients.end(); ++i) { - RemoteClient *client = i.getNode()->getValue(); + RemoteClient *client = i->second; // If definitions and textures have not been sent, don't // send objects either @@ -1281,8 +1315,8 @@ void Server::AsyncRunStep() } v3s16 pos = floatToInt(player->getPosition(), BS); - core::map removed_objects; - core::map added_objects; + std::set removed_objects; + std::set added_objects; m_env->getRemovedActiveObjects(pos, radius, client->m_known_objects, removed_objects); m_env->getAddedActiveObjects(pos, radius, @@ -1302,20 +1336,20 @@ void Server::AsyncRunStep() // Handle removed objects writeU16((u8*)buf, removed_objects.size()); data_buffer.append(buf, 2); - for(core::map::Iterator - i = removed_objects.getIterator(); - i.atEnd()==false; i++) + for(std::set::iterator + i = removed_objects.begin(); + i != removed_objects.end(); ++i) { // Get object - u16 id = i.getNode()->getKey(); + u16 id = *i; ServerActiveObject* obj = m_env->getActiveObject(id); // Add to data buffer for sending - writeU16((u8*)buf, i.getNode()->getKey()); + writeU16((u8*)buf, id); data_buffer.append(buf, 2); // Remove from known objects - client->m_known_objects.remove(i.getNode()->getKey()); + client->m_known_objects.erase(id); if(obj && obj->m_known_by_count > 0) obj->m_known_by_count--; @@ -1324,12 +1358,12 @@ void Server::AsyncRunStep() // Handle added objects writeU16((u8*)buf, added_objects.size()); data_buffer.append(buf, 2); - for(core::map::Iterator - i = added_objects.getIterator(); - i.atEnd()==false; i++) + for(std::set::iterator + i = added_objects.begin(); + i != added_objects.end(); ++i) { // Get object - u16 id = i.getNode()->getKey(); + u16 id = *i; ServerActiveObject* obj = m_env->getActiveObject(id); // Get object type @@ -1353,7 +1387,7 @@ void Server::AsyncRunStep() data_buffer.append(serializeLongString("")); // Add to known objects - client->m_known_objects.insert(i.getNode()->getKey(), false); + client->m_known_objects.insert(id); if(obj) obj->m_known_by_count++; @@ -1412,7 +1446,7 @@ void Server::AsyncRunStep() // Key = object id // Value = data sent by object - core::map* > buffered_messages; + std::map* > buffered_messages; // Get active object messages from environment for(;;) @@ -1421,43 +1455,43 @@ void Server::AsyncRunStep() if(aom.id == 0) break; - core::list* message_list = NULL; - core::map* >::Node *n; + std::list* message_list = NULL; + std::map* >::iterator n; n = buffered_messages.find(aom.id); - if(n == NULL) + if(n == buffered_messages.end()) { - message_list = new core::list; - buffered_messages.insert(aom.id, message_list); + message_list = new std::list; + buffered_messages[aom.id] = message_list; } else { - message_list = n->getValue(); + message_list = n->second; } message_list->push_back(aom); } // Route data to every client - for(core::map::Iterator - i = m_clients.getIterator(); - i.atEnd()==false; i++) + for(std::map::iterator + i = m_clients.begin(); + i != m_clients.end(); ++i) { - RemoteClient *client = i.getNode()->getValue(); + RemoteClient *client = i->second; std::string reliable_data; std::string unreliable_data; // Go through all objects in message buffer - for(core::map* >::Iterator - j = buffered_messages.getIterator(); - j.atEnd()==false; j++) + for(std::map* >::iterator + j = buffered_messages.begin(); + j != buffered_messages.end(); ++j) { // If object is not known by client, skip it - u16 id = j.getNode()->getKey(); - if(client->m_known_objects.find(id) == NULL) + u16 id = j->first; + if(client->m_known_objects.find(id) == client->m_known_objects.end()) continue; // Get message list of object - core::list* list = j.getNode()->getValue(); + std::list* list = j->second; // Go through every message - for(core::list::Iterator - k = list->begin(); k != list->end(); k++) + for(std::list::iterator + k = list->begin(); k != list->end(); ++k) { // Compose the full new data with header ActiveObjectMessage aom = *k; @@ -1495,7 +1529,7 @@ void Server::AsyncRunStep() memcpy((char*)&reply[2], unreliable_data.c_str(), unreliable_data.size()); // Send as unreliable - m_con.Send(client->peer_id, 0, reply, false); + m_con.Send(client->peer_id, 1, reply, false); } /*if(reliable_data.size() > 0 || unreliable_data.size() > 0) @@ -1508,11 +1542,11 @@ void Server::AsyncRunStep() } // Clear buffered_messages - for(core::map* >::Iterator - i = buffered_messages.getIterator(); - i.atEnd()==false; i++) + for(std::map* >::iterator + i = buffered_messages.begin(); + i != buffered_messages.end(); ++i) { - delete i.getNode()->getValue(); + delete i->second; } } @@ -1546,18 +1580,18 @@ void Server::AsyncRunStep() // Players far away from the change are stored here. // Instead of sending the changes, MapBlocks are set not sent // for them. - core::list far_players; + std::list far_players; - if(event->type == MEET_ADDNODE) + if(event->type == MEET_ADDNODE || event->type == MEET_SWAPNODE) { //infostream<<"Server: MEET_ADDNODE"<p, event->n, event->already_known_by_peer, - &far_players, 5); + &far_players, 5, event->type == MEET_ADDNODE); else sendAddNode(event->p, event->n, event->already_known_by_peer, - &far_players, 30); + &far_players, 30, event->type == MEET_ADDNODE); } else if(event->type == MEET_REMOVENODE) { @@ -1580,12 +1614,11 @@ void Server::AsyncRunStep() { infostream<<"Server: MEET_OTHER"<::Iterator - i = event->modified_blocks.getIterator(); - i.atEnd()==false; i++) + for(std::set::iterator + i = event->modified_blocks.begin(); + i != event->modified_blocks.end(); ++i) { - v3s16 p = i.getNode()->getKey(); - setBlockNotSent(p); + setBlockNotSent(*i); } } else @@ -1601,19 +1634,18 @@ void Server::AsyncRunStep() if(far_players.size() > 0) { // Convert list format to that wanted by SetBlocksNotSent - core::map modified_blocks2; - for(core::map::Iterator - i = event->modified_blocks.getIterator(); - i.atEnd()==false; i++) + std::map modified_blocks2; + for(std::set::iterator + i = event->modified_blocks.begin(); + i != event->modified_blocks.end(); ++i) { - v3s16 p = i.getNode()->getKey(); - modified_blocks2.insert(p, - m_env->getMap().getBlockNoCreateNoEx(p)); + modified_blocks2[*i] = + m_env->getMap().getBlockNoCreateNoEx(*i); } // Set blocks not sent - for(core::list::Iterator + for(std::list::iterator i = far_players.begin(); - i != far_players.end(); i++) + i != far_players.end(); ++i) { u16 peer_id = *i; RemoteClient *client = getClient(peer_id); @@ -1652,8 +1684,7 @@ void Server::AsyncRunStep() { counter = 0.0; - for (int i = 0; i != m_emerge->emergethread.size(); i++) - m_emerge->emergethread[i]->trigger(); + m_emerge->startAllThreads(); // Update m_enable_rollback_recording here too m_enable_rollback_recording = @@ -1673,8 +1704,8 @@ void Server::AsyncRunStep() ScopeProfiler sp(g_profiler, "Server: saving stuff"); //Ban stuff - if(m_banmanager.isModified()) - m_banmanager.save(); + if(m_banmanager->isModified()) + m_banmanager->save(); // Save changed parts of map m_env->getMap().save(MOD_STATE_WRITE_NEEDED); @@ -1738,19 +1769,20 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) ScopeProfiler sp(g_profiler, "Server::ProcessData"); + std::string addr_s; try{ Address address = m_con.GetPeerAddress(peer_id); - std::string addr_s = address.serializeString(); + addr_s = address.serializeString(); // drop player if is ip is banned - if(m_banmanager.isIpBanned(addr_s)){ + if(m_banmanager->isIpBanned(addr_s)){ + std::string ban_name = m_banmanager->getBanName(addr_s); infostream<<"Server: A banned client tried to connect from " <serialization_version; try @@ -1777,22 +1807,38 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) if(command == TOSERVER_INIT) { // [0] u16 TOSERVER_INIT - // [2] u8 SER_FMT_VER_HIGHEST + // [2] u8 SER_FMT_VER_HIGHEST_READ // [3] u8[20] player_name // [23] u8[28] password <--- can be sent without this, from old versions if(datasize < 2+1+PLAYERNAME_SIZE) return; - verbosestream<<"Server: Got TOSERVER_INIT from " - <net_proto_version != 0){ + verbosestream<<"Server: Ignoring multiple TOSERVER_INITs from " + < 1){ + infostream<<"Server: Not allowing another client ("< SERVER_PROTOCOL_VERSION_MAX) { - actionstream<<"Server: A mismatched client tried to connect from "<on_prejoinplayer(playername, addr_s, reason)) + { + actionstream<<"Server: Player with the name \""<= g_settings->getU16("max_users") && + !checkPriv(playername, "server") && + !checkPriv(playername, "ban") && + !checkPriv(playername, "privs") && + !checkPriv(playername, "password") && + playername != g_settings->get("name")) + { + actionstream<<"Server: "<getAuth(playername, &checkpwd, NULL); // If no authentication info exists for user, create it if(!has_auth){ if(!isSingleplayer() && g_settings->getBool("disallow_empty_password") && std::string(given_password) == ""){ - SendAccessDenied(m_con, peer_id, L"Empty passwords are " + actionstream<<"Server: "<createAuth(playername, initial_password); } - has_auth = scriptapi_get_auth(m_lua, playername, &checkpwd, NULL); + has_auth = m_script->getAuth(playername, &checkpwd, NULL); if(!has_auth){ - SendAccessDenied(m_con, peer_id, L"Not allowed to login"); + actionstream<<"Server: "< 1){ - infostream<<"Server: Not allowing another client to connect in" - <<" simple singleplayer mode"<= g_settings->getU16("max_users") && - !checkPriv(playername, "server") && - !checkPriv(playername, "ban") && - !checkPriv(playername, "privs") && - !checkPriv(playername, "password") && - playername != g_settings->get("name")) - { - actionstream<<"Server: "<(m_env->getPlayer(playername)); + if(player && player->peer_id != 0){ + errorstream<<"Server: "<net_proto_version); // Send node definitions SendNodeDef(m_con, peer_id, m_nodedef, client->net_proto_version); @@ -2096,6 +2164,9 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) if(g_settings->getBool("enable_damage")) SendPlayerHP(peer_id); + // Send Breath + SendPlayerBreath(peer_id); + // Send detached inventories sendDetachedInventories(peer_id); @@ -2105,9 +2176,9 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) // Send time of day { - SharedBuffer data = makePacket_TOCLIENT_TIME_OF_DAY( - m_env->getTimeOfDay(), g_settings->getFloat("time_speed")); - m_con.Send(peer_id, 0, data, true); + u16 time = m_env->getTimeOfDay(); + float time_speed = g_settings->getFloat("time_speed"); + SendTimeOfDay(peer_id, time, time_speed); } // Note things in chat if not in simple singleplayer mode @@ -2143,12 +2214,12 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) */ { std::ostringstream os(std::ios_base::binary); - for(core::map::Iterator - i = m_clients.getIterator(); - i.atEnd() == false; i++) + for(std::map::iterator + i = m_clients.begin(); + i != m_clients.end(); ++i) { - RemoteClient *client = i.getNode()->getValue(); - assert(client->peer_id == i.getNode()->getKey()); + RemoteClient *client = i->second; + assert(client->peer_id == i->first); if(client->serialization_version == SER_FMT_VER_INVALID) continue; // Get player @@ -2159,7 +2230,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) os<getName()<<" "; } - actionstream<getName()<<" joins game. List of players: " + actionstream<getName()<<" ["<control.LMB = (bool)(keyPressed&128); player->control.RMB = (bool)(keyPressed&256); + bool cheated = playersao->checkMovementCheat(); + if(cheated){ + // Call callbacks + m_script->on_cheat(playersao, "moved_too_fast"); + } + /*infostream<<"Server::ProcessData(): Moved player "<from_inv.dump())<<":"<from_list + <<" because src is "<from_list<getName(), "interact")) { @@ -2474,7 +2563,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) std::wstring name = narrow_to_wide(player->getName()); // Run script hook - bool ate = scriptapi_on_chat_message(m_lua, player->getName(), + bool ate = m_script->on_chat_message(player->getName(), wide_to_narrow(message)); // If script ate the message, don't proceed if(ate) @@ -2520,13 +2609,13 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) /* Send the message to clients */ - for(core::map::Iterator - i = m_clients.getIterator(); - i.atEnd() == false; i++) + for(std::map::iterator + i = m_clients.begin(); + i != m_clients.end(); ++i) { // Get client and check that it is valid - RemoteClient *client = i.getNode()->getValue(); - assert(client->peer_id == i.getNode()->getKey()); + RemoteClient *client = i->second; + assert(client->peer_id == i->first); if(client->serialization_version == SER_FMT_VER_INVALID) continue; @@ -2562,6 +2651,13 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) SendPlayerHP(peer_id); } } + else if(command == TOSERVER_BREATH) + { + std::string datastring((char*)&data[2], datasize-2); + std::istringstream is(datastring, std::ios_base::binary); + u16 breath = readU16(is); + playersao->setBreath(breath); + } else if(command == TOSERVER_PASSWORD) { /* @@ -2606,7 +2702,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) std::string playername = player->getName(); std::string checkpwd; - scriptapi_get_auth(m_lua, playername, &checkpwd, NULL); + m_script->getAuth(playername, &checkpwd, NULL); if(oldpwd != checkpwd) { @@ -2616,7 +2712,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) return; } - bool success = scriptapi_set_password(m_lua, playername, newpwd); + bool success = m_script->setPassword(playername, newpwd); if(success){ actionstream<getName()<<" changes password"< tosend; + std::list tosend; u16 numfiles = readU16(is); infostream<<"Sending "<SetBlockNotSent(blockpos); + // Call callbacks + m_script->on_cheat(playersao, "interacted_too_far"); // Do nothing else return; } @@ -2820,7 +2918,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) m_emerge->enqueueBlockEmerge(peer_id, getNodeBlockPos(p_above), false); } if(n.getContent() != CONTENT_IGNORE) - scriptapi_node_on_punch(m_lua, p_under, n, playersao); + m_script->node_on_punch(p_under, n, playersao); // Cheat prevention playersao->noCheatDigStart(p_under); } @@ -2890,6 +2988,8 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) <on_cheat(playersao, "finished_unknown_dig"); } // Get player's wielded item ItemStack playeritem; @@ -2915,22 +3015,40 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) <<", which is not diggable with tool. not digging." <on_cheat(playersao, "dug_unbreakable"); + } + // Check digging time + // If already invalidated, we don't have to + if(!is_valid_dig){ + // Well not our problem then } - // If time is considerably too short, ignore dig - // Check time only for medium and slow timed digs - if(params.diggable && params.time > 0.3 && nocheat_t < 0.5 * params.time){ + // Clean and long dig + else if(params.time > 2.0 && nocheat_t * 1.2 > params.time){ + // All is good, but grab time from pool; don't care if + // it's actually available + playersao->getDigPool().grab(params.time); + } + // Short or laggy dig + // Try getting the time from pool + else if(playersao->getDigPool().grab(params.time)){ + // All is good + } + // Dig not possible + else{ infostream<<"Server: NoCheat: "<getName() - <<" completed digging " - <on_cheat(playersao, "dug_too_fast"); } } /* Actually dig node */ if(is_valid_dig && n.getContent() != CONTENT_IGNORE) - scriptapi_node_on_dig(m_lua, p_under, n, playersao); + m_script->node_on_dig(p_under, n, playersao); // Send unusual result (that is, node not being removed) if(m_env->getMap().getNodeNoEx(p_under).getContent() != CONTENT_AIR) @@ -2970,7 +3088,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) // Do stuff pointed_object->rightClick(playersao); } - else if(scriptapi_item_on_place(m_lua, + else if(m_script->item_OnPlace( item, playersao, pointed)) { // Placement was handled in lua @@ -2979,12 +3097,16 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) playersao->setWieldedItem(item); } - // If item has node placement prediction, always send the above - // node to make sure the client knows what exactly happened + // If item has node placement prediction, always send the + // blocks to make sure the client knows what exactly happened if(item.getDefinition(m_itemdef).node_placement_prediction != ""){ RemoteClient *client = getClient(peer_id); v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS)); client->SetBlockNotSent(blockpos); + v3s16 blockpos2 = getNodeBlockPos(floatToInt(pointed_pos_under, BS)); + if(blockpos2 != blockpos){ + client->SetBlockNotSent(blockpos2); + } } } // action == 3 @@ -2998,7 +3120,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) actionstream<getName()<<" uses "<item_OnUse( item, playersao, pointed)) { // Apply returned ItemStack @@ -3057,8 +3179,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) // Check the target node for rollback data; leave others unnoticed RollbackNode rn_old(&m_env->getMap(), p, this); - scriptapi_node_on_receive_fields(m_lua, p, formname, fields, - playersao); + m_script->node_on_receive_fields(p, formname, fields,playersao); // Report rollback data RollbackNode rn_new(&m_env->getMap(), p, this); @@ -3082,7 +3203,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) fields[fieldname] = fieldvalue; } - scriptapi_on_player_receive_fields(m_lua, playersao, formname, fields); + m_script->on_playerReceiveFields(playersao, formname, fields); } else { @@ -3099,6 +3220,12 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) } } +void Server::setTimeOfDay(u32 time) +{ + m_env->setTimeOfDay(time); + m_time_of_day_send_timer = 0; +} + void Server::onMapEditEvent(MapEditEvent *event) { //infostream<<"Server::onMapEditEvent()"< Server::getPlayerInfo() -{ - DSTACK(__FUNCTION_NAME); - JMutexAutoLock envlock(m_env_mutex); - JMutexAutoLock conlock(m_con_mutex); - - core::list list; - - core::list players = m_env->getPlayers(); - - core::list::Iterator i; - for(i = players.begin(); - i != players.end(); i++) - { - PlayerInfo info; - - Player *player = *i; - - try{ - // Copy info from connection to info struct - info.id = player->peer_id; - info.address = m_con.GetPeerAddress(player->peer_id); - info.avg_rtt = m_con.GetPeerAvgRTT(player->peer_id); - } - catch(con::PeerNotFoundException &e) - { - // Set dummy peer info - info.id = 0; - info.address = Address(0,0,0,0,0); - info.avg_rtt = 0.0; - } - - snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName()); - info.position = player->getPosition(); - - list.push_back(info); - } - - return list; -} - - void Server::peerAdded(con::Peer *peer) { DSTACK(__FUNCTION_NAME); @@ -3302,6 +3387,21 @@ void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp) con.Send(peer_id, 0, data, true); } +void Server::SendBreath(con::Connection &con, u16 peer_id, u16 breath) +{ + DSTACK(__FUNCTION_NAME); + std::ostringstream os(std::ios_base::binary); + + writeU16(os, TOCLIENT_BREATH); + writeU16(os, breath); + + // Make data buffer + std::string s = os.str(); + SharedBuffer data((u8*)s.c_str(), s.size()); + // Send as reliable + con.Send(peer_id, 0, data, true); +} + void Server::SendAccessDenied(con::Connection &con, u16 peer_id, const std::wstring &reason) { @@ -3336,7 +3436,7 @@ void Server::SendDeathscreen(con::Connection &con, u16 peer_id, } void Server::SendItemDef(con::Connection &con, u16 peer_id, - IItemDefManager *itemdef) + IItemDefManager *itemdef, u16 protocol_version) { DSTACK(__FUNCTION_NAME); std::ostringstream os(std::ios_base::binary); @@ -3348,7 +3448,7 @@ void Server::SendItemDef(con::Connection &con, u16 peer_id, */ writeU16(os, TOCLIENT_ITEMDEF); std::ostringstream tmp_os(std::ios::binary); - itemdef->serialize(tmp_os); + itemdef->serialize(tmp_os, protocol_version); std::ostringstream tmp_os2(std::ios::binary); compressZlib(tmp_os.str(), tmp_os2); os< data((u8*)s.c_str(), s.size()); + // Send as reliable + m_con.Send(peer_id, 0, data, true); +} + +// Spawns a particle on all peers +void Server::SendSpawnParticleAll(v3f pos, v3f velocity, v3f acceleration, + float expirationtime, float size, bool collisiondetection, + std::string texture) { - for(core::map::Iterator - i = m_clients.getIterator(); - i.atEnd() == false; i++) + for(std::map::iterator + i = m_clients.begin(); + i != m_clients.end(); i++) { // Get client and check that it is valid - RemoteClient *client = i.getNode()->getValue(); - assert(client->peer_id == i.getNode()->getKey()); + RemoteClient *client = i->second; + assert(client->peer_id == i->first); if(client->serialization_version == SER_FMT_VER_INVALID) continue; - SendChatMessage(client->peer_id, message); + SendSpawnParticle(client->peer_id, pos, velocity, acceleration, + expirationtime, size, collisiondetection, texture); } } -void Server::SendPlayerHP(u16 peer_id) -{ - DSTACK(__FUNCTION_NAME); - PlayerSAO *playersao = getPlayerSAO(peer_id); - assert(playersao); - playersao->m_hp_not_sent = false; - SendHP(m_con, peer_id, playersao->getHP()); -} - -void Server::SendMovePlayer(u16 peer_id) +// Adds a ParticleSpawner on peer with peer_id +void Server::SendAddParticleSpawner(u16 peer_id, u16 amount, float spawntime, v3f minpos, v3f maxpos, + v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime, + float minsize, float maxsize, bool collisiondetection, std::string texture, u32 id) { DSTACK(__FUNCTION_NAME); - Player *player = m_env->getPlayer(peer_id); - assert(player); std::ostringstream os(std::ios_base::binary); - writeU16(os, TOCLIENT_MOVE_PLAYER); - writeV3F1000(os, player->getPosition()); - writeF1000(os, player->getPitch()); - writeF1000(os, player->getYaw()); + writeU16(os, TOCLIENT_ADD_PARTICLESPAWNER); + + writeU16(os, amount); + writeF1000(os, spawntime); + writeV3F1000(os, minpos); + writeV3F1000(os, maxpos); + writeV3F1000(os, minvel); + writeV3F1000(os, maxvel); + writeV3F1000(os, minacc); + writeV3F1000(os, maxacc); + writeF1000(os, minexptime); + writeF1000(os, maxexptime); + writeF1000(os, minsize); + writeF1000(os, maxsize); + writeU8(os, collisiondetection); + os<getPosition(); - f32 pitch = player->getPitch(); + // Make data buffer + std::string s = os.str(); + SharedBuffer data((u8*)s.c_str(), s.size()); + // Send as reliable + m_con.Send(peer_id, 0, data, true); +} + +// Adds a ParticleSpawner on all peers +void Server::SendAddParticleSpawnerAll(u16 amount, float spawntime, v3f minpos, v3f maxpos, + v3f minvel, v3f maxvel, v3f minacc, v3f maxacc, float minexptime, float maxexptime, + float minsize, float maxsize, bool collisiondetection, std::string texture, u32 id) +{ + for(std::map::iterator + i = m_clients.begin(); + i != m_clients.end(); i++) + { + // Get client and check that it is valid + RemoteClient *client = i->second; + assert(client->peer_id == i->first); + if(client->serialization_version == SER_FMT_VER_INVALID) + continue; + + SendAddParticleSpawner(client->peer_id, amount, spawntime, + minpos, maxpos, minvel, maxvel, minacc, maxacc, + minexptime, maxexptime, minsize, maxsize, collisiondetection, texture, id); + } +} + +void Server::SendDeleteParticleSpawner(u16 peer_id, u32 id) +{ + DSTACK(__FUNCTION_NAME); + + std::ostringstream os(std::ios_base::binary); + writeU16(os, TOCLIENT_DELETE_PARTICLESPAWNER); + + writeU16(os, id); + + // Make data buffer + std::string s = os.str(); + SharedBuffer data((u8*)s.c_str(), s.size()); + // Send as reliable + m_con.Send(peer_id, 0, data, true); +} + +void Server::SendDeleteParticleSpawnerAll(u32 id) +{ + for(std::map::iterator + i = m_clients.begin(); + i != m_clients.end(); i++) + { + // Get client and check that it is valid + RemoteClient *client = i->second; + assert(client->peer_id == i->first); + if(client->serialization_version == SER_FMT_VER_INVALID) + continue; + + SendDeleteParticleSpawner(client->peer_id, id); + } +} + +void Server::SendHUDAdd(u16 peer_id, u32 id, HudElement *form) +{ + std::ostringstream os(std::ios_base::binary); + + // Write command + writeU16(os, TOCLIENT_HUDADD); + writeU32(os, id); + writeU8(os, (u8)form->type); + writeV2F1000(os, form->pos); + os << serializeString(form->name); + writeV2F1000(os, form->scale); + os << serializeString(form->text); + writeU32(os, form->number); + writeU32(os, form->item); + writeU32(os, form->dir); + writeV2F1000(os, form->align); + writeV2F1000(os, form->offset); + + // Make data buffer + std::string s = os.str(); + SharedBuffer data((u8*)s.c_str(), s.size()); + // Send as reliable + m_con.Send(peer_id, 1, data, true); +} + +void Server::SendHUDRemove(u16 peer_id, u32 id) +{ + std::ostringstream os(std::ios_base::binary); + + // Write command + writeU16(os, TOCLIENT_HUDRM); + writeU32(os, id); + + // Make data buffer + std::string s = os.str(); + SharedBuffer data((u8*)s.c_str(), s.size()); + // Send as reliable + + m_con.Send(peer_id, 1, data, true); +} + +void Server::SendHUDChange(u16 peer_id, u32 id, HudElementStat stat, void *value) +{ + std::ostringstream os(std::ios_base::binary); + + // Write command + writeU16(os, TOCLIENT_HUDCHANGE); + writeU32(os, id); + writeU8(os, (u8)stat); + switch (stat) { + case HUD_STAT_POS: + case HUD_STAT_SCALE: + case HUD_STAT_ALIGN: + case HUD_STAT_OFFSET: + writeV2F1000(os, *(v2f *)value); + break; + case HUD_STAT_NAME: + case HUD_STAT_TEXT: + os << serializeString(*(std::string *)value); + break; + case HUD_STAT_NUMBER: + case HUD_STAT_ITEM: + case HUD_STAT_DIR: + default: + writeU32(os, *(u32 *)value); + break; + } + + // Make data buffer + std::string s = os.str(); + SharedBuffer data((u8 *)s.c_str(), s.size()); + // Send as reliable + m_con.Send(peer_id, 0, data, true); +} + +void Server::SendHUDSetFlags(u16 peer_id, u32 flags, u32 mask) +{ + std::ostringstream os(std::ios_base::binary); + + // Write command + writeU16(os, TOCLIENT_HUD_SET_FLAGS); + writeU32(os, flags); + writeU32(os, mask); + + // Make data buffer + std::string s = os.str(); + SharedBuffer data((u8 *)s.c_str(), s.size()); + // Send as reliable + m_con.Send(peer_id, 0, data, true); +} + +void Server::SendHUDSetParam(u16 peer_id, u16 param, const std::string &value) +{ + std::ostringstream os(std::ios_base::binary); + + // Write command + writeU16(os, TOCLIENT_HUD_SET_PARAM); + writeU16(os, param); + os< data((u8 *)s.c_str(), s.size()); + // Send as reliable + m_con.Send(peer_id, 0, data, true); +} + +void Server::BroadcastChatMessage(const std::wstring &message) +{ + for(std::map::iterator + i = m_clients.begin(); + i != m_clients.end(); ++i) + { + // Get client and check that it is valid + RemoteClient *client = i->second; + assert(client->peer_id == i->first); + if(client->serialization_version == SER_FMT_VER_INVALID) + continue; + + SendChatMessage(client->peer_id, message); + } +} + +void Server::SendTimeOfDay(u16 peer_id, u16 time, f32 time_speed) +{ + DSTACK(__FUNCTION_NAME); + + // Make packet + SharedBuffer data(2+2+4); + writeU16(&data[0], TOCLIENT_TIME_OF_DAY); + writeU16(&data[2], time); + writeF1000(&data[4], time_speed); + + // Send as reliable + m_con.Send(peer_id, 0, data, true); +} + +void Server::SendPlayerHP(u16 peer_id) +{ + DSTACK(__FUNCTION_NAME); + PlayerSAO *playersao = getPlayerSAO(peer_id); + assert(playersao); + playersao->m_hp_not_sent = false; + SendHP(m_con, peer_id, playersao->getHP()); + + // Send to other clients + std::string str = gob_cmd_punched(playersao->readDamage(), playersao->getHP()); + ActiveObjectMessage aom(playersao->getId(), true, str); + playersao->m_messages_out.push_back(aom); +} + +void Server::SendPlayerBreath(u16 peer_id) +{ + DSTACK(__FUNCTION_NAME); + PlayerSAO *playersao = getPlayerSAO(peer_id); + assert(playersao); + playersao->m_breath_not_sent = false; + SendBreath(m_con, peer_id, playersao->getBreath()); +} + +void Server::SendMovePlayer(u16 peer_id) +{ + DSTACK(__FUNCTION_NAME); + Player *player = m_env->getPlayer(peer_id); + assert(player); + + std::ostringstream os(std::ios_base::binary); + writeU16(os, TOCLIENT_MOVE_PLAYER); + writeV3F1000(os, player->getPosition()); + writeF1000(os, player->getPitch()); + writeF1000(os, player->getYaw()); + + { + v3f pos = player->getPosition(); + f32 pitch = player->getPitch(); f32 yaw = player->getYaw(); verbosestream<<"Server: Sending TOCLIENT_MOVE_PLAYER" <<" pos=("< privs; - scriptapi_get_auth(m_lua, player->getName(), NULL, &privs); + m_script->getAuth(player->getName(), NULL, &privs); std::ostringstream os(std::ios_base::binary); writeU16(os, TOCLIENT_PRIVILEGES); @@ -3595,10 +3962,10 @@ s32 Server::playSound(const SimpleSoundSpec &spec, } else { - for(core::map::Iterator - i = m_clients.getIterator(); i.atEnd() == false; i++) + for(std::map::iterator + i = m_clients.begin(); i != m_clients.end(); ++i) { - RemoteClient *client = i.getNode()->getValue(); + RemoteClient *client = i->second; Player *player = m_env->getPlayer(client->peer_id); if(!player) continue; @@ -3668,7 +4035,7 @@ void Server::stopSound(s32 handle) } void Server::sendRemoveNode(v3s16 p, u16 ignore_id, - core::list *far_players, float far_d_nodes) + std::list *far_players, float far_d_nodes) { float maxd = far_d_nodes*BS; v3f p_f = intToFloat(p, BS); @@ -3681,13 +4048,13 @@ void Server::sendRemoveNode(v3s16 p, u16 ignore_id, writeS16(&reply[4], p.Y); writeS16(&reply[6], p.Z); - for(core::map::Iterator - i = m_clients.getIterator(); - i.atEnd() == false; i++) + for(std::map::iterator + i = m_clients.begin(); + i != m_clients.end(); ++i) { // Get client and check that it is valid - RemoteClient *client = i.getNode()->getValue(); - assert(client->peer_id == i.getNode()->getKey()); + RemoteClient *client = i->second; + assert(client->peer_id == i->first); if(client->serialization_version == SER_FMT_VER_INVALID) continue; @@ -3717,18 +4084,19 @@ void Server::sendRemoveNode(v3s16 p, u16 ignore_id, } void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id, - core::list *far_players, float far_d_nodes) + std::list *far_players, float far_d_nodes, + bool remove_metadata) { float maxd = far_d_nodes*BS; v3f p_f = intToFloat(p, BS); - for(core::map::Iterator - i = m_clients.getIterator(); - i.atEnd() == false; i++) + for(std::map::iterator + i = m_clients.begin(); + i != m_clients.end(); ++i) { // Get client and check that it is valid - RemoteClient *client = i.getNode()->getValue(); - assert(client->peer_id == i.getNode()->getKey()); + RemoteClient *client = i->second; + assert(client->peer_id == i->first); if(client->serialization_version == SER_FMT_VER_INVALID) continue; @@ -3753,13 +4121,23 @@ void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id, } // Create packet - u32 replysize = 8 + MapNode::serializedLength(client->serialization_version); + u32 replysize = 9 + MapNode::serializedLength(client->serialization_version); SharedBuffer reply(replysize); writeU16(&reply[0], TOCLIENT_ADDNODE); writeS16(&reply[2], p.X); writeS16(&reply[4], p.Y); writeS16(&reply[6], p.Z); n.serialize(&reply[8], client->serialization_version); + u32 index = 8 + MapNode::serializedLength(client->serialization_version); + writeU8(&reply[index], remove_metadata ? 0 : 1); + + if (!remove_metadata) { + if (client->net_proto_version <= 21) { + // Old clients always clear metadata; fix it + // by sending the full block again. + client->SetBlockNotSent(p); + } + } // Send as reliable m_con.Send(client->peer_id, 0, reply, true); @@ -3768,16 +4146,16 @@ void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id, void Server::setBlockNotSent(v3s16 p) { - for(core::map::Iterator - i = m_clients.getIterator(); - i.atEnd()==false; i++) + for(std::map::iterator + i = m_clients.begin(); + i != m_clients.end(); ++i) { - RemoteClient *client = i.getNode()->getValue(); + RemoteClient *client = i->second; client->SetBlockNotSent(p); } } -void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver) +void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver, u16 net_proto_version) { DSTACK(__FUNCTION_NAME); @@ -3810,6 +4188,7 @@ void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver) std::ostringstream os(std::ios_base::binary); block->serialize(os, ver, false); + block->serializeNetworkSpecific(os, net_proto_version); std::string s = os.str(); SharedBuffer blockdata((u8*)s.c_str(), s.size()); @@ -3827,7 +4206,7 @@ void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver) /* Send packet */ - m_con.Send(peer_id, 1, reply, true); + m_con.Send(peer_id, 2, reply, true); } void Server::SendBlocks(float dtime) @@ -3839,19 +4218,19 @@ void Server::SendBlocks(float dtime) ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients"); - core::array queue; + std::vector queue; s32 total_sending = 0; { ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending"); - for(core::map::Iterator - i = m_clients.getIterator(); - i.atEnd() == false; i++) + for(std::map::iterator + i = m_clients.begin(); + i != m_clients.end(); ++i) { - RemoteClient *client = i.getNode()->getValue(); - assert(client->peer_id == i.getNode()->getKey()); + RemoteClient *client = i->second; + assert(client->peer_id == i->first); // If definitions and textures have not been sent, don't // send MapBlocks either @@ -3870,7 +4249,7 @@ void Server::SendBlocks(float dtime) // Sort. // Lowest priority number comes first. // Lowest is most important. - queue.sort(); + std::sort(queue.begin(), queue.end()); for(u32 i=0; idenied) + continue; - SendBlockNoLock(q.peer_id, block, client->serialization_version); + SendBlockNoLock(q.peer_id, block, client->serialization_version, client->net_proto_version); client->SentBlock(q.pos); @@ -3917,8 +4300,7 @@ void Server::fillMediaCache() paths.push_back(mod.path + DIR_DELIM + "media"); paths.push_back(mod.path + DIR_DELIM + "models"); } - std::string path_all = "textures"; - paths.push_back(path_all + DIR_DELIM + "all"); + paths.push_back(porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server"); // Collect media file information from paths into cache for(std::list::iterator i = paths.begin(); @@ -4017,7 +4399,7 @@ void Server::sendMediaAnnouncement(u16 peer_id) verbosestream<<"Server: Announcing files to id("< file_announcements; + std::list file_announcements; for(std::map::iterator i = m_media.begin(); i != m_media.end(); i++){ @@ -4043,9 +4425,9 @@ void Server::sendMediaAnnouncement(u16 peer_id) writeU16(os, TOCLIENT_ANNOUNCE_MEDIA); writeU16(os, file_announcements.size()); - for(core::list::Iterator + for(std::list::iterator j = file_announcements.begin(); - j != file_announcements.end(); j++){ + j != file_announcements.end(); ++j){ os<name); os<sha1_digest); } @@ -4074,7 +4456,7 @@ struct SendableMedia }; void Server::sendRequestedMedia(u16 peer_id, - const core::list &tosend) + const std::list &tosend) { DSTACK(__FUNCTION_NAME); @@ -4086,22 +4468,24 @@ void Server::sendRequestedMedia(u16 peer_id, // Put 5kB in one bunch (this is not accurate) u32 bytes_per_bunch = 5000; - core::array< core::list > file_bunches; - file_bunches.push_back(core::list()); + std::vector< std::list > file_bunches; + file_bunches.push_back(std::list()); u32 file_size_bunch_total = 0; - for(core::list::ConstIterator i = tosend.begin(); - i != tosend.end(); i++) + for(std::list::const_iterator i = tosend.begin(); + i != tosend.end(); ++i) { - if(m_media.find(i->name) == m_media.end()){ + const std::string &name = *i; + + if(m_media.find(name) == m_media.end()){ errorstream<<"Server::sendRequestedMedia(): Client asked for " - <<"unknown file \""<<(i->name)<<"\""<= bytes_per_bunch){ - file_bunches.push_back(core::list()); + file_bunches.push_back(std::list()); file_size_bunch_total = 0; } @@ -4169,9 +4553,9 @@ void Server::sendRequestedMedia(u16 peer_id, writeU16(os, i); writeU32(os, file_bunches[i].size()); - for(core::list::Iterator + for(std::list::iterator j = file_bunches[i].begin(); - j != file_bunches[i].end(); j++){ + j != file_bunches[i].end(); ++j){ os<name); os<data); } @@ -4184,7 +4568,7 @@ void Server::sendRequestedMedia(u16 peer_id, <<" size=" < data((u8*)s.c_str(), s.size()); // Send as reliable - m_con.Send(peer_id, 0, data, true); + m_con.Send(peer_id, 2, data, true); } } @@ -4212,10 +4596,10 @@ void Server::sendDetachedInventoryToAll(const std::string &name) { DSTACK(__FUNCTION_NAME); - for(core::map::Iterator - i = m_clients.getIterator(); - i.atEnd() == false; i++){ - RemoteClient *client = i.getNode()->getValue(); + for(std::map::iterator + i = m_clients.begin(); + i != m_clients.end(); ++i){ + RemoteClient *client = i->second; sendDetachedInventory(name, client->peer_id); } } @@ -4251,7 +4635,7 @@ void Server::DiePlayer(u16 peer_id) playersao->setHP(0); // Trigger scripted stuff - scriptapi_on_dieplayer(m_lua, playersao); + m_script->on_dieplayer(playersao); SendPlayerHP(peer_id); SendDeathscreen(m_con, peer_id, false, v3f(0,0,0)); @@ -4270,13 +4654,145 @@ void Server::RespawnPlayer(u16 peer_id) playersao->setHP(PLAYER_MAX_HP); - bool repositioned = scriptapi_on_respawnplayer(m_lua, playersao); + bool repositioned = m_script->on_respawnplayer(playersao); if(!repositioned){ v3f pos = findSpawnPos(m_env->getServerMap()); playersao->setPos(pos); } } +void Server::DenyAccess(u16 peer_id, const std::wstring &reason) +{ + DSTACK(__FUNCTION_NAME); + + SendAccessDenied(m_con, peer_id, reason); + + RemoteClient *client = getClientNoEx(peer_id); + if(client) + client->denied = true; + + // If there are way too many clients, get rid of denied new ones immediately + if((int)m_clients.size() > 2 * g_settings->getU16("max_users")){ + verbosestream<<"Server: DenyAccess: Too many clients; getting rid of " + <<"peer_id="<::iterator n; + n = m_clients.find(peer_id); + // The client may not exist; clients are immediately removed if their + // access is denied, and this event occurs later then. + if(n == m_clients.end()) + return; + + /* + Mark objects to be not known by the client + */ + RemoteClient *client = n->second; + // Handle objects + for(std::set::iterator + i = client->m_known_objects.begin(); + i != client->m_known_objects.end(); ++i) + { + // Get object + u16 id = *i; + ServerActiveObject* obj = m_env->getActiveObject(id); + + if(obj && obj->m_known_by_count > 0) + obj->m_known_by_count--; + } + + /* + Clear references to playing sounds + */ + for(std::map::iterator + i = m_playing_sounds.begin(); + i != m_playing_sounds.end();) + { + ServerPlayingSound &psound = i->second; + psound.clients.erase(peer_id); + if(psound.clients.size() == 0) + m_playing_sounds.erase(i++); + else + i++; + } + + Player *player = m_env->getPlayer(peer_id); + + // Collect information about leaving in chat + std::wstring message; + { + if(player != NULL && reason != CDR_DENY) + { + std::wstring name = narrow_to_wide(player->getName()); + message += L"*** "; + message += name; + message += L" left the game."; + if(reason == CDR_TIMEOUT) + message += L" (timed out)"; + } + } + + /* Run scripts and remove from environment */ + { + if(player != NULL) + { + PlayerSAO *playersao = player->getPlayerSAO(); + assert(playersao); + + m_script->on_leaveplayer(playersao); + + playersao->disconnected(); + } + } + + /* + Print out action + */ + { + if(player != NULL && reason != CDR_DENY) + { + std::ostringstream os(std::ios_base::binary); + for(std::map::iterator + i = m_clients.begin(); + i != m_clients.end(); ++i) + { + RemoteClient *client = i->second; + assert(client->peer_id == i->first); + if(client->serialization_version == SER_FMT_VER_INVALID) + continue; + // Get player + Player *player = m_env->getPlayer(client->peer_id); + if(!player) + continue; + // Get name of player + os<getName()<<" "; + } + + actionstream<getName()<<" " + <<(reason==CDR_TIMEOUT?"times out.":"leaves game.") + <<" List of players: "<getName()); getCraftingResult(&player->inventory, preview, false, this); + m_env->getScriptIface()->item_CraftPredict(preview, player->getPlayerSAO(), (&player->inventory)->getList("craft"), loc); // Put the new preview in InventoryList *plist = player->inventory.getList("craftpreview"); @@ -4297,13 +4816,36 @@ void Server::UpdateCrafting(u16 peer_id) RemoteClient* Server::getClient(u16 peer_id) { - DSTACK(__FUNCTION_NAME); - //JMutexAutoLock lock(m_con_mutex); - core::map::Node *n; + RemoteClient *client = getClientNoEx(peer_id); + if(!client) + throw ClientNotFoundException("Client not found"); + return client; +} +RemoteClient* Server::getClientNoEx(u16 peer_id) +{ + std::map::iterator n; n = m_clients.find(peer_id); - // A client should exist for all peers - assert(n != NULL); - return n->getValue(); + // The client may not exist; clients are immediately removed if their + // access is denied, and this event occurs later then. + if(n == m_clients.end()) + return NULL; + return n->second; +} + +std::string Server::getPlayerName(u16 peer_id) +{ + Player *player = m_env->getPlayer(peer_id); + if(player == NULL) + return "[id="+itos(peer_id)+"]"; + return player->getName(); +} + +PlayerSAO* Server::getPlayerSAO(u16 peer_id) +{ + Player *player = m_env->getPlayer(peer_id); + if(player == NULL) + return NULL; + return player->getPlayerSAO(); } std::wstring Server::getStatusString() @@ -4311,19 +4853,21 @@ std::wstring Server::getStatusString() std::wostringstream os(std::ios_base::binary); os<getValue(); - assert(client->peer_id == i.getNode()->getKey()); + RemoteClient *client = i->second; + assert(client->peer_id == i->first); if(client->serialization_version == SER_FMT_VER_INVALID) continue; // Get player @@ -4350,7 +4894,7 @@ std::wstring Server::getStatusString() std::set Server::getPlayerEffectivePrivs(const std::string &name) { std::set privs; - scriptapi_get_auth(m_lua, name, NULL, &privs); + m_script->getAuth(name, NULL, &privs); return privs; } @@ -4363,10 +4907,10 @@ bool Server::checkPriv(const std::string &name, const std::string &priv) void Server::reportPrivsModified(const std::string &name) { if(name == ""){ - for(core::map::Iterator - i = m_clients.getIterator(); - i.atEnd() == false; i++){ - RemoteClient *client = i.getNode()->getValue(); + for(std::map::iterator + i = m_clients.begin(); + i != m_clients.end(); ++i){ + RemoteClient *client = i->second; Player *player = m_env->getPlayer(client->peer_id); reportPrivsModified(player->getName()); } @@ -4392,19 +4936,30 @@ void Server::reportInventoryFormspecModified(const std::string &name) SendPlayerInventoryFormspec(player->peer_id); } -// Saves g_settings to configpath given at initialization -void Server::saveConfig() +void Server::setIpBanned(const std::string &ip, const std::string &name) +{ + m_banmanager->add(ip, name); +} + +void Server::unsetIpBanned(const std::string &ip_or_name) +{ + m_banmanager->remove(ip_or_name); +} + +std::string Server::getBanDescription(const std::string &ip_or_name) { - if(m_path_config != "") - g_settings->updateConfigFile(m_path_config.c_str()); + return m_banmanager->getBanDescription(ip_or_name); } -void Server::notifyPlayer(const char *name, const std::wstring msg) +void Server::notifyPlayer(const char *name, const std::wstring msg, const bool prepend = true) { Player *player = m_env->getPlayer(name); if(!player) return; - SendChatMessage(player->peer_id, std::wstring(L"Server: -!- ")+msg); + if (prepend) + SendChatMessage(player->peer_id, std::wstring(L"Server -!- ")+msg); + else + SendChatMessage(player->peer_id, msg); } bool Server::showFormspec(const char *playername, const std::string &formspec, const std::string &formname) @@ -4421,14 +4976,181 @@ bool Server::showFormspec(const char *playername, const std::string &formspec, c return true; } +u32 Server::hudAdd(Player *player, HudElement *form) { + if (!player) + return -1; + + u32 id = player->getFreeHudID(); + if (id < player->hud.size()) + player->hud[id] = form; + else + player->hud.push_back(form); + + SendHUDAdd(player->peer_id, id, form); + return id; +} + +bool Server::hudRemove(Player *player, u32 id) { + if (!player || id >= player->hud.size() || !player->hud[id]) + return false; + + delete player->hud[id]; + player->hud[id] = NULL; + + SendHUDRemove(player->peer_id, id); + return true; +} + +bool Server::hudChange(Player *player, u32 id, HudElementStat stat, void *data) { + if (!player) + return false; + + SendHUDChange(player->peer_id, id, stat, data); + return true; +} + +bool Server::hudSetFlags(Player *player, u32 flags, u32 mask) { + if (!player) + return false; + + SendHUDSetFlags(player->peer_id, flags, mask); + return true; +} + +bool Server::hudSetHotbarItemcount(Player *player, s32 hotbar_itemcount) { + if (!player) + return false; + if (hotbar_itemcount <= 0 || hotbar_itemcount > HUD_HOTBAR_ITEMCOUNT_MAX) + return false; + + std::ostringstream os(std::ios::binary); + writeS32(os, hotbar_itemcount); + SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_ITEMCOUNT, os.str()); + return true; +} + +void Server::hudSetHotbarImage(Player *player, std::string name) { + if (!player) + return; + + SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_IMAGE, name); +} + +void Server::hudSetHotbarSelectedImage(Player *player, std::string name) { + if (!player) + return; + + SendHUDSetParam(player->peer_id, HUD_PARAM_HOTBAR_SELECTED_IMAGE, name); +} + void Server::notifyPlayers(const std::wstring msg) { BroadcastChatMessage(msg); } -void Server::queueBlockEmerge(v3s16 blockpos, bool allow_generate) +void Server::spawnParticle(const char *playername, v3f pos, + v3f velocity, v3f acceleration, + float expirationtime, float size, bool + collisiondetection, std::string texture) { - m_emerge->enqueueBlockEmerge(PEER_ID_INEXISTENT, blockpos, allow_generate); + Player *player = m_env->getPlayer(playername); + if(!player) + return; + SendSpawnParticle(player->peer_id, pos, velocity, acceleration, + expirationtime, size, collisiondetection, texture); +} + +void Server::spawnParticleAll(v3f pos, v3f velocity, v3f acceleration, + float expirationtime, float size, + bool collisiondetection, std::string texture) +{ + SendSpawnParticleAll(pos, velocity, acceleration, + expirationtime, size, collisiondetection, texture); +} + +u32 Server::addParticleSpawner(const char *playername, + u16 amount, float spawntime, + v3f minpos, v3f maxpos, + v3f minvel, v3f maxvel, + v3f minacc, v3f maxacc, + float minexptime, float maxexptime, + float minsize, float maxsize, + bool collisiondetection, std::string texture) +{ + Player *player = m_env->getPlayer(playername); + if(!player) + return -1; + + u32 id = 0; + for(;;) // look for unused particlespawner id + { + id++; + if (std::find(m_particlespawner_ids.begin(), + m_particlespawner_ids.end(), id) + == m_particlespawner_ids.end()) + { + m_particlespawner_ids.push_back(id); + break; + } + } + + SendAddParticleSpawner(player->peer_id, amount, spawntime, + minpos, maxpos, minvel, maxvel, minacc, maxacc, + minexptime, maxexptime, minsize, maxsize, + collisiondetection, texture, id); + + return id; +} + +u32 Server::addParticleSpawnerAll(u16 amount, float spawntime, + v3f minpos, v3f maxpos, + v3f minvel, v3f maxvel, + v3f minacc, v3f maxacc, + float minexptime, float maxexptime, + float minsize, float maxsize, + bool collisiondetection, std::string texture) +{ + u32 id = 0; + for(;;) // look for unused particlespawner id + { + id++; + if (std::find(m_particlespawner_ids.begin(), + m_particlespawner_ids.end(), id) + == m_particlespawner_ids.end()) + { + m_particlespawner_ids.push_back(id); + break; + } + } + + SendAddParticleSpawnerAll(amount, spawntime, + minpos, maxpos, minvel, maxvel, minacc, maxacc, + minexptime, maxexptime, minsize, maxsize, + collisiondetection, texture, id); + + return id; +} + +void Server::deleteParticleSpawner(const char *playername, u32 id) +{ + Player *player = m_env->getPlayer(playername); + if(!player) + return; + + m_particlespawner_ids.erase( + std::remove(m_particlespawner_ids.begin(), + m_particlespawner_ids.end(), id), + m_particlespawner_ids.end()); + SendDeleteParticleSpawner(player->peer_id, id); +} + +void Server::deleteParticleSpawnerAll(u32 id) +{ + m_particlespawner_ids.erase( + std::remove(m_particlespawner_ids.begin(), + m_particlespawner_ids.end(), id), + m_particlespawner_ids.end()); + SendDeleteParticleSpawnerAll(id); } Inventory* Server::createDetachedInventory(const std::string &name) @@ -4579,11 +5301,11 @@ const ModSpec* Server::getModSpec(const std::string &modname) } return NULL; } -void Server::getModNames(core::list &modlist) +void Server::getModNames(std::list &modlist) { for(std::vector::iterator i = m_mods.begin(); i != m_mods.end(); i++) { - modlist.push_back((*i).name); + modlist.push_back(i->name); } } std::string Server::getBuiltinLuaPath() @@ -4610,36 +5332,28 @@ v3f findSpawnPos(ServerMap &map) { s32 range = 1 + i; // We're going to try to throw the player to this position - v2s16 nodepos2d = v2s16(-range + (myrand()%(range*2)), - -range + (myrand()%(range*2))); - //v2s16 sectorpos = getNodeSectorPos(nodepos2d); - // Get ground height at point (fallbacks to heightmap function) + v2s16 nodepos2d = v2s16( + -range + (myrand() % (range * 2)), + -range + (myrand() % (range * 2))); + + // Get ground height at point s16 groundheight = map.findGroundLevel(nodepos2d); - // Don't go underwater - if(groundheight <= water_level) - { - //infostream<<"-> Underwater"< water_level + 6) - { - //infostream<<"-> Underwater"< water_level + 6) // Don't go to high places continue; - } - nodepos = v3s16(nodepos2d.X, groundheight-2, nodepos2d.Y); + nodepos = v3s16(nodepos2d.X, groundheight, nodepos2d.Y); bool is_good = false; s32 air_count = 0; - for(s32 i=0; i<10; i++){ + for (s32 i = 0; i < 10; i++) { v3s16 blockpos = getNodeBlockPos(nodepos); map.emergeBlock(blockpos, true); - MapNode n = map.getNodeNoEx(nodepos); - if(n.getContent() == CONTENT_AIR){ + content_t c = map.getNodeNoEx(nodepos).getContent(); + if (c == CONTENT_AIR || c == CONTENT_IGNORE) { air_count++; - if(air_count >= 2){ + if (air_count >= 2){ is_good = true; - nodepos.Y -= 1; break; } } @@ -4709,14 +5423,17 @@ PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id) getPlayerEffectivePrivs(player->getName()), isSingleplayer()); + /* Clean up old HUD elements from previous sessions */ + player->hud.clear(); + /* Add object to environment */ m_env->addActiveObject(playersao); /* Run scripts */ if(newplayer) - scriptapi_on_newplayer(m_lua, playersao); + m_script->on_newplayer(playersao); - scriptapi_on_joinplayer(m_lua, playersao); + m_script->on_joinplayer(playersao); return playersao; } @@ -4733,15 +5450,15 @@ void Server::handlePeerChange(PeerChange &c) */ // Error check - core::map::Node *n; + std::map::iterator n; n = m_clients.find(c.peer_id); // The client shouldn't already exist - assert(n == NULL); + assert(n == m_clients.end()); // Create client RemoteClient *client = new RemoteClient(); client->peer_id = c.peer_id; - m_clients.insert(client->peer_id, client); + m_clients[client->peer_id] = client; } // PEER_ADDED else if(c.type == PEER_REMOVED) @@ -4750,113 +5467,7 @@ void Server::handlePeerChange(PeerChange &c) Delete */ - // Error check - core::map::Node *n; - n = m_clients.find(c.peer_id); - // The client should exist - assert(n != NULL); - - /* - Mark objects to be not known by the client - */ - RemoteClient *client = n->getValue(); - // Handle objects - for(core::map::Iterator - i = client->m_known_objects.getIterator(); - i.atEnd()==false; i++) - { - // Get object - u16 id = i.getNode()->getKey(); - ServerActiveObject* obj = m_env->getActiveObject(id); - - if(obj && obj->m_known_by_count > 0) - obj->m_known_by_count--; - } - - /* - Clear references to playing sounds - */ - for(std::map::iterator - i = m_playing_sounds.begin(); - i != m_playing_sounds.end();) - { - ServerPlayingSound &psound = i->second; - psound.clients.erase(c.peer_id); - if(psound.clients.size() == 0) - m_playing_sounds.erase(i++); - else - i++; - } - - Player *player = m_env->getPlayer(c.peer_id); - - // Collect information about leaving in chat - std::wstring message; - { - if(player != NULL) - { - std::wstring name = narrow_to_wide(player->getName()); - message += L"*** "; - message += name; - message += L" left the game."; - if(c.timeout) - message += L" (timed out)"; - } - } - - /* Run scripts and remove from environment */ - { - if(player != NULL) - { - PlayerSAO *playersao = player->getPlayerSAO(); - assert(playersao); - - scriptapi_on_leaveplayer(m_lua, playersao); - - playersao->disconnected(); - } - } - - /* - Print out action - */ - { - if(player != NULL) - { - std::ostringstream os(std::ios_base::binary); - for(core::map::Iterator - i = m_clients.getIterator(); - i.atEnd() == false; i++) - { - RemoteClient *client = i.getNode()->getValue(); - assert(client->peer_id == i.getNode()->getKey()); - if(client->serialization_version == SER_FMT_VER_INVALID) - continue; - // Get player - Player *player = m_env->getPlayer(client->peer_id); - if(!player) - continue; - // Get name of player - os<getName()<<" "; - } - - actionstream<getName()<<" " - <<(c.timeout?"times out.":"leaves game.") - <<" List of players: " - <