diff options
author | sapier <Sapier at GMX dot net> | 2014-01-31 00:24:00 +0100 |
---|---|---|
committer | sapier <Sapier at GMX dot net> | 2014-01-31 18:44:43 +0100 |
commit | e258675eabc874d31bc9c6cf49e4bbc1f7f3f417 (patch) | |
tree | 2d9dfd872c8b88db13cfdeaa0503b3d88545d179 /src | |
parent | 21f1bec72433748e220d19e97a846df83340518e (diff) | |
download | minetest-e258675eabc874d31bc9c6cf49e4bbc1f7f3f417.tar.gz minetest-e258675eabc874d31bc9c6cf49e4bbc1f7f3f417.tar.bz2 minetest-e258675eabc874d31bc9c6cf49e4bbc1f7f3f417.zip |
Add propper client initialization
-add client states to avoid server sending data to uninitialized clients
-don't show uninitialized clients to other players
-propper client disconnect handling
Minor comment fixes in server
Minor bugfixes in connection
-improved peer id calculation
-honor NDEBUG flag
-improved disconnect handling
-increased initial send window
Remove some dead code
Diffstat (limited to 'src')
-rw-r--r-- | src/CMakeLists.txt | 1 | ||||
-rw-r--r-- | src/client.cpp | 18 | ||||
-rw-r--r-- | src/clientiface.cpp | 769 | ||||
-rw-r--r-- | src/clientiface.h | 306 | ||||
-rw-r--r-- | src/clientmedia.cpp | 8 | ||||
-rw-r--r-- | src/connection.cpp | 170 | ||||
-rw-r--r-- | src/connection.h | 44 | ||||
-rw-r--r-- | src/emerge.cpp | 15 | ||||
-rw-r--r-- | src/server.cpp | 1732 | ||||
-rw-r--r-- | src/server.h | 310 |
10 files changed, 1791 insertions, 1582 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index f0b98f44b..025b549cf 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -308,6 +308,7 @@ set(common_SRCS connection.cpp environment.cpp server.cpp + clientiface.cpp socket.cpp mapblock.cpp mapsector.cpp diff --git a/src/client.cpp b/src/client.cpp index 6c4d9eea8..cc1ec2a98 100644 --- a/src/client.cpp +++ b/src/client.cpp @@ -383,13 +383,6 @@ void Client::step(float dtime) // 0ms ReceiveAll(); } - - { - //TimeTaker timer("m_con_mutex + m_con.RunTimeouts()", m_device); - // 0ms - //JMutexAutoLock lock(m_con_mutex); //bulk comment-out - m_con.RunTimeouts(dtime); - } /* Packet counter @@ -758,6 +751,7 @@ void Client::step(float dtime) if (m_media_downloader && m_media_downloader->isStarted()) { m_media_downloader->step(this); if (m_media_downloader->isDone()) { + received_media(); delete m_media_downloader; m_media_downloader = NULL; } @@ -1610,11 +1604,6 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id) } m_media_downloader->step(this); - if (m_media_downloader->isDone()) { - // might be done already if all media is in the cache - delete m_media_downloader; - m_media_downloader = NULL; - } } else if(command == TOCLIENT_MEDIA) { @@ -1666,11 +1655,6 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id) m_media_downloader->conventionalTransferDone( name, data, this); } - - if (m_media_downloader->isDone()) { - delete m_media_downloader; - m_media_downloader = NULL; - } } else if(command == TOCLIENT_TOOLDEF) { diff --git a/src/clientiface.cpp b/src/clientiface.cpp new file mode 100644 index 000000000..2468ef333 --- /dev/null +++ b/src/clientiface.cpp @@ -0,0 +1,769 @@ +/* +Minetest +Copyright (C) 2010-2014 celeron55, Perttu Ahola <celeron55@gmail.com> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + +#include "clientiface.h" +#include "player.h" +#include "settings.h" +#include "mapblock.h" +#include "connection.h" +#include "environment.h" +#include "map.h" +#include "emerge.h" +#include "serverobject.h" // TODO this is used for cleanup of only + +#include "util/numeric.h" + +#include "main.h" // for g_settings + +void RemoteClient::GetNextBlocks( + ServerEnvironment *env, + EmergeManager * emerge, + float dtime, + std::vector<PrioritySortedBlockTransfer> &dest) +{ + DSTACK(__FUNCTION_NAME); + + + // Increment timers + m_nothing_to_send_pause_timer -= dtime; + m_nearest_unsent_reset_timer += dtime; + + if(m_nothing_to_send_pause_timer >= 0) + return; + + Player *player = env->getPlayer(peer_id); + // This can happen sometimes; clients and players are not in perfect sync. + if(player == NULL) + return; + + // Won't send anything if already sending + if(m_blocks_sending.size() >= g_settings->getU16 + ("max_simultaneous_block_sends_per_client")) + { + //infostream<<"Not sending any blocks, Queue full."<<std::endl; + return; + } + + v3f playerpos = player->getPosition(); + v3f playerspeed = player->getSpeed(); + v3f playerspeeddir(0,0,0); + if(playerspeed.getLength() > 1.0*BS) + playerspeeddir = playerspeed / playerspeed.getLength(); + // Predict to next block + v3f playerpos_predicted = playerpos + playerspeeddir*MAP_BLOCKSIZE*BS; + + v3s16 center_nodepos = floatToInt(playerpos_predicted, BS); + + v3s16 center = getNodeBlockPos(center_nodepos); + + // Camera position and direction + v3f camera_pos = player->getEyePosition(); + v3f camera_dir = v3f(0,0,1); + camera_dir.rotateYZBy(player->getPitch()); + camera_dir.rotateXZBy(player->getYaw()); + + /*infostream<<"camera_dir=("<<camera_dir.X<<","<<camera_dir.Y<<"," + <<camera_dir.Z<<")"<<std::endl;*/ + + /* + Get the starting value of the block finder radius. + */ + + if(m_last_center != center) + { + m_nearest_unsent_d = 0; + m_last_center = center; + } + + /*infostream<<"m_nearest_unsent_reset_timer=" + <<m_nearest_unsent_reset_timer<<std::endl;*/ + + // Reset periodically to workaround for some bugs or stuff + if(m_nearest_unsent_reset_timer > 20.0) + { + m_nearest_unsent_reset_timer = 0; + m_nearest_unsent_d = 0; + //infostream<<"Resetting m_nearest_unsent_d for " + // <<server->getPlayerName(peer_id)<<std::endl; + } + + //s16 last_nearest_unsent_d = m_nearest_unsent_d; + s16 d_start = m_nearest_unsent_d; + + //infostream<<"d_start="<<d_start<<std::endl; + + u16 max_simul_sends_setting = g_settings->getU16 + ("max_simultaneous_block_sends_per_client"); + u16 max_simul_sends_usually = max_simul_sends_setting; + + /* + Check the time from last addNode/removeNode. + + Decrease send rate if player is building stuff. + */ + m_time_from_building += dtime; + if(m_time_from_building < g_settings->getFloat( + "full_block_send_enable_min_time_from_building")) + { + max_simul_sends_usually + = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS; + } + + /* + Number of blocks sending + number of blocks selected for sending + */ + u32 num_blocks_selected = m_blocks_sending.size(); + + /* + next time d will be continued from the d from which the nearest + unsent block was found this time. + + This is because not necessarily any of the blocks found this + time are actually sent. + */ + s32 new_nearest_unsent_d = -1; + + s16 d_max = g_settings->getS16("max_block_send_distance"); + s16 d_max_gen = g_settings->getS16("max_block_generate_distance"); + + // Don't loop very much at a time + s16 max_d_increment_at_time = 2; + if(d_max > d_start + max_d_increment_at_time) + d_max = d_start + max_d_increment_at_time; + + s32 nearest_emerged_d = -1; + s32 nearest_emergefull_d = -1; + s32 nearest_sent_d = -1; + bool queue_is_full = false; + + s16 d; + for(d = d_start; d <= d_max; d++) + { + /* + Get the border/face dot coordinates of a "d-radiused" + box + */ + std::list<v3s16> list; + getFacePositions(list, d); + + std::list<v3s16>::iterator li; + for(li=list.begin(); li!=list.end(); ++li) + { + v3s16 p = *li + center; + + /* + Send throttling + - Don't allow too many simultaneous transfers + - EXCEPT when the blocks are very close + + Also, don't send blocks that are already flying. + */ + + // Start with the usual maximum + u16 max_simul_dynamic = max_simul_sends_usually; + + // If block is very close, allow full maximum + if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D) + max_simul_dynamic = max_simul_sends_setting; + + // Don't select too many blocks for sending + if(num_blocks_selected >= max_simul_dynamic) + { + queue_is_full = true; + goto queue_full_break; + } + + // Don't send blocks that are currently being transferred + if(m_blocks_sending.find(p) != m_blocks_sending.end()) + continue; + + /* + Do not go over-limit + */ + if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE + || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE + || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE + || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE + || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE + || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE) + continue; + + // If this is true, inexistent block will be made from scratch + bool generate = d <= d_max_gen; + + { + /*// Limit the generating area vertically to 2/3 + if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3) + generate = false;*/ + + // Limit the send area vertically to 1/2 + if(abs(p.Y - center.Y) > d_max / 2) + continue; + } + + /* + Don't generate or send if not in sight + FIXME This only works if the client uses a small enough + FOV setting. The default of 72 degrees is fine. + */ + + float camera_fov = (72.0*M_PI/180) * 4./3.; + if(isBlockInSight(p, camera_pos, camera_dir, camera_fov, 10000*BS) == false) + { + continue; + } + + /* + Don't send already sent blocks + */ + { + if(m_blocks_sent.find(p) != m_blocks_sent.end()) + { + continue; + } + } + + /* + Check if map has this block + */ + MapBlock *block = env->getMap().getBlockNoCreateNoEx(p); + + bool surely_not_found_on_disk = false; + bool block_is_invalid = false; + if(block != NULL) + { + // Reset usage timer, this block will be of use in the future. + block->resetUsageTimer(); + + // Block is dummy if data doesn't exist. + // It means it has been not found from disk and not generated + if(block->isDummy()) + { + surely_not_found_on_disk = true; + } + + // Block is valid if lighting is up-to-date and data exists + if(block->isValid() == false) + { + block_is_invalid = true; + } + + if(block->isGenerated() == false) + block_is_invalid = true; + + /* + If block is not close, don't send it unless it is near + ground level. + + Block is near ground level if night-time mesh + differs from day-time mesh. + */ + if(d >= 4) + { + if(block->getDayNightDiff() == false) + continue; + } + } + + /* + If block has been marked to not exist on disk (dummy) + and generating new ones is not wanted, skip block. + */ + if(generate == false && surely_not_found_on_disk == true) + { + // get next one. + continue; + } + + /* + Add inexistent block to emerge queue. + */ + if(block == NULL || surely_not_found_on_disk || block_is_invalid) + { + if (emerge->enqueueBlockEmerge(peer_id, p, generate)) { + if (nearest_emerged_d == -1) + nearest_emerged_d = d; + } else { + if (nearest_emergefull_d == -1) + nearest_emergefull_d = d; + goto queue_full_break; + } + + // get next one. + continue; + } + + if(nearest_sent_d == -1) + nearest_sent_d = d; + + /* + Add block to send queue + */ + PrioritySortedBlockTransfer q((float)d, p, peer_id); + + dest.push_back(q); + + num_blocks_selected += 1; + } + } +queue_full_break: + + // If nothing was found for sending and nothing was queued for + // emerging, continue next time browsing from here + if(nearest_emerged_d != -1){ + new_nearest_unsent_d = nearest_emerged_d; + } else if(nearest_emergefull_d != -1){ + new_nearest_unsent_d = nearest_emergefull_d; + } else { + if(d > g_settings->getS16("max_block_send_distance")){ + new_nearest_unsent_d = 0; + m_nothing_to_send_pause_timer = 2.0; + } else { + if(nearest_sent_d != -1) + new_nearest_unsent_d = nearest_sent_d; + else + new_nearest_unsent_d = d; + } + } + + if(new_nearest_unsent_d != -1) + m_nearest_unsent_d = new_nearest_unsent_d; +} + +void RemoteClient::GotBlock(v3s16 p) +{ + if(m_blocks_sending.find(p) != m_blocks_sending.end()) + m_blocks_sending.erase(p); + else + { + m_excess_gotblocks++; + } + m_blocks_sent.insert(p); +} + +void RemoteClient::SentBlock(v3s16 p) +{ + if(m_blocks_sending.find(p) == m_blocks_sending.end()) + m_blocks_sending[p] = 0.0; + else + infostream<<"RemoteClient::SentBlock(): Sent block" + " already in m_blocks_sending"<<std::endl; +} + +void RemoteClient::SetBlockNotSent(v3s16 p) +{ + m_nearest_unsent_d = 0; + + 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); +} + +void RemoteClient::SetBlocksNotSent(std::map<v3s16, MapBlock*> &blocks) +{ + m_nearest_unsent_d = 0; + + for(std::map<v3s16, MapBlock*>::iterator + i = blocks.begin(); + i != blocks.end(); ++i) + { + v3s16 p = i->first; + + 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); + } +} + +void RemoteClient::notifyEvent(ClientStateEvent event) +{ + switch (m_state) + { + case Invalid: + assert("State update for client in invalid state" != 0); + break; + + case Created: + switch(event) + { + case Init: + m_state = InitSent; + break; + + case Disconnect: + m_state = Disconnecting; + break; + + case SetDenied: + m_state = Denied; + break; + + /* GotInit2 SetDefinitionsSent SetMediaSent */ + default: + assert("Invalid client state transition!" == 0); + } + break; + + case Denied: + /* don't do anything if in denied state */ + break; + + case InitSent: + switch(event) + { + case GotInit2: + confirmSerializationVersion(); + m_state = InitDone; + break; + + case Disconnect: + m_state = Disconnecting; + break; + + case SetDenied: + m_state = Denied; + break; + + /* Init SetDefinitionsSent SetMediaSent */ + default: + assert("Invalid client state transition!" == 0); + } + break; + + case InitDone: + switch(event) + { + case SetDefinitionsSent: + m_state = DefinitionsSent; + break; + + case Disconnect: + m_state = Disconnecting; + break; + + case SetDenied: + m_state = Denied; + break; + + /* Init GotInit2 SetMediaSent */ + default: + assert("Invalid client state transition!" == 0); + } + break; + + case DefinitionsSent: + switch(event) + { + case SetMediaSent: + m_state = Active; + break; + + case Disconnect: + m_state = Disconnecting; + break; + + case SetDenied: + m_state = Denied; + break; + + /* Init GotInit2 SetDefinitionsSent */ + default: + assert("Invalid client state transition!" == 0); + } + break; + + case Active: + switch(event) + { + case SetDenied: + m_state = Denied; + break; + + case Disconnect: + m_state = Disconnecting; + break; + + /* Init GotInit2 SetDefinitionsSent SetMediaSent SetDenied */ + default: + assert("Invalid client state transition!" == 0); + break; + } + break; + + case Disconnecting: + /* we are already disconnecting */ + break; + } +} + +ClientInterface::ClientInterface(con::Connection* con) +: + m_con(con), + m_env(NULL), + m_print_info_timer(0.0) +{ + +} +ClientInterface::~ClientInterface() +{ + /* + Delete clients + */ + { + JMutexAutoLock clientslock(m_clients_mutex); + + for(std::map<u16, RemoteClient*>::iterator + i = m_clients.begin(); + i != m_clients.end(); ++i) + { + + // Delete client + delete i->second; + } + } +} + +std::list<u16> ClientInterface::getClientIDs(ClientState min_state) +{ + std::list<u16> reply; + JMutexAutoLock clientslock(m_clients_mutex); + + for(std::map<u16, RemoteClient*>::iterator + i = m_clients.begin(); + i != m_clients.end(); ++i) + { + if (i->second->getState() >= min_state) + reply.push_back(i->second->peer_id); + } + + return reply; +} + +std::vector<std::string> ClientInterface::getPlayerNames() +{ + return m_clients_names; +} + + +void ClientInterface::step(float dtime) +{ + m_print_info_timer += dtime; + if(m_print_info_timer >= 30.0) + { + m_print_info_timer = 0.0; + UpdatePlayerList(); + } +} + +void ClientInterface::UpdatePlayerList() +{ + if (m_env != NULL) + { + std::list<u16> clients = getClientIDs(); + m_clients_names.clear(); + + + if(clients.size() != 0) + infostream<<"Players:"<<std::endl; + for(std::list<u16>::iterator + i = clients.begin(); + i != clients.end(); ++i) + { + Player *player = m_env->getPlayer(*i); + if(player==NULL) + continue; + infostream<<"* "<<player->getName()<<"\t"; + + { + JMutexAutoLock clientslock(m_clients_mutex); + RemoteClient* client = lockedGetClientNoEx(*i); + if(client != NULL) + client->PrintInfo(infostream); + } + m_clients_names.push_back(player->getName()); + } + } +} + +void ClientInterface::send(u16 peer_id,u8 channelnum, + SharedBuffer<u8> data, bool reliable) +{ + m_con->Send(peer_id, channelnum, data, reliable); +} + +void ClientInterface::sendToAll(u16 channelnum, + SharedBuffer<u8> data, bool reliable) +{ + JMutexAutoLock clientslock(m_clients_mutex); + for(std::map<u16, RemoteClient*>::iterator + i = m_clients.begin(); + i != m_clients.end(); ++i) + { + RemoteClient *client = i->second; + + if (client->net_proto_version != 0) + { + m_con->Send(client->peer_id, channelnum, data, reliable); + } + } +} + +RemoteClient* ClientInterface::getClientNoEx(u16 peer_id, ClientState state_min) +{ + JMutexAutoLock clientslock(m_clients_mutex); + std::map<u16, RemoteClient*>::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 NULL; + + if (n->second->getState() >= state_min) + return n->second; + else + return NULL; +} + +RemoteClient* ClientInterface::lockedGetClientNoEx(u16 peer_id, ClientState state_min) +{ + std::map<u16, RemoteClient*>::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 NULL; + + if (n->second->getState() >= state_min) + return n->second; + else + return NULL; +} + +ClientState ClientInterface::getClientState(u16 peer_id) +{ + JMutexAutoLock clientslock(m_clients_mutex); + std::map<u16, RemoteClient*>::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 Invalid; + + return n->second->getState(); +} + +void ClientInterface::setPlayerName(u16 peer_id,std::string name) +{ + JMutexAutoLock clientslock(m_clients_mutex); + std::map<u16, RemoteClient*>::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()) + n->second->setName(name); +} + +void ClientInterface::DeleteClient(u16 peer_id) +{ + JMutexAutoLock conlock(m_clients_mutex); + + // Error check + std::map<u16, RemoteClient*>::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 + */ + //TODO this should be done by client destructor!!! + RemoteClient *client = n->second; + // Handle objects + for(std::set<u16>::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--; + } + + // Delete client + delete m_clients[peer_id]; + m_clients.erase(peer_id); +} + +void ClientInterface::CreateClient(u16 peer_id) +{ + JMutexAutoLock conlock(m_clients_mutex); + + // Error check + std::map<u16, RemoteClient*>::iterator n; + n = m_clients.find(peer_id); + // The client shouldn't already exist + if(n != m_clients.end()) return; + + // Create client + RemoteClient *client = new RemoteClient(); + client->peer_id = peer_id; + m_clients[client->peer_id] = client; +} + +void ClientInterface::event(u16 peer_id, ClientStateEvent event) +{ + { + JMutexAutoLock clientlock(m_clients_mutex); + + // Error check + std::map<u16, RemoteClient*>::iterator n; + n = m_clients.find(peer_id); + + // No client to deliver event + if (n == m_clients.end()) + return; + n->second->notifyEvent(event); + } + + if ((event == SetMediaSent) || (event == Disconnect) || (event == SetDenied)) + { + UpdatePlayerList(); + } +} + +u16 ClientInterface::getProtocolVersion(u16 peer_id) +{ + JMutexAutoLock conlock(m_clients_mutex); + + // Error check + std::map<u16, RemoteClient*>::iterator n; + n = m_clients.find(peer_id); + + // No client to deliver event + if (n == m_clients.end()) + return 0; + + return n->second->net_proto_version; +} diff --git a/src/clientiface.h b/src/clientiface.h new file mode 100644 index 000000000..a2315b3bd --- /dev/null +++ b/src/clientiface.h @@ -0,0 +1,306 @@ +/* +Minetest +Copyright (C) 2010-2014 celeron55, Perttu Ahola <celeron55@gmail.com> + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ +#ifndef _CLIENTIFACE_H_ +#define _CLIENTIFACE_H_ + +#include "irr_v3d.h" // for irrlicht datatypes + +#include "constants.h" +#include "serialization.h" // for SER_FMT_VER_INVALID +#include "jthread/jmutex.h" + +#include <list> +#include <vector> +#include <map> +#include <set> + +class MapBlock; +class ServerEnvironment; +class EmergeManager; + +namespace con { + class Connection; +} + +enum ClientState +{ + Invalid, + Disconnecting, + Denied, + Created, + InitSent, + InitDone, + DefinitionsSent, + Active +}; + +enum ClientStateEvent +{ + Init, + GotInit2, + SetDenied, + SetDefinitionsSent, + SetMediaSent, + Disconnect +}; + +/* + Used for queueing and sorting block transfers in containers + + Lower priority number means higher priority. +*/ +struct PrioritySortedBlockTransfer +{ + PrioritySortedBlockTransfer(float a_priority, v3s16 a_pos, u16 a_peer_id) + { + priority = a_priority; + pos = a_pos; + peer_id = a_peer_id; + } + bool operator < (const PrioritySortedBlockTransfer &other) const + { + return priority < other.priority; + } + float priority; + v3s16 pos; + u16 peer_id; +}; + +class RemoteClient +{ +public: + // peer_id=0 means this client has no associated peer + // NOTE: If client is made allowed to exist while peer doesn't, + // this has to be set to 0 when there is no peer. + // Also, the client must be moved to some other container. + u16 peer_id; + // The serialization version to use with the client + u8 serialization_version; + // + u16 net_proto_version; + + RemoteClient(): + peer_id(PEER_ID_INEXISTENT), + serialization_version(SER_FMT_VER_INVALID), + net_proto_version(0), + m_time_from_building(9999), + m_pending_serialization_version(SER_FMT_VER_INVALID), + m_state(Created), + m_nearest_unsent_d(0), + m_nearest_unsent_reset_timer(0.0), + m_excess_gotblocks(0), + m_nothing_to_send_counter(0), + m_nothing_to_send_pause_timer(0.0), + m_name("") + { + } + ~RemoteClient() + { + } + + /* + Finds block that should be sent next to the client. + Environment should be locked when this is called. + dtime is used for resetting send radius at slow interval + */ + void GetNextBlocks(ServerEnvironment *env, EmergeManager* emerge, + float dtime, std::vector<PrioritySortedBlockTransfer> &dest); + + void GotBlock(v3s16 p); + + void SentBlock(v3s16 p); + + void SetBlockNotSent(v3s16 p); + void SetBlocksNotSent(std::map<v3s16, MapBlock*> &blocks); + + s32 SendingCount() + { + return m_blocks_sending.size(); + } + + // Increments timeouts and removes timed-out blocks from list + // NOTE: This doesn't fix the server-not-sending-block bug + // because it is related to emerging, not sending. + //void RunSendingTimeouts(float dtime, float timeout); + + void PrintInfo(std::ostream &o) + { + o<<"RemoteClient "<<peer_id<<": " + <<"m_blocks_sent.size()="<<m_blocks_sent.size() + <<", m_blocks_sending.size()="<<m_blocks_sending.size() + <<", m_nearest_unsent_d="<<m_nearest_unsent_d + <<", m_excess_gotblocks="<<m_excess_gotblocks + <<std::endl; + m_excess_gotblocks = 0; + } + + // Time from last placing or removing blocks + float m_time_from_building; + + /* + List of active objects that the client knows of. + Value is dummy. + */ + std::set<u16> m_known_objects; + + ClientState getState() + { return m_state; } + + std::string getName() + { return m_name; } + + void setName(std::string name) + { m_name = name; } + + /* update internal client state */ + void notifyEvent(ClientStateEvent event); + + /* set expected serialization version */ + void setPendingSerializationVersion(u8 version) + { m_pending_serialization_version = version; } + + void confirmSerializationVersion() + { serialization_version = m_pending_serialization_version; } + +private: + // Version is stored in here after INIT before INIT2 + u8 m_pending_serialization_version; + + /* current state of client */ + ClientState m_state; + + /* + Blocks that have been sent to client. + - These don't have to be sent again. + - A block is cleared from here when client says it has + deleted it from it's memory + + Key is position, value is dummy. + No MapBlock* is stored here because the blocks can get deleted. + */ + std::set<v3s16> m_blocks_sent; + s16 m_nearest_unsent_d; + v3s16 m_last_center; + float m_nearest_unsent_reset_timer; + + /* + Blocks that are currently on the line. + This is used for throttling the sending of blocks. + - The size of this list is limited to some value + Block is added when it is sent with BLOCKDATA. + Block is removed when GOTBLOCKS is received. + Value is time from sending. (not used at the moment) + */ + std::map<v3s16, float> m_blocks_sending; + + /* + Count of excess GotBlocks(). + There is an excess amount because the client sometimes + gets a block so late that the server sends it again, + and the client then sends two GOTBLOCKs. + This is resetted by PrintInfo() + */ + u32 m_excess_gotblocks; + + // CPU usage optimization + u32 m_nothing_to_send_counter; + float m_nothing_to_send_pause_timer; + std::string m_name; +}; + +class ClientInterface { +public: + + friend class Server; + + ClientInterface(con::Connection* con); + ~ClientInterface(); + + /* run sync step */ + void step(float dtime); + + /* get list of active client id's */ + std::list<u16> getClientIDs(ClientState min_state=Active); + + /* get list of client player names */ + std::vector<std::string> getPlayerNames(); + + /* send message to client */ + void send(u16 peer_id, u8 channelnum, SharedBuffer<u8> data, bool reliable); + + /* send to all clients */ + void sendToAll(u16 channelnum, SharedBuffer<u8> data, bool reliable); + + /* delete a client */ + void DeleteClient(u16 peer_id); + + /* create client */ + void CreateClient(u16 peer_id); + + /* get a client by peer_id */ + RemoteClient* getClientNoEx(u16 peer_id, ClientState state_min=Active); + + /* get client by peer_id (make sure you have list lock before!*/ + RemoteClient* lockedGetClientNoEx(u16 peer_id, ClientState state_min=Active); + + /* get state of client by id*/ + ClientState getClientState(u16 peer_id); + + /* set client playername */ + void setPlayerName(u16 peer_id,std::string name); + + /* get protocol version of client */ + u16 getProtocolVersion(u16 peer_id); + + /* event to update client state */ + void event(u16 peer_id, ClientStateEvent event); + + /* set environment */ + void setEnv(ServerEnvironment* env) + { assert(m_env == 0); m_env = env; } + +protected: + //TODO find way to avoid this functions + void Lock() + { m_clients_mutex.Lock(); } + void Unlock() + { m_clients_mutex.Unlock(); } + + std::map<u16, RemoteClient*>& getClientList() + { return m_clients; } + +private: + /* update internal player list */ + void UpdatePlayerList(); + + // Connection + con::Connection* m_con; + JMutex m_clients_mutex; + // Connected clients (behind the con mutex) + std::map<u16, RemoteClient*> m_clients; + std::vector<std::string> m_clients_names; //for announcing masterserver + + // Environment + ServerEnvironment *m_env; + JMutex m_env_mutex; + + float m_print_info_timer; +}; + +#endif diff --git a/src/clientmedia.cpp b/src/clientmedia.cpp index 8260d5f5a..dcae44023 100644 --- a/src/clientmedia.cpp +++ b/src/clientmedia.cpp @@ -480,13 +480,7 @@ void ClientMediaDownloader::startConventionalTransfers(Client *client) { assert(m_httpfetch_active == 0); - if (m_uncached_received_count == m_uncached_count) { - // In this case all media was found in the cache or - // has been downloaded from some remote server; - // report this fact to the server - client->received_media(); - } - else { + if (m_uncached_received_count != m_uncached_count) { // Some media files have not been received yet, use the // conventional slow method (minetest protocol) to get them std::list<std::string> file_requests; diff --git a/src/connection.cpp b/src/connection.cpp index 2e126a770..92f9f8ec2 100644 --- a/src/connection.cpp +++ b/src/connection.cpp @@ -34,6 +34,14 @@ with this program; if not, write to the Free Software Foundation, Inc., namespace con { +/******************************************************************************/ +/* defines used for debugging and profiling */ +/******************************************************************************/ +#ifdef NDEBUG +#define LOG(a) a +#define PROFILE(a) +#undef DEBUG_CONNECTION_KBPS +#else /* this mutex is used to achieve log message consistency */ JMutex log_message_mutex; #define LOG(a) \ @@ -41,15 +49,10 @@ JMutex log_message_mutex; JMutexAutoLock loglock(log_message_mutex); \ a; \ } - -/******************************************************************************/ -/* defines used for debugging and profiling */ -/******************************************************************************/ #define PROFILE(a) a -//#define PROFILE(a) - //#define DEBUG_CONNECTION_KBPS #undef DEBUG_CONNECTION_KBPS +#endif static inline float CALC_DTIME(unsigned int lasttime, unsigned int curtime) { @@ -960,15 +963,12 @@ void Peer::Drop() UDPPeer::UDPPeer(u16 a_id, Address a_address, Connection* connection) : Peer(a_address,a_id,connection), + m_pending_disconnect(false), resend_timeout(0.5), m_legacy_peer(true) { } -UDPPeer::~UDPPeer() -{ -} - bool UDPPeer::getAddress(MTProtocols type,Address& toset) { if ((type == UDP) || (type == MINETEST_RELIABLE_UDP) || (type == PRIMARY)) @@ -980,6 +980,15 @@ bool UDPPeer::getAddress(MTProtocols type,Address& toset) return false; } +void UDPPeer::setNonLegacyPeer() +{ + m_legacy_peer = false; + for(unsigned int i=0; i< CHANNEL_COUNT; i++) + { + channels->setWindowSize(g_settings->getU16("max_packets_per_iteration")); + } +} + void UDPPeer::reportRTT(float rtt) { if (rtt < 0.0) { @@ -1014,6 +1023,9 @@ bool UDPPeer::Ping(float dtime,SharedBuffer<u8>& data) void UDPPeer::PutReliableSendCommand(ConnectionCommand &c, unsigned int max_packet_size) { + if (m_pending_disconnect) + return; + if ( channels[c.channelnum].queued_commands.empty() && /* don't queue more packets then window size */ (channels[c.channelnum].queued_reliables.size() @@ -1040,6 +1052,9 @@ bool UDPPeer::processReliableSendCommand( ConnectionCommand &c, unsigned int max_packet_size) { + if (m_pending_disconnect) + return true; + u32 chunksize_max = max_packet_size - BASE_HEADER_SIZE - RELIABLE_HEADER_SIZE; @@ -1564,7 +1579,6 @@ void ConnectionSendThread::processReliableCommand(ConnectionCommand &c) case CONNCMD_SERVE: case CONNCMD_CONNECT: case CONNCMD_DISCONNECT: - case CONNCMD_DELETE_PEER: case CONCMD_ACK: assert("Got command that shouldn't be reliable as reliable command" == 0); default: @@ -1606,10 +1620,6 @@ void ConnectionSendThread::processNonReliableCommand(ConnectionCommand &c) LOG(dout_con<<m_connection->getDesc()<<" UDP processing CONNCMD_SEND_TO_ALL"<<std::endl); sendToAll(c.channelnum, c.data); return; - case CONNCMD_DELETE_PEER: - LOG(dout_con<<m_connection->getDesc()<<" UDP processing CONNCMD_DELETE_PEER"<<std::endl); - m_connection->deletePeer(c.peer_id, false); - return; case CONCMD_ACK: LOG(dout_con<<m_connection->getDesc()<<" UDP processing CONCMD_ACK"<<std::endl); sendAsPacket(c.peer_id,c.channelnum,c.data,true); @@ -1686,6 +1696,18 @@ void ConnectionSendThread::disconnect_peer(u16 peer_id) writeU8(&data[0], TYPE_CONTROL); writeU8(&data[1], CONTROLTYPE_DISCO); sendAsPacket(peer_id, 0,data,false); + + PeerHelper peer = m_connection->getPeerNoEx(peer_id); + + if (!peer) + return; + + if (dynamic_cast<UDPPeer*>(&peer) == 0) + { + return; + } + + dynamic_cast<UDPPeer*>(&peer)->m_pending_disconnect = true; } void ConnectionSendThread::send(u16 peer_id, u8 channelnum, @@ -1764,6 +1786,8 @@ void ConnectionSendThread::sendToAllReliable(ConnectionCommand &c) void ConnectionSendThread::sendPackets(float dtime) { std::list<u16> peerIds = m_connection->getPeerIDs(); + std::list<u16> pendingDisconnect; + std::map<u16,bool> pending_unreliable; for(std::list<u16>::iterator j = peerIds.begin(); @@ -1782,6 +1806,11 @@ void ConnectionSendThread::sendPackets(float dtime) continue; } + if (dynamic_cast<UDPPeer*>(&peer)->m_pending_disconnect) + { + pendingDisconnect.push_back(*j); + } + PROFILE(std::stringstream peerIdentifier); PROFILE(peerIdentifier << "sendPackets[" << m_connection->getDesc() << ";" << *j << ";RELIABLE]"); PROFILE(ScopeProfiler peerprofiler(g_profiler, peerIdentifier.str(), SPT_AVG)); @@ -1877,6 +1906,17 @@ void ConnectionSendThread::sendPackets(float dtime) } else { m_outgoing_queue.push_back(packet); + pending_unreliable[packet.peer_id] = true; + } + } + + for(std::list<u16>::iterator + k = pendingDisconnect.begin(); + k != pendingDisconnect.end(); ++k) + { + if (!pending_unreliable[*k]) + { + m_connection->deletePeer(*k,false); } } } @@ -1986,11 +2026,10 @@ void * ConnectionReceiveThread::Thread() // Receive packets from the network and buffers and create ConnectionEvents void ConnectionReceiveThread::receive() { - /* now reorder reliables */ - u32 datasize = m_max_packet_size * 2; // Double it just to be safe - // TODO: We can not know how many layers of header there are. - // For now, just assume there are no other than the base headers. - u32 packet_maxsize = datasize + BASE_HEADER_SIZE; + // use IPv6 minimum allowed MTU as receive buffer size as this is + // theoretical reliable upper boundary of a udp packet for all IPv6 enabled + // infrastructure + unsigned int packet_maxsize = 1500; SharedBuffer<u8> packetdata(packet_maxsize); bool packet_queued = true; @@ -2126,7 +2165,7 @@ void ConnectionReceiveThread::receive() LOG(dout_con<<m_connection->getDesc() <<" ProcessPacket from peer_id: " << peer_id - << ",channel: " << channelnum << ", returned " + << ",channel: " << (channelnum & 0xFF) << ", returned " << resultdata.getSize() << " bytes" <<std::endl); ConnectionEvent e; @@ -2262,6 +2301,10 @@ SharedBuffer<u8> ConnectionReceiveThread::processPacket(Channel *channel, } //put bytes for max bandwidth calculation channel->UpdateBytesSent(p.data.getSize(),1); + if (channel->outgoing_reliables_sent.size() == 0) + { + m_connection->TriggerSend(); + } } catch(NotFoundException &e){ LOG(derr_con<<m_connection->getDesc() @@ -2534,7 +2577,8 @@ Connection::Connection(u32 protocol_id, u32 max_packet_size, float timeout, m_info_mutex(), m_bc_peerhandler(0), m_bc_receive_timeout(0), - m_shutting_down(false) + m_shutting_down(false), + m_next_remote_peer_id(2) { m_udpSocket.setTimeoutMs(5); @@ -2554,7 +2598,8 @@ Connection::Connection(u32 protocol_id, u32 max_packet_size, float timeout, m_info_mutex(), m_bc_peerhandler(peerhandler), m_bc_receive_timeout(0), - m_shutting_down(false) + m_shutting_down(false), + m_next_remote_peer_id(2) { m_udpSocket.setTimeoutMs(5); @@ -2810,11 +2855,6 @@ void Connection::Send(u16 peer_id, u8 channelnum, putCommand(c); } -void Connection::RunTimeouts(float dtime) -{ - // No-op -} - Address Connection::GetPeerAddress(u16 peer_id) { PeerHelper peer = getPeerNoEx(peer_id); @@ -2838,46 +2878,43 @@ u16 Connection::createPeer(Address& sender, MTProtocols protocol, int fd) // Somebody wants to make a new connection // Get a unique peer id (2 or higher) - u16 peer_id_new = 2; + u16 peer_id_new = m_next_remote_peer_id; u16 overflow = MAX_UDP_PEERS; /* Find an unused peer id */ - bool out_of_ids = false; - for(;;) { - // Check if exists - if(m_peers.find(peer_id_new) == m_peers.end()) - break; - // Check for overflow - if(peer_id_new == overflow){ - out_of_ids = true; - break; + JMutexAutoLock lock(m_peers_mutex); + bool out_of_ids = false; + for(;;) + { + // Check if exists + if(m_peers.find(peer_id_new) == m_peers.end()) + break; + // Check for overflow + if(peer_id_new == overflow){ + out_of_ids = true; + break; + } + peer_id_new++; + } + if(out_of_ids){ + errorstream<<getDesc()<<" ran out of peer ids"<<std::endl; + return PEER_ID_INEXISTENT; } - peer_id_new++; - } - if(out_of_ids){ - errorstream<<getDesc()<<" ran out of peer ids"<<std::endl; - return PEER_ID_INEXISTENT; - } - - LOG(dout_con<<getDesc() - <<"createPeer(): giving peer_id="<<peer_id_new<<std::endl); - // Create a peer - Peer *peer = 0; + // Create a peer + Peer *peer = 0; + peer = new UDPPeer(peer_id_new, sender, this); - peer = new UDPPeer(peer_id_new, sender, this); + m_peers[peer->id] = peer; + } - m_peers_mutex.Lock(); - m_peers[peer->id] = peer; - m_peers_mutex.Unlock(); + m_next_remote_peer_id = (peer_id_new +1) % MAX_UDP_PEERS; - // Create peer addition event - ConnectionEvent e; - e.peerAdded(peer_id_new, sender); - putEvent(e); + LOG(dout_con<<getDesc() + <<"createPeer(): giving peer_id="<<peer_id_new<<std::endl); ConnectionCommand cmd; SharedBuffer<u8> reply(4); @@ -2887,17 +2924,15 @@ u16 Connection::createPeer(Address& sender, MTProtocols protocol, int fd) cmd.createPeer(peer_id_new,reply); this->putCommand(cmd); + // Create peer addition event + ConnectionEvent e; + e.peerAdded(peer_id_new, sender); + putEvent(e); + // We're now talking to a valid peer_id return peer_id_new; } -void Connection::DeletePeer(u16 peer_id) -{ - ConnectionCommand c; - c.deletePeer(peer_id); - putCommand(c); -} - void Connection::PrintInfo(std::ostream &out) { m_info_mutex.Lock(); @@ -2915,6 +2950,13 @@ const std::string Connection::getDesc() return std::string("con(")+itos(m_udpSocket.GetHandle())+"/"+itos(m_peer_id)+")"; } +void Connection::DisconnectPeer(u16 peer_id) +{ + ConnectionCommand discon; + discon.disconnect_peer(peer_id); + putCommand(discon); +} + void Connection::sendAck(u16 peer_id, u8 channelnum, u16 seqnum) { assert(channelnum < CHANNEL_COUNT); diff --git a/src/connection.h b/src/connection.h index e43d93fb3..c9474032d 100644 --- a/src/connection.h +++ b/src/connection.h @@ -71,14 +71,6 @@ public: {} }; -/*class ThrottlingException : public BaseException -{ -public: - ThrottlingException(const char *s): - BaseException(s) - {} -};*/ - class InvalidIncomingDataException : public BaseException { public: @@ -406,7 +398,6 @@ enum ConnectionCommandType{ CONNCMD_DISCONNECT_PEER, CONNCMD_SEND, CONNCMD_SEND_TO_ALL, - CONNCMD_DELETE_PEER, CONCMD_ACK, CONCMD_CREATE_PEER, CONCMD_DISABLE_LEGACY @@ -460,11 +451,6 @@ struct ConnectionCommand data = data_; reliable = reliable_; } - void deletePeer(u16 peer_id_) - { - type = CONNCMD_DELETE_PEER; - peer_id = peer_id_; - } void ack(u16 peer_id_, u8 channelnum_, SharedBuffer<u8> data_) { @@ -580,16 +566,29 @@ private: class Peer; +enum PeerChangeType +{ + PEER_ADDED, + PEER_REMOVED +}; +struct PeerChange +{ + PeerChangeType type; + u16 peer_id; + bool timeout; +}; + class PeerHandler { public: + PeerHandler() { } virtual ~PeerHandler() { } - + /* This is called after the Peer has been inserted into the Connection's peer container. @@ -771,7 +770,7 @@ public: friend class ConnectionSendThread; UDPPeer(u16 a_id, Address a_address, Connection* connection); - virtual ~UDPPeer(); + virtual ~UDPPeer() {}; void PutReliableSendCommand(ConnectionCommand &c, unsigned int max_packet_size); @@ -781,8 +780,7 @@ public: bool getAddress(MTProtocols type, Address& toset); - void setNonLegacyPeer() - { m_legacy_peer = false; } + void setNonLegacyPeer(); bool getLegacyPeer() { return m_legacy_peer; } @@ -793,6 +791,8 @@ public: SharedBuffer<u8> addSpiltPacket(u8 channel, BufferedPacket toadd, bool reliable); + + protected: /* Calculates avg_rtt and resend_timeout. @@ -813,6 +813,7 @@ protected: bool Ping(float dtime,SharedBuffer<u8>& data); Channel channels[CHANNEL_COUNT]; + bool m_pending_disconnect; private: // This is changed dynamically float resend_timeout; @@ -1002,13 +1003,12 @@ public: u32 Receive(u16 &peer_id, SharedBuffer<u8> &data); void SendToAll(u8 channelnum, SharedBuffer<u8> data, bool reliable); void Send(u16 peer_id, u8 channelnum, SharedBuffer<u8> data, bool reliable); - void RunTimeouts(float dtime); // dummy u16 GetPeerID(){ return m_peer_id; } Address GetPeerAddress(u16 peer_id); float GetPeerAvgRTT(u16 peer_id); - void DeletePeer(u16 peer_id); const u32 GetProtocolID() const { return m_protocol_id; }; const std::string getDesc(); + void DisconnectPeer(u16 peer_id); protected: PeerHelper getPeer(u16 peer_id); @@ -1033,6 +1033,8 @@ protected: void putEvent(ConnectionEvent &e); + void TriggerSend() + { m_sendThread.Trigger(); } private: std::list<Peer*> getPeers(); @@ -1054,6 +1056,8 @@ private: int m_bc_receive_timeout; bool m_shutting_down; + + u16 m_next_remote_peer_id; }; } // namespace diff --git a/src/emerge.cpp b/src/emerge.cpp index bd9b7c7bd..f63bc5dfe 100644 --- a/src/emerge.cpp +++ b/src/emerge.cpp @@ -592,23 +592,12 @@ void *EmergeThread::Thread() { /* Set sent status of modified blocks on clients */ - - // NOTE: Server's clients are also behind the connection mutex - //conlock: consistently takes 30-40ms to acquire - JMutexAutoLock lock(m_server->m_con_mutex); // Add the originally fetched block to the modified list if (block) modified_blocks[p] = block; - // Set the modified blocks unsent for all the clients - for (std::map<u16, RemoteClient*>::iterator - i = m_server->m_clients.begin(); - i != m_server->m_clients.end(); ++i) { - RemoteClient *client = i->second; - if (modified_blocks.size() > 0) { - // Remove block from sent history - client->SetBlocksNotSent(modified_blocks); - } + if (modified_blocks.size() > 0) { + m_server->SetBlocksNotSent(modified_blocks); } } catch (VersionMismatchException &e) { diff --git a/src/server.cpp b/src/server.cpp index 9fb0455eb..ce0b44110 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -104,13 +104,10 @@ void * ServerThread::Thread() try{ //TimeTaker timer("AsyncRunStep() + Receive()"); - { - //TimeTaker timer("AsyncRunStep()"); - m_server->AsyncRunStep(); - } + m_server->AsyncRunStep(); - //infostream<<"Running m_server->Receive()"<<std::endl; m_server->Receive(); + } catch(con::NoIncomingDataException &e) { @@ -158,481 +155,7 @@ v3f ServerSoundParams::getPos(ServerEnvironment *env, bool *pos_exists) const return v3f(0,0,0); } -void RemoteClient::GetNextBlocks(Server *server, float dtime, - std::vector<PrioritySortedBlockTransfer> &dest) -{ - DSTACK(__FUNCTION_NAME); - - /*u32 timer_result; - TimeTaker timer("RemoteClient::GetNextBlocks", &timer_result);*/ - - // Increment timers - m_nothing_to_send_pause_timer -= dtime; - m_nearest_unsent_reset_timer += dtime; - - if(m_nothing_to_send_pause_timer >= 0) - return; - - Player *player = server->m_env->getPlayer(peer_id); - // This can happen sometimes; clients and players are not in perfect sync. - if(player == NULL) - return; - - // Won't send anything if already sending - if(m_blocks_sending.size() >= g_settings->getU16 - ("max_simultaneous_block_sends_per_client")) - { - //infostream<<"Not sending any blocks, Queue full."<<std::endl; - return; - } - - //TimeTaker timer("RemoteClient::GetNextBlocks"); - - v3f playerpos = player->getPosition(); - v3f playerspeed = player->getSpeed(); - v3f playerspeeddir(0,0,0); - if(playerspeed.getLength() > 1.0*BS) - playerspeeddir = playerspeed / playerspeed.getLength(); - // Predict to next block - v3f playerpos_predicted = playerpos + playerspeeddir*MAP_BLOCKSIZE*BS; - - v3s16 center_nodepos = floatToInt(playerpos_predicted, BS); - - v3s16 center = getNodeBlockPos(center_nodepos); - - // Camera position and direction - v3f camera_pos = player->getEyePosition(); - v3f camera_dir = v3f(0,0,1); - camera_dir.rotateYZBy(player->getPitch()); - camera_dir.rotateXZBy(player->getYaw()); - - /*infostream<<"camera_dir=("<<camera_dir.X<<","<<camera_dir.Y<<"," - <<camera_dir.Z<<")"<<std::endl;*/ - /* - Get the starting value of the block finder radius. - */ - - if(m_last_center != center) - { - m_nearest_unsent_d = 0; - m_last_center = center; - } - - /*infostream<<"m_nearest_unsent_reset_timer=" - <<m_nearest_unsent_reset_timer<<std::endl;*/ - - // Reset periodically to workaround for some bugs or stuff - if(m_nearest_unsent_reset_timer > 20.0) - { - m_nearest_unsent_reset_timer = 0; - m_nearest_unsent_d = 0; - //infostream<<"Resetting m_nearest_unsent_d for " - // <<server->getPlayerName(peer_id)<<std::endl; - } - - //s16 last_nearest_unsent_d = m_nearest_unsent_d; - s16 d_start = m_nearest_unsent_d; - - //infostream<<"d_start="<<d_start<<std::endl; - - u16 max_simul_sends_setting = g_settings->getU16 - ("max_simultaneous_block_sends_per_client"); - u16 max_simul_sends_usually = max_simul_sends_setting; - - /* - Check the time from last addNode/removeNode. - - Decrease send rate if player is building stuff. - */ - m_time_from_building += dtime; - if(m_time_from_building < g_settings->getFloat( - "full_block_send_enable_min_time_from_building")) - { - max_simul_sends_usually - = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS; - } - - /* - Number of blocks sending + number of blocks selected for sending - */ - u32 num_blocks_selected = m_blocks_sending.size(); - - /* - next time d will be continued from the d from which the nearest - unsent block was found this time. - - This is because not necessarily any of the blocks found this - time are actually sent. - */ - s32 new_nearest_unsent_d = -1; - - s16 d_max = g_settings->getS16("max_block_send_distance"); - s16 d_max_gen = g_settings->getS16("max_block_generate_distance"); - - // Don't loop very much at a time - s16 max_d_increment_at_time = 2; - if(d_max > d_start + max_d_increment_at_time) - d_max = d_start + max_d_increment_at_time; - /*if(d_max_gen > d_start+2) - d_max_gen = d_start+2;*/ - - //infostream<<"Starting from "<<d_start<<std::endl; - - s32 nearest_emerged_d = -1; - s32 nearest_emergefull_d = -1; - s32 nearest_sent_d = -1; - bool queue_is_full = false; - - s16 d; - for(d = d_start; d <= d_max; d++) - { - /*errorstream<<"checking d="<<d<<" for " - <<server->getPlayerName(peer_id)<<std::endl;*/ - //infostream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl; - - /* - If m_nearest_unsent_d was changed by the EmergeThread - (it can change it to 0 through SetBlockNotSent), - update our d to it. - Else update m_nearest_unsent_d - */ - /*if(m_nearest_unsent_d != last_nearest_unsent_d) - { - d = m_nearest_unsent_d; - last_nearest_unsent_d = m_nearest_unsent_d; - }*/ - - /* - Get the border/face dot coordinates of a "d-radiused" - box - */ - std::list<v3s16> list; - getFacePositions(list, d); - - std::list<v3s16>::iterator li; - for(li=list.begin(); li!=list.end(); ++li) - { - v3s16 p = *li + center; - - /* - Send throttling - - Don't allow too many simultaneous transfers - - EXCEPT when the blocks are very close - - Also, don't send blocks that are already flying. - */ - - // Start with the usual maximum - u16 max_simul_dynamic = max_simul_sends_usually; - - // If block is very close, allow full maximum - if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D) - max_simul_dynamic = max_simul_sends_setting; - - // Don't select too many blocks for sending - if(num_blocks_selected >= max_simul_dynamic) - { - queue_is_full = true; - goto queue_full_break; - } - - // Don't send blocks that are currently being transferred - if(m_blocks_sending.find(p) != m_blocks_sending.end()) - continue; - - /* - Do not go over-limit - */ - if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE - || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE - || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE - || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE - || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE - || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE) - continue; - - // If this is true, inexistent block will be made from scratch - bool generate = d <= d_max_gen; - - { - /*// Limit the generating area vertically to 2/3 - if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3) - generate = false;*/ - - // Limit the send area vertically to 1/2 - if(abs(p.Y - center.Y) > d_max / 2) - continue; - } - -#if 0 - /* - If block is far away, don't generate it unless it is - near ground level. - */ - if(d >= 4) - { - #if 1 - // Block center y in nodes - f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2); - // Don't generate if it's very high or very low - if(y < -64 || y > 64) - generate = false; - #endif - #if 0 - v2s16 p2d_nodes_center( - MAP_BLOCKSIZE*p.X, - MAP_BLOCKSIZE*p.Z); - - // Get ground height in nodes - s16 gh = server->m_env->getServerMap().findGroundLevel( - p2d_nodes_center); - - // If differs a lot, don't generate - if(fabs(gh - y) > MAP_BLOCKSIZE*2) - generate = false; - // Actually, don't even send it - //continue; - #endif - } -#endif - - //infostream<<"d="<<d<<std::endl; -#if 1 - /* - Don't generate or send if not in sight - FIXME This only works if the client uses a small enough - FOV setting. The default of 72 degrees is fine. - */ - - float camera_fov = (72.0*M_PI/180) * 4./3.; - if(isBlockInSight(p, camera_pos, camera_dir, camera_fov, 10000*BS) == false) - { - continue; - } -#endif - /* - Don't send already sent blocks - */ - { - if(m_blocks_sent.find(p) != m_blocks_sent.end()) - { - continue; - } - } - - /* - Check if map has this block - */ - MapBlock *block = server->m_env->getMap().getBlockNoCreateNoEx(p); - - bool surely_not_found_on_disk = false; - bool block_is_invalid = false; - if(block != NULL) - { - // Reset usage timer, this block will be of use in the future. - block->resetUsageTimer(); - - // Block is dummy if data doesn't exist. - // It means it has been not found from disk and not generated - if(block->isDummy()) - { - surely_not_found_on_disk = true; - } - - // Block is valid if lighting is up-to-date and data exists - if(block->isValid() == false) - { - block_is_invalid = true; - } - - /*if(block->isFullyGenerated() == false) - { - block_is_invalid = true; - }*/ - -#if 0 - v2s16 p2d(p.X, p.Z); - ServerMap *map = (ServerMap*)(&server->m_env->getMap()); - v2s16 chunkpos = map->sector_to_chunk(p2d); - if(map->chunkNonVolatile(chunkpos) == false) - block_is_invalid = true; -#endif - if(block->isGenerated() == false) - block_is_invalid = true; -#if 1 - /* - If block is not close, don't send it unless it is near - ground level. - - Block is near ground level if night-time mesh - differs from day-time mesh. - */ - if(d >= 4) - { - if(block->getDayNightDiff() == false) - continue; - } -#endif - } - - /* - If block has been marked to not exist on disk (dummy) - and generating new ones is not wanted, skip block. - */ - if(generate == false && surely_not_found_on_disk == true) - { - // get next one. - continue; - } - - /* - Add inexistent block to emerge queue. - */ - if(block == NULL || surely_not_found_on_disk || block_is_invalid) - { - /* //TODO: Get value from somewhere - // Allow only one block in emerge queue - //if(server->m_emerge_queue.peerItemCount(peer_id) < 1) - // Allow two blocks in queue per client - //if(server->m_emerge_queue.peerItemCount(peer_id) < 2) - u32 max_emerge = 5; - // Make it more responsive when needing to generate stuff - if(surely_not_found_on_disk) - max_emerge = 1; - if(server->m_emerge_queue.peerItemCount(peer_id) < max_emerge) - { - //infostream<<"Adding block to emerge queue"<<std::endl; - - // Add it to the emerge queue and trigger the thread - - u8 flags = 0; - if(generate == false) - flags |= BLOCK_EMERGE_FLAG_FROMDISK; - - server->m_emerge_queue.addBlock(peer_id, p, flags); - server->m_emergethread.trigger(); - - if(nearest_emerged_d == -1) - nearest_emerged_d = d; - } else { - if(nearest_emergefull_d == -1) - nearest_emergefull_d = d; - goto queue_full_break; - } - */ - - if (server->m_emerge->enqueueBlockEmerge(peer_id, p, generate)) { - if (nearest_emerged_d == -1) - nearest_emerged_d = d; - } else { - if (nearest_emergefull_d == -1) - nearest_emergefull_d = d; - goto queue_full_break; - } - - // get next one. - continue; - } - - if(nearest_sent_d == -1) - nearest_sent_d = d; - - /* - Add block to send queue - */ - - /*errorstream<<"sending from d="<<d<<" to " - <<server->getPlayerName(peer_id)<<std::endl;*/ - - PrioritySortedBlockTransfer q((float)d, p, peer_id); - - dest.push_back(q); - - num_blocks_selected += 1; - } - } -queue_full_break: - - //infostream<<"Stopped at "<<d<<std::endl; - - // If nothing was found for sending and nothing was queued for - // emerging, continue next time browsing from here - if(nearest_emerged_d != -1){ - new_nearest_unsent_d = nearest_emerged_d; - } else if(nearest_emergefull_d != -1){ - new_nearest_unsent_d = nearest_emergefull_d; - } else { - if(d > g_settings->getS16("max_block_send_distance")){ - new_nearest_unsent_d = 0; - m_nothing_to_send_pause_timer = 2.0; - /*infostream<<"GetNextBlocks(): d wrapped around for " - <<server->getPlayerName(peer_id) - <<"; setting to 0 and pausing"<<std::endl;*/ - } else { - if(nearest_sent_d != -1) - new_nearest_unsent_d = nearest_sent_d; - else - new_nearest_unsent_d = d; - } - } - - if(new_nearest_unsent_d != -1) - m_nearest_unsent_d = new_nearest_unsent_d; - - /*timer_result = timer.stop(true); - if(timer_result != 0) - infostream<<"GetNextBlocks timeout: "<<timer_result<<" (!=0)"<<std::endl;*/ -} - -void RemoteClient::GotBlock(v3s16 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"<<std::endl;*/ - m_excess_gotblocks++; - } - m_blocks_sent.insert(p); -} - -void RemoteClient::SentBlock(v3s16 p) -{ - if(m_blocks_sending.find(p) == m_blocks_sending.end()) - m_blocks_sending[p] = 0.0; - else - infostream<<"RemoteClient::SentBlock(): Sent block" - " already in m_blocks_sending"<<std::endl; -} - -void RemoteClient::SetBlockNotSent(v3s16 p) -{ - m_nearest_unsent_d = 0; - - 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); -} - -void RemoteClient::SetBlocksNotSent(std::map<v3s16, MapBlock*> &blocks) -{ - m_nearest_unsent_d = 0; - - for(std::map<v3s16, MapBlock*>::iterator - i = blocks.begin(); - i != blocks.end(); ++i) - { - v3s16 p = i->first; - - 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); - } -} /* Server @@ -648,8 +171,11 @@ Server::Server( m_simple_singleplayer_mode(simple_singleplayer_mode), m_async_fatal_error(""), m_env(NULL), - m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, - g_settings->getBool("enable_ipv6") && g_settings->getBool("ipv6_server"), this), + m_con(PROTOCOL_ID, + 512, + CONNECTION_TIMEOUT, + g_settings->getBool("enable_ipv6") && g_settings->getBool("ipv6_server"), + this), m_banmanager(NULL), m_rollback(NULL), m_rollback_sink_enabled(true), @@ -663,9 +189,11 @@ Server::Server( m_thread(NULL), m_time_of_day_send_timer(0), m_uptime(0), + m_clients(&m_con), m_shutdown_requested(false), m_ignore_map_edit_events(false), m_ignore_map_edit_events_peer_id(0) + { m_liquid_transform_timer = 0.0; m_liquid_transform_every = 1.0; @@ -768,10 +296,8 @@ Server::Server( // Lock environment JMutexAutoLock envlock(m_env_mutex); - JMutexAutoLock conlock(m_con_mutex); // Initialize scripting - infostream<<"Server: Initializing Lua"<<std::endl; m_script = new GameScripting(this); @@ -818,11 +344,13 @@ Server::Server( // Initialize Environment ServerMap *servermap = new ServerMap(path_world, this, m_emerge); m_env = new ServerEnvironment(servermap, m_script, this, m_emerge); - + + m_clients.setEnv(m_env); + // 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); @@ -860,34 +388,12 @@ Server::~Server() Send shutdown message */ { - JMutexAutoLock conlock(m_con_mutex); - std::wstring line = L"*** Server shutting down"; - - /* - Send the message to clients - */ - for(std::map<u16, RemoteClient*>::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; - - try{ - SendChatMessage(client->peer_id, line); - } - catch(con::PeerNotFoundException &e) - {} - } + SendChatMessage(PEER_ID_INEXISTENT, line); } { JMutexAutoLock envlock(m_env_mutex); - JMutexAutoLock conlock(m_con_mutex); /* Execute script shutdown hooks @@ -921,22 +427,6 @@ Server::~Server() // requested blocks to be emerged m_emerge->stopThreads(); - /* - Delete clients - */ - { - JMutexAutoLock clientslock(m_con_mutex); - - for(std::map<u16, RemoteClient*>::iterator - i = m_clients.begin(); - i != m_clients.end(); ++i) - { - - // Delete client - delete i->second; - } - } - // Delete things in the reverse order of creation delete m_env; @@ -1061,18 +551,7 @@ void Server::AsyncRunStep(bool initial_step) m_uptime.set(m_uptime.get() + dtime); } - { - // Process connection's timeouts - JMutexAutoLock lock2(m_con_mutex); - ScopeProfiler sp(g_profiler, "Server: connection timeout processing"); - m_con.RunTimeouts(dtime); - } - - { - // This has to be called so that the client list gets synced - // with the peer list of the connection - handlePeerChanges(); - } + handlePeerChanges(); /* Update time of day and overall game time @@ -1090,20 +569,9 @@ void Server::AsyncRunStep(bool initial_step) if(m_time_of_day_send_timer < 0.0) { m_time_of_day_send_timer = g_settings->getFloat("time_send_interval"); - - //JMutexAutoLock envlock(m_env_mutex); - JMutexAutoLock conlock(m_con_mutex); - u16 time = m_env->getTimeOfDay(); float time_speed = g_settings->getFloat("time_speed"); - - for(std::map<u16, RemoteClient*>::iterator - i = m_clients.begin(); - i != m_clients.end(); ++i) - { - RemoteClient *client = i->second; - SendTimeOfDay(client->peer_id, time, time_speed); - } + SendTimeOfDay(PEER_ID_INEXISTENT, time, time_speed); } } @@ -1144,16 +612,16 @@ void Server::AsyncRunStep(bool initial_step) */ { JMutexAutoLock lock(m_env_mutex); - JMutexAutoLock lock2(m_con_mutex); + + std::list<u16> clientids = m_clients.getClientIDs(); ScopeProfiler sp(g_profiler, "Server: handle players"); - for(std::map<u16, RemoteClient*>::iterator - i = m_clients.begin(); - i != m_clients.end(); ++i) + for(std::list<u16>::iterator + i = clientids.begin(); + i != clientids.end(); ++i) { - RemoteClient *client = i->second; - PlayerSAO *playersao = getPlayerSAO(client->peer_id); + PlayerSAO *playersao = getPlayerSAO(*i); if(playersao == NULL) continue; @@ -1163,28 +631,28 @@ void Server::AsyncRunStep(bool initial_step) if(playersao->m_hp_not_sent && g_settings->getBool("enable_damage")) { if(playersao->getHP() == 0) - DiePlayer(client->peer_id); + DiePlayer(*i); else - SendPlayerHP(client->peer_id); + SendPlayerHP(*i); } /* Send player breath if changed */ if(playersao->m_breath_not_sent){ - SendPlayerBreath(client->peer_id); + SendPlayerBreath(*i); } /* Send player inventories if necessary */ if(playersao->m_moved){ - SendMovePlayer(client->peer_id); + SendMovePlayer(*i); playersao->m_moved = false; } if(playersao->m_inventory_not_sent){ - UpdateCrafting(client->peer_id); - SendInventory(client->peer_id); + UpdateCrafting(*i); + SendInventory(*i); } } } @@ -1221,77 +689,43 @@ void Server::AsyncRunStep(bool initial_step) /* Set the modified blocks unsent for all the clients */ - - JMutexAutoLock lock2(m_con_mutex); - - for(std::map<u16, RemoteClient*>::iterator - i = m_clients.begin(); - i != m_clients.end(); ++i) + if(modified_blocks.size() > 0) { - RemoteClient *client = i->second; - - if(modified_blocks.size() > 0) - { - // Remove block from sent history - client->SetBlocksNotSent(modified_blocks); - } + SetBlocksNotSent(modified_blocks); } } - - // Periodically print some info - { - float &counter = m_print_info_timer; - counter += dtime; - if(counter >= 30.0) - { - counter = 0.0; - - JMutexAutoLock lock2(m_con_mutex); - m_clients_names.clear(); - if(m_clients.size() != 0) - infostream<<"Players:"<<std::endl; - for(std::map<u16, RemoteClient*>::iterator - i = m_clients.begin(); - i != m_clients.end(); ++i) - { - //u16 peer_id = i.getNode()->getKey(); - RemoteClient *client = i->second; - Player *player = m_env->getPlayer(client->peer_id); - if(player==NULL) - continue; - infostream<<"* "<<player->getName()<<"\t"; - client->PrintInfo(infostream); - m_clients_names.push_back(player->getName()); - } - } - } - + m_clients.step(dtime); m_lag += (m_lag > dtime ? -1 : 1) * dtime/100; #if USE_CURL // send masterserver announce { float &counter = m_masterserver_timer; - if(!isSingleplayer() && (!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_names, m_uptime.get(), m_env->getGameTime(), m_lag, m_gamespec.id, m_mods); + ServerList::sendAnnounce(!counter ? "start" : "update", + m_clients.getPlayerNames(), + m_uptime.get(), + m_env->getGameTime(), + m_lag, + m_gamespec.id, + m_mods); counter = 0.01; } counter += dtime; } #endif - //if(g_settings->getBool("enable_experimental")) - { - /* Check added and deleted active objects */ { //infostream<<"Server: Checking added and deleted active objects"<<std::endl; JMutexAutoLock envlock(m_env_mutex); - JMutexAutoLock conlock(m_con_mutex); + m_clients.Lock(); + std::map<u16, RemoteClient*> clients = m_clients.getClientList(); ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs"); // Radius inside which objects are active @@ -1299,14 +733,14 @@ void Server::AsyncRunStep(bool initial_step) radius *= MAP_BLOCKSIZE; for(std::map<u16, RemoteClient*>::iterator - i = m_clients.begin(); - i != m_clients.end(); ++i) + i = clients.begin(); + i != clients.end(); ++i) { RemoteClient *client = i->second; // If definitions and textures have not been sent, don't // send objects either - if(!client->definitions_sent) + if (client->getState() < DefinitionsSent) continue; Player *player = m_env->getPlayer(client->peer_id); @@ -1404,14 +838,14 @@ void Server::AsyncRunStep(bool initial_step) memcpy((char*)&reply[2], data_buffer.c_str(), data_buffer.size()); // Send as reliable - m_con.Send(client->peer_id, 0, reply, true); + m_clients.send(client->peer_id, 0, reply, true); verbosestream<<"Server: Sent object remove/add: " <<removed_objects.size()<<" removed, " <<added_objects.size()<<" added, " <<"packet size is "<<reply.getSize()<<std::endl; } - + m_clients.Unlock(); #if 0 /* Collect a list of all the objects known by the clients @@ -1445,8 +879,6 @@ void Server::AsyncRunStep(bool initial_step) */ { JMutexAutoLock envlock(m_env_mutex); - JMutexAutoLock conlock(m_con_mutex); - ScopeProfiler sp(g_profiler, "Server: sending object messages"); // Key = object id @@ -1475,10 +907,12 @@ void Server::AsyncRunStep(bool initial_step) message_list->push_back(aom); } + m_clients.Lock(); + std::map<u16, RemoteClient*> clients = m_clients.getClientList(); // Route data to every client for(std::map<u16, RemoteClient*>::iterator - i = m_clients.begin(); - i != m_clients.end(); ++i) + i = clients.begin(); + i != clients.end(); ++i) { RemoteClient *client = i->second; std::string reliable_data; @@ -1525,7 +959,7 @@ void Server::AsyncRunStep(bool initial_step) memcpy((char*)&reply[2], reliable_data.c_str(), reliable_data.size()); // Send as reliable - m_con.Send(client->peer_id, 0, reply, true); + m_clients.send(client->peer_id, 0, reply, true); } if(unreliable_data.size() > 0) { @@ -1534,7 +968,7 @@ void Server::AsyncRunStep(bool initial_step) memcpy((char*)&reply[2], unreliable_data.c_str(), unreliable_data.size()); // Send as unreliable - m_con.Send(client->peer_id, 1, reply, false); + m_clients.send(client->peer_id, 1, reply, false); } /*if(reliable_data.size() > 0 || unreliable_data.size() > 0) @@ -1545,6 +979,7 @@ void Server::AsyncRunStep(bool initial_step) <<std::endl; }*/ } + m_clients.Unlock(); // Clear buffered_messages for(std::map<u16, std::list<ActiveObjectMessage>* >::iterator @@ -1555,15 +990,12 @@ void Server::AsyncRunStep(bool initial_step) } } - } // enable_experimental - /* Send queued-for-sending map edit events. */ { - // We will be accessing the environment and the connection + // We will be accessing the environment JMutexAutoLock lock(m_env_mutex); - JMutexAutoLock conlock(m_con_mutex); // Don't send too many at a time //u32 count = 0; @@ -1731,15 +1163,7 @@ void Server::Receive() u16 peer_id; u32 datasize; try{ - { - JMutexAutoLock conlock(m_con_mutex); - datasize = m_con.Receive(peer_id, data); - } - - // This has to be called so that the client list gets synced - // with the peer list of the connection - handlePeerChanges(); - + datasize = m_con.Receive(peer_id,data); ProcessData(*data, datasize, peer_id); } catch(con::InvalidIncomingDataException &e) @@ -1770,13 +1194,12 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) DSTACK(__FUNCTION_NAME); // Environment is locked first. JMutexAutoLock envlock(m_env_mutex); - JMutexAutoLock conlock(m_con_mutex); ScopeProfiler sp(g_profiler, "Server::ProcessData"); std::string addr_s; try{ - Address address = m_con.GetPeerAddress(peer_id); + Address address = getPeerAddress(peer_id); addr_s = address.serializeString(); // drop player if is ip is banned @@ -1788,19 +1211,16 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) // This actually doesn't seem to transfer to the client DenyAccess(peer_id, L"Your ip is banned. Banned name was " +narrow_to_wide(ban_name)); - m_con.DeletePeer(peer_id); return; } } catch(con::PeerNotFoundException &e) { - infostream<<"Server::ProcessData(): Cancelling: peer " + errorstream<<"Server::ProcessData(): Cancelling: peer " <<peer_id<<" not found"<<std::endl; return; } - u8 peer_ser_ver = getClient(peer_id)->serialization_version; - try { @@ -1819,8 +1239,11 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) if(datasize < 2+1+PLAYERNAME_SIZE) return; + RemoteClient* client = getClient(peer_id,Created); + // If net_proto_version is set, this client has already been handled - if(getClient(peer_id)->net_proto_version != 0){ + if(client->getState() > Created) + { verbosestream<<"Server: Ignoring multiple TOSERVER_INITs from " <<addr_s<<" (peer_id="<<peer_id<<")"<<std::endl; return; @@ -1830,8 +1253,8 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) <<peer_id<<")"<<std::endl; // Do not allow multiple players in simple singleplayer mode. - // This isn't a perfect way to do it, but will suffice for now. - if(m_simple_singleplayer_mode && m_clients.size() > 1){ + // This isn't a perfect way to do it, but will suffice for now + if(m_simple_singleplayer_mode && m_clients.getClientIDs().size() > 1){ infostream<<"Server: Not allowing another client ("<<addr_s <<") to connect in simple singleplayer mode"<<std::endl; DenyAccess(peer_id, L"Running in simple singleplayer mode."); @@ -1848,9 +1271,6 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) if(deployed < SER_FMT_VER_LOWEST) deployed = SER_FMT_VER_INVALID; - //peer->serialization_version = deployed; - getClient(peer_id)->pending_serialization_version = deployed; - if(deployed == SER_FMT_VER_INVALID) { actionstream<<"Server: A mismatched client tried to connect from " @@ -1865,6 +1285,8 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) return; } + client->setPendingSerializationVersion(deployed); + /* Read and check network protocol version */ @@ -1898,7 +1320,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) <<min_net_proto_version<<", max: "<<max_net_proto_version <<", chosen: "<<net_proto_version<<std::endl; - getClient(peer_id)->net_proto_version = net_proto_version; + client->net_proto_version = net_proto_version; if(net_proto_version < SERVER_PROTOCOL_VERSION_MIN || net_proto_version > SERVER_PROTOCOL_VERSION_MAX) @@ -2019,7 +1441,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) // Enforce user limit. // Don't enforce for users that have some admin right - if(m_clients.size() >= g_settings->getU16("max_users") && + if(m_clients.getClientIDs(Created).size() >= g_settings->getU16("max_users") && !checkPriv(playername, "server") && !checkPriv(playername, "ban") && !checkPriv(playername, "privs") && @@ -2075,28 +1497,19 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) return; } - // Get player - PlayerSAO *playersao = emergePlayer(playername, peer_id); + RemotePlayer *player = + static_cast<RemotePlayer*>(m_env->getPlayer(playername)); - // If failed, cancel - if(playersao == NULL) - { - RemotePlayer *player = - static_cast<RemotePlayer*>(m_env->getPlayer(playername)); - if(player && player->peer_id != 0){ - errorstream<<"Server: "<<playername<<": Failed to emerge player" - <<" (player allocated to an another client)"<<std::endl; - DenyAccess(peer_id, L"Another client is connected with this " - L"name. If your client closed unexpectedly, try again in " - L"a minute."); - } else { - errorstream<<"Server: "<<playername<<": Failed to emerge player" - <<std::endl; - DenyAccess(peer_id, L"Could not allocate player."); - } - return; + if(player && player->peer_id != 0){ + errorstream<<"Server: "<<playername<<": Failed to emerge player" + <<" (player allocated to an another client)"<<std::endl; + DenyAccess(peer_id, L"Another client is connected with this " + L"name. If your client closed unexpectedly, try again in " + L"a minute."); } + m_clients.setPlayerName(peer_id,playername); + /* Answer with a TOCLIENT_INIT */ @@ -2104,37 +1517,27 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) SharedBuffer<u8> reply(2+1+6+8+4); writeU16(&reply[0], TOCLIENT_INIT); writeU8(&reply[2], deployed); - writeV3S16(&reply[2+1], floatToInt(playersao->getPlayer()->getPosition()+v3f(0,BS/2,0), BS)); + //send dummy pos for legacy reasons only + writeV3S16(&reply[2+1], floatToInt(v3f(0,0,0), BS)); writeU64(&reply[2+1+6], m_env->getServerMap().getSeed()); writeF1000(&reply[2+1+6+8], g_settings->getFloat("dedicated_server_step")); // Send as reliable - m_con.Send(peer_id, 0, reply, true); + m_clients.send(peer_id, 0, reply, true); + m_clients.event(peer_id, Init); } - /* - Send complete position information - */ - SendMovePlayer(peer_id); - return; } if(command == TOSERVER_INIT2) { + verbosestream<<"Server: Got TOSERVER_INIT2 from " <<peer_id<<std::endl; - Player *player = m_env->getPlayer(peer_id); - if(!player){ - verbosestream<<"Server: TOSERVER_INIT2: " - <<"Player not found; ignoring."<<std::endl; - return; - } - - RemoteClient *client = getClient(peer_id); - client->serialization_version = - getClient(peer_id)->pending_serialization_version; + m_clients.event(peer_id, GotInit2); + u16 protocol_version = m_clients.getProtocolVersion(peer_id); /* Send some initialization data @@ -2144,17 +1547,105 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) <<getPlayerName(peer_id)<<std::endl; // Send player movement settings - SendMovement(m_con, peer_id); + SendMovement(peer_id); // Send item definitions - SendItemDef(m_con, peer_id, m_itemdef, client->net_proto_version); + SendItemDef(peer_id, m_itemdef, protocol_version); // Send node definitions - SendNodeDef(m_con, peer_id, m_nodedef, client->net_proto_version); + SendNodeDef(peer_id, m_nodedef, protocol_version); + + m_clients.event(peer_id, SetDefinitionsSent); // Send media announcement sendMediaAnnouncement(peer_id); + // Send detached inventories + sendDetachedInventories(peer_id); + + // Send time of day + u16 time = m_env->getTimeOfDay(); + float time_speed = g_settings->getFloat("time_speed"); + SendTimeOfDay(peer_id, time, time_speed); + + // Warnings about protocol version can be issued here + if(getClient(peer_id)->net_proto_version < LATEST_PROTOCOL_VERSION) + { + SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT'S " + L"VERSION MAY NOT BE FULLY COMPATIBLE WITH THIS SERVER!"); + } + + return; + } + + u8 peer_ser_ver = getClient(peer_id,InitDone)->serialization_version; + + if(peer_ser_ver == SER_FMT_VER_INVALID) + { + errorstream<<"Server::ProcessData(): Cancelling: Peer" + " serialization format invalid or not initialized." + " Skipping incoming command="<<command<<std::endl; + return; + } + + /* Handle commands relate to client startup */ + if(command == TOSERVER_REQUEST_MEDIA) { + std::string datastring((char*)&data[2], datasize-2); + std::istringstream is(datastring, std::ios_base::binary); + + std::list<std::string> tosend; + u16 numfiles = readU16(is); + + infostream<<"Sending "<<numfiles<<" files to " + <<getPlayerName(peer_id)<<std::endl; + verbosestream<<"TOSERVER_REQUEST_MEDIA: "<<std::endl; + + for(int i = 0; i < numfiles; i++) { + std::string name = deSerializeString(is); + tosend.push_back(name); + verbosestream<<"TOSERVER_REQUEST_MEDIA: requested file " + <<name<<std::endl; + } + + sendRequestedMedia(peer_id, tosend); + return; + } + else if(command == TOSERVER_RECEIVED_MEDIA) { + std::string playername = ""; + PlayerSAO *playersao = NULL; + m_clients.Lock(); + RemoteClient* client = m_clients.lockedGetClientNoEx(peer_id,DefinitionsSent); + if (client != NULL) { + playername = client->getName(); + playersao = emergePlayer(playername.c_str(), peer_id); + } + m_clients.Unlock(); + + RemotePlayer *player = + static_cast<RemotePlayer*>(m_env->getPlayer(playername.c_str())); + + // If failed, cancel + if((playersao == NULL) || (player == NULL)) + { + if(player && player->peer_id != 0){ + errorstream<<"Server: "<<playername<<": Failed to emerge player" + <<" (player allocated to an another client)"<<std::endl; + DenyAccess(peer_id, L"Another client is connected with this " + L"name. If your client closed unexpectedly, try again in " + L"a minute."); + } else { + errorstream<<"Server: "<<playername<<": Failed to emerge player" + <<std::endl; + DenyAccess(peer_id, L"Could not allocate player."); + } + return; + } + + /* + Send complete position information + */ + SendMovePlayer(peer_id); + // Send privileges SendPlayerPrivileges(peer_id); @@ -2172,19 +1663,9 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) // Send Breath SendPlayerBreath(peer_id); - // Send detached inventories - sendDetachedInventories(peer_id); - // Show death screen if necessary if(player->hp == 0) - SendDeathscreen(m_con, peer_id, false, v3f(0,0,0)); - - // Send time of day - { - u16 time = m_env->getTimeOfDay(); - float time_speed = g_settings->getFloat("time_speed"); - SendTimeOfDay(peer_id, time, time_speed); - } + SendDeathscreen(peer_id, false, v3f(0,0,0)); // Note things in chat if not in simple singleplayer mode if(!m_simple_singleplayer_mode) @@ -2203,56 +1684,74 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) message += L"*** "; message += name; message += L" joined the game."; - BroadcastChatMessage(message); + SendChatMessage(PEER_ID_INEXISTENT,message); } } - // Warnings about protocol version can be issued here - if(getClient(peer_id)->net_proto_version < LATEST_PROTOCOL_VERSION) - { - SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT'S " - L"VERSION MAY NOT BE FULLY COMPATIBLE WITH THIS SERVER!"); - } - + actionstream<<player->getName()<<" ["<<addr_s<<"] "<<"joins game. " << std::endl; /* Print out action */ { - std::ostringstream os(std::ios_base::binary); - for(std::map<u16, RemoteClient*>::iterator - i = m_clients.begin(); - i != m_clients.end(); ++i) + std::vector<std::string> names = m_clients.getPlayerNames(); + + actionstream<<player->getName()<<" ["<<addr_s<<"] " + <<"joins game. List of players: "; + + for (std::vector<std::string>::iterator i = names.begin(); + i != names.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<<player->getName()<<" "; + actionstream << *i << " "; } - actionstream<<player->getName()<<" ["<<addr_s<<"] "<<"joins game. List of players: " - <<os.str()<<std::endl; + actionstream<<std::endl; } + m_clients.event(peer_id,SetMediaSent); + m_script->on_joinplayer(playersao); return; } + else if(command == TOSERVER_GOTBLOCKS) + { + if(datasize < 2+1) + return; - if(peer_ser_ver == SER_FMT_VER_INVALID) + /* + [0] u16 command + [2] u8 count + [3] v3s16 pos_0 + [3+6] v3s16 pos_1 + ... + */ + + u16 count = data[2]; + for(u16 i=0; i<count; i++) + { + if((s16)datasize < 2+1+(i+1)*6) + throw con::InvalidIncomingDataException + ("GOTBLOCKS length is too short"); + v3s16 p = readV3S16(&data[2+1+i*6]); + /*infostream<<"Server: GOTBLOCKS (" + <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/ + RemoteClient *client = getClient(peer_id); + client->GotBlock(p); + } + return; + } + + if (m_clients.getClientState(peer_id) < Active) { - infostream<<"Server::ProcessData(): Cancelling: Peer" - " serialization format invalid or not initialized." - " Skipping incoming command="<<command<<std::endl; + if (command == TOSERVER_PLAYERPOS) return; + + errorstream<<"Got packet command: " << command << " for peer id " + << peer_id << " but client isn't active yet. Dropping packet " + <<std::endl; return; } Player *player = m_env->getPlayer(peer_id); if(player == NULL){ - infostream<<"Server::ProcessData(): Cancelling: " + errorstream<<"Server::ProcessData(): Cancelling: " "No player for peer_id="<<peer_id <<std::endl; return; @@ -2260,7 +1759,7 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) PlayerSAO *playersao = player->getPlayerSAO(); if(playersao == NULL){ - infostream<<"Server::ProcessData(): Cancelling: " + errorstream<<"Server::ProcessData(): Cancelling: " "No player object for peer_id="<<peer_id <<std::endl; return; @@ -2309,32 +1808,6 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")" <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/ } - else if(command == TOSERVER_GOTBLOCKS) - { - if(datasize < 2+1) - return; - - /* - [0] u16 command - [2] u8 count - [3] v3s16 pos_0 - [3+6] v3s16 pos_1 - ... - */ - - u16 count = data[2]; - for(u16 i=0; i<count; i++) - { - if((s16)datasize < 2+1+(i+1)*6) - throw con::InvalidIncomingDataException - ("GOTBLOCKS length is too short"); - v3s16 p = readV3S16(&data[2+1+i*6]); - /*infostream<<"Server: GOTBLOCKS (" - <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/ - RemoteClient *client = getClient(peer_id); - client->GotBlock(p); - } - } else if(command == TOSERVER_DELETEDBLOCKS) { if(datasize < 2+1) @@ -2577,16 +2050,14 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) // Line to send to players std::wstring line; // Whether to send to the player that sent the line - bool send_to_sender = false; - // Whether to send to other players - bool send_to_others = false; + bool send_to_sender_only = false; // Commands are implemented in Lua, so only catch invalid // commands that were not "eaten" and send an error back if(message[0] == L'/') { message = message.substr(1); - send_to_sender = true; + send_to_sender_only = true; if(message.length() == 0) line += L"-!- Empty command"; else @@ -2599,39 +2070,37 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) line += name; line += L"> "; line += message; - send_to_others = true; } else { line += L"-!- You don't have permission to shout."; - send_to_sender = true; + send_to_sender_only = true; } } if(line != L"") { - if(send_to_others) - actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl; - /* - Send the message to clients + Send the message to sender */ - for(std::map<u16, RemoteClient*>::iterator - i = m_clients.begin(); - i != m_clients.end(); ++i) + if (send_to_sender_only) { - // 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(peer_id, line); + } + /* + Send the message to others + */ + else + { + actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl; - // Filter recipient - bool sender_selected = (peer_id == client->peer_id); - if(sender_selected == true && send_to_sender == false) - continue; - if(sender_selected == false && send_to_others == false) - continue; + std::list<u16> clients = m_clients.getClientIDs(); - SendChatMessage(client->peer_id, line); + for(std::list<u16>::iterator + i = clients.begin(); + i != clients.end(); ++i) + { + if (*i != peer_id) + SendChatMessage(*i, line); + } } } } @@ -2748,33 +2217,6 @@ void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id) // ActiveObject is added to environment in AsyncRunStep after // the previous addition has been succesfully removed } - else if(command == TOSERVER_REQUEST_MEDIA) { - std::string datastring((char*)&data[2], datasize-2); - std::istringstream is(datastring, std::ios_base::binary); - - std::list<std::string> tosend; - u16 numfiles = readU16(is); - - infostream<<"Sending "<<numfiles<<" files to " - <<getPlayerName(peer_id)<<std::endl; - verbosestream<<"TOSERVER_REQUEST_MEDIA: "<<std::endl; - - for(int i = 0; i < numfiles; i++) { - std::string name = deSerializeString(is); - tosend.push_back(name); - verbosestream<<"TOSERVER_REQUEST_MEDIA: requested file " - <<name<<std::endl; - } - - sendRequestedMedia(peer_id, tosend); - - // Now the client should know about everything - // (definitions and files) - getClient(peer_id)->definitions_sent = true; - } - else if(command == TOSERVER_RECEIVED_MEDIA) { - getClient(peer_id)->definitions_sent = true; - } else if(command == TOSERVER_INTERACT) { std::string datastring((char*)&data[2], datasize-2); @@ -3313,7 +2755,7 @@ void Server::setInventoryModified(const InventoryLocation &loc) break; case InventoryLocation::DETACHED: { - sendDetachedInventoryToAll(loc.name); + sendDetachedInventory(loc.name,PEER_ID_INEXISTENT); } break; default: @@ -3321,14 +2763,29 @@ void Server::setInventoryModified(const InventoryLocation &loc) } } +void Server::SetBlocksNotSent(std::map<v3s16, MapBlock *>& block) +{ + std::list<u16> clients = m_clients.getClientIDs(); + m_clients.Lock(); + // Set the modified blocks unsent for all the clients + for (std::list<u16>::iterator + i = clients.begin(); + i != clients.end(); ++i) { + RemoteClient *client = m_clients.lockedGetClientNoEx(*i); + if (client != NULL) + client->SetBlocksNotSent(block); + } + m_clients.Unlock(); +} + void Server::peerAdded(con::Peer *peer) { DSTACK(__FUNCTION_NAME); verbosestream<<"Server::peerAdded(): peer->id=" <<peer->id<<std::endl; - PeerChange c; - c.type = PEER_ADDED; + con::PeerChange c; + c.type = con::PEER_ADDED; c.peer_id = peer->id; c.timeout = false; m_peer_change_queue.push_back(c); @@ -3340,18 +2797,42 @@ void Server::deletingPeer(con::Peer *peer, bool timeout) verbosestream<<"Server::deletingPeer(): peer->id=" <<peer->id<<", timeout="<<timeout<<std::endl; - PeerChange c; - c.type = PEER_REMOVED; + m_clients.event(peer->id,Disconnect); + con::PeerChange c; + c.type = con::PEER_REMOVED; c.peer_id = peer->id; c.timeout = timeout; m_peer_change_queue.push_back(c); } -/* - Static send methods -*/ +void Server::handlePeerChanges() +{ + while(m_peer_change_queue.size() > 0) + { + con::PeerChange c = m_peer_change_queue.pop_front(); + + verbosestream<<"Server: Handling peer change: " + <<"id="<<c.peer_id<<", timeout="<<c.timeout + <<std::endl; + + switch(c.type) + { + case con::PEER_ADDED: + m_clients.CreateClient(c.peer_id); + break; + + case con::PEER_REMOVED: + DeleteClient(c.peer_id, c.timeout?CDR_TIMEOUT:CDR_LEAVE); + break; -void Server::SendMovement(con::Connection &con, u16 peer_id) + default: + assert("Invalid peer change event received!" == 0); + break; + } + } +} + +void Server::SendMovement(u16 peer_id) { DSTACK(__FUNCTION_NAME); std::ostringstream os(std::ios_base::binary); @@ -3374,10 +2855,10 @@ void Server::SendMovement(con::Connection &con, u16 peer_id) std::string s = os.str(); SharedBuffer<u8> data((u8*)s.c_str(), s.size()); // Send as reliable - con.Send(peer_id, 0, data, true); + m_clients.send(peer_id, 0, data, true); } -void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp) +void Server::SendHP(u16 peer_id, u8 hp) { DSTACK(__FUNCTION_NAME); std::ostringstream os(std::ios_base::binary); @@ -3389,10 +2870,10 @@ void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp) std::string s = os.str(); SharedBuffer<u8> data((u8*)s.c_str(), s.size()); // Send as reliable - con.Send(peer_id, 0, data, true); + m_clients.send(peer_id, 0, data, true); } -void Server::SendBreath(con::Connection &con, u16 peer_id, u16 breath) +void Server::SendBreath(u16 peer_id, u16 breath) { DSTACK(__FUNCTION_NAME); std::ostringstream os(std::ios_base::binary); @@ -3404,11 +2885,10 @@ void Server::SendBreath(con::Connection &con, u16 peer_id, u16 breath) std::string s = os.str(); SharedBuffer<u8> data((u8*)s.c_str(), s.size()); // Send as reliable - con.Send(peer_id, 0, data, true); + m_clients.send(peer_id, 0, data, true); } -void Server::SendAccessDenied(con::Connection &con, u16 peer_id, - const std::wstring &reason) +void Server::SendAccessDenied(u16 peer_id,const std::wstring &reason) { DSTACK(__FUNCTION_NAME); std::ostringstream os(std::ios_base::binary); @@ -3420,11 +2900,11 @@ void Server::SendAccessDenied(con::Connection &con, u16 peer_id, std::string s = os.str(); SharedBuffer<u8> data((u8*)s.c_str(), s.size()); // Send as reliable - con.Send(peer_id, 0, data, true); + m_clients.send(peer_id, 0, data, true); } -void Server::SendDeathscreen(con::Connection &con, u16 peer_id, - bool set_camera_point_target, v3f camera_point_target) +void Server::SendDeathscreen(u16 peer_id,bool set_camera_point_target, + v3f camera_point_target) { DSTACK(__FUNCTION_NAME); std::ostringstream os(std::ios_base::binary); @@ -3437,10 +2917,10 @@ void Server::SendDeathscreen(con::Connection &con, u16 peer_id, std::string s = os.str(); SharedBuffer<u8> data((u8*)s.c_str(), s.size()); // Send as reliable - con.Send(peer_id, 0, data, true); + m_clients.send(peer_id, 0, data, true); } -void Server::SendItemDef(con::Connection &con, u16 peer_id, +void Server::SendItemDef(u16 peer_id, IItemDefManager *itemdef, u16 protocol_version) { DSTACK(__FUNCTION_NAME); @@ -3464,10 +2944,10 @@ void Server::SendItemDef(con::Connection &con, u16 peer_id, <<"): size="<<s.size()<<std::endl; SharedBuffer<u8> data((u8*)s.c_str(), s.size()); // Send as reliable - con.Send(peer_id, 0, data, true); + m_clients.send(peer_id, 0, data, true); } -void Server::SendNodeDef(con::Connection &con, u16 peer_id, +void Server::SendNodeDef(u16 peer_id, INodeDefManager *nodedef, u16 protocol_version) { DSTACK(__FUNCTION_NAME); @@ -3491,7 +2971,7 @@ void Server::SendNodeDef(con::Connection &con, u16 peer_id, <<"): size="<<s.size()<<std::endl; SharedBuffer<u8> data((u8*)s.c_str(), s.size()); // Send as reliable - con.Send(peer_id, 0, data, true); + m_clients.send(peer_id, 0, data, true); } /* @@ -3521,7 +3001,7 @@ void Server::SendInventory(u16 peer_id) memcpy(&data[2], s.c_str(), s.size()); // Send as reliable - m_con.Send(peer_id, 0, data, true); + m_clients.send(peer_id, 0, data, true); } void Server::SendChatMessage(u16 peer_id, const std::wstring &message) @@ -3550,8 +3030,16 @@ void Server::SendChatMessage(u16 peer_id, const std::wstring &message) // Make data buffer std::string s = os.str(); SharedBuffer<u8> data((u8*)s.c_str(), s.size()); - // Send as reliable - m_con.Send(peer_id, 0, data, true); + + if (peer_id != PEER_ID_INEXISTENT) + { + // Send as reliable + m_clients.send(peer_id, 0, data, true); + } + else + { + m_clients.sendToAll(0,data,true); + } } void Server::SendShowFormspecMessage(u16 peer_id, const std::string formspec, @@ -3572,7 +3060,7 @@ void Server::SendShowFormspecMessage(u16 peer_id, const std::string formspec, std::string s = os.str(); SharedBuffer<u8> data((u8*)s.c_str(), s.size()); // Send as reliable - m_con.Send(peer_id, 0, data, true); + m_clients.send(peer_id, 0, data, true); } // Spawns a particle on peer with peer_id @@ -3596,27 +3084,15 @@ void Server::SendSpawnParticle(u16 peer_id, v3f pos, v3f velocity, v3f accelerat // Make data buffer std::string s = os.str(); SharedBuffer<u8> 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, - bool vertical, std::string texture) -{ - for(std::map<u16, RemoteClient*>::iterator - i = m_clients.begin(); - i != m_clients.end(); i++) + if (peer_id != PEER_ID_INEXISTENT) { - // 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; - - SendSpawnParticle(client->peer_id, pos, velocity, acceleration, - expirationtime, size, collisiondetection, vertical, texture); + // Send as reliable + m_clients.send(peer_id, 0, data, true); + } + else + { + m_clients.sendToAll(0,data,true); } } @@ -3650,28 +3126,14 @@ void Server::SendAddParticleSpawner(u16 peer_id, u16 amount, float spawntime, v3 // Make data buffer std::string s = os.str(); SharedBuffer<u8> 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, bool vertical, std::string texture, u32 id) -{ - for(std::map<u16, RemoteClient*>::iterator - i = m_clients.begin(); - i != m_clients.end(); i++) + if (peer_id != PEER_ID_INEXISTENT) { - // 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, vertical, texture, id); + // Send as reliable + m_clients.send(peer_id, 0, data, true); + } + else { + m_clients.sendToAll(0,data,true); } } @@ -3687,24 +3149,15 @@ void Server::SendDeleteParticleSpawner(u16 peer_id, u32 id) // Make data buffer std::string s = os.str(); SharedBuffer<u8> 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<u16, RemoteClient*>::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); + if (peer_id != PEER_ID_INEXISTENT) { + // Send as reliable + m_clients.send(peer_id, 0, data, true); + } + else { + m_clients.sendToAll(0,data,true); } + } void Server::SendHUDAdd(u16 peer_id, u32 id, HudElement *form) @@ -3730,7 +3183,7 @@ void Server::SendHUDAdd(u16 peer_id, u32 id, HudElement *form) std::string s = os.str(); SharedBuffer<u8> data((u8*)s.c_str(), s.size()); // Send as reliable - m_con.Send(peer_id, 1, data, true); + m_clients.send(peer_id, 1, data, true); } void Server::SendHUDRemove(u16 peer_id, u32 id) @@ -3746,7 +3199,7 @@ void Server::SendHUDRemove(u16 peer_id, u32 id) SharedBuffer<u8> data((u8*)s.c_str(), s.size()); // Send as reliable - m_con.Send(peer_id, 1, data, true); + m_clients.send(peer_id, 1, data, true); } void Server::SendHUDChange(u16 peer_id, u32 id, HudElementStat stat, void *value) @@ -3783,7 +3236,7 @@ void Server::SendHUDChange(u16 peer_id, u32 id, HudElementStat stat, void *value std::string s = os.str(); SharedBuffer<u8> data((u8 *)s.c_str(), s.size()); // Send as reliable - m_con.Send(peer_id, 0, data, true); + m_clients.send(peer_id, 0, data, true); } void Server::SendHUDSetFlags(u16 peer_id, u32 flags, u32 mask) @@ -3799,7 +3252,7 @@ void Server::SendHUDSetFlags(u16 peer_id, u32 flags, u32 mask) std::string s = os.str(); SharedBuffer<u8> data((u8 *)s.c_str(), s.size()); // Send as reliable - m_con.Send(peer_id, 0, data, true); + m_clients.send(peer_id, 0, data, true); } void Server::SendHUDSetParam(u16 peer_id, u16 param, const std::string &value) @@ -3815,23 +3268,7 @@ void Server::SendHUDSetParam(u16 peer_id, u16 param, const std::string &value) std::string s = os.str(); SharedBuffer<u8> 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<u16, RemoteClient*>::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); - } + m_clients.send(peer_id, 0, data, true); } void Server::SendTimeOfDay(u16 peer_id, u16 time, f32 time_speed) @@ -3844,8 +3281,13 @@ void Server::SendTimeOfDay(u16 peer_id, u16 time, f32 time_speed) writeU16(&data[2], time); writeF1000(&data[4], time_speed); - // Send as reliable - m_con.Send(peer_id, 0, data, true); + if (peer_id == PEER_ID_INEXISTENT) { + m_clients.sendToAll(0,data,true); + } + else { + // Send as reliable + m_clients.send(peer_id, 0, data, true); + } } void Server::SendPlayerHP(u16 peer_id) @@ -3854,7 +3296,7 @@ void Server::SendPlayerHP(u16 peer_id) PlayerSAO *playersao = getPlayerSAO(peer_id); assert(playersao); playersao->m_hp_not_sent = false; - SendHP(m_con, peer_id, playersao->getHP()); + SendHP(peer_id, playersao->getHP()); // Send to other clients std::string str = gob_cmd_punched(playersao->readDamage(), playersao->getHP()); @@ -3868,7 +3310,7 @@ void Server::SendPlayerBreath(u16 peer_id) PlayerSAO *playersao = getPlayerSAO(peer_id); assert(playersao); playersao->m_breath_not_sent = false; - SendBreath(m_con, peer_id, playersao->getBreath()); + SendBreath(peer_id, playersao->getBreath()); } void Server::SendMovePlayer(u16 peer_id) @@ -3898,7 +3340,7 @@ void Server::SendMovePlayer(u16 peer_id) std::string s = os.str(); SharedBuffer<u8> data((u8*)s.c_str(), s.size()); // Send as reliable - m_con.Send(peer_id, 0, data, true); + m_clients.send(peer_id, 0, data, true); } void Server::SendPlayerPrivileges(u16 peer_id) @@ -3923,7 +3365,7 @@ void Server::SendPlayerPrivileges(u16 peer_id) std::string s = os.str(); SharedBuffer<u8> data((u8*)s.c_str(), s.size()); // Send as reliable - m_con.Send(peer_id, 0, data, true); + m_clients.send(peer_id, 0, data, true); } void Server::SendPlayerInventoryFormspec(u16 peer_id) @@ -3941,7 +3383,7 @@ void Server::SendPlayerInventoryFormspec(u16 peer_id) std::string s = os.str(); SharedBuffer<u8> data((u8*)s.c_str(), s.size()); // Send as reliable - m_con.Send(peer_id, 0, data, true); + m_clients.send(peer_id, 0, data, true); } s32 Server::playSound(const SimpleSoundSpec &spec, @@ -3953,8 +3395,9 @@ s32 Server::playSound(const SimpleSoundSpec &spec, // If position is not found while it should be, cancel sound if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL)) return -1; + // Filter destination clients - std::set<RemoteClient*> dst_clients; + std::list<u16> dst_clients; if(params.to_player != "") { Player *player = m_env->getPlayer(params.to_player.c_str()); @@ -3968,16 +3411,16 @@ s32 Server::playSound(const SimpleSoundSpec &spec, <<"\" not connected"<<std::endl; return -1; } - RemoteClient *client = getClient(player->peer_id); - dst_clients.insert(client); + dst_clients.push_back(player->peer_id); } else { - for(std::map<u16, RemoteClient*>::iterator - i = m_clients.begin(); i != m_clients.end(); ++i) + std::list<u16> clients = m_clients.getClientIDs(); + + for(std::list<u16>::iterator + i = clients.begin(); i != clients.end(); ++i) { - RemoteClient *client = i->second; - Player *player = m_env->getPlayer(client->peer_id); + Player *player = m_env->getPlayer(*i); if(!player) continue; if(pos_exists){ @@ -3985,20 +3428,21 @@ s32 Server::playSound(const SimpleSoundSpec &spec, params.max_hear_distance) continue; } - dst_clients.insert(client); + dst_clients.push_back(*i); } } if(dst_clients.size() == 0) return -1; + // Create the sound s32 id = m_next_sound_id++; // The sound will exist as a reference in m_playing_sounds m_playing_sounds[id] = ServerPlayingSound(); ServerPlayingSound &psound = m_playing_sounds[id]; psound.params = params; - for(std::set<RemoteClient*>::iterator i = dst_clients.begin(); + for(std::list<u16>::iterator i = dst_clients.begin(); i != dst_clients.end(); i++) - psound.clients.insert((*i)->peer_id); + psound.clients.insert(*i); // Create packet std::ostringstream os(std::ios_base::binary); writeU16(os, TOCLIENT_PLAY_SOUND); @@ -4013,10 +3457,10 @@ s32 Server::playSound(const SimpleSoundSpec &spec, std::string s = os.str(); SharedBuffer<u8> data((u8*)s.c_str(), s.size()); // Send - for(std::set<RemoteClient*>::iterator i = dst_clients.begin(); + for(std::list<u16>::iterator i = dst_clients.begin(); i != dst_clients.end(); i++){ // Send as reliable - m_con.Send((*i)->peer_id, 0, data, true); + m_clients.send(*i, 0, data, true); } return id; } @@ -4039,7 +3483,7 @@ void Server::stopSound(s32 handle) for(std::set<u16>::iterator i = psound.clients.begin(); i != psound.clients.end(); i++){ // Send as reliable - m_con.Send(*i, 0, data, true); + m_clients.send(*i, 0, data, true); } // Remove sound reference m_playing_sounds.erase(i); @@ -4059,38 +3503,29 @@ void Server::sendRemoveNode(v3s16 p, u16 ignore_id, writeS16(&reply[4], p.Y); writeS16(&reply[6], p.Z); - for(std::map<u16, RemoteClient*>::iterator - i = m_clients.begin(); - i != m_clients.end(); ++i) + std::list<u16> clients = m_clients.getClientIDs(); + for(std::list<u16>::iterator + i = clients.begin(); + i != 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; - - // Don't send if it's the same one - if(client->peer_id == ignore_id) - continue; - if(far_players) { // Get player - Player *player = m_env->getPlayer(client->peer_id); + Player *player = m_env->getPlayer(*i); if(player) { // If player is far away, only set modified blocks not sent v3f player_pos = player->getPosition(); if(player_pos.getDistanceFrom(p_f) > maxd) { - far_players->push_back(client->peer_id); + far_players->push_back(*i); continue; } } } // Send as reliable - m_con.Send(client->peer_id, 0, reply, true); + m_clients.send(*i, 0, reply, true); } } @@ -4101,69 +3536,71 @@ void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id, float maxd = far_d_nodes*BS; v3f p_f = intToFloat(p, BS); - for(std::map<u16, RemoteClient*>::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; - - // Don't send if it's the same one - if(client->peer_id == ignore_id) - continue; + std::list<u16> clients = m_clients.getClientIDs(); + for(std::list<u16>::iterator + i = clients.begin(); + i != clients.end(); ++i) + { if(far_players) { // Get player - Player *player = m_env->getPlayer(client->peer_id); + Player *player = m_env->getPlayer(*i); if(player) { // If player is far away, only set modified blocks not sent v3f player_pos = player->getPosition(); if(player_pos.getDistanceFrom(p_f) > maxd) { - far_players->push_back(client->peer_id); + far_players->push_back(*i); continue; } } } - - // Create packet - u32 replysize = 9 + MapNode::serializedLength(client->serialization_version); - SharedBuffer<u8> 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); + SharedBuffer<u8> reply(0); + m_clients.Lock(); + RemoteClient* client = m_clients.lockedGetClientNoEx(*i); + if (client != 0) + { + // Create packet + u32 replysize = 9 + MapNode::serializedLength(client->serialization_version); + reply = SharedBuffer<u8>(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); + } } } + m_clients.Unlock(); // Send as reliable - m_con.Send(client->peer_id, 0, reply, true); + if (reply.getSize() > 0) + m_clients.send(*i, 0, reply, true); } } void Server::setBlockNotSent(v3s16 p) { - for(std::map<u16, RemoteClient*>::iterator - i = m_clients.begin(); - i != m_clients.end(); ++i) + std::list<u16> clients = m_clients.getClientIDs(); + m_clients.Lock(); + for(std::list<u16>::iterator + i = clients.begin(); + i != clients.end(); ++i) { - RemoteClient *client = i->second; + RemoteClient *client = m_clients.lockedGetClientNoEx(*i); client->SetBlockNotSent(p); } + m_clients.Unlock(); } void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver, u16 net_proto_version) @@ -4217,7 +3654,7 @@ void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver, u16 net_proto /* Send packet */ - m_con.Send(peer_id, 2, reply, true); + m_clients.send(peer_id, 2, reply, true); } void Server::SendBlocks(float dtime) @@ -4225,7 +3662,7 @@ void Server::SendBlocks(float dtime) DSTACK(__FUNCTION_NAME); JMutexAutoLock envlock(m_env_mutex); - JMutexAutoLock conlock(m_con_mutex); + //TODO check if one big lock could be faster then multiple small ones ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients"); @@ -4236,25 +3673,22 @@ void Server::SendBlocks(float dtime) { ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending"); - for(std::map<u16, RemoteClient*>::iterator - i = m_clients.begin(); - i != m_clients.end(); ++i) + std::list<u16> clients = m_clients.getClientIDs(); + + m_clients.Lock(); + for(std::list<u16>::iterator + i = clients.begin(); + i != clients.end(); ++i) { - RemoteClient *client = i->second; - assert(client->peer_id == i->first); + RemoteClient *client = m_clients.lockedGetClientNoEx(*i,Active); - // If definitions and textures have not been sent, don't - // send MapBlocks either - if(!client->definitions_sent) - continue; + if (client == NULL) + return; total_sending += client->SendingCount(); - - if(client->serialization_version == SER_FMT_VER_INVALID) - continue; - - client->GetNextBlocks(this, dtime, queue); + client->GetNextBlocks(m_env,m_emerge, dtime, queue); } + m_clients.Unlock(); } // Sort. @@ -4262,6 +3696,7 @@ void Server::SendBlocks(float dtime) // Lowest is most important. std::sort(queue.begin(), queue.end()); + m_clients.Lock(); for(u32 i=0; i<queue.size(); i++) { //TODO: Calculate limit dynamically @@ -4281,18 +3716,17 @@ void Server::SendBlocks(float dtime) continue; } - RemoteClient *client = getClientNoEx(q.peer_id); + RemoteClient *client = m_clients.lockedGetClientNoEx(q.peer_id,Active); + if(!client) continue; - if(client->denied) - continue; SendBlockNoLock(q.peer_id, block, client->serialization_version, client->net_proto_version); client->SentBlock(q.pos); - total_sending++; } + m_clients.Unlock(); } void Server::fillMediaCache() @@ -4449,7 +3883,7 @@ void Server::sendMediaAnnouncement(u16 peer_id) SharedBuffer<u8> data((u8*)s.c_str(), s.size()); // Send as reliable - m_con.Send(peer_id, 0, data, true); + m_clients.send(peer_id, 0, data, true); } struct SendableMedia @@ -4579,7 +4013,7 @@ void Server::sendRequestedMedia(u16 peer_id, <<" size=" <<s.size()<<std::endl; SharedBuffer<u8> data((u8*)s.c_str(), s.size()); // Send as reliable - m_con.Send(peer_id, 2, data, true); + m_clients.send(peer_id, 2, data, true); } } @@ -4599,19 +4033,15 @@ void Server::sendDetachedInventory(const std::string &name, u16 peer_id) // Make data buffer std::string s = os.str(); SharedBuffer<u8> data((u8*)s.c_str(), s.size()); - // Send as reliable - m_con.Send(peer_id, 0, data, true); -} -void Server::sendDetachedInventoryToAll(const std::string &name) -{ - DSTACK(__FUNCTION_NAME); - - for(std::map<u16, RemoteClient*>::iterator - i = m_clients.begin(); - i != m_clients.end(); ++i){ - RemoteClient *client = i->second; - sendDetachedInventory(name, client->peer_id); + if (peer_id != PEER_ID_INEXISTENT) + { + // Send as reliable + m_clients.send(peer_id, 0, data, true); + } + else + { + m_clients.sendToAll(0,data,true); } } @@ -4649,7 +4079,7 @@ void Server::DiePlayer(u16 peer_id) m_script->on_dieplayer(playersao); SendPlayerHP(peer_id); - SendDeathscreen(m_con, peer_id, false, v3f(0,0,0)); + SendDeathscreen(peer_id, false, v3f(0,0,0)); } void Server::RespawnPlayer(u16 peer_id) @@ -4676,132 +4106,93 @@ 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="<<peer_id<<" immediately"<<std::endl; - // Delete peer to stop sending it data - m_con.DeletePeer(peer_id); - // Delete client also to stop block sends and other stuff - DeleteClient(peer_id, CDR_DENY); - } + SendAccessDenied(peer_id, reason); + m_clients.event(peer_id,SetDenied); + m_con.DisconnectPeer(peer_id); } void Server::DeleteClient(u16 peer_id, ClientDeletionReason reason) { DSTACK(__FUNCTION_NAME); - - // Error check - std::map<u16, RemoteClient*>::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<u16>::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<s32, ServerPlayingSound>::iterator - i = m_playing_sounds.begin(); - i != m_playing_sounds.end();) + std::wstring message; { - ServerPlayingSound &psound = i->second; - psound.clients.erase(peer_id); - if(psound.clients.size() == 0) - m_playing_sounds.erase(i++); - else - i++; - } + /* + Clear references to playing sounds + */ + for(std::map<s32, ServerPlayingSound>::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); + Player *player = m_env->getPlayer(peer_id); - // Collect information about leaving in chat - std::wstring message; - { - if(player != NULL && reason != CDR_DENY) + // Collect information about leaving in chat { - 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)"; + 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) + /* Run scripts and remove from environment */ { - PlayerSAO *playersao = player->getPlayerSAO(); - assert(playersao); + if(player != NULL) + { + PlayerSAO *playersao = player->getPlayerSAO(); + assert(playersao); - m_script->on_leaveplayer(playersao); + m_script->on_leaveplayer(playersao); - playersao->disconnected(); + playersao->disconnected(); + } } - } - /* - Print out action - */ - { - if(player != NULL && reason != CDR_DENY) + /* + Print out action + */ { - std::ostringstream os(std::ios_base::binary); - for(std::map<u16, RemoteClient*>::iterator - i = m_clients.begin(); - i != m_clients.end(); ++i) + if(player != NULL && reason != CDR_DENY) { - 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<<player->getName()<<" "; - } + std::ostringstream os(std::ios_base::binary); + std::list<u16> clients = m_clients.getClientIDs(); + + for(std::list<u16>::iterator + i = clients.begin(); + i != clients.end(); ++i) + { + // Get player + Player *player = m_env->getPlayer(*i); + if(!player) + continue; + // Get name of player + os<<player->getName()<<" "; + } - actionstream<<player->getName()<<" " - <<(reason==CDR_TIMEOUT?"times out.":"leaves game.") - <<" List of players: "<<os.str()<<std::endl; + actionstream<<player->getName()<<" " + <<(reason==CDR_TIMEOUT?"times out.":"leaves game.") + <<" List of players: "<<os.str()<<std::endl; + } } + m_env_mutex.Lock(); + m_clients.DeleteClient(peer_id); + m_env_mutex.Unlock(); } - // Delete client - delete m_clients[peer_id]; - m_clients.erase(peer_id); - // Send leave chat message to all remaining clients if(message.length() != 0) - BroadcastChatMessage(message); + SendChatMessage(PEER_ID_INEXISTENT,message); } void Server::UpdateCrafting(u16 peer_id) @@ -4825,22 +4216,17 @@ void Server::UpdateCrafting(u16 peer_id) plist->changeItem(0, preview); } -RemoteClient* Server::getClient(u16 peer_id) +RemoteClient* Server::getClient(u16 peer_id, ClientState state_min) { - RemoteClient *client = getClientNoEx(peer_id); + RemoteClient *client = getClientNoEx(peer_id,state_min); if(!client) throw ClientNotFoundException("Client not found"); + return client; } -RemoteClient* Server::getClientNoEx(u16 peer_id) +RemoteClient* Server::getClientNoEx(u16 peer_id, ClientState state_min) { - std::map<u16, RemoteClient*>::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 NULL; - return n->second; + return m_clients.getClientNoEx(peer_id, state_min); } std::string Server::getPlayerName(u16 peer_id) @@ -4870,19 +4256,14 @@ std::wstring Server::getStatusString() // Max lag estimate os<<L", max_lag="<<m_env->getMaxLagEstimate(); // Information about clients - std::map<u16, RemoteClient*>::iterator i; - bool first; + bool first = true; os<<L", clients={"; - for(i = m_clients.begin(), first = true; - i != m_clients.end(); ++i) + std::list<u16> clients = m_clients.getClientIDs(); + for(std::list<u16>::iterator i = clients.begin(); + i != 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; // Get player - Player *player = m_env->getPlayer(client->peer_id); + Player *player = m_env->getPlayer(*i); // Get name of player std::wstring name = L"unknown"; if(player != NULL) @@ -4918,11 +4299,11 @@ bool Server::checkPriv(const std::string &name, const std::string &priv) void Server::reportPrivsModified(const std::string &name) { if(name == ""){ - for(std::map<u16, RemoteClient*>::iterator - i = m_clients.begin(); - i != m_clients.end(); ++i){ - RemoteClient *client = i->second; - Player *player = m_env->getPlayer(client->peer_id); + std::list<u16> clients = m_clients.getClientIDs(); + for(std::list<u16>::iterator + i = clients.begin(); + i != clients.end(); ++i){ + Player *player = m_env->getPlayer(*i); reportPrivsModified(player->getName()); } } else { @@ -5056,7 +4437,7 @@ void Server::hudSetHotbarSelectedImage(Player *player, std::string name) { void Server::notifyPlayers(const std::wstring msg) { - BroadcastChatMessage(msg); + SendChatMessage(PEER_ID_INEXISTENT,msg); } void Server::spawnParticle(const char *playername, v3f pos, @@ -5075,7 +4456,7 @@ void Server::spawnParticleAll(v3f pos, v3f velocity, v3f acceleration, float expirationtime, float size, bool collisiondetection, bool vertical, std::string texture) { - SendSpawnParticleAll(pos, velocity, acceleration, + SendSpawnParticle(PEER_ID_INEXISTENT,pos, velocity, acceleration, expirationtime, size, collisiondetection, vertical, texture); } @@ -5134,7 +4515,7 @@ u32 Server::addParticleSpawnerAll(u16 amount, float spawntime, } } - SendAddParticleSpawnerAll(amount, spawntime, + SendAddParticleSpawner(PEER_ID_INEXISTENT, amount, spawntime, minpos, maxpos, minvel, maxvel, minacc, maxacc, minexptime, maxexptime, minsize, maxsize, collisiondetection, vertical, texture, id); @@ -5161,7 +4542,7 @@ void Server::deleteParticleSpawnerAll(u32 id) std::remove(m_particlespawner_ids.begin(), m_particlespawner_ids.end(), id), m_particlespawner_ids.end()); - SendDeleteParticleSpawnerAll(id); + SendDeleteParticleSpawner(PEER_ID_INEXISTENT, id); } Inventory* Server::createDetachedInventory(const std::string &name) @@ -5175,7 +4556,8 @@ Inventory* Server::createDetachedInventory(const std::string &name) Inventory *inv = new Inventory(m_itemdef); assert(inv); m_detached_inventories[name] = inv; - sendDetachedInventoryToAll(name); + //TODO find a better way to do this + sendDetachedInventory(name,PEER_ID_INEXISTENT); return inv; } @@ -5444,63 +4826,9 @@ PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id) if(newplayer) m_script->on_newplayer(playersao); - m_script->on_joinplayer(playersao); - return playersao; } -void Server::handlePeerChange(PeerChange &c) -{ - JMutexAutoLock envlock(m_env_mutex); - JMutexAutoLock conlock(m_con_mutex); - - if(c.type == PEER_ADDED) - { - /* - Add - */ - - // Error check - std::map<u16, RemoteClient*>::iterator n; - n = m_clients.find(c.peer_id); - // The client shouldn't already exist - assert(n == m_clients.end()); - - // Create client - RemoteClient *client = new RemoteClient(); - client->peer_id = c.peer_id; - m_clients[client->peer_id] = client; - - } // PEER_ADDED - else if(c.type == PEER_REMOVED) - { - /* - Delete - */ - - DeleteClient(c.peer_id, c.timeout?CDR_TIMEOUT:CDR_LEAVE); - - } // PEER_REMOVED - else - { - assert(0); - } -} - -void Server::handlePeerChanges() -{ - while(m_peer_change_queue.size() > 0) - { - PeerChange c = m_peer_change_queue.pop_front(); - - verbosestream<<"Server: Handling peer change: " - <<"id="<<c.peer_id<<", timeout="<<c.timeout - <<std::endl; - - handlePeerChange(c); - } -} - void dedicated_server_loop(Server &server, bool &kill) { DSTACK(__FUNCTION_NAME); diff --git a/src/server.h b/src/server.h index 1163400f7..94b0b6a2d 100644 --- a/src/server.h +++ b/src/server.h @@ -33,6 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/numeric.h" #include "util/thread.h" #include "environment.h" +#include "clientiface.h" #include <string> #include <list> #include <map> @@ -53,14 +54,19 @@ class EmergeManager; class GameScripting; class ServerEnvironment; struct SimpleSoundSpec; +class ServerThread; +enum ClientDeletionReason { + CDR_LEAVE, + CDR_TIMEOUT, + CDR_DENY +}; /* Some random functions */ v3f findSpawnPos(ServerMap &map); - class MapEditEventIgnorer { public: @@ -111,31 +117,6 @@ private: VoxelArea *m_ignorevariable; }; -class Server; -class ServerThread; - -/* - Used for queueing and sorting block transfers in containers - - Lower priority number means higher priority. -*/ -struct PrioritySortedBlockTransfer -{ - PrioritySortedBlockTransfer(float a_priority, v3s16 a_pos, u16 a_peer_id) - { - priority = a_priority; - pos = a_pos; - peer_id = a_peer_id; - } - bool operator < (const PrioritySortedBlockTransfer &other) const - { - return priority < other.priority; - } - float priority; - v3s16 pos; - u16 peer_id; -}; - struct MediaInfo { std::string path; @@ -182,134 +163,6 @@ struct ServerPlayingSound std::set<u16> clients; // peer ids }; -class RemoteClient -{ -public: - // peer_id=0 means this client has no associated peer - // NOTE: If client is made allowed to exist while peer doesn't, - // this has to be set to 0 when there is no peer. - // Also, the client must be moved to some other container. - u16 peer_id; - // The serialization version to use with the client - u8 serialization_version; - // - u16 net_proto_version; - // Version is stored in here after INIT before INIT2 - u8 pending_serialization_version; - - bool definitions_sent; - - bool denied; - - RemoteClient(): - m_time_from_building(9999), - m_excess_gotblocks(0) - { - peer_id = 0; - serialization_version = SER_FMT_VER_INVALID; - net_proto_version = 0; - pending_serialization_version = SER_FMT_VER_INVALID; - definitions_sent = false; - denied = false; - m_nearest_unsent_d = 0; - m_nearest_unsent_reset_timer = 0.0; - m_nothing_to_send_counter = 0; - m_nothing_to_send_pause_timer = 0; - } - ~RemoteClient() - { - } - - /* - Finds block that should be sent next to the client. - Environment should be locked when this is called. - dtime is used for resetting send radius at slow interval - */ - void GetNextBlocks(Server *server, float dtime, - std::vector<PrioritySortedBlockTransfer> &dest); - - void GotBlock(v3s16 p); - - void SentBlock(v3s16 p); - - void SetBlockNotSent(v3s16 p); - void SetBlocksNotSent(std::map<v3s16, MapBlock*> &blocks); - - s32 SendingCount() - { - return m_blocks_sending.size(); - } - - // Increments timeouts and removes timed-out blocks from list - // NOTE: This doesn't fix the server-not-sending-block bug - // because it is related to emerging, not sending. - //void RunSendingTimeouts(float dtime, float timeout); - - void PrintInfo(std::ostream &o) - { - o<<"RemoteClient "<<peer_id<<": " - <<"m_blocks_sent.size()="<<m_blocks_sent.size() - <<", m_blocks_sending.size()="<<m_blocks_sending.size() - <<", m_nearest_unsent_d="<<m_nearest_unsent_d - <<", m_excess_gotblocks="<<m_excess_gotblocks - <<std::endl; - m_excess_gotblocks = 0; - } - - // Time from last placing or removing blocks - float m_time_from_building; - - /*JMutex m_dig_mutex; - float m_dig_time_remaining; - // -1 = not digging - s16 m_dig_tool_item; - v3s16 m_dig_position;*/ - - /* - List of active objects that the client knows of. - Value is dummy. - */ - std::set<u16> m_known_objects; - -private: - /* - Blocks that have been sent to client. - - These don't have to be sent again. - - A block is cleared from here when client says it has - deleted it from it's memory - - Key is position, value is dummy. - No MapBlock* is stored here because the blocks can get deleted. - */ - std::set<v3s16> m_blocks_sent; - s16 m_nearest_unsent_d; - v3s16 m_last_center; - float m_nearest_unsent_reset_timer; - - /* - Blocks that are currently on the line. - This is used for throttling the sending of blocks. - - The size of this list is limited to some value - Block is added when it is sent with BLOCKDATA. - Block is removed when GOTBLOCKS is received. - Value is time from sending. (not used at the moment) - */ - std::map<v3s16, float> m_blocks_sending; - - /* - Count of excess GotBlocks(). - There is an excess amount because the client sometimes - gets a block so late that the server sends it again, - and the client then sends two GOTBLOCKs. - This is resetted by PrintInfo() - */ - u32 m_excess_gotblocks; - - // CPU usage optimization - u32 m_nothing_to_send_counter; - float m_nothing_to_send_pause_timer; -}; - class Server : public con::PeerHandler, public MapEventReceiver, public InventoryManager, public IGameDef { @@ -337,11 +190,6 @@ public: // Environment must be locked when called void setTimeOfDay(u32 time); - bool getShutdownRequested() - { - return m_shutdown_requested; - } - /* Shall be called with the environment locked. This is accessed by the map, which is inside the environment, @@ -358,17 +206,20 @@ public: // Connection must be locked when called std::wstring getStatusString(); - void requestShutdown(void) - { - m_shutdown_requested = true; - } + // read shutdown state + inline bool getShutdownRequested() + { return m_shutdown_requested; } + + // request server to shutdown + inline void requestShutdown(void) + { m_shutdown_requested = true; } // Returns -1 if failed, sound handle on success - // Envlock + conlock + // Envlock s32 playSound(const SimpleSoundSpec &spec, const ServerSoundParams ¶ms); void stopSound(s32 handle); - // Envlock + conlock + // Envlock std::set<std::string> getPlayerEffectivePrivs(const std::string &name); bool checkPriv(const std::string &name, const std::string &priv); void reportPrivsModified(const std::string &name=""); // ""=all @@ -378,12 +229,6 @@ public: void unsetIpBanned(const std::string &ip_or_name); std::string getBanDescription(const std::string &ip_or_name); - Address getPeerAddress(u16 peer_id) - { - return m_con.GetPeerAddress(peer_id); - } - - // Envlock and conlock should be locked when calling this void notifyPlayer(const char *name, const std::wstring msg, const bool prepend); void notifyPlayers(const std::wstring msg); void spawnParticle(const char *playername, @@ -451,15 +296,14 @@ public: const ModSpec* getModSpec(const std::string &modname); void getModNames(std::list<std::string> &modlist); std::string getBuiltinLuaPath(); + inline std::string getWorldPath() + { return m_path_world; } - std::string getWorldPath(){ return m_path_world; } - - bool isSingleplayer(){ return m_simple_singleplayer_mode; } + inline bool isSingleplayer() + { return m_simple_singleplayer_mode; } - void setAsyncFatalError(const std::string &error) - { - m_async_fatal_error.set(error); - } + inline void setAsyncFatalError(const std::string &error) + { m_async_fatal_error.set(error); } bool showFormspec(const char *name, const std::string &formspec, const std::string &formname); Map & getMap() { return m_env->getMap(); } @@ -473,41 +317,32 @@ public: void hudSetHotbarImage(Player *player, std::string name); void hudSetHotbarSelectedImage(Player *player, std::string name); -private: + inline Address getPeerAddress(u16 peer_id) + { return m_con.GetPeerAddress(peer_id); } - // con::PeerHandler implementation. - // These queue stuff to be processed by handlePeerChanges(). - // As of now, these create and remove clients and players. + /* con::PeerHandler implementation. */ void peerAdded(con::Peer *peer); void deletingPeer(con::Peer *peer, bool timeout); - /* - Static send methods - */ +private: - static void SendMovement(con::Connection &con, u16 peer_id); - static void SendHP(con::Connection &con, u16 peer_id, u8 hp); - static void SendBreath(con::Connection &con, u16 peer_id, u16 breath); - static void SendAccessDenied(con::Connection &con, u16 peer_id, - const std::wstring &reason); - static void SendDeathscreen(con::Connection &con, u16 peer_id, - bool set_camera_point_target, v3f camera_point_target); - static void SendItemDef(con::Connection &con, u16 peer_id, - IItemDefManager *itemdef, u16 protocol_version); - static void SendNodeDef(con::Connection &con, u16 peer_id, - INodeDefManager *nodedef, u16 protocol_version); + friend class EmergeThread; + friend class RemoteClient; - /* - Non-static send methods. - Conlock should be always used. - Envlock usage is documented badly but it's easy to figure out - which ones access the environment. - */ + void SendMovement(u16 peer_id); + void SendHP(u16 peer_id, u8 hp); + void SendBreath(u16 peer_id, u16 breath); + void SendAccessDenied(u16 peer_id,const std::wstring &reason); + void SendDeathscreen(u16 peer_id,bool set_camera_point_target, v3f camera_point_target); + void SendItemDef(u16 peer_id,IItemDefManager *itemdef, u16 protocol_version); + void SendNodeDef(u16 peer_id,INodeDefManager *nodedef, u16 protocol_version); + + /* mark blocks not sent for all clients */ + void SetBlocksNotSent(std::map<v3s16, MapBlock *>& block); // Envlock and conlock should be locked when calling these void SendInventory(u16 peer_id); void SendChatMessage(u16 peer_id, const std::wstring &message); - void BroadcastChatMessage(const std::wstring &message); void SendTimeOfDay(u16 peer_id, u16 time, f32 time_speed); void SendPlayerHP(u16 peer_id); void SendPlayerBreath(u16 peer_id); @@ -546,10 +381,9 @@ private: const std::list<std::string> &tosend); void sendDetachedInventory(const std::string &name, u16 peer_id); - void sendDetachedInventoryToAll(const std::string &name); void sendDetachedInventories(u16 peer_id); - // Adds a ParticleSpawner on peer with peer_id + // Adds a ParticleSpawner on peer with peer_id (PEER_ID_INEXISTENT == all) void SendAddParticleSpawner(u16 peer_id, u16 amount, float spawntime, v3f minpos, v3f maxpos, v3f minvel, v3f maxvel, @@ -558,32 +392,14 @@ private: float minsize, float maxsize, bool collisiondetection, bool vertical, std::string texture, u32 id); - // Adds a ParticleSpawner on all peers - void 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, bool vertical, std::string texture, u32 id); - - // Deletes ParticleSpawner on a single client void SendDeleteParticleSpawner(u16 peer_id, u32 id); - // Deletes ParticleSpawner on all clients - void SendDeleteParticleSpawnerAll(u32 id); - - // Spawns particle on single client + // Spawns particle on peer with peer_id (PEER_ID_INEXISTENT == all) void SendSpawnParticle(u16 peer_id, v3f pos, v3f velocity, v3f acceleration, float expirationtime, float size, bool collisiondetection, bool vertical, std::string texture); - // Spawns particle on all clients - void SendSpawnParticleAll(v3f pos, v3f velocity, v3f acceleration, - float expirationtime, float size, - bool collisiondetection, bool vertical, std::string texture); - /* Something random */ @@ -591,19 +407,12 @@ private: void DiePlayer(u16 peer_id); void RespawnPlayer(u16 peer_id); void DenyAccess(u16 peer_id, const std::wstring &reason); - - enum ClientDeletionReason { - CDR_LEAVE, - CDR_TIMEOUT, - CDR_DENY - }; void DeleteClient(u16 peer_id, ClientDeletionReason reason); - void UpdateCrafting(u16 peer_id); // When called, connection mutex should be locked - RemoteClient* getClient(u16 peer_id); - RemoteClient* getClientNoEx(u16 peer_id); + RemoteClient* getClient(u16 peer_id,ClientState state_min=Active); + RemoteClient* getClientNoEx(u16 peer_id,ClientState state_min=Active); // When called, environment mutex should be locked std::string getPlayerName(u16 peer_id); @@ -618,9 +427,6 @@ private: */ PlayerSAO *emergePlayer(const char *name, u16 peer_id); - // Locks environment and connection by its own - struct PeerChange; - void handlePeerChange(PeerChange &c); void handlePeerChanges(); /* @@ -648,19 +454,12 @@ private: float m_savemap_timer; IntervalLimiter m_map_timer_and_unload_interval; - // NOTE: If connection and environment are both to be locked, - // environment shall be locked first. - // Environment ServerEnvironment *m_env; JMutex m_env_mutex; - // Connection + // server connection con::Connection m_con; - JMutex m_con_mutex; - // Connected clients (behind the con mutex) - std::map<u16, RemoteClient*> m_clients; - std::vector<std::string> m_clients_names; //for announcing masterserver // Ban checking BanManager *m_banmanager; @@ -701,6 +500,7 @@ private: float m_step_dtime; JMutex m_step_dtime_mutex; + // current server step lag counter float m_lag; // The server mainly operates in this thread @@ -716,22 +516,16 @@ private: MutexedVariable<double> m_uptime; /* + Client interface + */ + ClientInterface m_clients; + + /* Peer change queue. Queues stuff from peerAdded() and deletingPeer() to handlePeerChanges() */ - enum PeerChangeType - { - PEER_ADDED, - PEER_REMOVED - }; - struct PeerChange - { - PeerChangeType type; - u16 peer_id; - bool timeout; - }; - Queue<PeerChange> m_peer_change_queue; + Queue<con::PeerChange> m_peer_change_queue; /* Random stuff @@ -776,9 +570,7 @@ private: */ u16 m_ignore_map_edit_events_peer_id; - friend class EmergeThread; - friend class RemoteClient; - + // media files known to server std::map<std::string,MediaInfo> m_media; /* |