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
This commit is contained in:
sapier 2014-01-31 00:24:00 +01:00
parent 21f1bec724
commit e258675eab
10 changed files with 1818 additions and 1609 deletions

@ -308,6 +308,7 @@ set(common_SRCS
connection.cpp connection.cpp
environment.cpp environment.cpp
server.cpp server.cpp
clientiface.cpp
socket.cpp socket.cpp
mapblock.cpp mapblock.cpp
mapsector.cpp mapsector.cpp

@ -384,13 +384,6 @@ void Client::step(float dtime)
ReceiveAll(); 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 Packet counter
*/ */
@ -758,6 +751,7 @@ void Client::step(float dtime)
if (m_media_downloader && m_media_downloader->isStarted()) { if (m_media_downloader && m_media_downloader->isStarted()) {
m_media_downloader->step(this); m_media_downloader->step(this);
if (m_media_downloader->isDone()) { if (m_media_downloader->isDone()) {
received_media();
delete m_media_downloader; delete m_media_downloader;
m_media_downloader = NULL; m_media_downloader = NULL;
} }
@ -1610,11 +1604,6 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
} }
m_media_downloader->step(this); 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) else if(command == TOCLIENT_MEDIA)
{ {
@ -1666,11 +1655,6 @@ void Client::ProcessData(u8 *data, u32 datasize, u16 sender_peer_id)
m_media_downloader->conventionalTransferDone( m_media_downloader->conventionalTransferDone(
name, data, this); name, data, this);
} }
if (m_media_downloader->isDone()) {
delete m_media_downloader;
m_media_downloader = NULL;
}
} }
else if(command == TOCLIENT_TOOLDEF) else if(command == TOCLIENT_TOOLDEF)
{ {

769
src/clientiface.cpp Normal file

@ -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;
}

306
src/clientiface.h Normal file

@ -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

@ -480,13 +480,7 @@ void ClientMediaDownloader::startConventionalTransfers(Client *client)
{ {
assert(m_httpfetch_active == 0); assert(m_httpfetch_active == 0);
if (m_uncached_received_count == m_uncached_count) { 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 {
// Some media files have not been received yet, use the // Some media files have not been received yet, use the
// conventional slow method (minetest protocol) to get them // conventional slow method (minetest protocol) to get them
std::list<std::string> file_requests; std::list<std::string> file_requests;

@ -34,6 +34,14 @@ with this program; if not, write to the Free Software Foundation, Inc.,
namespace con 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 */ /* this mutex is used to achieve log message consistency */
JMutex log_message_mutex; JMutex log_message_mutex;
#define LOG(a) \ #define LOG(a) \
@ -41,15 +49,10 @@ JMutex log_message_mutex;
JMutexAutoLock loglock(log_message_mutex); \ JMutexAutoLock loglock(log_message_mutex); \
a; \ a; \
} }
/******************************************************************************/
/* defines used for debugging and profiling */
/******************************************************************************/
#define PROFILE(a) a #define PROFILE(a) a
//#define PROFILE(a)
//#define DEBUG_CONNECTION_KBPS //#define DEBUG_CONNECTION_KBPS
#undef DEBUG_CONNECTION_KBPS #undef DEBUG_CONNECTION_KBPS
#endif
static inline float CALC_DTIME(unsigned int lasttime, unsigned int curtime) { 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) : UDPPeer::UDPPeer(u16 a_id, Address a_address, Connection* connection) :
Peer(a_address,a_id,connection), Peer(a_address,a_id,connection),
m_pending_disconnect(false),
resend_timeout(0.5), resend_timeout(0.5),
m_legacy_peer(true) m_legacy_peer(true)
{ {
} }
UDPPeer::~UDPPeer()
{
}
bool UDPPeer::getAddress(MTProtocols type,Address& toset) bool UDPPeer::getAddress(MTProtocols type,Address& toset)
{ {
if ((type == UDP) || (type == MINETEST_RELIABLE_UDP) || (type == PRIMARY)) if ((type == UDP) || (type == MINETEST_RELIABLE_UDP) || (type == PRIMARY))
@ -980,6 +980,15 @@ bool UDPPeer::getAddress(MTProtocols type,Address& toset)
return false; 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) void UDPPeer::reportRTT(float rtt)
{ {
if (rtt < 0.0) { if (rtt < 0.0) {
@ -1014,6 +1023,9 @@ bool UDPPeer::Ping(float dtime,SharedBuffer<u8>& data)
void UDPPeer::PutReliableSendCommand(ConnectionCommand &c, void UDPPeer::PutReliableSendCommand(ConnectionCommand &c,
unsigned int max_packet_size) unsigned int max_packet_size)
{ {
if (m_pending_disconnect)
return;
if ( channels[c.channelnum].queued_commands.empty() && if ( channels[c.channelnum].queued_commands.empty() &&
/* don't queue more packets then window size */ /* don't queue more packets then window size */
(channels[c.channelnum].queued_reliables.size() (channels[c.channelnum].queued_reliables.size()
@ -1040,6 +1052,9 @@ bool UDPPeer::processReliableSendCommand(
ConnectionCommand &c, ConnectionCommand &c,
unsigned int max_packet_size) unsigned int max_packet_size)
{ {
if (m_pending_disconnect)
return true;
u32 chunksize_max = max_packet_size u32 chunksize_max = max_packet_size
- BASE_HEADER_SIZE - BASE_HEADER_SIZE
- RELIABLE_HEADER_SIZE; - RELIABLE_HEADER_SIZE;
@ -1564,7 +1579,6 @@ void ConnectionSendThread::processReliableCommand(ConnectionCommand &c)
case CONNCMD_SERVE: case CONNCMD_SERVE:
case CONNCMD_CONNECT: case CONNCMD_CONNECT:
case CONNCMD_DISCONNECT: case CONNCMD_DISCONNECT:
case CONNCMD_DELETE_PEER:
case CONCMD_ACK: case CONCMD_ACK:
assert("Got command that shouldn't be reliable as reliable command" == 0); assert("Got command that shouldn't be reliable as reliable command" == 0);
default: default:
@ -1606,10 +1620,6 @@ void ConnectionSendThread::processNonReliableCommand(ConnectionCommand &c)
LOG(dout_con<<m_connection->getDesc()<<" UDP processing CONNCMD_SEND_TO_ALL"<<std::endl); LOG(dout_con<<m_connection->getDesc()<<" UDP processing CONNCMD_SEND_TO_ALL"<<std::endl);
sendToAll(c.channelnum, c.data); sendToAll(c.channelnum, c.data);
return; 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: case CONCMD_ACK:
LOG(dout_con<<m_connection->getDesc()<<" UDP processing CONCMD_ACK"<<std::endl); LOG(dout_con<<m_connection->getDesc()<<" UDP processing CONCMD_ACK"<<std::endl);
sendAsPacket(c.peer_id,c.channelnum,c.data,true); 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[0], TYPE_CONTROL);
writeU8(&data[1], CONTROLTYPE_DISCO); writeU8(&data[1], CONTROLTYPE_DISCO);
sendAsPacket(peer_id, 0,data,false); 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, void ConnectionSendThread::send(u16 peer_id, u8 channelnum,
@ -1764,6 +1786,8 @@ void ConnectionSendThread::sendToAllReliable(ConnectionCommand &c)
void ConnectionSendThread::sendPackets(float dtime) void ConnectionSendThread::sendPackets(float dtime)
{ {
std::list<u16> peerIds = m_connection->getPeerIDs(); std::list<u16> peerIds = m_connection->getPeerIDs();
std::list<u16> pendingDisconnect;
std::map<u16,bool> pending_unreliable;
for(std::list<u16>::iterator for(std::list<u16>::iterator
j = peerIds.begin(); j = peerIds.begin();
@ -1782,6 +1806,11 @@ void ConnectionSendThread::sendPackets(float dtime)
continue; continue;
} }
if (dynamic_cast<UDPPeer*>(&peer)->m_pending_disconnect)
{
pendingDisconnect.push_back(*j);
}
PROFILE(std::stringstream peerIdentifier); PROFILE(std::stringstream peerIdentifier);
PROFILE(peerIdentifier << "sendPackets[" << m_connection->getDesc() << ";" << *j << ";RELIABLE]"); PROFILE(peerIdentifier << "sendPackets[" << m_connection->getDesc() << ";" << *j << ";RELIABLE]");
PROFILE(ScopeProfiler peerprofiler(g_profiler, peerIdentifier.str(), SPT_AVG)); PROFILE(ScopeProfiler peerprofiler(g_profiler, peerIdentifier.str(), SPT_AVG));
@ -1877,6 +1906,17 @@ void ConnectionSendThread::sendPackets(float dtime)
} }
else { else {
m_outgoing_queue.push_back(packet); 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 // Receive packets from the network and buffers and create ConnectionEvents
void ConnectionReceiveThread::receive() void ConnectionReceiveThread::receive()
{ {
/* now reorder reliables */ // use IPv6 minimum allowed MTU as receive buffer size as this is
u32 datasize = m_max_packet_size * 2; // Double it just to be safe // theoretical reliable upper boundary of a udp packet for all IPv6 enabled
// TODO: We can not know how many layers of header there are. // infrastructure
// For now, just assume there are no other than the base headers. unsigned int packet_maxsize = 1500;
u32 packet_maxsize = datasize + BASE_HEADER_SIZE;
SharedBuffer<u8> packetdata(packet_maxsize); SharedBuffer<u8> packetdata(packet_maxsize);
bool packet_queued = true; bool packet_queued = true;
@ -2126,7 +2165,7 @@ void ConnectionReceiveThread::receive()
LOG(dout_con<<m_connection->getDesc() LOG(dout_con<<m_connection->getDesc()
<<" ProcessPacket from peer_id: " << peer_id <<" ProcessPacket from peer_id: " << peer_id
<< ",channel: " << channelnum << ", returned " << ",channel: " << (channelnum & 0xFF) << ", returned "
<< resultdata.getSize() << " bytes" <<std::endl); << resultdata.getSize() << " bytes" <<std::endl);
ConnectionEvent e; ConnectionEvent e;
@ -2262,6 +2301,10 @@ SharedBuffer<u8> ConnectionReceiveThread::processPacket(Channel *channel,
} }
//put bytes for max bandwidth calculation //put bytes for max bandwidth calculation
channel->UpdateBytesSent(p.data.getSize(),1); channel->UpdateBytesSent(p.data.getSize(),1);
if (channel->outgoing_reliables_sent.size() == 0)
{
m_connection->TriggerSend();
}
} }
catch(NotFoundException &e){ catch(NotFoundException &e){
LOG(derr_con<<m_connection->getDesc() 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_info_mutex(),
m_bc_peerhandler(0), m_bc_peerhandler(0),
m_bc_receive_timeout(0), m_bc_receive_timeout(0),
m_shutting_down(false) m_shutting_down(false),
m_next_remote_peer_id(2)
{ {
m_udpSocket.setTimeoutMs(5); m_udpSocket.setTimeoutMs(5);
@ -2554,7 +2598,8 @@ Connection::Connection(u32 protocol_id, u32 max_packet_size, float timeout,
m_info_mutex(), m_info_mutex(),
m_bc_peerhandler(peerhandler), m_bc_peerhandler(peerhandler),
m_bc_receive_timeout(0), m_bc_receive_timeout(0),
m_shutting_down(false) m_shutting_down(false),
m_next_remote_peer_id(2)
{ {
m_udpSocket.setTimeoutMs(5); m_udpSocket.setTimeoutMs(5);
@ -2810,11 +2855,6 @@ void Connection::Send(u16 peer_id, u8 channelnum,
putCommand(c); putCommand(c);
} }
void Connection::RunTimeouts(float dtime)
{
// No-op
}
Address Connection::GetPeerAddress(u16 peer_id) Address Connection::GetPeerAddress(u16 peer_id)
{ {
PeerHelper peer = getPeerNoEx(peer_id); PeerHelper peer = getPeerNoEx(peer_id);
@ -2838,12 +2878,14 @@ u16 Connection::createPeer(Address& sender, MTProtocols protocol, int fd)
// Somebody wants to make a new connection // Somebody wants to make a new connection
// Get a unique peer id (2 or higher) // 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; u16 overflow = MAX_UDP_PEERS;
/* /*
Find an unused peer id Find an unused peer id
*/ */
{
JMutexAutoLock lock(m_peers_mutex);
bool out_of_ids = false; bool out_of_ids = false;
for(;;) for(;;)
{ {
@ -2862,22 +2904,17 @@ u16 Connection::createPeer(Address& sender, MTProtocols protocol, int fd)
return PEER_ID_INEXISTENT; return PEER_ID_INEXISTENT;
} }
LOG(dout_con<<getDesc()
<<"createPeer(): giving peer_id="<<peer_id_new<<std::endl);
// Create a peer // Create a peer
Peer *peer = 0; Peer *peer = 0;
peer = new UDPPeer(peer_id_new, sender, this); peer = new UDPPeer(peer_id_new, sender, this);
m_peers_mutex.Lock();
m_peers[peer->id] = peer; m_peers[peer->id] = peer;
m_peers_mutex.Unlock(); }
// Create peer addition event m_next_remote_peer_id = (peer_id_new +1) % MAX_UDP_PEERS;
ConnectionEvent e;
e.peerAdded(peer_id_new, sender); LOG(dout_con<<getDesc()
putEvent(e); <<"createPeer(): giving peer_id="<<peer_id_new<<std::endl);
ConnectionCommand cmd; ConnectionCommand cmd;
SharedBuffer<u8> reply(4); SharedBuffer<u8> reply(4);
@ -2887,17 +2924,15 @@ u16 Connection::createPeer(Address& sender, MTProtocols protocol, int fd)
cmd.createPeer(peer_id_new,reply); cmd.createPeer(peer_id_new,reply);
this->putCommand(cmd); 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 // We're now talking to a valid peer_id
return peer_id_new; return peer_id_new;
} }
void Connection::DeletePeer(u16 peer_id)
{
ConnectionCommand c;
c.deletePeer(peer_id);
putCommand(c);
}
void Connection::PrintInfo(std::ostream &out) void Connection::PrintInfo(std::ostream &out)
{ {
m_info_mutex.Lock(); 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)+")"; 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) { void Connection::sendAck(u16 peer_id, u8 channelnum, u16 seqnum) {
assert(channelnum < CHANNEL_COUNT); assert(channelnum < CHANNEL_COUNT);

@ -71,14 +71,6 @@ public:
{} {}
}; };
/*class ThrottlingException : public BaseException
{
public:
ThrottlingException(const char *s):
BaseException(s)
{}
};*/
class InvalidIncomingDataException : public BaseException class InvalidIncomingDataException : public BaseException
{ {
public: public:
@ -406,7 +398,6 @@ enum ConnectionCommandType{
CONNCMD_DISCONNECT_PEER, CONNCMD_DISCONNECT_PEER,
CONNCMD_SEND, CONNCMD_SEND,
CONNCMD_SEND_TO_ALL, CONNCMD_SEND_TO_ALL,
CONNCMD_DELETE_PEER,
CONCMD_ACK, CONCMD_ACK,
CONCMD_CREATE_PEER, CONCMD_CREATE_PEER,
CONCMD_DISABLE_LEGACY CONCMD_DISABLE_LEGACY
@ -460,11 +451,6 @@ struct ConnectionCommand
data = data_; data = data_;
reliable = reliable_; 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_) void ack(u16 peer_id_, u8 channelnum_, SharedBuffer<u8> data_)
{ {
@ -580,9 +566,22 @@ private:
class Peer; class Peer;
enum PeerChangeType
{
PEER_ADDED,
PEER_REMOVED
};
struct PeerChange
{
PeerChangeType type;
u16 peer_id;
bool timeout;
};
class PeerHandler class PeerHandler
{ {
public: public:
PeerHandler() PeerHandler()
{ {
} }
@ -771,7 +770,7 @@ public:
friend class ConnectionSendThread; friend class ConnectionSendThread;
UDPPeer(u16 a_id, Address a_address, Connection* connection); UDPPeer(u16 a_id, Address a_address, Connection* connection);
virtual ~UDPPeer(); virtual ~UDPPeer() {};
void PutReliableSendCommand(ConnectionCommand &c, void PutReliableSendCommand(ConnectionCommand &c,
unsigned int max_packet_size); unsigned int max_packet_size);
@ -781,8 +780,7 @@ public:
bool getAddress(MTProtocols type, Address& toset); bool getAddress(MTProtocols type, Address& toset);
void setNonLegacyPeer() void setNonLegacyPeer();
{ m_legacy_peer = false; }
bool getLegacyPeer() bool getLegacyPeer()
{ return m_legacy_peer; } { return m_legacy_peer; }
@ -793,6 +791,8 @@ public:
SharedBuffer<u8> addSpiltPacket(u8 channel, SharedBuffer<u8> addSpiltPacket(u8 channel,
BufferedPacket toadd, BufferedPacket toadd,
bool reliable); bool reliable);
protected: protected:
/* /*
Calculates avg_rtt and resend_timeout. Calculates avg_rtt and resend_timeout.
@ -813,6 +813,7 @@ protected:
bool Ping(float dtime,SharedBuffer<u8>& data); bool Ping(float dtime,SharedBuffer<u8>& data);
Channel channels[CHANNEL_COUNT]; Channel channels[CHANNEL_COUNT];
bool m_pending_disconnect;
private: private:
// This is changed dynamically // This is changed dynamically
float resend_timeout; float resend_timeout;
@ -1002,13 +1003,12 @@ public:
u32 Receive(u16 &peer_id, SharedBuffer<u8> &data); u32 Receive(u16 &peer_id, SharedBuffer<u8> &data);
void SendToAll(u8 channelnum, SharedBuffer<u8> data, bool reliable); void SendToAll(u8 channelnum, SharedBuffer<u8> data, bool reliable);
void Send(u16 peer_id, 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; } u16 GetPeerID(){ return m_peer_id; }
Address GetPeerAddress(u16 peer_id); Address GetPeerAddress(u16 peer_id);
float GetPeerAvgRTT(u16 peer_id); float GetPeerAvgRTT(u16 peer_id);
void DeletePeer(u16 peer_id);
const u32 GetProtocolID() const { return m_protocol_id; }; const u32 GetProtocolID() const { return m_protocol_id; };
const std::string getDesc(); const std::string getDesc();
void DisconnectPeer(u16 peer_id);
protected: protected:
PeerHelper getPeer(u16 peer_id); PeerHelper getPeer(u16 peer_id);
@ -1033,6 +1033,8 @@ protected:
void putEvent(ConnectionEvent &e); void putEvent(ConnectionEvent &e);
void TriggerSend()
{ m_sendThread.Trigger(); }
private: private:
std::list<Peer*> getPeers(); std::list<Peer*> getPeers();
@ -1054,6 +1056,8 @@ private:
int m_bc_receive_timeout; int m_bc_receive_timeout;
bool m_shutting_down; bool m_shutting_down;
u16 m_next_remote_peer_id;
}; };
} // namespace } // namespace

@ -592,23 +592,12 @@ void *EmergeThread::Thread() {
/* /*
Set sent status of modified blocks on clients 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 // Add the originally fetched block to the modified list
if (block) if (block)
modified_blocks[p] = 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) { if (modified_blocks.size() > 0) {
// Remove block from sent history m_server->SetBlocksNotSent(modified_blocks);
client->SetBlocksNotSent(modified_blocks);
}
} }
} }
catch (VersionMismatchException &e) { catch (VersionMismatchException &e) {

File diff suppressed because it is too large Load Diff

@ -33,6 +33,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "util/numeric.h" #include "util/numeric.h"
#include "util/thread.h" #include "util/thread.h"
#include "environment.h" #include "environment.h"
#include "clientiface.h"
#include <string> #include <string>
#include <list> #include <list>
#include <map> #include <map>
@ -53,14 +54,19 @@ class EmergeManager;
class GameScripting; class GameScripting;
class ServerEnvironment; class ServerEnvironment;
struct SimpleSoundSpec; struct SimpleSoundSpec;
class ServerThread;
enum ClientDeletionReason {
CDR_LEAVE,
CDR_TIMEOUT,
CDR_DENY
};
/* /*
Some random functions Some random functions
*/ */
v3f findSpawnPos(ServerMap &map); v3f findSpawnPos(ServerMap &map);
class MapEditEventIgnorer class MapEditEventIgnorer
{ {
public: public:
@ -111,31 +117,6 @@ private:
VoxelArea *m_ignorevariable; 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 struct MediaInfo
{ {
std::string path; std::string path;
@ -182,134 +163,6 @@ struct ServerPlayingSound
std::set<u16> clients; // peer ids 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, class Server : public con::PeerHandler, public MapEventReceiver,
public InventoryManager, public IGameDef public InventoryManager, public IGameDef
{ {
@ -337,11 +190,6 @@ public:
// Environment must be locked when called // Environment must be locked when called
void setTimeOfDay(u32 time); void setTimeOfDay(u32 time);
bool getShutdownRequested()
{
return m_shutdown_requested;
}
/* /*
Shall be called with the environment locked. Shall be called with the environment locked.
This is accessed by the map, which is inside the environment, This is accessed by the map, which is inside the environment,
@ -358,17 +206,20 @@ public:
// Connection must be locked when called // Connection must be locked when called
std::wstring getStatusString(); std::wstring getStatusString();
void requestShutdown(void) // read shutdown state
{ inline bool getShutdownRequested()
m_shutdown_requested = true; { return m_shutdown_requested; }
}
// request server to shutdown
inline void requestShutdown(void)
{ m_shutdown_requested = true; }
// Returns -1 if failed, sound handle on success // Returns -1 if failed, sound handle on success
// Envlock + conlock // Envlock
s32 playSound(const SimpleSoundSpec &spec, const ServerSoundParams &params); s32 playSound(const SimpleSoundSpec &spec, const ServerSoundParams &params);
void stopSound(s32 handle); void stopSound(s32 handle);
// Envlock + conlock // Envlock
std::set<std::string> getPlayerEffectivePrivs(const std::string &name); std::set<std::string> getPlayerEffectivePrivs(const std::string &name);
bool checkPriv(const std::string &name, const std::string &priv); bool checkPriv(const std::string &name, const std::string &priv);
void reportPrivsModified(const std::string &name=""); // ""=all void reportPrivsModified(const std::string &name=""); // ""=all
@ -378,12 +229,6 @@ public:
void unsetIpBanned(const std::string &ip_or_name); void unsetIpBanned(const std::string &ip_or_name);
std::string getBanDescription(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 notifyPlayer(const char *name, const std::wstring msg, const bool prepend);
void notifyPlayers(const std::wstring msg); void notifyPlayers(const std::wstring msg);
void spawnParticle(const char *playername, void spawnParticle(const char *playername,
@ -451,15 +296,14 @@ public:
const ModSpec* getModSpec(const std::string &modname); const ModSpec* getModSpec(const std::string &modname);
void getModNames(std::list<std::string> &modlist); void getModNames(std::list<std::string> &modlist);
std::string getBuiltinLuaPath(); std::string getBuiltinLuaPath();
inline std::string getWorldPath()
{ return m_path_world; }
std::string getWorldPath(){ return m_path_world; } inline bool isSingleplayer()
{ return m_simple_singleplayer_mode; }
bool isSingleplayer(){ return m_simple_singleplayer_mode; } inline void setAsyncFatalError(const std::string &error)
{ m_async_fatal_error.set(error); }
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); bool showFormspec(const char *name, const std::string &formspec, const std::string &formname);
Map & getMap() { return m_env->getMap(); } Map & getMap() { return m_env->getMap(); }
@ -473,41 +317,32 @@ public:
void hudSetHotbarImage(Player *player, std::string name); void hudSetHotbarImage(Player *player, std::string name);
void hudSetHotbarSelectedImage(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. /* con::PeerHandler implementation. */
// These queue stuff to be processed by handlePeerChanges().
// As of now, these create and remove clients and players.
void peerAdded(con::Peer *peer); void peerAdded(con::Peer *peer);
void deletingPeer(con::Peer *peer, bool timeout); void deletingPeer(con::Peer *peer, bool timeout);
/* private:
Static send methods
*/
static void SendMovement(con::Connection &con, u16 peer_id); friend class EmergeThread;
static void SendHP(con::Connection &con, u16 peer_id, u8 hp); friend class RemoteClient;
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);
/* void SendMovement(u16 peer_id);
Non-static send methods. void SendHP(u16 peer_id, u8 hp);
Conlock should be always used. void SendBreath(u16 peer_id, u16 breath);
Envlock usage is documented badly but it's easy to figure out void SendAccessDenied(u16 peer_id,const std::wstring &reason);
which ones access the environment. 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 // Envlock and conlock should be locked when calling these
void SendInventory(u16 peer_id); void SendInventory(u16 peer_id);
void SendChatMessage(u16 peer_id, const std::wstring &message); 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 SendTimeOfDay(u16 peer_id, u16 time, f32 time_speed);
void SendPlayerHP(u16 peer_id); void SendPlayerHP(u16 peer_id);
void SendPlayerBreath(u16 peer_id); void SendPlayerBreath(u16 peer_id);
@ -546,10 +381,9 @@ private:
const std::list<std::string> &tosend); const std::list<std::string> &tosend);
void sendDetachedInventory(const std::string &name, u16 peer_id); void sendDetachedInventory(const std::string &name, u16 peer_id);
void sendDetachedInventoryToAll(const std::string &name);
void sendDetachedInventories(u16 peer_id); 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, void SendAddParticleSpawner(u16 peer_id, u16 amount, float spawntime,
v3f minpos, v3f maxpos, v3f minpos, v3f maxpos,
v3f minvel, v3f maxvel, v3f minvel, v3f maxvel,
@ -558,32 +392,14 @@ private:
float minsize, float maxsize, float minsize, float maxsize,
bool collisiondetection, bool vertical, std::string texture, u32 id); 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); void SendDeleteParticleSpawner(u16 peer_id, u32 id);
// Deletes ParticleSpawner on all clients // Spawns particle on peer with peer_id (PEER_ID_INEXISTENT == all)
void SendDeleteParticleSpawnerAll(u32 id);
// Spawns particle on single client
void SendSpawnParticle(u16 peer_id, void SendSpawnParticle(u16 peer_id,
v3f pos, v3f velocity, v3f acceleration, v3f pos, v3f velocity, v3f acceleration,
float expirationtime, float size, float expirationtime, float size,
bool collisiondetection, bool vertical, std::string texture); 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 Something random
*/ */
@ -591,19 +407,12 @@ private:
void DiePlayer(u16 peer_id); void DiePlayer(u16 peer_id);
void RespawnPlayer(u16 peer_id); void RespawnPlayer(u16 peer_id);
void DenyAccess(u16 peer_id, const std::wstring &reason); 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 DeleteClient(u16 peer_id, ClientDeletionReason reason);
void UpdateCrafting(u16 peer_id); void UpdateCrafting(u16 peer_id);
// When called, connection mutex should be locked // When called, connection mutex should be locked
RemoteClient* getClient(u16 peer_id); RemoteClient* getClient(u16 peer_id,ClientState state_min=Active);
RemoteClient* getClientNoEx(u16 peer_id); RemoteClient* getClientNoEx(u16 peer_id,ClientState state_min=Active);
// When called, environment mutex should be locked // When called, environment mutex should be locked
std::string getPlayerName(u16 peer_id); std::string getPlayerName(u16 peer_id);
@ -618,9 +427,6 @@ private:
*/ */
PlayerSAO *emergePlayer(const char *name, u16 peer_id); PlayerSAO *emergePlayer(const char *name, u16 peer_id);
// Locks environment and connection by its own
struct PeerChange;
void handlePeerChange(PeerChange &c);
void handlePeerChanges(); void handlePeerChanges();
/* /*
@ -648,19 +454,12 @@ private:
float m_savemap_timer; float m_savemap_timer;
IntervalLimiter m_map_timer_and_unload_interval; IntervalLimiter m_map_timer_and_unload_interval;
// NOTE: If connection and environment are both to be locked,
// environment shall be locked first.
// Environment // Environment
ServerEnvironment *m_env; ServerEnvironment *m_env;
JMutex m_env_mutex; JMutex m_env_mutex;
// Connection // server connection
con::Connection m_con; 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 // Ban checking
BanManager *m_banmanager; BanManager *m_banmanager;
@ -701,6 +500,7 @@ private:
float m_step_dtime; float m_step_dtime;
JMutex m_step_dtime_mutex; JMutex m_step_dtime_mutex;
// current server step lag counter
float m_lag; float m_lag;
// The server mainly operates in this thread // The server mainly operates in this thread
@ -715,23 +515,17 @@ private:
// Uptime of server in seconds // Uptime of server in seconds
MutexedVariable<double> m_uptime; MutexedVariable<double> m_uptime;
/*
Client interface
*/
ClientInterface m_clients;
/* /*
Peer change queue. Peer change queue.
Queues stuff from peerAdded() and deletingPeer() to Queues stuff from peerAdded() and deletingPeer() to
handlePeerChanges() handlePeerChanges()
*/ */
enum PeerChangeType Queue<con::PeerChange> m_peer_change_queue;
{
PEER_ADDED,
PEER_REMOVED
};
struct PeerChange
{
PeerChangeType type;
u16 peer_id;
bool timeout;
};
Queue<PeerChange> m_peer_change_queue;
/* /*
Random stuff Random stuff
@ -776,9 +570,7 @@ private:
*/ */
u16 m_ignore_map_edit_events_peer_id; u16 m_ignore_map_edit_events_peer_id;
friend class EmergeThread; // media files known to server
friend class RemoteClient;
std::map<std::string,MediaInfo> m_media; std::map<std::string,MediaInfo> m_media;
/* /*