clientobject: add null checks
[waspsaliva.git] / src / clientiface.cpp
blob01852c5d1ba013d346184641e4f0c13cc1932a2c
1 /*
2 Minetest
3 Copyright (C) 2010-2014 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include <sstream>
21 #include "clientiface.h"
22 #include "network/connection.h"
23 #include "network/serveropcodes.h"
24 #include "remoteplayer.h"
25 #include "settings.h"
26 #include "mapblock.h"
27 #include "serverenvironment.h"
28 #include "map.h"
29 #include "emerge.h"
30 #include "server/luaentity_sao.h"
31 #include "server/player_sao.h"
32 #include "log.h"
33 #include "util/srp.h"
34 #include "face_position_cache.h"
36 const char *ClientInterface::statenames[] = {
37 "Invalid",
38 "Disconnecting",
39 "Denied",
40 "Created",
41 "AwaitingInit2",
42 "HelloSent",
43 "InitDone",
44 "DefinitionsSent",
45 "Active",
46 "SudoMode",
51 std::string ClientInterface::state2Name(ClientState state)
53 return statenames[state];
56 RemoteClient::RemoteClient() :
57 m_max_simul_sends(g_settings->getU16("max_simultaneous_block_sends_per_client")),
58 m_min_time_from_building(
59 g_settings->getFloat("full_block_send_enable_min_time_from_building")),
60 m_max_send_distance(g_settings->getS16("max_block_send_distance")),
61 m_block_optimize_distance(g_settings->getS16("block_send_optimize_distance")),
62 m_max_gen_distance(g_settings->getS16("max_block_generate_distance")),
63 m_occ_cull(g_settings->getBool("server_side_occlusion_culling"))
67 void RemoteClient::ResendBlockIfOnWire(v3s16 p)
69 // if this block is on wire, mark it for sending again as soon as possible
70 if (m_blocks_sending.find(p) != m_blocks_sending.end()) {
71 SetBlockNotSent(p);
75 LuaEntitySAO *getAttachedObject(PlayerSAO *sao, ServerEnvironment *env)
77 if (!sao->isAttached())
78 return nullptr;
80 int id;
81 std::string bone;
82 v3f dummy;
83 bool force_visible;
84 sao->getAttachment(&id, &bone, &dummy, &dummy, &force_visible);
85 ServerActiveObject *ao = env->getActiveObject(id);
86 while (id && ao) {
87 ao->getAttachment(&id, &bone, &dummy, &dummy, &force_visible);
88 if (id)
89 ao = env->getActiveObject(id);
91 return dynamic_cast<LuaEntitySAO *>(ao);
94 void RemoteClient::GetNextBlocks (
95 ServerEnvironment *env,
96 EmergeManager * emerge,
97 float dtime,
98 std::vector<PrioritySortedBlockTransfer> &dest)
100 // Increment timers
101 m_nothing_to_send_pause_timer -= dtime;
103 if (m_nothing_to_send_pause_timer >= 0)
104 return;
106 RemotePlayer *player = env->getPlayer(peer_id);
107 // This can happen sometimes; clients and players are not in perfect sync.
108 if (!player)
109 return;
111 PlayerSAO *sao = player->getPlayerSAO();
112 if (!sao)
113 return;
115 // Won't send anything if already sending
116 if (m_blocks_sending.size() >= m_max_simul_sends) {
117 //infostream<<"Not sending any blocks, Queue full."<<std::endl;
118 return;
121 v3f playerpos = sao->getBasePosition();
122 // if the player is attached, get the velocity from the attached object
123 LuaEntitySAO *lsao = getAttachedObject(sao, env);
124 const v3f &playerspeed = lsao? lsao->getVelocity() : player->getSpeed();
125 v3f playerspeeddir(0,0,0);
126 if (playerspeed.getLength() > 1.0f * BS)
127 playerspeeddir = playerspeed / playerspeed.getLength();
128 // Predict to next block
129 v3f playerpos_predicted = playerpos + playerspeeddir * (MAP_BLOCKSIZE * BS);
131 v3s16 center_nodepos = floatToInt(playerpos_predicted, BS);
133 v3s16 center = getNodeBlockPos(center_nodepos);
135 // Camera position and direction
136 v3f camera_pos = sao->getEyePosition();
137 v3f camera_dir = v3f(0,0,1);
138 camera_dir.rotateYZBy(sao->getLookPitch());
139 camera_dir.rotateXZBy(sao->getRotation().Y);
141 u16 max_simul_sends_usually = m_max_simul_sends;
144 Check the time from last addNode/removeNode.
146 Decrease send rate if player is building stuff.
148 m_time_from_building += dtime;
149 if (m_time_from_building < m_min_time_from_building) {
150 max_simul_sends_usually
151 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
155 Number of blocks sending + number of blocks selected for sending
157 u32 num_blocks_selected = m_blocks_sending.size();
160 next time d will be continued from the d from which the nearest
161 unsent block was found this time.
163 This is because not necessarily any of the blocks found this
164 time are actually sent.
166 s32 new_nearest_unsent_d = -1;
168 // Get view range and camera fov (radians) from the client
169 s16 wanted_range = sao->getWantedRange() + 1;
170 float camera_fov = sao->getFov();
173 Get the starting value of the block finder radius.
175 if (m_last_center != center) {
176 m_nearest_unsent_d = 0;
177 m_last_center = center;
179 // reset the unsent distance if the view angle has changed more that 10% of the fov
180 // (this matches isBlockInSight which allows for an extra 10%)
181 if (camera_dir.dotProduct(m_last_camera_dir) < std::cos(camera_fov * 0.1f)) {
182 m_nearest_unsent_d = 0;
183 m_last_camera_dir = camera_dir;
185 if (m_nearest_unsent_d > 0) {
186 // make sure any blocks modified since the last time we sent blocks are resent
187 for (const v3s16 &p : m_blocks_modified) {
188 m_nearest_unsent_d = std::min(m_nearest_unsent_d, center.getDistanceFrom(p));
191 m_blocks_modified.clear();
193 s16 d_start = m_nearest_unsent_d;
195 // Distrust client-sent FOV and get server-set player object property
196 // zoom FOV (degrees) as a check to avoid hacked clients using FOV to load
197 // distant world.
198 // (zoom is disabled by value 0)
199 float prop_zoom_fov = sao->getZoomFOV() < 0.001f ?
200 0.0f :
201 std::max(camera_fov, sao->getZoomFOV() * core::DEGTORAD);
203 const s16 full_d_max = std::min(adjustDist(m_max_send_distance, prop_zoom_fov),
204 wanted_range);
205 const s16 d_opt = std::min(adjustDist(m_block_optimize_distance, prop_zoom_fov),
206 wanted_range);
207 const s16 d_blocks_in_sight = full_d_max * BS * MAP_BLOCKSIZE;
209 s16 d_max_gen = std::min(adjustDist(m_max_gen_distance, prop_zoom_fov),
210 wanted_range);
212 s16 d_max = full_d_max;
214 // Don't loop very much at a time
215 s16 max_d_increment_at_time = 2;
216 if (d_max > d_start + max_d_increment_at_time)
217 d_max = d_start + max_d_increment_at_time;
219 // cos(angle between velocity and camera) * |velocity|
220 // Limit to 0.0f in case player moves backwards.
221 f32 dot = rangelim(camera_dir.dotProduct(playerspeed), 0.0f, 300.0f);
223 // Reduce the field of view when a player moves and looks forward.
224 // limit max fov effect to 50%, 60% at 20n/s fly speed
225 camera_fov = camera_fov / (1 + dot / 300.0f);
227 s32 nearest_emerged_d = -1;
228 s32 nearest_emergefull_d = -1;
229 s32 nearest_sent_d = -1;
230 //bool queue_is_full = false;
232 const v3s16 cam_pos_nodes = floatToInt(camera_pos, BS);
234 s16 d;
235 for (d = d_start; d <= d_max; d++) {
237 Get the border/face dot coordinates of a "d-radiused"
240 std::vector<v3s16> list = FacePositionCache::getFacePositions(d);
242 std::vector<v3s16>::iterator li;
243 for (li = list.begin(); li != list.end(); ++li) {
244 v3s16 p = *li + center;
247 Send throttling
248 - Don't allow too many simultaneous transfers
249 - EXCEPT when the blocks are very close
251 Also, don't send blocks that are already flying.
254 // Start with the usual maximum
255 u16 max_simul_dynamic = max_simul_sends_usually;
257 // If block is very close, allow full maximum
258 if (d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
259 max_simul_dynamic = m_max_simul_sends;
261 // Don't select too many blocks for sending
262 if (num_blocks_selected >= max_simul_dynamic) {
263 //queue_is_full = true;
264 goto queue_full_break;
267 // Don't send blocks that are currently being transferred
268 if (m_blocks_sending.find(p) != m_blocks_sending.end())
269 continue;
272 Do not go over max mapgen limit
274 if (blockpos_over_max_limit(p))
275 continue;
277 // If this is true, inexistent block will be made from scratch
278 bool generate = d <= d_max_gen;
281 Don't generate or send if not in sight
282 FIXME This only works if the client uses a small enough
283 FOV setting. The default of 72 degrees is fine.
284 Also retrieve a smaller view cone in the direction of the player's
285 movement.
286 (0.1 is about 4 degrees)
288 f32 dist;
289 if (!(isBlockInSight(p, camera_pos, camera_dir, camera_fov,
290 d_blocks_in_sight, &dist) ||
291 (playerspeed.getLength() > 1.0f * BS &&
292 isBlockInSight(p, camera_pos, playerspeeddir, 0.1f,
293 d_blocks_in_sight)))) {
294 continue;
298 Don't send already sent blocks
300 if (m_blocks_sent.find(p) != m_blocks_sent.end())
301 continue;
304 Check if map has this block
306 MapBlock *block = env->getMap().getBlockNoCreateNoEx(p);
308 bool block_not_found = false;
309 if (block) {
310 // Reset usage timer, this block will be of use in the future.
311 block->resetUsageTimer();
313 // Check whether the block exists (with data)
314 if (block->isDummy() || !block->isGenerated())
315 block_not_found = true;
318 If block is not close, don't send it unless it is near
319 ground level.
321 Block is near ground level if night-time mesh
322 differs from day-time mesh.
324 if (d >= d_opt) {
325 if (!block->getIsUnderground() && !block->getDayNightDiff())
326 continue;
329 if (m_occ_cull && !block_not_found &&
330 env->getMap().isBlockOccluded(block, cam_pos_nodes)) {
331 continue;
336 If block has been marked to not exist on disk (dummy) or is
337 not generated and generating new ones is not wanted, skip block.
339 if (!generate && block_not_found) {
340 // get next one.
341 continue;
345 Add inexistent block to emerge queue.
347 if (block == NULL || block_not_found) {
348 if (emerge->enqueueBlockEmerge(peer_id, p, generate)) {
349 if (nearest_emerged_d == -1)
350 nearest_emerged_d = d;
351 } else {
352 if (nearest_emergefull_d == -1)
353 nearest_emergefull_d = d;
354 goto queue_full_break;
357 // get next one.
358 continue;
361 if (nearest_sent_d == -1)
362 nearest_sent_d = d;
365 Add block to send queue
367 PrioritySortedBlockTransfer q((float)dist, p, peer_id);
369 dest.push_back(q);
371 num_blocks_selected += 1;
374 queue_full_break:
376 // If nothing was found for sending and nothing was queued for
377 // emerging, continue next time browsing from here
378 if (nearest_emerged_d != -1) {
379 new_nearest_unsent_d = nearest_emerged_d;
380 } else if (nearest_emergefull_d != -1) {
381 new_nearest_unsent_d = nearest_emergefull_d;
382 } else {
383 if (d > full_d_max) {
384 new_nearest_unsent_d = 0;
385 m_nothing_to_send_pause_timer = 2.0f;
386 } else {
387 if (nearest_sent_d != -1)
388 new_nearest_unsent_d = nearest_sent_d;
389 else
390 new_nearest_unsent_d = d;
394 if (new_nearest_unsent_d != -1)
395 m_nearest_unsent_d = new_nearest_unsent_d;
398 void RemoteClient::GotBlock(v3s16 p)
400 if (m_blocks_sending.find(p) != m_blocks_sending.end()) {
401 m_blocks_sending.erase(p);
402 // only add to sent blocks if it actually was sending
403 // (it might have been modified since)
404 m_blocks_sent.insert(p);
405 } else {
406 m_excess_gotblocks++;
410 void RemoteClient::SentBlock(v3s16 p)
412 if (m_blocks_sending.find(p) == m_blocks_sending.end())
413 m_blocks_sending[p] = 0.0f;
414 else
415 infostream<<"RemoteClient::SentBlock(): Sent block"
416 " already in m_blocks_sending"<<std::endl;
419 void RemoteClient::SetBlockNotSent(v3s16 p)
421 m_nothing_to_send_pause_timer = 0;
423 // remove the block from sending and sent sets,
424 // and mark as modified if found
425 if (m_blocks_sending.erase(p) + m_blocks_sent.erase(p) > 0)
426 m_blocks_modified.insert(p);
429 void RemoteClient::SetBlocksNotSent(std::map<v3s16, MapBlock*> &blocks)
431 m_nothing_to_send_pause_timer = 0;
433 for (auto &block : blocks) {
434 v3s16 p = block.first;
435 // remove the block from sending and sent sets,
436 // and mark as modified if found
437 if (m_blocks_sending.erase(p) + m_blocks_sent.erase(p) > 0)
438 m_blocks_modified.insert(p);
442 void RemoteClient::notifyEvent(ClientStateEvent event)
444 std::ostringstream myerror;
445 switch (m_state)
447 case CS_Invalid:
448 //intentionally do nothing
449 break;
450 case CS_Created:
451 switch (event) {
452 case CSE_Hello:
453 m_state = CS_HelloSent;
454 break;
455 case CSE_InitLegacy:
456 m_state = CS_AwaitingInit2;
457 break;
458 case CSE_Disconnect:
459 m_state = CS_Disconnecting;
460 break;
461 case CSE_SetDenied:
462 m_state = CS_Denied;
463 break;
464 /* GotInit2 SetDefinitionsSent SetMediaSent */
465 default:
466 myerror << "Created: Invalid client state transition! " << event;
467 throw ClientStateError(myerror.str());
469 break;
470 case CS_Denied:
471 /* don't do anything if in denied state */
472 break;
473 case CS_HelloSent:
474 switch(event)
476 case CSE_AuthAccept:
477 m_state = CS_AwaitingInit2;
478 if (chosen_mech == AUTH_MECHANISM_SRP ||
479 chosen_mech == AUTH_MECHANISM_LEGACY_PASSWORD)
480 srp_verifier_delete((SRPVerifier *) auth_data);
481 chosen_mech = AUTH_MECHANISM_NONE;
482 break;
483 case CSE_Disconnect:
484 m_state = CS_Disconnecting;
485 break;
486 case CSE_SetDenied:
487 m_state = CS_Denied;
488 if (chosen_mech == AUTH_MECHANISM_SRP ||
489 chosen_mech == AUTH_MECHANISM_LEGACY_PASSWORD)
490 srp_verifier_delete((SRPVerifier *) auth_data);
491 chosen_mech = AUTH_MECHANISM_NONE;
492 break;
493 default:
494 myerror << "HelloSent: Invalid client state transition! " << event;
495 throw ClientStateError(myerror.str());
497 break;
498 case CS_AwaitingInit2:
499 switch(event)
501 case CSE_GotInit2:
502 confirmSerializationVersion();
503 m_state = CS_InitDone;
504 break;
505 case CSE_Disconnect:
506 m_state = CS_Disconnecting;
507 break;
508 case CSE_SetDenied:
509 m_state = CS_Denied;
510 break;
512 /* Init SetDefinitionsSent SetMediaSent */
513 default:
514 myerror << "InitSent: Invalid client state transition! " << event;
515 throw ClientStateError(myerror.str());
517 break;
519 case CS_InitDone:
520 switch(event)
522 case CSE_SetDefinitionsSent:
523 m_state = CS_DefinitionsSent;
524 break;
525 case CSE_Disconnect:
526 m_state = CS_Disconnecting;
527 break;
528 case CSE_SetDenied:
529 m_state = CS_Denied;
530 break;
532 /* Init GotInit2 SetMediaSent */
533 default:
534 myerror << "InitDone: Invalid client state transition! " << event;
535 throw ClientStateError(myerror.str());
537 break;
538 case CS_DefinitionsSent:
539 switch(event)
541 case CSE_SetClientReady:
542 m_state = CS_Active;
543 break;
544 case CSE_Disconnect:
545 m_state = CS_Disconnecting;
546 break;
547 case CSE_SetDenied:
548 m_state = CS_Denied;
549 break;
550 /* Init GotInit2 SetDefinitionsSent */
551 default:
552 myerror << "DefinitionsSent: Invalid client state transition! " << event;
553 throw ClientStateError(myerror.str());
555 break;
556 case CS_Active:
557 switch(event)
559 case CSE_SetDenied:
560 m_state = CS_Denied;
561 break;
562 case CSE_Disconnect:
563 m_state = CS_Disconnecting;
564 break;
565 case CSE_SudoSuccess:
566 m_state = CS_SudoMode;
567 if (chosen_mech == AUTH_MECHANISM_SRP)
568 srp_verifier_delete((SRPVerifier *) auth_data);
569 chosen_mech = AUTH_MECHANISM_NONE;
570 break;
571 /* Init GotInit2 SetDefinitionsSent SetMediaSent SetDenied */
572 default:
573 myerror << "Active: Invalid client state transition! " << event;
574 throw ClientStateError(myerror.str());
575 break;
577 break;
578 case CS_SudoMode:
579 switch(event)
581 case CSE_SetDenied:
582 m_state = CS_Denied;
583 break;
584 case CSE_Disconnect:
585 m_state = CS_Disconnecting;
586 break;
587 case CSE_SudoLeave:
588 m_state = CS_Active;
589 break;
590 default:
591 myerror << "Active: Invalid client state transition! " << event;
592 throw ClientStateError(myerror.str());
593 break;
595 break;
596 case CS_Disconnecting:
597 /* we are already disconnecting */
598 break;
602 u64 RemoteClient::uptime() const
604 return porting::getTimeS() - m_connection_time;
607 ClientInterface::ClientInterface(const std::shared_ptr<con::Connection> & con)
609 m_con(con),
610 m_env(NULL),
611 m_print_info_timer(0.0f)
615 ClientInterface::~ClientInterface()
618 Delete clients
621 RecursiveMutexAutoLock clientslock(m_clients_mutex);
623 for (auto &client_it : m_clients) {
624 // Delete client
625 delete client_it.second;
630 std::vector<session_t> ClientInterface::getClientIDs(ClientState min_state)
632 std::vector<session_t> reply;
633 RecursiveMutexAutoLock clientslock(m_clients_mutex);
635 for (const auto &m_client : m_clients) {
636 if (m_client.second->getState() >= min_state)
637 reply.push_back(m_client.second->peer_id);
640 return reply;
643 void ClientInterface::markBlockposAsNotSent(const v3s16 &pos)
645 RecursiveMutexAutoLock clientslock(m_clients_mutex);
646 for (const auto &client : m_clients) {
647 if (client.second->getState() >= CS_Active)
648 client.second->SetBlockNotSent(pos);
653 * Verify if user limit was reached.
654 * User limit count all clients from HelloSent state (MT protocol user) to Active state
655 * @return true if user limit was reached
657 bool ClientInterface::isUserLimitReached()
659 return getClientIDs(CS_HelloSent).size() >= g_settings->getU16("max_users");
662 void ClientInterface::step(float dtime)
664 m_print_info_timer += dtime;
665 if (m_print_info_timer >= 30.0f) {
666 m_print_info_timer = 0.0f;
667 UpdatePlayerList();
671 void ClientInterface::UpdatePlayerList()
673 if (m_env) {
674 std::vector<session_t> clients = getClientIDs();
675 m_clients_names.clear();
678 if (!clients.empty())
679 infostream<<"Players:"<<std::endl;
681 for (session_t i : clients) {
682 RemotePlayer *player = m_env->getPlayer(i);
684 if (player == NULL)
685 continue;
687 infostream << "* " << player->getName() << "\t";
690 RecursiveMutexAutoLock clientslock(m_clients_mutex);
691 RemoteClient* client = lockedGetClientNoEx(i);
692 if (client)
693 client->PrintInfo(infostream);
696 m_clients_names.emplace_back(player->getName());
701 void ClientInterface::send(session_t peer_id, u8 channelnum,
702 NetworkPacket *pkt, bool reliable)
704 m_con->Send(peer_id, channelnum, pkt, reliable);
707 void ClientInterface::sendToAll(NetworkPacket *pkt)
709 RecursiveMutexAutoLock clientslock(m_clients_mutex);
710 for (auto &client_it : m_clients) {
711 RemoteClient *client = client_it.second;
713 if (client->net_proto_version != 0) {
714 m_con->Send(client->peer_id,
715 clientCommandFactoryTable[pkt->getCommand()].channel, pkt,
716 clientCommandFactoryTable[pkt->getCommand()].reliable);
721 void ClientInterface::sendToAllCompat(NetworkPacket *pkt, NetworkPacket *legacypkt,
722 u16 min_proto_ver)
724 RecursiveMutexAutoLock clientslock(m_clients_mutex);
725 for (auto &client_it : m_clients) {
726 RemoteClient *client = client_it.second;
727 NetworkPacket *pkt_to_send = nullptr;
729 if (client->net_proto_version >= min_proto_ver) {
730 pkt_to_send = pkt;
731 } else if (client->net_proto_version != 0) {
732 pkt_to_send = legacypkt;
733 } else {
734 warningstream << "Client with unhandled version to handle: '"
735 << client->net_proto_version << "'";
736 continue;
739 m_con->Send(client->peer_id,
740 clientCommandFactoryTable[pkt_to_send->getCommand()].channel,
741 pkt_to_send,
742 clientCommandFactoryTable[pkt_to_send->getCommand()].reliable);
746 RemoteClient* ClientInterface::getClientNoEx(session_t peer_id, ClientState state_min)
748 RecursiveMutexAutoLock clientslock(m_clients_mutex);
749 RemoteClientMap::const_iterator n = m_clients.find(peer_id);
750 // The client may not exist; clients are immediately removed if their
751 // access is denied, and this event occurs later then.
752 if (n == m_clients.end())
753 return NULL;
755 if (n->second->getState() >= state_min)
756 return n->second;
758 return NULL;
761 RemoteClient* ClientInterface::lockedGetClientNoEx(session_t peer_id, ClientState state_min)
763 RemoteClientMap::const_iterator n = m_clients.find(peer_id);
764 // The client may not exist; clients are immediately removed if their
765 // access is denied, and this event occurs later then.
766 if (n == m_clients.end())
767 return NULL;
769 if (n->second->getState() >= state_min)
770 return n->second;
772 return NULL;
775 ClientState ClientInterface::getClientState(session_t peer_id)
777 RecursiveMutexAutoLock clientslock(m_clients_mutex);
778 RemoteClientMap::const_iterator n = m_clients.find(peer_id);
779 // The client may not exist; clients are immediately removed if their
780 // access is denied, and this event occurs later then.
781 if (n == m_clients.end())
782 return CS_Invalid;
784 return n->second->getState();
787 void ClientInterface::setPlayerName(session_t peer_id, const std::string &name)
789 RecursiveMutexAutoLock clientslock(m_clients_mutex);
790 RemoteClientMap::iterator n = m_clients.find(peer_id);
791 // The client may not exist; clients are immediately removed if their
792 // access is denied, and this event occurs later then.
793 if (n != m_clients.end())
794 n->second->setName(name);
797 void ClientInterface::DeleteClient(session_t peer_id)
799 RecursiveMutexAutoLock conlock(m_clients_mutex);
801 // Error check
802 RemoteClientMap::iterator n = m_clients.find(peer_id);
803 // The client may not exist; clients are immediately removed if their
804 // access is denied, and this event occurs later then.
805 if (n == m_clients.end())
806 return;
809 Mark objects to be not known by the client
811 //TODO this should be done by client destructor!!!
812 RemoteClient *client = n->second;
813 // Handle objects
814 for (u16 id : client->m_known_objects) {
815 // Get object
816 ServerActiveObject* obj = m_env->getActiveObject(id);
818 if(obj && obj->m_known_by_count > 0)
819 obj->m_known_by_count--;
822 // Delete client
823 delete m_clients[peer_id];
824 m_clients.erase(peer_id);
827 void ClientInterface::CreateClient(session_t peer_id)
829 RecursiveMutexAutoLock conlock(m_clients_mutex);
831 // Error check
832 RemoteClientMap::iterator n = m_clients.find(peer_id);
833 // The client shouldn't already exist
834 if (n != m_clients.end()) return;
836 // Create client
837 RemoteClient *client = new RemoteClient();
838 client->peer_id = peer_id;
839 m_clients[client->peer_id] = client;
842 void ClientInterface::event(session_t peer_id, ClientStateEvent event)
845 RecursiveMutexAutoLock clientlock(m_clients_mutex);
847 // Error check
848 RemoteClientMap::iterator n = m_clients.find(peer_id);
850 // No client to deliver event
851 if (n == m_clients.end())
852 return;
853 n->second->notifyEvent(event);
856 if ((event == CSE_SetClientReady) ||
857 (event == CSE_Disconnect) ||
858 (event == CSE_SetDenied))
860 UpdatePlayerList();
864 u16 ClientInterface::getProtocolVersion(session_t peer_id)
866 RecursiveMutexAutoLock conlock(m_clients_mutex);
868 // Error check
869 RemoteClientMap::iterator n = m_clients.find(peer_id);
871 // No client to get version
872 if (n == m_clients.end())
873 return 0;
875 return n->second->net_proto_version;
878 void ClientInterface::setClientVersion(session_t peer_id, u8 major, u8 minor, u8 patch,
879 const std::string &full)
881 RecursiveMutexAutoLock conlock(m_clients_mutex);
883 // Error check
884 RemoteClientMap::iterator n = m_clients.find(peer_id);
886 // No client to set versions
887 if (n == m_clients.end())
888 return;
890 n->second->setVersionInfo(major, minor, patch, full);