3 Copyright (C) 2010-2011 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 General Public License as published by
7 the Free Software Foundation; either version 2 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 General Public License for more details.
15 You should have received a copy of the GNU 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.
23 #include "clientserver.h"
25 #include "jmutexautolock.h"
27 #include "constants.h"
29 #include "materials.h"
32 #include "servercommand.h"
34 #include "content_mapnode.h"
35 #include "content_craft.h"
36 #include "content_nodemeta.h"
38 #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
40 class MapEditEventIgnorer
43 MapEditEventIgnorer(bool *flag):
52 ~MapEditEventIgnorer()
65 void * ServerThread::Thread()
69 DSTACK(__FUNCTION_NAME);
71 BEGIN_DEBUG_EXCEPTION_HANDLER
76 //TimeTaker timer("AsyncRunStep() + Receive()");
79 //TimeTaker timer("AsyncRunStep()");
80 m_server->AsyncRunStep();
83 //dout_server<<"Running m_server->Receive()"<<std::endl;
86 catch(con::NoIncomingDataException &e)
89 catch(con::PeerNotFoundException &e)
91 dout_server<<"Server: PeerNotFoundException"<<std::endl;
95 END_DEBUG_EXCEPTION_HANDLER
100 void * EmergeThread::Thread()
104 DSTACK(__FUNCTION_NAME);
108 BEGIN_DEBUG_EXCEPTION_HANDLER
111 Get block info from queue, emerge them and send them
114 After queue is empty, exit.
118 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
122 SharedPtr<QueuedBlockEmerge> q(qptr);
128 Do not generate over-limit
130 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
131 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
132 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
133 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
134 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
135 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
138 //derr_server<<"EmergeThread::Thread(): running"<<std::endl;
140 //TimeTaker timer("block emerge");
143 Try to emerge it from somewhere.
145 If it is only wanted as optional, only loading from disk
150 Check if any peer wants it as non-optional. In that case it
153 Also decrement the emerge queue count in clients.
156 bool optional = true;
159 core::map<u16, u8>::Iterator i;
160 for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
162 //u16 peer_id = i.getNode()->getKey();
165 u8 flags = i.getNode()->getValue();
166 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
172 /*dstream<<"EmergeThread: p="
173 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
174 <<"optional="<<optional<<std::endl;*/
176 ServerMap &map = ((ServerMap&)m_server->m_env.getMap());
178 //core::map<v3s16, MapBlock*> changed_blocks;
179 //core::map<v3s16, MapBlock*> lighting_invalidated_blocks;
181 MapBlock *block = NULL;
182 bool got_block = true;
183 core::map<v3s16, MapBlock*> modified_blocks;
185 bool only_from_disk = false;
188 only_from_disk = true;
191 Fetch block from map or generate a single block
194 JMutexAutoLock envlock(m_server->m_env_mutex);
196 // Load sector if it isn't loaded
197 if(map.getSectorNoGenerateNoEx(p2d) == NULL)
198 //map.loadSectorFull(p2d);
199 map.loadSectorMeta(p2d);
201 block = map.getBlockNoCreateNoEx(p);
202 if(!block || block->isDummy() || !block->isGenerated())
204 // Get, load or create sector
205 /*ServerMapSector *sector =
206 (ServerMapSector*)map.createSector(p2d);*/
208 // Load/generate block
210 /*block = map.emergeBlock(p, sector, changed_blocks,
211 lighting_invalidated_blocks);*/
213 block = map.loadBlock(p);
215 if(block == NULL && only_from_disk == false)
216 block = map.generateBlock(p, modified_blocks);
217 //block = map.generateBlock(p, changed_blocks);
218 /*block = map.generateBlock(p, block, sector, changed_blocks,
219 lighting_invalidated_blocks);*/
228 Ignore map edit events, they will not need to be
229 sent to anybody because the block hasn't been sent
232 MapEditEventIgnorer ign(&m_server->m_ignore_map_edit_events);
234 // Activate objects and stuff
235 m_server->m_env.activateBlock(block, 3600);
240 /*if(block->getLightingExpired()){
241 lighting_invalidated_blocks[block->getPos()] = block;
245 // TODO: Some additional checking and lighting updating,
250 JMutexAutoLock envlock(m_server->m_env_mutex);
255 Collect a list of blocks that have been modified in
256 addition to the fetched one.
260 if(lighting_invalidated_blocks.size() > 0)
262 /*dstream<<"lighting "<<lighting_invalidated_blocks.size()
263 <<" blocks"<<std::endl;*/
265 // 50-100ms for single block generation
266 //TimeTaker timer("** EmergeThread updateLighting");
268 // Update lighting without locking the environment mutex,
269 // add modified blocks to changed blocks
270 map.updateLighting(lighting_invalidated_blocks, modified_blocks);
273 // Add all from changed_blocks to modified_blocks
274 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
275 i.atEnd() == false; i++)
277 MapBlock *block = i.getNode()->getValue();
278 modified_blocks.insert(block->getPos(), block);
282 // If we got no block, there should be no invalidated blocks
285 //assert(lighting_invalidated_blocks.size() == 0);
291 Set sent status of modified blocks on clients
294 // NOTE: Server's clients are also behind the connection mutex
295 JMutexAutoLock lock(m_server->m_con_mutex);
298 Add the originally fetched block to the modified list
302 modified_blocks.insert(p, block);
306 Set the modified blocks unsent for all the clients
309 for(core::map<u16, RemoteClient*>::Iterator
310 i = m_server->m_clients.getIterator();
311 i.atEnd() == false; i++)
313 RemoteClient *client = i.getNode()->getValue();
315 if(modified_blocks.size() > 0)
317 // Remove block from sent history
318 client->SetBlocksNotSent(modified_blocks);
324 END_DEBUG_EXCEPTION_HANDLER
329 void RemoteClient::GetNextBlocks(Server *server, float dtime,
330 core::array<PrioritySortedBlockTransfer> &dest)
332 DSTACK(__FUNCTION_NAME);
335 TimeTaker timer("RemoteClient::GetNextBlocks", &timer_result);*/
338 m_nothing_to_send_pause_timer -= dtime;
340 if(m_nothing_to_send_pause_timer >= 0)
343 m_nearest_unsent_reset_timer = 0;
347 // Won't send anything if already sending
348 if(m_blocks_sending.size() >= g_settings.getU16
349 ("max_simultaneous_block_sends_per_client"))
351 //dstream<<"Not sending any blocks, Queue full."<<std::endl;
355 //TimeTaker timer("RemoteClient::GetNextBlocks");
357 Player *player = server->m_env.getPlayer(peer_id);
359 assert(player != NULL);
361 v3f playerpos = player->getPosition();
362 v3f playerspeed = player->getSpeed();
363 v3f playerspeeddir(0,0,0);
364 if(playerspeed.getLength() > 1.0*BS)
365 playerspeeddir = playerspeed / playerspeed.getLength();
366 // Predict to next block
367 v3f playerpos_predicted = playerpos + playerspeeddir*MAP_BLOCKSIZE*BS;
369 v3s16 center_nodepos = floatToInt(playerpos_predicted, BS);
371 v3s16 center = getNodeBlockPos(center_nodepos);
373 // Camera position and direction
375 playerpos + v3f(0, BS+BS/2, 0);
376 v3f camera_dir = v3f(0,0,1);
377 camera_dir.rotateYZBy(player->getPitch());
378 camera_dir.rotateXZBy(player->getYaw());
380 /*dstream<<"camera_dir=("<<camera_dir.X<<","<<camera_dir.Y<<","
381 <<camera_dir.Z<<")"<<std::endl;*/
384 Get the starting value of the block finder radius.
387 if(m_last_center != center)
389 m_nearest_unsent_d = 0;
390 m_last_center = center;
393 /*dstream<<"m_nearest_unsent_reset_timer="
394 <<m_nearest_unsent_reset_timer<<std::endl;*/
396 // This has to be incremented only when the nothing to send pause
398 m_nearest_unsent_reset_timer += dtime;
400 // Reset periodically to avoid possible bugs or other mishaps
401 if(m_nearest_unsent_reset_timer > 10.0)
403 m_nearest_unsent_reset_timer = 0;
404 m_nearest_unsent_d = 0;
405 /*dstream<<"Resetting m_nearest_unsent_d for "
406 <<server->getPlayerName(peer_id)<<std::endl;*/
409 //s16 last_nearest_unsent_d = m_nearest_unsent_d;
410 s16 d_start = m_nearest_unsent_d;
412 //dstream<<"d_start="<<d_start<<std::endl;
414 u16 max_simul_sends_setting = g_settings.getU16
415 ("max_simultaneous_block_sends_per_client");
416 u16 max_simul_sends_usually = max_simul_sends_setting;
419 Check the time from last addNode/removeNode.
421 Decrease send rate if player is building stuff.
423 m_time_from_building += dtime;
424 if(m_time_from_building < g_settings.getFloat(
425 "full_block_send_enable_min_time_from_building"))
427 max_simul_sends_usually
428 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
432 Number of blocks sending + number of blocks selected for sending
434 u32 num_blocks_selected = m_blocks_sending.size();
437 next time d will be continued from the d from which the nearest
438 unsent block was found this time.
440 This is because not necessarily any of the blocks found this
441 time are actually sent.
443 s32 new_nearest_unsent_d = -1;
445 s16 d_max = g_settings.getS16("max_block_send_distance");
446 s16 d_max_gen = g_settings.getS16("max_block_generate_distance");
448 // Don't loop very much at a time
449 if(d_max > d_start+1)
451 /*if(d_max_gen > d_start+2)
452 d_max_gen = d_start+2;*/
454 //dstream<<"Starting from "<<d_start<<std::endl;
456 bool sending_something = false;
458 bool no_blocks_found_for_sending = true;
460 bool queue_is_full = false;
463 for(d = d_start; d <= d_max; d++)
465 //dstream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
468 If m_nearest_unsent_d was changed by the EmergeThread
469 (it can change it to 0 through SetBlockNotSent),
471 Else update m_nearest_unsent_d
473 /*if(m_nearest_unsent_d != last_nearest_unsent_d)
475 d = m_nearest_unsent_d;
476 last_nearest_unsent_d = m_nearest_unsent_d;
480 Get the border/face dot coordinates of a "d-radiused"
483 core::list<v3s16> list;
484 getFacePositions(list, d);
486 core::list<v3s16>::Iterator li;
487 for(li=list.begin(); li!=list.end(); li++)
489 v3s16 p = *li + center;
493 - Don't allow too many simultaneous transfers
494 - EXCEPT when the blocks are very close
496 Also, don't send blocks that are already flying.
499 // Start with the usual maximum
500 u16 max_simul_dynamic = max_simul_sends_usually;
502 // If block is very close, allow full maximum
503 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
504 max_simul_dynamic = max_simul_sends_setting;
506 // Don't select too many blocks for sending
507 if(num_blocks_selected >= max_simul_dynamic)
509 queue_is_full = true;
510 goto queue_full_break;
513 // Don't send blocks that are currently being transferred
514 if(m_blocks_sending.find(p) != NULL)
520 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
521 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
522 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
523 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
524 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
525 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
528 // If this is true, inexistent block will be made from scratch
529 bool generate = d <= d_max_gen;
532 /*// Limit the generating area vertically to 2/3
533 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
536 // Limit the send area vertically to 2/3
537 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
543 If block is far away, don't generate it unless it is
549 // Block center y in nodes
550 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
551 // Don't generate if it's very high or very low
552 if(y < -64 || y > 64)
556 v2s16 p2d_nodes_center(
560 // Get ground height in nodes
561 s16 gh = server->m_env.getServerMap().findGroundLevel(
564 // If differs a lot, don't generate
565 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
567 // Actually, don't even send it
573 //dstream<<"d="<<d<<std::endl;
576 Don't generate or send if not in sight
579 if(isBlockInSight(p, camera_pos, camera_dir, 10000*BS) == false)
585 Don't send already sent blocks
588 if(m_blocks_sent.find(p) != NULL)
595 Check if map has this block
597 MapBlock *block = server->m_env.getMap().getBlockNoCreateNoEx(p);
599 bool surely_not_found_on_disk = false;
600 bool block_is_invalid = false;
603 // Block is dummy if data doesn't exist.
604 // It means it has been not found from disk and not generated
607 surely_not_found_on_disk = true;
610 // Block is valid if lighting is up-to-date and data exists
611 if(block->isValid() == false)
613 block_is_invalid = true;
616 /*if(block->isFullyGenerated() == false)
618 block_is_invalid = true;
623 ServerMap *map = (ServerMap*)(&server->m_env.getMap());
624 v2s16 chunkpos = map->sector_to_chunk(p2d);
625 if(map->chunkNonVolatile(chunkpos) == false)
626 block_is_invalid = true;
628 if(block->isGenerated() == false)
629 block_is_invalid = true;
632 If block is not close, don't send it unless it is near
635 Block is near ground level if night-time mesh
636 differs from day-time mesh.
640 if(block->dayNightDiffed() == false)
647 If block has been marked to not exist on disk (dummy)
648 and generating new ones is not wanted, skip block.
650 if(generate == false && surely_not_found_on_disk == true)
657 Record the lowest d from which a block has been
658 found being not sent and possibly to exist
660 if(no_blocks_found_for_sending)
663 new_nearest_unsent_d = d;
666 no_blocks_found_for_sending = false;
669 Add inexistent block to emerge queue.
671 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
673 //TODO: Get value from somewhere
674 // Allow only one block in emerge queue
675 //if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
676 // Allow two blocks in queue per client
677 if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
679 //dstream<<"Adding block to emerge queue"<<std::endl;
681 // Add it to the emerge queue and trigger the thread
684 if(generate == false)
685 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
687 server->m_emerge_queue.addBlock(peer_id, p, flags);
688 server->m_emergethread.trigger();
696 Add block to send queue
699 PrioritySortedBlockTransfer q((float)d, p, peer_id);
703 num_blocks_selected += 1;
704 sending_something = true;
709 //dstream<<"Stopped at "<<d<<std::endl;
711 if(no_blocks_found_for_sending)
713 if(queue_is_full == false)
714 new_nearest_unsent_d = d;
717 if(new_nearest_unsent_d != -1)
718 m_nearest_unsent_d = new_nearest_unsent_d;
720 if(sending_something == false)
722 m_nothing_to_send_counter++;
723 if((s16)m_nothing_to_send_counter >=
724 g_settings.getS16("max_block_send_distance"))
726 // Pause time in seconds
727 m_nothing_to_send_pause_timer = 1.0;
728 /*dstream<<"nothing to send to "
729 <<server->getPlayerName(peer_id)
730 <<" (d="<<d<<")"<<std::endl;*/
735 m_nothing_to_send_counter = 0;
738 /*timer_result = timer.stop(true);
739 if(timer_result != 0)
740 dstream<<"GetNextBlocks duration: "<<timer_result<<" (!=0)"<<std::endl;*/
743 void RemoteClient::SendObjectData(
746 core::map<v3s16, bool> &stepped_blocks
749 DSTACK(__FUNCTION_NAME);
751 // Can't send anything without knowing version
752 if(serialization_version == SER_FMT_VER_INVALID)
754 dstream<<"RemoteClient::SendObjectData(): Not sending, no version."
760 Send a TOCLIENT_OBJECTDATA packet.
764 u16 number of player positions
775 std::ostringstream os(std::ios_base::binary);
779 writeU16(buf, TOCLIENT_OBJECTDATA);
780 os.write((char*)buf, 2);
783 Get and write player data
786 // Get connected players
787 core::list<Player*> players = server->m_env.getPlayers(true);
789 // Write player count
790 u16 playercount = players.size();
791 writeU16(buf, playercount);
792 os.write((char*)buf, 2);
794 core::list<Player*>::Iterator i;
795 for(i = players.begin();
796 i != players.end(); i++)
800 v3f pf = player->getPosition();
801 v3f sf = player->getSpeed();
803 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
804 v3s32 speed_i (sf.X*100, sf.Y*100, sf.Z*100);
805 s32 pitch_i (player->getPitch() * 100);
806 s32 yaw_i (player->getYaw() * 100);
808 writeU16(buf, player->peer_id);
809 os.write((char*)buf, 2);
810 writeV3S32(buf, position_i);
811 os.write((char*)buf, 12);
812 writeV3S32(buf, speed_i);
813 os.write((char*)buf, 12);
814 writeS32(buf, pitch_i);
815 os.write((char*)buf, 4);
816 writeS32(buf, yaw_i);
817 os.write((char*)buf, 4);
821 Get and write object data
827 For making players to be able to build to their nearby
828 environment (building is not possible on blocks that are not
831 - Add blocks to emerge queue if they are not found
833 SUGGESTION: These could be ignored from the backside of the player
836 Player *player = server->m_env.getPlayer(peer_id);
840 v3f playerpos = player->getPosition();
841 v3f playerspeed = player->getSpeed();
843 v3s16 center_nodepos = floatToInt(playerpos, BS);
844 v3s16 center = getNodeBlockPos(center_nodepos);
846 s16 d_max = g_settings.getS16("active_object_range");
848 // Number of blocks whose objects were written to bos
851 std::ostringstream bos(std::ios_base::binary);
853 for(s16 d = 0; d <= d_max; d++)
855 core::list<v3s16> list;
856 getFacePositions(list, d);
858 core::list<v3s16>::Iterator li;
859 for(li=list.begin(); li!=list.end(); li++)
861 v3s16 p = *li + center;
864 Ignore blocks that haven't been sent to the client
867 if(m_blocks_sent.find(p) == NULL)
871 // Try stepping block and add it to a send queue
876 MapBlock *block = server->m_env.getMap().getBlockNoCreate(p);
879 Step block if not in stepped_blocks and add to stepped_blocks.
881 if(stepped_blocks.find(p) == NULL)
883 block->stepObjects(dtime, true, server->m_env.getDayNightRatio());
884 stepped_blocks.insert(p, true);
885 block->setChangedFlag();
888 // Skip block if there are no objects
889 if(block->getObjectCount() == 0)
898 bos.write((char*)buf, 6);
901 //block->serializeObjects(bos, serialization_version); // DEPRECATED
908 Stop collecting objects if data is already too big
910 // Sum of player and object data sizes
911 s32 sum = (s32)os.tellp() + 2 + (s32)bos.tellp();
912 // break out if data too big
913 if(sum > MAX_OBJECTDATA_SIZE)
915 goto skip_subsequent;
919 catch(InvalidPositionException &e)
922 // Add it to the emerge queue and trigger the thread.
923 // Fetch the block only if it is on disk.
925 // Grab and increment counter
926 /*SharedPtr<JMutexAutoLock> lock
927 (m_num_blocks_in_emerge_queue.getLock());
928 m_num_blocks_in_emerge_queue.m_value++;*/
930 // Add to queue as an anonymous fetch from disk
931 u8 flags = BLOCK_EMERGE_FLAG_FROMDISK;
932 server->m_emerge_queue.addBlock(0, p, flags);
933 server->m_emergethread.trigger();
941 writeU16(buf, blockcount);
942 os.write((char*)buf, 2);
944 // Write block objects
951 //dstream<<"Server: Sending object data to "<<peer_id<<std::endl;
954 std::string s = os.str();
955 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
956 // Send as unreliable
957 server->m_con.Send(peer_id, 0, data, false);
960 void RemoteClient::GotBlock(v3s16 p)
962 if(m_blocks_sending.find(p) != NULL)
963 m_blocks_sending.remove(p);
966 /*dstream<<"RemoteClient::GotBlock(): Didn't find in"
967 " m_blocks_sending"<<std::endl;*/
968 m_excess_gotblocks++;
970 m_blocks_sent.insert(p, true);
973 void RemoteClient::SentBlock(v3s16 p)
975 if(m_blocks_sending.find(p) == NULL)
976 m_blocks_sending.insert(p, 0.0);
978 dstream<<"RemoteClient::SentBlock(): Sent block"
979 " already in m_blocks_sending"<<std::endl;
982 void RemoteClient::SetBlockNotSent(v3s16 p)
984 m_nearest_unsent_d = 0;
986 if(m_blocks_sending.find(p) != NULL)
987 m_blocks_sending.remove(p);
988 if(m_blocks_sent.find(p) != NULL)
989 m_blocks_sent.remove(p);
992 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
994 m_nearest_unsent_d = 0;
996 for(core::map<v3s16, MapBlock*>::Iterator
997 i = blocks.getIterator();
998 i.atEnd()==false; i++)
1000 v3s16 p = i.getNode()->getKey();
1002 if(m_blocks_sending.find(p) != NULL)
1003 m_blocks_sending.remove(p);
1004 if(m_blocks_sent.find(p) != NULL)
1005 m_blocks_sent.remove(p);
1013 PlayerInfo::PlayerInfo()
1019 void PlayerInfo::PrintLine(std::ostream *s)
1022 (*s)<<"\""<<name<<"\" ("
1023 <<(position.X/10)<<","<<(position.Y/10)
1024 <<","<<(position.Z/10)<<") ";
1026 (*s)<<" avg_rtt="<<avg_rtt;
1030 u32 PIChecksum(core::list<PlayerInfo> &l)
1032 core::list<PlayerInfo>::Iterator i;
1035 for(i=l.begin(); i!=l.end(); i++)
1037 checksum += a * (i->id+1);
1038 checksum ^= 0x435aafcd;
1049 std::string mapsavedir
1051 m_env(new ServerMap(mapsavedir), this),
1052 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
1053 m_authmanager(mapsavedir+"/auth.txt"),
1055 m_emergethread(this),
1057 m_time_of_day_send_timer(0),
1059 m_mapsavedir(mapsavedir),
1060 m_shutdown_requested(false),
1061 m_ignore_map_edit_events(false),
1062 m_ignore_map_edit_events_peer_id(0)
1064 m_liquid_transform_timer = 0.0;
1065 m_print_info_timer = 0.0;
1066 m_objectdata_timer = 0.0;
1067 m_emergethread_trigger_timer = 0.0;
1068 m_savemap_timer = 0.0;
1072 m_step_dtime_mutex.Init();
1075 // Register us to receive map edit events
1076 m_env.getMap().addEventReceiver(this);
1078 // If file exists, load environment metadata
1079 if(fs::PathExists(m_mapsavedir+"/env_meta.txt"))
1081 dstream<<"Server: Loading environment metadata"<<std::endl;
1082 m_env.loadMeta(m_mapsavedir);
1086 dstream<<"Server: Loading players"<<std::endl;
1087 m_env.deSerializePlayers(m_mapsavedir);
1092 dstream<<"Server::~Server()"<<std::endl;
1095 Send shutdown message
1098 JMutexAutoLock conlock(m_con_mutex);
1100 std::wstring line = L"*** Server shutting down";
1103 Send the message to clients
1105 for(core::map<u16, RemoteClient*>::Iterator
1106 i = m_clients.getIterator();
1107 i.atEnd() == false; i++)
1109 // Get client and check that it is valid
1110 RemoteClient *client = i.getNode()->getValue();
1111 assert(client->peer_id == i.getNode()->getKey());
1112 if(client->serialization_version == SER_FMT_VER_INVALID)
1116 SendChatMessage(client->peer_id, line);
1118 catch(con::PeerNotFoundException &e)
1126 dstream<<"Server: Saving players"<<std::endl;
1127 m_env.serializePlayers(m_mapsavedir);
1130 Save environment metadata
1132 dstream<<"Server: Saving environment metadata"<<std::endl;
1133 m_env.saveMeta(m_mapsavedir);
1144 JMutexAutoLock clientslock(m_con_mutex);
1146 for(core::map<u16, RemoteClient*>::Iterator
1147 i = m_clients.getIterator();
1148 i.atEnd() == false; i++)
1151 // NOTE: These are removed by env destructor
1153 u16 peer_id = i.getNode()->getKey();
1154 JMutexAutoLock envlock(m_env_mutex);
1155 m_env.removePlayer(peer_id);
1159 delete i.getNode()->getValue();
1164 void Server::start(unsigned short port)
1166 DSTACK(__FUNCTION_NAME);
1167 // Stop thread if already running
1170 // Initialize connection
1171 m_con.setTimeoutMs(30);
1175 m_thread.setRun(true);
1178 dout_server<<"Server: Started on port "<<port<<std::endl;
1183 DSTACK(__FUNCTION_NAME);
1185 // Stop threads (set run=false first so both start stopping)
1186 m_thread.setRun(false);
1187 m_emergethread.setRun(false);
1189 m_emergethread.stop();
1191 dout_server<<"Server: Threads stopped"<<std::endl;
1194 void Server::step(float dtime)
1196 DSTACK(__FUNCTION_NAME);
1201 JMutexAutoLock lock(m_step_dtime_mutex);
1202 m_step_dtime += dtime;
1206 void Server::AsyncRunStep()
1208 DSTACK(__FUNCTION_NAME);
1212 JMutexAutoLock lock1(m_step_dtime_mutex);
1213 dtime = m_step_dtime;
1217 ScopeProfiler sp(&g_profiler, "Server: selecting and sending "
1218 "blocks to clients");
1219 // Send blocks to clients
1226 //dstream<<"Server steps "<<dtime<<std::endl;
1227 //dstream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1230 JMutexAutoLock lock1(m_step_dtime_mutex);
1231 m_step_dtime -= dtime;
1238 m_uptime.set(m_uptime.get() + dtime);
1242 Update m_time_of_day and overall game time
1245 JMutexAutoLock envlock(m_env_mutex);
1247 m_time_counter += dtime;
1248 f32 speed = g_settings.getFloat("time_speed") * 24000./(24.*3600);
1249 u32 units = (u32)(m_time_counter*speed);
1250 m_time_counter -= (f32)units / speed;
1252 m_env.setTimeOfDay((m_env.getTimeOfDay() + units) % 24000);
1254 //dstream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1257 Send to clients at constant intervals
1260 m_time_of_day_send_timer -= dtime;
1261 if(m_time_of_day_send_timer < 0.0)
1263 m_time_of_day_send_timer = g_settings.getFloat("time_send_interval");
1265 //JMutexAutoLock envlock(m_env_mutex);
1266 JMutexAutoLock conlock(m_con_mutex);
1268 for(core::map<u16, RemoteClient*>::Iterator
1269 i = m_clients.getIterator();
1270 i.atEnd() == false; i++)
1272 RemoteClient *client = i.getNode()->getValue();
1273 //Player *player = m_env.getPlayer(client->peer_id);
1275 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1276 m_env.getTimeOfDay());
1278 m_con.Send(client->peer_id, 0, data, true);
1284 // Process connection's timeouts
1285 JMutexAutoLock lock2(m_con_mutex);
1286 ScopeProfiler sp(&g_profiler, "Server: connection timeout processing");
1287 m_con.RunTimeouts(dtime);
1291 // This has to be called so that the client list gets synced
1292 // with the peer list of the connection
1293 ScopeProfiler sp(&g_profiler, "Server: peer change handling");
1294 handlePeerChanges();
1299 // This also runs Map's timers
1300 JMutexAutoLock lock(m_env_mutex);
1301 ScopeProfiler sp(&g_profiler, "Server: environment step");
1312 m_liquid_transform_timer += dtime;
1313 if(m_liquid_transform_timer >= 1.00)
1315 m_liquid_transform_timer -= 1.00;
1317 JMutexAutoLock lock(m_env_mutex);
1319 ScopeProfiler sp(&g_profiler, "Server: liquid transform");
1321 core::map<v3s16, MapBlock*> modified_blocks;
1322 m_env.getMap().transformLiquids(modified_blocks);
1327 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1328 ServerMap &map = ((ServerMap&)m_env.getMap());
1329 map.updateLighting(modified_blocks, lighting_modified_blocks);
1331 // Add blocks modified by lighting to modified_blocks
1332 for(core::map<v3s16, MapBlock*>::Iterator
1333 i = lighting_modified_blocks.getIterator();
1334 i.atEnd() == false; i++)
1336 MapBlock *block = i.getNode()->getValue();
1337 modified_blocks.insert(block->getPos(), block);
1341 Set the modified blocks unsent for all the clients
1344 JMutexAutoLock lock2(m_con_mutex);
1346 for(core::map<u16, RemoteClient*>::Iterator
1347 i = m_clients.getIterator();
1348 i.atEnd() == false; i++)
1350 RemoteClient *client = i.getNode()->getValue();
1352 if(modified_blocks.size() > 0)
1354 // Remove block from sent history
1355 client->SetBlocksNotSent(modified_blocks);
1360 // Periodically print some info
1362 float &counter = m_print_info_timer;
1368 JMutexAutoLock lock2(m_con_mutex);
1370 for(core::map<u16, RemoteClient*>::Iterator
1371 i = m_clients.getIterator();
1372 i.atEnd() == false; i++)
1374 //u16 peer_id = i.getNode()->getKey();
1375 RemoteClient *client = i.getNode()->getValue();
1376 Player *player = m_env.getPlayer(client->peer_id);
1379 std::cout<<player->getName()<<"\t";
1380 client->PrintInfo(std::cout);
1385 //if(g_settings.getBool("enable_experimental"))
1389 Check added and deleted active objects
1392 //dstream<<"Server: Checking added and deleted active objects"<<std::endl;
1393 JMutexAutoLock envlock(m_env_mutex);
1394 JMutexAutoLock conlock(m_con_mutex);
1396 ScopeProfiler sp(&g_profiler, "Server: checking added and deleted objects");
1398 // Radius inside which objects are active
1401 for(core::map<u16, RemoteClient*>::Iterator
1402 i = m_clients.getIterator();
1403 i.atEnd() == false; i++)
1405 RemoteClient *client = i.getNode()->getValue();
1406 Player *player = m_env.getPlayer(client->peer_id);
1409 // This can happen if the client timeouts somehow
1410 /*dstream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
1412 <<" has no associated player"<<std::endl;*/
1415 v3s16 pos = floatToInt(player->getPosition(), BS);
1417 core::map<u16, bool> removed_objects;
1418 core::map<u16, bool> added_objects;
1419 m_env.getRemovedActiveObjects(pos, radius,
1420 client->m_known_objects, removed_objects);
1421 m_env.getAddedActiveObjects(pos, radius,
1422 client->m_known_objects, added_objects);
1424 // Ignore if nothing happened
1425 if(removed_objects.size() == 0 && added_objects.size() == 0)
1427 //dstream<<"INFO: active objects: none changed"<<std::endl;
1431 std::string data_buffer;
1435 // Handle removed objects
1436 writeU16((u8*)buf, removed_objects.size());
1437 data_buffer.append(buf, 2);
1438 for(core::map<u16, bool>::Iterator
1439 i = removed_objects.getIterator();
1440 i.atEnd()==false; i++)
1443 u16 id = i.getNode()->getKey();
1444 ServerActiveObject* obj = m_env.getActiveObject(id);
1446 // Add to data buffer for sending
1447 writeU16((u8*)buf, i.getNode()->getKey());
1448 data_buffer.append(buf, 2);
1450 // Remove from known objects
1451 client->m_known_objects.remove(i.getNode()->getKey());
1453 if(obj && obj->m_known_by_count > 0)
1454 obj->m_known_by_count--;
1457 // Handle added objects
1458 writeU16((u8*)buf, added_objects.size());
1459 data_buffer.append(buf, 2);
1460 for(core::map<u16, bool>::Iterator
1461 i = added_objects.getIterator();
1462 i.atEnd()==false; i++)
1465 u16 id = i.getNode()->getKey();
1466 ServerActiveObject* obj = m_env.getActiveObject(id);
1469 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1471 dstream<<"WARNING: "<<__FUNCTION_NAME
1472 <<": NULL object"<<std::endl;
1474 type = obj->getType();
1476 // Add to data buffer for sending
1477 writeU16((u8*)buf, id);
1478 data_buffer.append(buf, 2);
1479 writeU8((u8*)buf, type);
1480 data_buffer.append(buf, 1);
1483 data_buffer.append(serializeLongString(
1484 obj->getClientInitializationData()));
1486 data_buffer.append(serializeLongString(""));
1488 // Add to known objects
1489 client->m_known_objects.insert(i.getNode()->getKey(), false);
1492 obj->m_known_by_count++;
1496 SharedBuffer<u8> reply(2 + data_buffer.size());
1497 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1498 memcpy((char*)&reply[2], data_buffer.c_str(),
1499 data_buffer.size());
1501 m_con.Send(client->peer_id, 0, reply, true);
1503 dstream<<"INFO: Server: Sent object remove/add: "
1504 <<removed_objects.size()<<" removed, "
1505 <<added_objects.size()<<" added, "
1506 <<"packet size is "<<reply.getSize()<<std::endl;
1511 Collect a list of all the objects known by the clients
1512 and report it back to the environment.
1515 core::map<u16, bool> all_known_objects;
1517 for(core::map<u16, RemoteClient*>::Iterator
1518 i = m_clients.getIterator();
1519 i.atEnd() == false; i++)
1521 RemoteClient *client = i.getNode()->getValue();
1522 // Go through all known objects of client
1523 for(core::map<u16, bool>::Iterator
1524 i = client->m_known_objects.getIterator();
1525 i.atEnd()==false; i++)
1527 u16 id = i.getNode()->getKey();
1528 all_known_objects[id] = true;
1532 m_env.setKnownActiveObjects(whatever);
1538 Send object messages
1541 JMutexAutoLock envlock(m_env_mutex);
1542 JMutexAutoLock conlock(m_con_mutex);
1544 ScopeProfiler sp(&g_profiler, "Server: sending object messages");
1547 // Value = data sent by object
1548 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1550 // Get active object messages from environment
1553 ActiveObjectMessage aom = m_env.getActiveObjectMessage();
1557 core::list<ActiveObjectMessage>* message_list = NULL;
1558 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1559 n = buffered_messages.find(aom.id);
1562 message_list = new core::list<ActiveObjectMessage>;
1563 buffered_messages.insert(aom.id, message_list);
1567 message_list = n->getValue();
1569 message_list->push_back(aom);
1572 // Route data to every client
1573 for(core::map<u16, RemoteClient*>::Iterator
1574 i = m_clients.getIterator();
1575 i.atEnd()==false; i++)
1577 RemoteClient *client = i.getNode()->getValue();
1578 std::string reliable_data;
1579 std::string unreliable_data;
1580 // Go through all objects in message buffer
1581 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1582 j = buffered_messages.getIterator();
1583 j.atEnd()==false; j++)
1585 // If object is not known by client, skip it
1586 u16 id = j.getNode()->getKey();
1587 if(client->m_known_objects.find(id) == NULL)
1589 // Get message list of object
1590 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1591 // Go through every message
1592 for(core::list<ActiveObjectMessage>::Iterator
1593 k = list->begin(); k != list->end(); k++)
1595 // Compose the full new data with header
1596 ActiveObjectMessage aom = *k;
1597 std::string new_data;
1600 writeU16((u8*)&buf[0], aom.id);
1601 new_data.append(buf, 2);
1603 new_data += serializeString(aom.datastring);
1604 // Add data to buffer
1606 reliable_data += new_data;
1608 unreliable_data += new_data;
1612 reliable_data and unreliable_data are now ready.
1615 if(reliable_data.size() > 0)
1617 SharedBuffer<u8> reply(2 + reliable_data.size());
1618 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1619 memcpy((char*)&reply[2], reliable_data.c_str(),
1620 reliable_data.size());
1622 m_con.Send(client->peer_id, 0, reply, true);
1624 if(unreliable_data.size() > 0)
1626 SharedBuffer<u8> reply(2 + unreliable_data.size());
1627 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1628 memcpy((char*)&reply[2], unreliable_data.c_str(),
1629 unreliable_data.size());
1630 // Send as unreliable
1631 m_con.Send(client->peer_id, 0, reply, false);
1634 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1636 dstream<<"INFO: Server: Size of object message data: "
1637 <<"reliable: "<<reliable_data.size()
1638 <<", unreliable: "<<unreliable_data.size()
1643 // Clear buffered_messages
1644 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1645 i = buffered_messages.getIterator();
1646 i.atEnd()==false; i++)
1648 delete i.getNode()->getValue();
1652 } // enable_experimental
1655 Send queued-for-sending map edit events.
1658 // Don't send too many at a time
1660 while(m_unsent_map_edit_queue.size() != 0)
1662 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1664 // Players far away from the change are stored here.
1665 // Instead of sending the changes, MapBlocks are set not sent
1667 core::list<u16> far_players;
1669 if(event->type == MEET_ADDNODE)
1671 dstream<<"Server: MEET_ADDNODE"<<std::endl;
1672 sendAddNode(event->p, event->n, event->already_known_by_peer,
1675 else if(event->type == MEET_REMOVENODE)
1677 dstream<<"Server: MEET_REMOVENODE"<<std::endl;
1678 sendRemoveNode(event->p, event->already_known_by_peer,
1681 else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
1683 dstream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
1684 setBlockNotSent(event->p);
1686 else if(event->type == MEET_OTHER)
1688 dstream<<"WARNING: Server: MEET_OTHER not implemented"
1693 dstream<<"WARNING: Server: Unknown MapEditEvent "
1694 <<((u32)event->type)<<std::endl;
1698 Set blocks not sent to far players
1700 core::map<v3s16, MapBlock*> modified_blocks2;
1701 for(core::map<v3s16, bool>::Iterator
1702 i = event->modified_blocks.getIterator();
1703 i.atEnd()==false; i++)
1705 v3s16 p = i.getNode()->getKey();
1706 modified_blocks2.insert(p, m_env.getMap().getBlockNoCreateNoEx(p));
1708 for(core::list<u16>::Iterator
1709 i = far_players.begin();
1710 i != far_players.end(); i++)
1713 RemoteClient *client = getClient(peer_id);
1716 client->SetBlocksNotSent(modified_blocks2);
1721 // Don't send too many at a time
1723 if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
1729 Send object positions
1730 TODO: Get rid of MapBlockObjects
1733 float &counter = m_objectdata_timer;
1735 if(counter >= g_settings.getFloat("objectdata_interval"))
1737 JMutexAutoLock lock1(m_env_mutex);
1738 JMutexAutoLock lock2(m_con_mutex);
1740 ScopeProfiler sp(&g_profiler, "Server: sending mbo positions");
1742 SendObjectData(counter);
1750 TODO: Move to ServerEnvironment and utilize active block stuff
1753 //TimeTaker timer("Step node metadata");
1755 JMutexAutoLock envlock(m_env_mutex);
1756 JMutexAutoLock conlock(m_con_mutex);
1758 ScopeProfiler sp(&g_profiler, "Server: stepping node metadata");
1760 core::map<v3s16, MapBlock*> changed_blocks;
1761 m_env.getMap().nodeMetadataStep(dtime, changed_blocks);
1763 // Use setBlockNotSent
1765 for(core::map<v3s16, MapBlock*>::Iterator
1766 i = changed_blocks.getIterator();
1767 i.atEnd() == false; i++)
1769 MapBlock *block = i.getNode()->getValue();
1771 for(core::map<u16, RemoteClient*>::Iterator
1772 i = m_clients.getIterator();
1773 i.atEnd()==false; i++)
1775 RemoteClient *client = i.getNode()->getValue();
1776 client->SetBlockNotSent(block->getPos());
1782 Trigger emergethread (it somehow gets to a non-triggered but
1783 bysy state sometimes)
1786 float &counter = m_emergethread_trigger_timer;
1792 m_emergethread.trigger();
1796 // Save map, players and auth stuff
1798 float &counter = m_savemap_timer;
1800 if(counter >= g_settings.getFloat("server_map_save_interval"))
1804 ScopeProfiler sp(&g_profiler, "Server: saving stuff");
1807 if(m_authmanager.isModified())
1808 m_authmanager.save();
1811 JMutexAutoLock lock(m_env_mutex);
1812 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == true)
1814 // Save only changed parts
1815 m_env.getMap().save(true);
1817 // Delete unused sectors
1818 u32 deleted_count = m_env.getMap().unloadUnusedData(
1819 g_settings.getFloat("server_unload_unused_sectors_timeout"));
1820 if(deleted_count > 0)
1822 dout_server<<"Server: Unloaded "<<deleted_count
1823 <<" sectors from memory"<<std::endl;
1827 m_env.serializePlayers(m_mapsavedir);
1829 // Save environment metadata
1830 m_env.saveMeta(m_mapsavedir);
1836 void Server::Receive()
1838 DSTACK(__FUNCTION_NAME);
1839 u32 data_maxsize = 10000;
1840 Buffer<u8> data(data_maxsize);
1845 JMutexAutoLock conlock(m_con_mutex);
1846 datasize = m_con.Receive(peer_id, *data, data_maxsize);
1849 // This has to be called so that the client list gets synced
1850 // with the peer list of the connection
1851 handlePeerChanges();
1853 ProcessData(*data, datasize, peer_id);
1855 catch(con::InvalidIncomingDataException &e)
1857 derr_server<<"Server::Receive(): "
1858 "InvalidIncomingDataException: what()="
1859 <<e.what()<<std::endl;
1861 catch(con::PeerNotFoundException &e)
1863 //NOTE: This is not needed anymore
1865 // The peer has been disconnected.
1866 // Find the associated player and remove it.
1868 /*JMutexAutoLock envlock(m_env_mutex);
1870 dout_server<<"ServerThread: peer_id="<<peer_id
1871 <<" has apparently closed connection. "
1872 <<"Removing player."<<std::endl;
1874 m_env.removePlayer(peer_id);*/
1878 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1880 DSTACK(__FUNCTION_NAME);
1881 // Environment is locked first.
1882 JMutexAutoLock envlock(m_env_mutex);
1883 JMutexAutoLock conlock(m_con_mutex);
1887 peer = m_con.GetPeer(peer_id);
1889 catch(con::PeerNotFoundException &e)
1891 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1892 <<peer_id<<" not found"<<std::endl;
1896 u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1904 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1906 if(command == TOSERVER_INIT)
1908 // [0] u16 TOSERVER_INIT
1909 // [2] u8 SER_FMT_VER_HIGHEST
1910 // [3] u8[20] player_name
1911 // [23] u8[28] password <--- can be sent without this, from old versions
1913 if(datasize < 2+1+PLAYERNAME_SIZE)
1916 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1917 <<peer->id<<std::endl;
1919 // First byte after command is maximum supported
1920 // serialization version
1921 u8 client_max = data[2];
1922 u8 our_max = SER_FMT_VER_HIGHEST;
1923 // Use the highest version supported by both
1924 u8 deployed = core::min_(client_max, our_max);
1925 // If it's lower than the lowest supported, give up.
1926 if(deployed < SER_FMT_VER_LOWEST)
1927 deployed = SER_FMT_VER_INVALID;
1929 //peer->serialization_version = deployed;
1930 getClient(peer->id)->pending_serialization_version = deployed;
1932 if(deployed == SER_FMT_VER_INVALID)
1934 derr_server<<DTIME<<"Server: Cannot negotiate "
1935 "serialization version with peer "
1936 <<peer_id<<std::endl;
1945 char playername[PLAYERNAME_SIZE];
1946 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
1948 playername[i] = data[3+i];
1950 playername[PLAYERNAME_SIZE-1] = 0;
1952 if(playername[0]=='\0')
1954 derr_server<<DTIME<<"Server: Player has empty name"<<std::endl;
1955 SendAccessDenied(m_con, peer_id,
1960 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
1962 derr_server<<DTIME<<"Server: Player has invalid name"<<std::endl;
1963 SendAccessDenied(m_con, peer_id,
1964 L"Name contains unallowed characters");
1969 char password[PASSWORD_SIZE];
1970 if(datasize == 2+1+PLAYERNAME_SIZE)
1972 // old version - assume blank password
1977 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
1979 password[i] = data[23+i];
1981 password[PASSWORD_SIZE-1] = 0;
1984 std::string checkpwd;
1985 if(m_authmanager.exists(playername))
1987 checkpwd = m_authmanager.getPassword(playername);
1991 checkpwd = g_settings.get("default_password");
1994 if(password != checkpwd && checkpwd != "")
1996 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1997 <<": supplied invalid password for "
1998 <<playername<<std::endl;
1999 SendAccessDenied(m_con, peer_id, L"Invalid password");
2003 // Add player to auth manager
2004 if(m_authmanager.exists(playername) == false)
2006 derr_server<<DTIME<<"Server: adding player "<<playername
2007 <<" to auth manager"<<std::endl;
2008 m_authmanager.add(playername);
2009 m_authmanager.setPassword(playername, checkpwd);
2010 m_authmanager.setPrivs(playername,
2011 stringToPrivs(g_settings.get("default_privs")));
2012 m_authmanager.save();
2016 Player *player = emergePlayer(playername, password, peer_id);
2020 // DEBUG: Test serialization
2021 std::ostringstream test_os;
2022 player->serialize(test_os);
2023 dstream<<"Player serialization test: \""<<test_os.str()
2025 std::istringstream test_is(test_os.str());
2026 player->deSerialize(test_is);
2029 // If failed, cancel
2032 derr_server<<DTIME<<"Server: peer_id="<<peer_id
2033 <<": failed to emerge player"<<std::endl;
2038 // If a client is already connected to the player, cancel
2039 if(player->peer_id != 0)
2041 derr_server<<DTIME<<"Server: peer_id="<<peer_id
2042 <<" tried to connect to "
2043 "an already connected player (peer_id="
2044 <<player->peer_id<<")"<<std::endl;
2047 // Set client of player
2048 player->peer_id = peer_id;
2051 // Check if player doesn't exist
2053 throw con::InvalidIncomingDataException
2054 ("Server::ProcessData(): INIT: Player doesn't exist");
2056 /*// update name if it was supplied
2057 if(datasize >= 20+3)
2060 player->updateName((const char*)&data[3]);
2064 Answer with a TOCLIENT_INIT
2067 SharedBuffer<u8> reply(2+1+6+8);
2068 writeU16(&reply[0], TOCLIENT_INIT);
2069 writeU8(&reply[2], deployed);
2070 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
2071 writeU64(&reply[2+1+6], m_env.getServerMap().getSeed());
2074 m_con.Send(peer_id, 0, reply, true);
2078 Send complete position information
2080 SendMovePlayer(player);
2085 if(command == TOSERVER_INIT2)
2087 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
2088 <<peer->id<<std::endl;
2091 getClient(peer->id)->serialization_version
2092 = getClient(peer->id)->pending_serialization_version;
2095 Send some initialization data
2098 // Send player info to all players
2101 // Send inventory to player
2102 UpdateCrafting(peer->id);
2103 SendInventory(peer->id);
2107 Player *player = m_env.getPlayer(peer_id);
2108 SendPlayerHP(player);
2113 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2114 m_env.getTimeOfDay());
2115 m_con.Send(peer->id, 0, data, true);
2118 // Send information about server to player in chat
2119 SendChatMessage(peer_id, getStatusString());
2121 // Send information about joining in chat
2123 std::wstring name = L"unknown";
2124 Player *player = m_env.getPlayer(peer_id);
2126 name = narrow_to_wide(player->getName());
2128 std::wstring message;
2131 message += L" joined game";
2132 BroadcastChatMessage(message);
2138 if(peer_ser_ver == SER_FMT_VER_INVALID)
2140 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
2141 " serialization format invalid or not initialized."
2142 " Skipping incoming command="<<command<<std::endl;
2146 Player *player = m_env.getPlayer(peer_id);
2149 derr_server<<"Server::ProcessData(): Cancelling: "
2150 "No player for peer_id="<<peer_id
2154 if(command == TOSERVER_PLAYERPOS)
2156 if(datasize < 2+12+12+4+4)
2160 v3s32 ps = readV3S32(&data[start+2]);
2161 v3s32 ss = readV3S32(&data[start+2+12]);
2162 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2163 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2164 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2165 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2166 pitch = wrapDegrees(pitch);
2167 yaw = wrapDegrees(yaw);
2168 player->setPosition(position);
2169 player->setSpeed(speed);
2170 player->setPitch(pitch);
2171 player->setYaw(yaw);
2173 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2174 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2175 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2177 else if(command == TOSERVER_GOTBLOCKS)
2190 u16 count = data[2];
2191 for(u16 i=0; i<count; i++)
2193 if((s16)datasize < 2+1+(i+1)*6)
2194 throw con::InvalidIncomingDataException
2195 ("GOTBLOCKS length is too short");
2196 v3s16 p = readV3S16(&data[2+1+i*6]);
2197 /*dstream<<"Server: GOTBLOCKS ("
2198 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2199 RemoteClient *client = getClient(peer_id);
2200 client->GotBlock(p);
2203 else if(command == TOSERVER_DELETEDBLOCKS)
2216 u16 count = data[2];
2217 for(u16 i=0; i<count; i++)
2219 if((s16)datasize < 2+1+(i+1)*6)
2220 throw con::InvalidIncomingDataException
2221 ("DELETEDBLOCKS length is too short");
2222 v3s16 p = readV3S16(&data[2+1+i*6]);
2223 /*dstream<<"Server: DELETEDBLOCKS ("
2224 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2225 RemoteClient *client = getClient(peer_id);
2226 client->SetBlockNotSent(p);
2229 else if(command == TOSERVER_CLICK_OBJECT)
2234 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2239 [2] u8 button (0=left, 1=right)
2244 u8 button = readU8(&data[2]);
2246 p.X = readS16(&data[3]);
2247 p.Y = readS16(&data[5]);
2248 p.Z = readS16(&data[7]);
2249 s16 id = readS16(&data[9]);
2250 //u16 item_i = readU16(&data[11]);
2252 MapBlock *block = NULL;
2255 block = m_env.getMap().getBlockNoCreate(p);
2257 catch(InvalidPositionException &e)
2259 derr_server<<"CLICK_OBJECT block not found"<<std::endl;
2263 MapBlockObject *obj = block->getObject(id);
2267 derr_server<<"CLICK_OBJECT object not found"<<std::endl;
2271 //TODO: Check that object is reasonably close
2276 InventoryList *ilist = player->inventory.getList("main");
2277 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
2280 // Skip if inventory has no free space
2281 if(ilist->getUsedSlots() == ilist->getSize())
2283 dout_server<<"Player inventory has no free space"<<std::endl;
2288 Create the inventory item
2290 InventoryItem *item = NULL;
2291 // If it is an item-object, take the item from it
2292 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
2294 item = ((ItemObject*)obj)->createInventoryItem();
2296 // Else create an item of the object
2299 item = new MapBlockObjectItem
2300 (obj->getInventoryString());
2303 // Add to inventory and send inventory
2304 ilist->addItem(item);
2305 UpdateCrafting(player->peer_id);
2306 SendInventory(player->peer_id);
2309 // Remove from block
2310 block->removeObject(id);
2313 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2318 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2324 [2] u8 button (0=left, 1=right)
2328 u8 button = readU8(&data[2]);
2329 u16 id = readS16(&data[3]);
2330 u16 item_i = readU16(&data[11]);
2332 ServerActiveObject *obj = m_env.getActiveObject(id);
2336 derr_server<<"Server: CLICK_ACTIVEOBJECT: object not found"
2341 //TODO: Check that object is reasonably close
2343 // Left click, pick object up (usually)
2346 InventoryList *ilist = player->inventory.getList("main");
2347 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
2350 // Skip if inventory has no free space
2351 if(ilist->getUsedSlots() == ilist->getSize())
2353 dout_server<<"Player inventory has no free space"<<std::endl;
2357 // Skip if object has been removed
2362 Create the inventory item
2364 InventoryItem *item = obj->createPickedUpItem();
2368 // Add to inventory and send inventory
2369 ilist->addItem(item);
2370 UpdateCrafting(player->peer_id);
2371 SendInventory(player->peer_id);
2373 // Remove object from environment
2374 obj->m_removed = true;
2379 Item cannot be picked up. Punch it instead.
2382 ToolItem *titem = NULL;
2383 std::string toolname = "";
2385 InventoryList *mlist = player->inventory.getList("main");
2388 InventoryItem *item = mlist->getItem(item_i);
2389 if(item && (std::string)item->getName() == "ToolItem")
2391 titem = (ToolItem*)item;
2392 toolname = titem->getToolName();
2396 u16 wear = obj->punch(toolname);
2400 bool weared_out = titem->addWear(wear);
2402 mlist->deleteItem(item_i);
2403 SendInventory(player->peer_id);
2409 else if(command == TOSERVER_GROUND_ACTION)
2417 [3] v3s16 nodepos_undersurface
2418 [9] v3s16 nodepos_abovesurface
2423 2: stop digging (all parameters ignored)
2424 3: digging completed
2426 u8 action = readU8(&data[2]);
2428 p_under.X = readS16(&data[3]);
2429 p_under.Y = readS16(&data[5]);
2430 p_under.Z = readS16(&data[7]);
2432 p_over.X = readS16(&data[9]);
2433 p_over.Y = readS16(&data[11]);
2434 p_over.Z = readS16(&data[13]);
2435 u16 item_i = readU16(&data[15]);
2437 //TODO: Check that target is reasonably close
2445 NOTE: This can be used in the future to check if
2446 somebody is cheating, by checking the timing.
2453 else if(action == 2)
2456 RemoteClient *client = getClient(peer->id);
2457 JMutexAutoLock digmutex(client->m_dig_mutex);
2458 client->m_dig_tool_item = -1;
2463 3: Digging completed
2465 else if(action == 3)
2467 // Mandatory parameter; actually used for nothing
2468 core::map<v3s16, MapBlock*> modified_blocks;
2470 u8 material = CONTENT_IGNORE;
2471 u8 mineral = MINERAL_NONE;
2473 bool cannot_remove_node = false;
2477 MapNode n = m_env.getMap().getNode(p_under);
2479 mineral = n.getMineral();
2480 // Get material at position
2482 // If not yet cancelled
2483 if(cannot_remove_node == false)
2485 // If it's not diggable, do nothing
2486 if(content_diggable(material) == false)
2488 derr_server<<"Server: Not finishing digging: "
2489 <<"Node not diggable"
2491 cannot_remove_node = true;
2494 // If not yet cancelled
2495 if(cannot_remove_node == false)
2497 // Get node metadata
2498 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p_under);
2499 if(meta && meta->nodeRemovalDisabled() == true)
2501 derr_server<<"Server: Not finishing digging: "
2502 <<"Node metadata disables removal"
2504 cannot_remove_node = true;
2508 catch(InvalidPositionException &e)
2510 derr_server<<"Server: Not finishing digging: Node not found."
2511 <<" Adding block to emerge queue."
2513 m_emerge_queue.addBlock(peer_id,
2514 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2515 cannot_remove_node = true;
2518 // Make sure the player is allowed to do it
2519 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2521 dstream<<"Player "<<player->getName()<<" cannot remove node"
2522 <<" because privileges are "<<getPlayerPrivs(player)
2524 cannot_remove_node = true;
2528 If node can't be removed, set block to be re-sent to
2531 if(cannot_remove_node)
2533 derr_server<<"Server: Not finishing digging."<<std::endl;
2535 // Client probably has wrong data.
2536 // Set block not sent, so that client will get
2538 dstream<<"Client "<<peer_id<<" tried to dig "
2539 <<"node; but node cannot be removed."
2540 <<" setting MapBlock not sent."<<std::endl;
2541 RemoteClient *client = getClient(peer_id);
2542 v3s16 blockpos = getNodeBlockPos(p_under);
2543 client->SetBlockNotSent(blockpos);
2549 Send the removal to all close-by players.
2550 - If other player is close, send REMOVENODE
2551 - Otherwise set blocks not sent
2553 core::list<u16> far_players;
2554 sendRemoveNode(p_under, peer_id, &far_players, 30);
2557 Update and send inventory
2560 if(g_settings.getBool("creative_mode") == false)
2565 InventoryList *mlist = player->inventory.getList("main");
2568 InventoryItem *item = mlist->getItem(item_i);
2569 if(item && (std::string)item->getName() == "ToolItem")
2571 ToolItem *titem = (ToolItem*)item;
2572 std::string toolname = titem->getToolName();
2574 // Get digging properties for material and tool
2575 DiggingProperties prop =
2576 getDiggingProperties(material, toolname);
2578 if(prop.diggable == false)
2580 derr_server<<"Server: WARNING: Player digged"
2581 <<" with impossible material + tool"
2582 <<" combination"<<std::endl;
2585 bool weared_out = titem->addWear(prop.wear);
2589 mlist->deleteItem(item_i);
2595 Add dug item to inventory
2598 InventoryItem *item = NULL;
2600 if(mineral != MINERAL_NONE)
2601 item = getDiggedMineralItem(mineral);
2606 std::string &dug_s = content_features(material).dug_item;
2609 std::istringstream is(dug_s, std::ios::binary);
2610 item = InventoryItem::deSerialize(is);
2616 // Add a item to inventory
2617 player->inventory.addItem("main", item);
2620 UpdateCrafting(player->peer_id);
2621 SendInventory(player->peer_id);
2627 (this takes some time so it is done after the quick stuff)
2630 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
2632 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2635 Set blocks not sent to far players
2637 for(core::list<u16>::Iterator
2638 i = far_players.begin();
2639 i != far_players.end(); i++)
2642 RemoteClient *client = getClient(peer_id);
2645 client->SetBlocksNotSent(modified_blocks);
2652 else if(action == 1)
2655 InventoryList *ilist = player->inventory.getList("main");
2660 InventoryItem *item = ilist->getItem(item_i);
2662 // If there is no item, it is not possible to add it anywhere
2667 Handle material items
2669 if(std::string("MaterialItem") == item->getName())
2672 // Don't add a node if this is not a free space
2673 MapNode n2 = m_env.getMap().getNode(p_over);
2674 bool no_enough_privs =
2675 ((getPlayerPrivs(player) & PRIV_BUILD)==0);
2677 dstream<<"Player "<<player->getName()<<" cannot add node"
2678 <<" because privileges are "<<getPlayerPrivs(player)
2681 if(content_buildable_to(n2.d) == false
2684 // Client probably has wrong data.
2685 // Set block not sent, so that client will get
2687 dstream<<"Client "<<peer_id<<" tried to place"
2688 <<" node in invalid position; setting"
2689 <<" MapBlock not sent."<<std::endl;
2690 RemoteClient *client = getClient(peer_id);
2691 v3s16 blockpos = getNodeBlockPos(p_over);
2692 client->SetBlockNotSent(blockpos);
2696 catch(InvalidPositionException &e)
2698 derr_server<<"Server: Ignoring ADDNODE: Node not found"
2699 <<" Adding block to emerge queue."
2701 m_emerge_queue.addBlock(peer_id,
2702 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2706 // Reset build time counter
2707 getClient(peer->id)->m_time_from_building = 0.0;
2710 MaterialItem *mitem = (MaterialItem*)item;
2712 n.d = mitem->getMaterial();
2713 if(content_features(n.d).wall_mounted)
2714 n.dir = packDir(p_under - p_over);
2717 Send to all close-by players
2719 core::list<u16> far_players;
2720 sendAddNode(p_over, n, 0, &far_players, 30);
2725 InventoryList *ilist = player->inventory.getList("main");
2726 if(g_settings.getBool("creative_mode") == false && ilist)
2728 // Remove from inventory and send inventory
2729 if(mitem->getCount() == 1)
2730 ilist->deleteItem(item_i);
2734 UpdateCrafting(peer_id);
2735 SendInventory(peer_id);
2741 This takes some time so it is done after the quick stuff
2743 core::map<v3s16, MapBlock*> modified_blocks;
2745 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
2747 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2750 Set blocks not sent to far players
2752 for(core::list<u16>::Iterator
2753 i = far_players.begin();
2754 i != far_players.end(); i++)
2757 RemoteClient *client = getClient(peer_id);
2760 client->SetBlocksNotSent(modified_blocks);
2764 Calculate special events
2767 /*if(n.d == CONTENT_MESE)
2770 for(s16 z=-1; z<=1; z++)
2771 for(s16 y=-1; y<=1; y++)
2772 for(s16 x=-1; x<=1; x++)
2779 Place other item (not a block)
2783 v3s16 blockpos = getNodeBlockPos(p_over);
2786 Check that the block is loaded so that the item
2787 can properly be added to the static list too
2789 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2792 derr_server<<"Error while placing object: "
2793 "block not found"<<std::endl;
2797 dout_server<<"Placing a miscellaneous item on map"
2800 // Calculate a position for it
2801 v3f pos = intToFloat(p_over, BS);
2803 pos.Y -= BS*0.25; // let it drop a bit
2805 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2806 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2811 ServerActiveObject *obj = item->createSAO(&m_env, 0, pos);
2815 derr_server<<"WARNING: item resulted in NULL object, "
2816 <<"not placing onto map"
2821 // Add the object to the environment
2822 m_env.addActiveObject(obj);
2824 dout_server<<"Placed object"<<std::endl;
2826 if(g_settings.getBool("creative_mode") == false)
2828 // Delete the right amount of items from the slot
2829 u16 dropcount = item->getDropCount();
2831 // Delete item if all gone
2832 if(item->getCount() <= dropcount)
2834 if(item->getCount() < dropcount)
2835 dstream<<"WARNING: Server: dropped more items"
2836 <<" than the slot contains"<<std::endl;
2838 InventoryList *ilist = player->inventory.getList("main");
2840 // Remove from inventory and send inventory
2841 ilist->deleteItem(item_i);
2843 // Else decrement it
2845 item->remove(dropcount);
2848 UpdateCrafting(peer_id);
2849 SendInventory(peer_id);
2857 Catch invalid actions
2861 derr_server<<"WARNING: Server: Invalid action "
2862 <<action<<std::endl;
2866 else if(command == TOSERVER_RELEASE)
2875 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2878 else if(command == TOSERVER_SIGNTEXT)
2880 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2889 std::string datastring((char*)&data[2], datasize-2);
2890 std::istringstream is(datastring, std::ios_base::binary);
2893 is.read((char*)buf, 6);
2894 v3s16 blockpos = readV3S16(buf);
2895 is.read((char*)buf, 2);
2896 s16 id = readS16(buf);
2897 is.read((char*)buf, 2);
2898 u16 textlen = readU16(buf);
2900 for(u16 i=0; i<textlen; i++)
2902 is.read((char*)buf, 1);
2903 text += (char)buf[0];
2906 MapBlock *block = NULL;
2909 block = m_env.getMap().getBlockNoCreate(blockpos);
2911 catch(InvalidPositionException &e)
2913 derr_server<<"Error while setting sign text: "
2914 "block not found"<<std::endl;
2918 MapBlockObject *obj = block->getObject(id);
2921 derr_server<<"Error while setting sign text: "
2922 "object not found"<<std::endl;
2926 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2928 derr_server<<"Error while setting sign text: "
2929 "object is not a sign"<<std::endl;
2933 ((SignObject*)obj)->setText(text);
2935 obj->getBlock()->setChangedFlag();
2937 else if(command == TOSERVER_SIGNNODETEXT)
2939 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2947 std::string datastring((char*)&data[2], datasize-2);
2948 std::istringstream is(datastring, std::ios_base::binary);
2951 is.read((char*)buf, 6);
2952 v3s16 p = readV3S16(buf);
2953 is.read((char*)buf, 2);
2954 u16 textlen = readU16(buf);
2956 for(u16 i=0; i<textlen; i++)
2958 is.read((char*)buf, 1);
2959 text += (char)buf[0];
2962 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2965 if(meta->typeId() != CONTENT_SIGN_WALL)
2967 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
2968 signmeta->setText(text);
2970 v3s16 blockpos = getNodeBlockPos(p);
2971 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2974 block->setChangedFlag();
2977 for(core::map<u16, RemoteClient*>::Iterator
2978 i = m_clients.getIterator();
2979 i.atEnd()==false; i++)
2981 RemoteClient *client = i.getNode()->getValue();
2982 client->SetBlockNotSent(blockpos);
2985 else if(command == TOSERVER_INVENTORY_ACTION)
2987 /*// Ignore inventory changes if in creative mode
2988 if(g_settings.getBool("creative_mode") == true)
2990 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2994 // Strip command and create a stream
2995 std::string datastring((char*)&data[2], datasize-2);
2996 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2997 std::istringstream is(datastring, std::ios_base::binary);
2999 InventoryAction *a = InventoryAction::deSerialize(is);
3004 c.current_player = player;
3007 Handle craftresult specially if not in creative mode
3009 bool disable_action = false;
3010 if(a->getType() == IACTION_MOVE
3011 && g_settings.getBool("creative_mode") == false)
3013 IMoveAction *ma = (IMoveAction*)a;
3014 if(ma->to_inv == "current_player" &&
3015 ma->from_inv == "current_player")
3017 InventoryList *rlist = player->inventory.getList("craftresult");
3019 InventoryList *clist = player->inventory.getList("craft");
3021 InventoryList *mlist = player->inventory.getList("main");
3024 Craftresult is no longer preview if something
3027 if(ma->to_list == "craftresult"
3028 && ma->from_list != "craftresult")
3030 // If it currently is a preview, remove
3032 if(player->craftresult_is_preview)
3034 rlist->deleteItem(0);
3036 player->craftresult_is_preview = false;
3039 Crafting takes place if this condition is true.
3041 if(player->craftresult_is_preview &&
3042 ma->from_list == "craftresult")
3044 player->craftresult_is_preview = false;
3045 clist->decrementMaterials(1);
3048 If the craftresult is placed on itself, move it to
3049 main inventory instead of doing the action
3051 if(ma->to_list == "craftresult"
3052 && ma->from_list == "craftresult")
3054 disable_action = true;
3056 InventoryItem *item1 = rlist->changeItem(0, NULL);
3057 mlist->addItem(item1);
3062 if(disable_action == false)
3064 // Feed action to player inventory
3072 UpdateCrafting(player->peer_id);
3073 SendInventory(player->peer_id);
3078 dstream<<"TOSERVER_INVENTORY_ACTION: "
3079 <<"InventoryAction::deSerialize() returned NULL"
3083 else if(command == TOSERVER_CHAT_MESSAGE)
3091 std::string datastring((char*)&data[2], datasize-2);
3092 std::istringstream is(datastring, std::ios_base::binary);
3095 is.read((char*)buf, 2);
3096 u16 len = readU16(buf);
3098 std::wstring message;
3099 for(u16 i=0; i<len; i++)
3101 is.read((char*)buf, 2);
3102 message += (wchar_t)readU16(buf);
3105 // Get player name of this client
3106 std::wstring name = narrow_to_wide(player->getName());
3108 // Line to send to players
3110 // Whether to send to the player that sent the line
3111 bool send_to_sender = false;
3112 // Whether to send to other players
3113 bool send_to_others = false;
3115 // Local player gets all privileges regardless of
3116 // what's set on their account.
3117 u64 privs = getPlayerPrivs(player);
3120 std::wstring commandprefix = L"/#";
3121 if(message.substr(0, commandprefix.size()) == commandprefix)
3123 line += L"Server: ";
3125 message = message.substr(commandprefix.size());
3127 ServerCommandContext *ctx = new ServerCommandContext(
3128 str_split(message, L' '),
3134 line += processServerCommand(ctx);
3135 send_to_sender = ctx->flags & 1;
3136 send_to_others = ctx->flags & 2;
3142 if(privs & PRIV_SHOUT)
3148 send_to_others = true;
3152 line += L"Server: You are not allowed to shout";
3153 send_to_sender = true;
3159 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
3162 Send the message to clients
3164 for(core::map<u16, RemoteClient*>::Iterator
3165 i = m_clients.getIterator();
3166 i.atEnd() == false; i++)
3168 // Get client and check that it is valid
3169 RemoteClient *client = i.getNode()->getValue();
3170 assert(client->peer_id == i.getNode()->getKey());
3171 if(client->serialization_version == SER_FMT_VER_INVALID)
3175 bool sender_selected = (peer_id == client->peer_id);
3176 if(sender_selected == true && send_to_sender == false)
3178 if(sender_selected == false && send_to_others == false)
3181 SendChatMessage(client->peer_id, line);
3185 else if(command == TOSERVER_DAMAGE)
3187 if(g_settings.getBool("enable_damage"))
3189 std::string datastring((char*)&data[2], datasize-2);
3190 std::istringstream is(datastring, std::ios_base::binary);
3191 u8 damage = readU8(is);
3192 if(player->hp > damage)
3194 player->hp -= damage;
3200 dstream<<"TODO: Server: TOSERVER_HP_DECREMENT: Player dies"
3203 v3f pos = findSpawnPos(m_env.getServerMap());
3204 player->setPosition(pos);
3206 SendMovePlayer(player);
3207 SendPlayerHP(player);
3209 //TODO: Throw items around
3213 SendPlayerHP(player);
3215 else if(command == TOSERVER_PASSWORD)
3218 [0] u16 TOSERVER_PASSWORD
3219 [2] u8[28] old password
3220 [30] u8[28] new password
3223 if(datasize != 2+PASSWORD_SIZE*2)
3225 /*char password[PASSWORD_SIZE];
3226 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3227 password[i] = data[2+i];
3228 password[PASSWORD_SIZE-1] = 0;*/
3230 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3238 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3240 char c = data[2+PASSWORD_SIZE+i];
3246 std::string playername = player->getName();
3248 if(m_authmanager.exists(playername) == false)
3250 dstream<<"Server: playername not found in authmanager"<<std::endl;
3251 // Wrong old password supplied!!
3252 SendChatMessage(peer_id, L"playername not found in authmanager");
3256 std::string checkpwd = m_authmanager.getPassword(playername);
3258 if(oldpwd != checkpwd)
3260 dstream<<"Server: invalid old password"<<std::endl;
3261 // Wrong old password supplied!!
3262 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
3266 m_authmanager.setPassword(playername, newpwd);
3268 dstream<<"Server: password change successful for "<<playername
3270 SendChatMessage(peer_id, L"Password change successful");
3274 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
3275 "unknown command "<<command<<std::endl;
3279 catch(SendFailedException &e)
3281 derr_server<<"Server::ProcessData(): SendFailedException: "
3287 void Server::onMapEditEvent(MapEditEvent *event)
3289 dstream<<"Server::onMapEditEvent()"<<std::endl;
3290 if(m_ignore_map_edit_events)
3292 MapEditEvent *e = event->clone();
3293 m_unsent_map_edit_queue.push_back(e);
3296 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3298 if(id == "current_player")
3300 assert(c->current_player);
3301 return &(c->current_player->inventory);
3305 std::string id0 = fn.next(":");
3307 if(id0 == "nodemeta")
3310 p.X = stoi(fn.next(","));
3311 p.Y = stoi(fn.next(","));
3312 p.Z = stoi(fn.next(","));
3313 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3315 return meta->getInventory();
3316 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3317 <<"no metadata found"<<std::endl;
3321 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3324 void Server::inventoryModified(InventoryContext *c, std::string id)
3326 if(id == "current_player")
3328 assert(c->current_player);
3330 UpdateCrafting(c->current_player->peer_id);
3331 SendInventory(c->current_player->peer_id);
3336 std::string id0 = fn.next(":");
3338 if(id0 == "nodemeta")
3341 p.X = stoi(fn.next(","));
3342 p.Y = stoi(fn.next(","));
3343 p.Z = stoi(fn.next(","));
3344 v3s16 blockpos = getNodeBlockPos(p);
3346 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3348 meta->inventoryModified();
3350 for(core::map<u16, RemoteClient*>::Iterator
3351 i = m_clients.getIterator();
3352 i.atEnd()==false; i++)
3354 RemoteClient *client = i.getNode()->getValue();
3355 client->SetBlockNotSent(blockpos);
3361 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3364 core::list<PlayerInfo> Server::getPlayerInfo()
3366 DSTACK(__FUNCTION_NAME);
3367 JMutexAutoLock envlock(m_env_mutex);
3368 JMutexAutoLock conlock(m_con_mutex);
3370 core::list<PlayerInfo> list;
3372 core::list<Player*> players = m_env.getPlayers();
3374 core::list<Player*>::Iterator i;
3375 for(i = players.begin();
3376 i != players.end(); i++)
3380 Player *player = *i;
3383 con::Peer *peer = m_con.GetPeer(player->peer_id);
3384 // Copy info from peer to info struct
3386 info.address = peer->address;
3387 info.avg_rtt = peer->avg_rtt;
3389 catch(con::PeerNotFoundException &e)
3391 // Set dummy peer info
3393 info.address = Address(0,0,0,0,0);
3397 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3398 info.position = player->getPosition();
3400 list.push_back(info);
3407 void Server::peerAdded(con::Peer *peer)
3409 DSTACK(__FUNCTION_NAME);
3410 dout_server<<"Server::peerAdded(): peer->id="
3411 <<peer->id<<std::endl;
3414 c.type = PEER_ADDED;
3415 c.peer_id = peer->id;
3417 m_peer_change_queue.push_back(c);
3420 void Server::deletingPeer(con::Peer *peer, bool timeout)
3422 DSTACK(__FUNCTION_NAME);
3423 dout_server<<"Server::deletingPeer(): peer->id="
3424 <<peer->id<<", timeout="<<timeout<<std::endl;
3427 c.type = PEER_REMOVED;
3428 c.peer_id = peer->id;
3429 c.timeout = timeout;
3430 m_peer_change_queue.push_back(c);
3437 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3439 DSTACK(__FUNCTION_NAME);
3440 std::ostringstream os(std::ios_base::binary);
3442 writeU16(os, TOCLIENT_HP);
3446 std::string s = os.str();
3447 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3449 con.Send(peer_id, 0, data, true);
3452 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3453 const std::wstring &reason)
3455 DSTACK(__FUNCTION_NAME);
3456 std::ostringstream os(std::ios_base::binary);
3458 writeU16(os, TOCLIENT_ACCESS_DENIED);
3459 os<<serializeWideString(reason);
3462 std::string s = os.str();
3463 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3465 con.Send(peer_id, 0, data, true);
3469 Non-static send methods
3472 void Server::SendObjectData(float dtime)
3474 DSTACK(__FUNCTION_NAME);
3476 core::map<v3s16, bool> stepped_blocks;
3478 for(core::map<u16, RemoteClient*>::Iterator
3479 i = m_clients.getIterator();
3480 i.atEnd() == false; i++)
3482 u16 peer_id = i.getNode()->getKey();
3483 RemoteClient *client = i.getNode()->getValue();
3484 assert(client->peer_id == peer_id);
3486 if(client->serialization_version == SER_FMT_VER_INVALID)
3489 client->SendObjectData(this, dtime, stepped_blocks);
3493 void Server::SendPlayerInfos()
3495 DSTACK(__FUNCTION_NAME);
3497 //JMutexAutoLock envlock(m_env_mutex);
3499 // Get connected players
3500 core::list<Player*> players = m_env.getPlayers(true);
3502 u32 player_count = players.getSize();
3503 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3505 SharedBuffer<u8> data(datasize);
3506 writeU16(&data[0], TOCLIENT_PLAYERINFO);
3509 core::list<Player*>::Iterator i;
3510 for(i = players.begin();
3511 i != players.end(); i++)
3513 Player *player = *i;
3515 /*dstream<<"Server sending player info for player with "
3516 "peer_id="<<player->peer_id<<std::endl;*/
3518 writeU16(&data[start], player->peer_id);
3519 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3520 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3521 start += 2+PLAYERNAME_SIZE;
3524 //JMutexAutoLock conlock(m_con_mutex);
3527 m_con.SendToAll(0, data, true);
3530 void Server::SendInventory(u16 peer_id)
3532 DSTACK(__FUNCTION_NAME);
3534 Player* player = m_env.getPlayer(peer_id);
3541 std::ostringstream os;
3542 //os.imbue(std::locale("C"));
3544 player->inventory.serialize(os);
3546 std::string s = os.str();
3548 SharedBuffer<u8> data(s.size()+2);
3549 writeU16(&data[0], TOCLIENT_INVENTORY);
3550 memcpy(&data[2], s.c_str(), s.size());
3553 m_con.Send(peer_id, 0, data, true);
3556 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3558 DSTACK(__FUNCTION_NAME);
3560 std::ostringstream os(std::ios_base::binary);
3564 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3565 os.write((char*)buf, 2);
3568 writeU16(buf, message.size());
3569 os.write((char*)buf, 2);
3572 for(u32 i=0; i<message.size(); i++)
3576 os.write((char*)buf, 2);
3580 std::string s = os.str();
3581 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3583 m_con.Send(peer_id, 0, data, true);
3586 void Server::BroadcastChatMessage(const std::wstring &message)
3588 for(core::map<u16, RemoteClient*>::Iterator
3589 i = m_clients.getIterator();
3590 i.atEnd() == false; i++)
3592 // Get client and check that it is valid
3593 RemoteClient *client = i.getNode()->getValue();
3594 assert(client->peer_id == i.getNode()->getKey());
3595 if(client->serialization_version == SER_FMT_VER_INVALID)
3598 SendChatMessage(client->peer_id, message);
3602 void Server::SendPlayerHP(Player *player)
3604 SendHP(m_con, player->peer_id, player->hp);
3607 void Server::SendMovePlayer(Player *player)
3609 DSTACK(__FUNCTION_NAME);
3610 std::ostringstream os(std::ios_base::binary);
3612 writeU16(os, TOCLIENT_MOVE_PLAYER);
3613 writeV3F1000(os, player->getPosition());
3614 writeF1000(os, player->getPitch());
3615 writeF1000(os, player->getYaw());
3618 v3f pos = player->getPosition();
3619 f32 pitch = player->getPitch();
3620 f32 yaw = player->getYaw();
3621 dstream<<"Server sending TOCLIENT_MOVE_PLAYER"
3622 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3629 std::string s = os.str();
3630 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3632 m_con.Send(player->peer_id, 0, data, true);
3635 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3636 core::list<u16> *far_players, float far_d_nodes)
3638 float maxd = far_d_nodes*BS;
3639 v3f p_f = intToFloat(p, BS);
3643 SharedBuffer<u8> reply(replysize);
3644 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3645 writeS16(&reply[2], p.X);
3646 writeS16(&reply[4], p.Y);
3647 writeS16(&reply[6], p.Z);
3649 for(core::map<u16, RemoteClient*>::Iterator
3650 i = m_clients.getIterator();
3651 i.atEnd() == false; i++)
3653 // Get client and check that it is valid
3654 RemoteClient *client = i.getNode()->getValue();
3655 assert(client->peer_id == i.getNode()->getKey());
3656 if(client->serialization_version == SER_FMT_VER_INVALID)
3659 // Don't send if it's the same one
3660 if(client->peer_id == ignore_id)
3666 Player *player = m_env.getPlayer(client->peer_id);
3669 // If player is far away, only set modified blocks not sent
3670 v3f player_pos = player->getPosition();
3671 if(player_pos.getDistanceFrom(p_f) > maxd)
3673 far_players->push_back(client->peer_id);
3680 m_con.Send(client->peer_id, 0, reply, true);
3684 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3685 core::list<u16> *far_players, float far_d_nodes)
3687 float maxd = far_d_nodes*BS;
3688 v3f p_f = intToFloat(p, BS);
3690 for(core::map<u16, RemoteClient*>::Iterator
3691 i = m_clients.getIterator();
3692 i.atEnd() == false; i++)
3694 // Get client and check that it is valid
3695 RemoteClient *client = i.getNode()->getValue();
3696 assert(client->peer_id == i.getNode()->getKey());
3697 if(client->serialization_version == SER_FMT_VER_INVALID)
3700 // Don't send if it's the same one
3701 if(client->peer_id == ignore_id)
3707 Player *player = m_env.getPlayer(client->peer_id);
3710 // If player is far away, only set modified blocks not sent
3711 v3f player_pos = player->getPosition();
3712 if(player_pos.getDistanceFrom(p_f) > maxd)
3714 far_players->push_back(client->peer_id);
3721 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3722 SharedBuffer<u8> reply(replysize);
3723 writeU16(&reply[0], TOCLIENT_ADDNODE);
3724 writeS16(&reply[2], p.X);
3725 writeS16(&reply[4], p.Y);
3726 writeS16(&reply[6], p.Z);
3727 n.serialize(&reply[8], client->serialization_version);
3730 m_con.Send(client->peer_id, 0, reply, true);
3734 void Server::setBlockNotSent(v3s16 p)
3736 for(core::map<u16, RemoteClient*>::Iterator
3737 i = m_clients.getIterator();
3738 i.atEnd()==false; i++)
3740 RemoteClient *client = i.getNode()->getValue();
3741 client->SetBlockNotSent(p);
3745 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3747 DSTACK(__FUNCTION_NAME);
3749 v3s16 p = block->getPos();
3753 bool completely_air = true;
3754 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3755 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3756 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3758 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
3760 completely_air = false;
3761 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
3766 dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
3768 dstream<<"[completely air] ";
3773 Create a packet with the block in the right format
3776 std::ostringstream os(std::ios_base::binary);
3777 block->serialize(os, ver);
3778 std::string s = os.str();
3779 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3781 u32 replysize = 8 + blockdata.getSize();
3782 SharedBuffer<u8> reply(replysize);
3783 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3784 writeS16(&reply[2], p.X);
3785 writeS16(&reply[4], p.Y);
3786 writeS16(&reply[6], p.Z);
3787 memcpy(&reply[8], *blockdata, blockdata.getSize());
3789 /*dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3790 <<": \tpacket size: "<<replysize<<std::endl;*/
3795 m_con.Send(peer_id, 1, reply, true);
3798 void Server::SendBlocks(float dtime)
3800 DSTACK(__FUNCTION_NAME);
3802 JMutexAutoLock envlock(m_env_mutex);
3803 JMutexAutoLock conlock(m_con_mutex);
3805 //TimeTaker timer("Server::SendBlocks");
3807 core::array<PrioritySortedBlockTransfer> queue;
3809 s32 total_sending = 0;
3812 ScopeProfiler sp(&g_profiler, "Server: selecting blocks for sending");
3814 for(core::map<u16, RemoteClient*>::Iterator
3815 i = m_clients.getIterator();
3816 i.atEnd() == false; i++)
3818 RemoteClient *client = i.getNode()->getValue();
3819 assert(client->peer_id == i.getNode()->getKey());
3821 total_sending += client->SendingCount();
3823 if(client->serialization_version == SER_FMT_VER_INVALID)
3826 client->GetNextBlocks(this, dtime, queue);
3831 // Lowest priority number comes first.
3832 // Lowest is most important.
3835 for(u32 i=0; i<queue.size(); i++)
3837 //TODO: Calculate limit dynamically
3838 if(total_sending >= g_settings.getS32
3839 ("max_simultaneous_block_sends_server_total"))
3842 PrioritySortedBlockTransfer q = queue[i];
3844 MapBlock *block = NULL;
3847 block = m_env.getMap().getBlockNoCreate(q.pos);
3849 catch(InvalidPositionException &e)
3854 RemoteClient *client = getClient(q.peer_id);
3856 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3858 client->SentBlock(q.pos);
3868 void Server::UpdateCrafting(u16 peer_id)
3870 DSTACK(__FUNCTION_NAME);
3872 Player* player = m_env.getPlayer(peer_id);
3876 Calculate crafting stuff
3878 if(g_settings.getBool("creative_mode") == false)
3880 InventoryList *clist = player->inventory.getList("craft");
3881 InventoryList *rlist = player->inventory.getList("craftresult");
3883 if(rlist->getUsedSlots() == 0)
3884 player->craftresult_is_preview = true;
3886 if(rlist && player->craftresult_is_preview)
3888 rlist->clearItems();
3890 if(clist && rlist && player->craftresult_is_preview)
3892 InventoryItem *items[9];
3893 for(u16 i=0; i<9; i++)
3895 items[i] = clist->getItem(i);
3898 // Get result of crafting grid
3899 InventoryItem *result = craft_get_result(items);
3901 rlist->addItem(result);
3904 } // if creative_mode == false
3907 RemoteClient* Server::getClient(u16 peer_id)
3909 DSTACK(__FUNCTION_NAME);
3910 //JMutexAutoLock lock(m_con_mutex);
3911 core::map<u16, RemoteClient*>::Node *n;
3912 n = m_clients.find(peer_id);
3913 // A client should exist for all peers
3915 return n->getValue();
3918 std::wstring Server::getStatusString()
3920 std::wostringstream os(std::ios_base::binary);
3923 os<<L"version="<<narrow_to_wide(VERSION_STRING);
3925 os<<L", uptime="<<m_uptime.get();
3926 // Information about clients
3928 for(core::map<u16, RemoteClient*>::Iterator
3929 i = m_clients.getIterator();
3930 i.atEnd() == false; i++)
3932 // Get client and check that it is valid
3933 RemoteClient *client = i.getNode()->getValue();
3934 assert(client->peer_id == i.getNode()->getKey());
3935 if(client->serialization_version == SER_FMT_VER_INVALID)
3938 Player *player = m_env.getPlayer(client->peer_id);
3939 // Get name of player
3940 std::wstring name = L"unknown";
3942 name = narrow_to_wide(player->getName());
3943 // Add name to information string
3947 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
3948 os<<" WARNING: Map saving is disabled."<<std::endl;
3952 v3f findSpawnPos(ServerMap &map)
3954 //return v3f(50,50,50)*BS;
3957 s16 groundheight = 0;
3960 nodepos = v2s16(0,0);
3965 // Try to find a good place a few times
3966 for(s32 i=0; i<1000; i++)
3969 // We're going to try to throw the player to this position
3970 nodepos = v2s16(-range + (myrand()%(range*2)),
3971 -range + (myrand()%(range*2)));
3972 v2s16 sectorpos = getNodeSectorPos(nodepos);
3973 // Get sector (NOTE: Don't get because it's slow)
3974 //m_env.getMap().emergeSector(sectorpos);
3975 // Get ground height at point (fallbacks to heightmap function)
3976 groundheight = map.findGroundLevel(nodepos);
3977 // Don't go underwater
3978 if(groundheight < WATER_LEVEL)
3980 //dstream<<"-> Underwater"<<std::endl;
3983 // Don't go to high places
3984 if(groundheight > WATER_LEVEL + 4)
3986 //dstream<<"-> Underwater"<<std::endl;
3990 // Found a good place
3991 //dstream<<"Searched through "<<i<<" places."<<std::endl;
3996 // If no suitable place was not found, go above water at least.
3997 if(groundheight < WATER_LEVEL)
3998 groundheight = WATER_LEVEL;
4000 return intToFloat(v3s16(
4007 Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id)
4010 Try to get an existing player
4012 Player *player = m_env.getPlayer(name);
4015 // If player is already connected, cancel
4016 if(player->peer_id != 0)
4018 dstream<<"emergePlayer(): Player already connected"<<std::endl;
4023 player->peer_id = peer_id;
4025 // Reset inventory to creative if in creative mode
4026 if(g_settings.getBool("creative_mode"))
4028 craft_set_creative_inventory(player);
4035 If player with the wanted peer_id already exists, cancel.
4037 if(m_env.getPlayer(peer_id) != NULL)
4039 dstream<<"emergePlayer(): Player with wrong name but same"
4040 " peer_id already exists"<<std::endl;
4048 player = new ServerRemotePlayer();
4049 //player->peer_id = c.peer_id;
4050 //player->peer_id = PEER_ID_INEXISTENT;
4051 player->peer_id = peer_id;
4052 player->updateName(name);
4053 m_authmanager.add(name);
4054 m_authmanager.setPassword(name, password);
4055 m_authmanager.setPrivs(name,
4056 stringToPrivs(g_settings.get("default_privs")));
4062 dstream<<"Server: Finding spawn place for player \""
4063 <<player->getName()<<"\""<<std::endl;
4065 v3f pos = findSpawnPos(m_env.getServerMap());
4067 player->setPosition(pos);
4070 Add player to environment
4073 m_env.addPlayer(player);
4076 Add stuff to inventory
4079 if(g_settings.getBool("creative_mode"))
4081 craft_set_creative_inventory(player);
4083 else if(g_settings.getBool("give_initial_stuff"))
4085 craft_give_initial_stuff(player);
4090 } // create new player
4093 void Server::handlePeerChange(PeerChange &c)
4095 JMutexAutoLock envlock(m_env_mutex);
4096 JMutexAutoLock conlock(m_con_mutex);
4098 if(c.type == PEER_ADDED)
4105 core::map<u16, RemoteClient*>::Node *n;
4106 n = m_clients.find(c.peer_id);
4107 // The client shouldn't already exist
4111 RemoteClient *client = new RemoteClient();
4112 client->peer_id = c.peer_id;
4113 m_clients.insert(client->peer_id, client);
4116 else if(c.type == PEER_REMOVED)
4123 core::map<u16, RemoteClient*>::Node *n;
4124 n = m_clients.find(c.peer_id);
4125 // The client should exist
4129 Mark objects to be not known by the client
4131 RemoteClient *client = n->getValue();
4133 for(core::map<u16, bool>::Iterator
4134 i = client->m_known_objects.getIterator();
4135 i.atEnd()==false; i++)
4138 u16 id = i.getNode()->getKey();
4139 ServerActiveObject* obj = m_env.getActiveObject(id);
4141 if(obj && obj->m_known_by_count > 0)
4142 obj->m_known_by_count--;
4145 // Collect information about leaving in chat
4146 std::wstring message;
4148 std::wstring name = L"unknown";
4149 Player *player = m_env.getPlayer(c.peer_id);
4151 name = narrow_to_wide(player->getName());
4155 message += L" left game";
4157 message += L" (timed out)";
4162 m_env.removePlayer(c.peer_id);
4165 // Set player client disconnected
4167 Player *player = m_env.getPlayer(c.peer_id);
4169 player->peer_id = 0;
4173 delete m_clients[c.peer_id];
4174 m_clients.remove(c.peer_id);
4176 // Send player info to all remaining clients
4179 // Send leave chat message to all remaining clients
4180 BroadcastChatMessage(message);
4189 void Server::handlePeerChanges()
4191 while(m_peer_change_queue.size() > 0)
4193 PeerChange c = m_peer_change_queue.pop_front();
4195 dout_server<<"Server: Handling peer change: "
4196 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4199 handlePeerChange(c);
4203 u64 Server::getPlayerPrivs(Player *player)
4207 std::string playername = player->getName();
4208 // Local player gets all privileges regardless of
4209 // what's set on their account.
4210 if(g_settings.get("name") == playername)
4216 return getPlayerAuthPrivs(playername);
4220 void dedicated_server_loop(Server &server, bool &kill)
4222 DSTACK(__FUNCTION_NAME);
4224 dstream<<DTIME<<std::endl;
4225 dstream<<"========================"<<std::endl;
4226 dstream<<"Running dedicated server"<<std::endl;
4227 dstream<<"========================"<<std::endl;
4230 IntervalLimiter m_profiler_interval;
4234 // This is kind of a hack but can be done like this
4235 // because server.step() is very light
4237 ScopeProfiler sp(&g_profiler, "dedicated server sleep");
4242 if(server.getShutdownRequested() || kill)
4244 dstream<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4251 float profiler_print_interval =
4252 g_settings.getFloat("profiler_print_interval");
4253 if(profiler_print_interval != 0)
4255 if(m_profiler_interval.step(0.030, profiler_print_interval))
4257 dstream<<"Profiler:"<<std::endl;
4258 g_profiler.print(dstream);
4266 static int counter = 0;
4272 core::list<PlayerInfo> list = server.getPlayerInfo();
4273 core::list<PlayerInfo>::Iterator i;
4274 static u32 sum_old = 0;
4275 u32 sum = PIChecksum(list);
4278 dstream<<DTIME<<"Player info:"<<std::endl;
4279 for(i=list.begin(); i!=list.end(); i++)
4281 i->PrintLine(&dstream);