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 void * ServerThread::Thread()
44 DSTACK(__FUNCTION_NAME);
46 BEGIN_DEBUG_EXCEPTION_HANDLER
51 //TimeTaker timer("AsyncRunStep() + Receive()");
54 //TimeTaker timer("AsyncRunStep()");
55 m_server->AsyncRunStep();
58 //dout_server<<"Running m_server->Receive()"<<std::endl;
61 catch(con::NoIncomingDataException &e)
64 catch(con::PeerNotFoundException &e)
66 dout_server<<"Server: PeerNotFoundException"<<std::endl;
70 END_DEBUG_EXCEPTION_HANDLER
75 void * EmergeThread::Thread()
79 DSTACK(__FUNCTION_NAME);
83 BEGIN_DEBUG_EXCEPTION_HANDLER
86 Get block info from queue, emerge them and send them
89 After queue is empty, exit.
93 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
97 SharedPtr<QueuedBlockEmerge> q(qptr);
103 Do not generate over-limit
105 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
106 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
107 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
108 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
109 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
110 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
113 //derr_server<<"EmergeThread::Thread(): running"<<std::endl;
115 //TimeTaker timer("block emerge");
118 Try to emerge it from somewhere.
120 If it is only wanted as optional, only loading from disk
125 Check if any peer wants it as non-optional. In that case it
128 Also decrement the emerge queue count in clients.
131 bool optional = true;
134 core::map<u16, u8>::Iterator i;
135 for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
137 //u16 peer_id = i.getNode()->getKey();
140 u8 flags = i.getNode()->getValue();
141 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
147 /*dstream<<"EmergeThread: p="
148 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
149 <<"optional="<<optional<<std::endl;*/
151 ServerMap &map = ((ServerMap&)m_server->m_env.getMap());
153 core::map<v3s16, MapBlock*> changed_blocks;
154 core::map<v3s16, MapBlock*> lighting_invalidated_blocks;
156 MapBlock *block = NULL;
157 bool got_block = true;
158 core::map<v3s16, MapBlock*> modified_blocks;
160 bool only_from_disk = false;
163 only_from_disk = true;
165 v2s16 chunkpos = map.sector_to_chunk(p2d);
167 bool generate_chunk = false;
168 if(only_from_disk == false)
170 JMutexAutoLock envlock(m_server->m_env_mutex);
171 if(map.chunkNonVolatile(chunkpos) == false)
172 generate_chunk = true;
179 JMutexAutoLock envlock(m_server->m_env_mutex);
180 map.initChunkMake(data, chunkpos);
186 JMutexAutoLock envlock(m_server->m_env_mutex);
187 map.finishChunkMake(data, changed_blocks);
192 Fetch block from map or generate a single block
195 JMutexAutoLock envlock(m_server->m_env_mutex);
197 // Load sector if it isn't loaded
198 if(map.getSectorNoGenerateNoEx(p2d) == NULL)
199 map.loadSectorFull(p2d);
201 block = map.getBlockNoCreateNoEx(p);
202 if(!block || block->isDummy())
210 // Get, load or create sector
211 ServerMapSector *sector =
212 (ServerMapSector*)map.createSector(p2d);
214 block = map.generateBlock(p, block, sector, changed_blocks,
215 lighting_invalidated_blocks);
222 if(block->getLightingExpired()){
223 lighting_invalidated_blocks[block->getPos()] = block;
227 // TODO: Some additional checking and lighting updating,
232 JMutexAutoLock envlock(m_server->m_env_mutex);
237 Collect a list of blocks that have been modified in
238 addition to the fetched one.
241 if(lighting_invalidated_blocks.size() > 0)
243 /*dstream<<"lighting "<<lighting_invalidated_blocks.size()
244 <<" blocks"<<std::endl;*/
246 // 50-100ms for single block generation
247 //TimeTaker timer("** EmergeThread updateLighting");
249 // Update lighting without locking the environment mutex,
250 // add modified blocks to changed blocks
251 map.updateLighting(lighting_invalidated_blocks, modified_blocks);
254 // Add all from changed_blocks to modified_blocks
255 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
256 i.atEnd() == false; i++)
258 MapBlock *block = i.getNode()->getValue();
259 modified_blocks.insert(block->getPos(), block);
262 // If we got no block, there should be no invalidated blocks
265 assert(lighting_invalidated_blocks.size() == 0);
271 Set sent status of modified blocks on clients
274 // NOTE: Server's clients are also behind the connection mutex
275 JMutexAutoLock lock(m_server->m_con_mutex);
278 Add the originally fetched block to the modified list
282 modified_blocks.insert(p, block);
286 Set the modified blocks unsent for all the clients
289 for(core::map<u16, RemoteClient*>::Iterator
290 i = m_server->m_clients.getIterator();
291 i.atEnd() == false; i++)
293 RemoteClient *client = i.getNode()->getValue();
295 if(modified_blocks.size() > 0)
297 // Remove block from sent history
298 client->SetBlocksNotSent(modified_blocks);
304 END_DEBUG_EXCEPTION_HANDLER
309 void RemoteClient::GetNextBlocks(Server *server, float dtime,
310 core::array<PrioritySortedBlockTransfer> &dest)
312 DSTACK(__FUNCTION_NAME);
315 TimeTaker timer("RemoteClient::GetNextBlocks", &timer_result);*/
318 m_nothing_to_send_pause_timer -= dtime;
320 if(m_nothing_to_send_pause_timer >= 0)
323 m_nearest_unsent_reset_timer = 0;
327 // Won't send anything if already sending
328 if(m_blocks_sending.size() >= g_settings.getU16
329 ("max_simultaneous_block_sends_per_client"))
331 //dstream<<"Not sending any blocks, Queue full."<<std::endl;
335 //TimeTaker timer("RemoteClient::GetNextBlocks");
337 Player *player = server->m_env.getPlayer(peer_id);
339 assert(player != NULL);
341 v3f playerpos = player->getPosition();
342 v3f playerspeed = player->getSpeed();
343 v3f playerspeeddir(0,0,0);
344 if(playerspeed.getLength() > 1.0*BS)
345 playerspeeddir = playerspeed / playerspeed.getLength();
346 // Predict to next block
347 v3f playerpos_predicted = playerpos + playerspeeddir*MAP_BLOCKSIZE*BS;
349 v3s16 center_nodepos = floatToInt(playerpos_predicted, BS);
351 v3s16 center = getNodeBlockPos(center_nodepos);
353 // Camera position and direction
355 playerpos + v3f(0, BS+BS/2, 0);
356 v3f camera_dir = v3f(0,0,1);
357 camera_dir.rotateYZBy(player->getPitch());
358 camera_dir.rotateXZBy(player->getYaw());
360 /*dstream<<"camera_dir=("<<camera_dir.X<<","<<camera_dir.Y<<","
361 <<camera_dir.Z<<")"<<std::endl;*/
364 Get the starting value of the block finder radius.
367 if(m_last_center != center)
369 m_nearest_unsent_d = 0;
370 m_last_center = center;
373 /*dstream<<"m_nearest_unsent_reset_timer="
374 <<m_nearest_unsent_reset_timer<<std::endl;*/
376 // This has to be incremented only when the nothing to send pause
378 m_nearest_unsent_reset_timer += dtime;
380 // Reset periodically to avoid possible bugs or other mishaps
381 if(m_nearest_unsent_reset_timer > 10.0)
383 m_nearest_unsent_reset_timer = 0;
384 m_nearest_unsent_d = 0;
385 /*dstream<<"Resetting m_nearest_unsent_d for "
386 <<server->getPlayerName(peer_id)<<std::endl;*/
389 //s16 last_nearest_unsent_d = m_nearest_unsent_d;
390 s16 d_start = m_nearest_unsent_d;
392 //dstream<<"d_start="<<d_start<<std::endl;
394 u16 max_simul_sends_setting = g_settings.getU16
395 ("max_simultaneous_block_sends_per_client");
396 u16 max_simul_sends_usually = max_simul_sends_setting;
399 Check the time from last addNode/removeNode.
401 Decrease send rate if player is building stuff.
403 m_time_from_building += dtime;
404 if(m_time_from_building < g_settings.getFloat(
405 "full_block_send_enable_min_time_from_building"))
407 max_simul_sends_usually
408 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
412 Number of blocks sending + number of blocks selected for sending
414 u32 num_blocks_selected = m_blocks_sending.size();
417 next time d will be continued from the d from which the nearest
418 unsent block was found this time.
420 This is because not necessarily any of the blocks found this
421 time are actually sent.
423 s32 new_nearest_unsent_d = -1;
425 s16 d_max = g_settings.getS16("max_block_send_distance");
426 s16 d_max_gen = g_settings.getS16("max_block_generate_distance");
428 // Don't loop very much at a time
429 if(d_max > d_start+1)
431 /*if(d_max_gen > d_start+2)
432 d_max_gen = d_start+2;*/
434 //dstream<<"Starting from "<<d_start<<std::endl;
436 bool sending_something = false;
438 bool no_blocks_found_for_sending = true;
440 bool queue_is_full = false;
443 for(d = d_start; d <= d_max; d++)
445 //dstream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
448 If m_nearest_unsent_d was changed by the EmergeThread
449 (it can change it to 0 through SetBlockNotSent),
451 Else update m_nearest_unsent_d
453 /*if(m_nearest_unsent_d != last_nearest_unsent_d)
455 d = m_nearest_unsent_d;
456 last_nearest_unsent_d = m_nearest_unsent_d;
460 Get the border/face dot coordinates of a "d-radiused"
463 core::list<v3s16> list;
464 getFacePositions(list, d);
466 core::list<v3s16>::Iterator li;
467 for(li=list.begin(); li!=list.end(); li++)
469 v3s16 p = *li + center;
473 - Don't allow too many simultaneous transfers
474 - EXCEPT when the blocks are very close
476 Also, don't send blocks that are already flying.
479 // Start with the usual maximum
480 u16 max_simul_dynamic = max_simul_sends_usually;
482 // If block is very close, allow full maximum
483 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
484 max_simul_dynamic = max_simul_sends_setting;
486 // Don't select too many blocks for sending
487 if(num_blocks_selected >= max_simul_dynamic)
489 queue_is_full = true;
490 goto queue_full_break;
493 // Don't send blocks that are currently being transferred
494 if(m_blocks_sending.find(p) != NULL)
500 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
501 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
502 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
503 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
504 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
505 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
508 // If this is true, inexistent block will be made from scratch
509 bool generate = d <= d_max_gen;
512 /*// Limit the generating area vertically to 2/3
513 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
516 // Limit the send area vertically to 2/3
517 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
523 If block is far away, don't generate it unless it is
529 // Block center y in nodes
530 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
531 // Don't generate if it's very high or very low
532 if(y < -64 || y > 64)
536 v2s16 p2d_nodes_center(
540 // Get ground height in nodes
541 s16 gh = server->m_env.getServerMap().findGroundLevel(
544 // If differs a lot, don't generate
545 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
547 // Actually, don't even send it
553 //dstream<<"d="<<d<<std::endl;
556 Don't generate or send if not in sight
559 if(isBlockInSight(p, camera_pos, camera_dir, 10000*BS) == false)
565 Don't send already sent blocks
568 if(m_blocks_sent.find(p) != NULL)
575 Check if map has this block
577 MapBlock *block = server->m_env.getMap().getBlockNoCreateNoEx(p);
579 bool surely_not_found_on_disk = false;
580 bool block_is_invalid = false;
583 // Block is dummy if data doesn't exist.
584 // It means it has been not found from disk and not generated
587 surely_not_found_on_disk = true;
590 // Block is valid if lighting is up-to-date and data exists
591 if(block->isValid() == false)
593 block_is_invalid = true;
596 /*if(block->isFullyGenerated() == false)
598 block_is_invalid = true;
602 ServerMap *map = (ServerMap*)(&server->m_env.getMap());
603 v2s16 chunkpos = map->sector_to_chunk(p2d);
604 if(map->chunkNonVolatile(chunkpos) == false)
605 block_is_invalid = true;
608 If block is not close, don't send it unless it is near
611 Block is near ground level if night-time mesh
612 differs from day-time mesh.
616 if(block->dayNightDiffed() == false)
623 If block has been marked to not exist on disk (dummy)
624 and generating new ones is not wanted, skip block.
626 if(generate == false && surely_not_found_on_disk == true)
633 Record the lowest d from which a block has been
634 found being not sent and possibly to exist
636 if(no_blocks_found_for_sending)
639 new_nearest_unsent_d = d;
642 no_blocks_found_for_sending = false;
645 Add inexistent block to emerge queue.
647 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
649 //TODO: Get value from somewhere
650 // Allow only one block in emerge queue
651 //if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
652 if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
654 //dstream<<"Adding block to emerge queue"<<std::endl;
656 // Add it to the emerge queue and trigger the thread
659 if(generate == false)
660 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
662 server->m_emerge_queue.addBlock(peer_id, p, flags);
663 server->m_emergethread.trigger();
671 Add block to send queue
674 PrioritySortedBlockTransfer q((float)d, p, peer_id);
678 num_blocks_selected += 1;
679 sending_something = true;
684 //dstream<<"Stopped at "<<d<<std::endl;
686 if(no_blocks_found_for_sending)
688 if(queue_is_full == false)
689 new_nearest_unsent_d = d;
692 if(new_nearest_unsent_d != -1)
693 m_nearest_unsent_d = new_nearest_unsent_d;
695 if(sending_something == false)
697 m_nothing_to_send_counter++;
698 if((s16)m_nothing_to_send_counter >=
699 g_settings.getS16("max_block_send_distance"))
701 // Pause time in seconds
702 m_nothing_to_send_pause_timer = 1.0;
703 /*dstream<<"nothing to send to "
704 <<server->getPlayerName(peer_id)
705 <<" (d="<<d<<")"<<std::endl;*/
710 m_nothing_to_send_counter = 0;
713 /*timer_result = timer.stop(true);
714 if(timer_result != 0)
715 dstream<<"GetNextBlocks duration: "<<timer_result<<" (!=0)"<<std::endl;*/
718 void RemoteClient::SendObjectData(
721 core::map<v3s16, bool> &stepped_blocks
724 DSTACK(__FUNCTION_NAME);
726 // Can't send anything without knowing version
727 if(serialization_version == SER_FMT_VER_INVALID)
729 dstream<<"RemoteClient::SendObjectData(): Not sending, no version."
735 Send a TOCLIENT_OBJECTDATA packet.
739 u16 number of player positions
750 std::ostringstream os(std::ios_base::binary);
754 writeU16(buf, TOCLIENT_OBJECTDATA);
755 os.write((char*)buf, 2);
758 Get and write player data
761 // Get connected players
762 core::list<Player*> players = server->m_env.getPlayers(true);
764 // Write player count
765 u16 playercount = players.size();
766 writeU16(buf, playercount);
767 os.write((char*)buf, 2);
769 core::list<Player*>::Iterator i;
770 for(i = players.begin();
771 i != players.end(); i++)
775 v3f pf = player->getPosition();
776 v3f sf = player->getSpeed();
778 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
779 v3s32 speed_i (sf.X*100, sf.Y*100, sf.Z*100);
780 s32 pitch_i (player->getPitch() * 100);
781 s32 yaw_i (player->getYaw() * 100);
783 writeU16(buf, player->peer_id);
784 os.write((char*)buf, 2);
785 writeV3S32(buf, position_i);
786 os.write((char*)buf, 12);
787 writeV3S32(buf, speed_i);
788 os.write((char*)buf, 12);
789 writeS32(buf, pitch_i);
790 os.write((char*)buf, 4);
791 writeS32(buf, yaw_i);
792 os.write((char*)buf, 4);
796 Get and write object data
802 For making players to be able to build to their nearby
803 environment (building is not possible on blocks that are not
806 - Add blocks to emerge queue if they are not found
808 SUGGESTION: These could be ignored from the backside of the player
811 Player *player = server->m_env.getPlayer(peer_id);
815 v3f playerpos = player->getPosition();
816 v3f playerspeed = player->getSpeed();
818 v3s16 center_nodepos = floatToInt(playerpos, BS);
819 v3s16 center = getNodeBlockPos(center_nodepos);
821 s16 d_max = g_settings.getS16("active_object_range");
823 // Number of blocks whose objects were written to bos
826 std::ostringstream bos(std::ios_base::binary);
828 for(s16 d = 0; d <= d_max; d++)
830 core::list<v3s16> list;
831 getFacePositions(list, d);
833 core::list<v3s16>::Iterator li;
834 for(li=list.begin(); li!=list.end(); li++)
836 v3s16 p = *li + center;
839 Ignore blocks that haven't been sent to the client
842 if(m_blocks_sent.find(p) == NULL)
846 // Try stepping block and add it to a send queue
851 MapBlock *block = server->m_env.getMap().getBlockNoCreate(p);
854 Step block if not in stepped_blocks and add to stepped_blocks.
856 if(stepped_blocks.find(p) == NULL)
858 block->stepObjects(dtime, true, server->m_env.getDayNightRatio());
859 stepped_blocks.insert(p, true);
860 block->setChangedFlag();
863 // Skip block if there are no objects
864 if(block->getObjectCount() == 0)
873 bos.write((char*)buf, 6);
876 block->serializeObjects(bos, serialization_version);
881 Stop collecting objects if data is already too big
883 // Sum of player and object data sizes
884 s32 sum = (s32)os.tellp() + 2 + (s32)bos.tellp();
885 // break out if data too big
886 if(sum > MAX_OBJECTDATA_SIZE)
888 goto skip_subsequent;
892 catch(InvalidPositionException &e)
895 // Add it to the emerge queue and trigger the thread.
896 // Fetch the block only if it is on disk.
898 // Grab and increment counter
899 /*SharedPtr<JMutexAutoLock> lock
900 (m_num_blocks_in_emerge_queue.getLock());
901 m_num_blocks_in_emerge_queue.m_value++;*/
903 // Add to queue as an anonymous fetch from disk
904 u8 flags = BLOCK_EMERGE_FLAG_FROMDISK;
905 server->m_emerge_queue.addBlock(0, p, flags);
906 server->m_emergethread.trigger();
914 writeU16(buf, blockcount);
915 os.write((char*)buf, 2);
917 // Write block objects
924 //dstream<<"Server: Sending object data to "<<peer_id<<std::endl;
927 std::string s = os.str();
928 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
929 // Send as unreliable
930 server->m_con.Send(peer_id, 0, data, false);
933 void RemoteClient::GotBlock(v3s16 p)
935 if(m_blocks_sending.find(p) != NULL)
936 m_blocks_sending.remove(p);
939 /*dstream<<"RemoteClient::GotBlock(): Didn't find in"
940 " m_blocks_sending"<<std::endl;*/
941 m_excess_gotblocks++;
943 m_blocks_sent.insert(p, true);
946 void RemoteClient::SentBlock(v3s16 p)
948 if(m_blocks_sending.find(p) == NULL)
949 m_blocks_sending.insert(p, 0.0);
951 dstream<<"RemoteClient::SentBlock(): Sent block"
952 " already in m_blocks_sending"<<std::endl;
955 void RemoteClient::SetBlockNotSent(v3s16 p)
957 m_nearest_unsent_d = 0;
959 if(m_blocks_sending.find(p) != NULL)
960 m_blocks_sending.remove(p);
961 if(m_blocks_sent.find(p) != NULL)
962 m_blocks_sent.remove(p);
965 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
967 m_nearest_unsent_d = 0;
969 for(core::map<v3s16, MapBlock*>::Iterator
970 i = blocks.getIterator();
971 i.atEnd()==false; i++)
973 v3s16 p = i.getNode()->getKey();
975 if(m_blocks_sending.find(p) != NULL)
976 m_blocks_sending.remove(p);
977 if(m_blocks_sent.find(p) != NULL)
978 m_blocks_sent.remove(p);
986 PlayerInfo::PlayerInfo()
992 void PlayerInfo::PrintLine(std::ostream *s)
995 (*s)<<"\""<<name<<"\" ("
996 <<(position.X/10)<<","<<(position.Y/10)
997 <<","<<(position.Z/10)<<") ";
999 (*s)<<" avg_rtt="<<avg_rtt;
1003 u32 PIChecksum(core::list<PlayerInfo> &l)
1005 core::list<PlayerInfo>::Iterator i;
1008 for(i=l.begin(); i!=l.end(); i++)
1010 checksum += a * (i->id+1);
1011 checksum ^= 0x435aafcd;
1022 std::string mapsavedir
1024 m_env(new ServerMap(mapsavedir), this),
1025 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
1026 m_authmanager(mapsavedir+"/auth.txt"),
1028 m_emergethread(this),
1030 m_time_of_day_send_timer(0),
1032 m_mapsavedir(mapsavedir),
1033 m_shutdown_requested(false),
1034 m_ignore_map_edit_events(false),
1035 m_ignore_map_edit_events_peer_id(0)
1037 m_liquid_transform_timer = 0.0;
1038 m_print_info_timer = 0.0;
1039 m_objectdata_timer = 0.0;
1040 m_emergethread_trigger_timer = 0.0;
1041 m_savemap_timer = 0.0;
1045 m_step_dtime_mutex.Init();
1048 // Register us to receive map edit events
1049 m_env.getMap().addEventReceiver(this);
1051 // If file exists, load environment metadata
1052 if(fs::PathExists(m_mapsavedir+"/env_meta.txt"))
1054 dstream<<"Server: Loading environment metadata"<<std::endl;
1055 m_env.loadMeta(m_mapsavedir);
1059 dstream<<"Server: Loading players"<<std::endl;
1060 m_env.deSerializePlayers(m_mapsavedir);
1065 dstream<<"Server::~Server()"<<std::endl;
1068 Send shutdown message
1071 JMutexAutoLock conlock(m_con_mutex);
1073 std::wstring line = L"*** Server shutting down";
1076 Send the message to clients
1078 for(core::map<u16, RemoteClient*>::Iterator
1079 i = m_clients.getIterator();
1080 i.atEnd() == false; i++)
1082 // Get client and check that it is valid
1083 RemoteClient *client = i.getNode()->getValue();
1084 assert(client->peer_id == i.getNode()->getKey());
1085 if(client->serialization_version == SER_FMT_VER_INVALID)
1089 SendChatMessage(client->peer_id, line);
1091 catch(con::PeerNotFoundException &e)
1099 dstream<<"Server: Saving players"<<std::endl;
1100 m_env.serializePlayers(m_mapsavedir);
1103 Save environment metadata
1105 dstream<<"Server: Saving environment metadata"<<std::endl;
1106 m_env.saveMeta(m_mapsavedir);
1117 JMutexAutoLock clientslock(m_con_mutex);
1119 for(core::map<u16, RemoteClient*>::Iterator
1120 i = m_clients.getIterator();
1121 i.atEnd() == false; i++)
1124 // NOTE: These are removed by env destructor
1126 u16 peer_id = i.getNode()->getKey();
1127 JMutexAutoLock envlock(m_env_mutex);
1128 m_env.removePlayer(peer_id);
1132 delete i.getNode()->getValue();
1137 void Server::start(unsigned short port)
1139 DSTACK(__FUNCTION_NAME);
1140 // Stop thread if already running
1143 // Initialize connection
1144 m_con.setTimeoutMs(30);
1148 m_thread.setRun(true);
1151 dout_server<<"Server: Started on port "<<port<<std::endl;
1156 DSTACK(__FUNCTION_NAME);
1158 // Stop threads (set run=false first so both start stopping)
1159 m_thread.setRun(false);
1160 m_emergethread.setRun(false);
1162 m_emergethread.stop();
1164 dout_server<<"Server: Threads stopped"<<std::endl;
1167 void Server::step(float dtime)
1169 DSTACK(__FUNCTION_NAME);
1174 JMutexAutoLock lock(m_step_dtime_mutex);
1175 m_step_dtime += dtime;
1179 void Server::AsyncRunStep()
1181 DSTACK(__FUNCTION_NAME);
1185 JMutexAutoLock lock1(m_step_dtime_mutex);
1186 dtime = m_step_dtime;
1190 ScopeProfiler sp(&g_profiler, "Server: selecting and sending "
1191 "blocks to clients");
1192 // Send blocks to clients
1199 //dstream<<"Server steps "<<dtime<<std::endl;
1200 //dstream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1203 JMutexAutoLock lock1(m_step_dtime_mutex);
1204 m_step_dtime -= dtime;
1211 m_uptime.set(m_uptime.get() + dtime);
1215 Update m_time_of_day and overall game time
1218 JMutexAutoLock envlock(m_env_mutex);
1220 m_time_counter += dtime;
1221 f32 speed = g_settings.getFloat("time_speed") * 24000./(24.*3600);
1222 u32 units = (u32)(m_time_counter*speed);
1223 m_time_counter -= (f32)units / speed;
1225 m_env.setTimeOfDay((m_env.getTimeOfDay() + units) % 24000);
1227 //dstream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1230 Send to clients at constant intervals
1233 m_time_of_day_send_timer -= dtime;
1234 if(m_time_of_day_send_timer < 0.0)
1236 m_time_of_day_send_timer = g_settings.getFloat("time_send_interval");
1238 //JMutexAutoLock envlock(m_env_mutex);
1239 JMutexAutoLock conlock(m_con_mutex);
1241 for(core::map<u16, RemoteClient*>::Iterator
1242 i = m_clients.getIterator();
1243 i.atEnd() == false; i++)
1245 RemoteClient *client = i.getNode()->getValue();
1246 //Player *player = m_env.getPlayer(client->peer_id);
1248 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1249 m_env.getTimeOfDay());
1251 m_con.Send(client->peer_id, 0, data, true);
1257 // Process connection's timeouts
1258 JMutexAutoLock lock2(m_con_mutex);
1259 ScopeProfiler sp(&g_profiler, "Server: connection timeout processing");
1260 m_con.RunTimeouts(dtime);
1264 // This has to be called so that the client list gets synced
1265 // with the peer list of the connection
1266 ScopeProfiler sp(&g_profiler, "Server: peer change handling");
1267 handlePeerChanges();
1272 // This also runs Map's timers
1273 JMutexAutoLock lock(m_env_mutex);
1274 ScopeProfiler sp(&g_profiler, "Server: environment step");
1285 m_liquid_transform_timer += dtime;
1286 if(m_liquid_transform_timer >= 1.00)
1288 m_liquid_transform_timer -= 1.00;
1290 JMutexAutoLock lock(m_env_mutex);
1292 ScopeProfiler sp(&g_profiler, "Server: liquid transform");
1294 core::map<v3s16, MapBlock*> modified_blocks;
1295 m_env.getMap().transformLiquids(modified_blocks);
1300 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1301 ServerMap &map = ((ServerMap&)m_env.getMap());
1302 map.updateLighting(modified_blocks, lighting_modified_blocks);
1304 // Add blocks modified by lighting to modified_blocks
1305 for(core::map<v3s16, MapBlock*>::Iterator
1306 i = lighting_modified_blocks.getIterator();
1307 i.atEnd() == false; i++)
1309 MapBlock *block = i.getNode()->getValue();
1310 modified_blocks.insert(block->getPos(), block);
1314 Set the modified blocks unsent for all the clients
1317 JMutexAutoLock lock2(m_con_mutex);
1319 for(core::map<u16, RemoteClient*>::Iterator
1320 i = m_clients.getIterator();
1321 i.atEnd() == false; i++)
1323 RemoteClient *client = i.getNode()->getValue();
1325 if(modified_blocks.size() > 0)
1327 // Remove block from sent history
1328 client->SetBlocksNotSent(modified_blocks);
1333 // Periodically print some info
1335 float &counter = m_print_info_timer;
1341 JMutexAutoLock lock2(m_con_mutex);
1343 for(core::map<u16, RemoteClient*>::Iterator
1344 i = m_clients.getIterator();
1345 i.atEnd() == false; i++)
1347 //u16 peer_id = i.getNode()->getKey();
1348 RemoteClient *client = i.getNode()->getValue();
1349 Player *player = m_env.getPlayer(client->peer_id);
1352 std::cout<<player->getName()<<"\t";
1353 client->PrintInfo(std::cout);
1358 //if(g_settings.getBool("enable_experimental"))
1362 Check added and deleted active objects
1365 //dstream<<"Server: Checking added and deleted active objects"<<std::endl;
1366 JMutexAutoLock envlock(m_env_mutex);
1367 JMutexAutoLock conlock(m_con_mutex);
1369 ScopeProfiler sp(&g_profiler, "Server: checking added and deleted objects");
1371 // Radius inside which objects are active
1374 for(core::map<u16, RemoteClient*>::Iterator
1375 i = m_clients.getIterator();
1376 i.atEnd() == false; i++)
1378 RemoteClient *client = i.getNode()->getValue();
1379 Player *player = m_env.getPlayer(client->peer_id);
1382 // This can happen if the client timeouts somehow
1383 /*dstream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
1385 <<" has no associated player"<<std::endl;*/
1388 v3s16 pos = floatToInt(player->getPosition(), BS);
1390 core::map<u16, bool> removed_objects;
1391 core::map<u16, bool> added_objects;
1392 m_env.getRemovedActiveObjects(pos, radius,
1393 client->m_known_objects, removed_objects);
1394 m_env.getAddedActiveObjects(pos, radius,
1395 client->m_known_objects, added_objects);
1397 // Ignore if nothing happened
1398 if(removed_objects.size() == 0 && added_objects.size() == 0)
1400 //dstream<<"INFO: active objects: none changed"<<std::endl;
1404 std::string data_buffer;
1408 // Handle removed objects
1409 writeU16((u8*)buf, removed_objects.size());
1410 data_buffer.append(buf, 2);
1411 for(core::map<u16, bool>::Iterator
1412 i = removed_objects.getIterator();
1413 i.atEnd()==false; i++)
1416 u16 id = i.getNode()->getKey();
1417 ServerActiveObject* obj = m_env.getActiveObject(id);
1419 // Add to data buffer for sending
1420 writeU16((u8*)buf, i.getNode()->getKey());
1421 data_buffer.append(buf, 2);
1423 // Remove from known objects
1424 client->m_known_objects.remove(i.getNode()->getKey());
1426 if(obj && obj->m_known_by_count > 0)
1427 obj->m_known_by_count--;
1430 // Handle added objects
1431 writeU16((u8*)buf, added_objects.size());
1432 data_buffer.append(buf, 2);
1433 for(core::map<u16, bool>::Iterator
1434 i = added_objects.getIterator();
1435 i.atEnd()==false; i++)
1438 u16 id = i.getNode()->getKey();
1439 ServerActiveObject* obj = m_env.getActiveObject(id);
1442 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1444 dstream<<"WARNING: "<<__FUNCTION_NAME
1445 <<": NULL object"<<std::endl;
1447 type = obj->getType();
1449 // Add to data buffer for sending
1450 writeU16((u8*)buf, id);
1451 data_buffer.append(buf, 2);
1452 writeU8((u8*)buf, type);
1453 data_buffer.append(buf, 1);
1456 data_buffer.append(serializeLongString(
1457 obj->getClientInitializationData()));
1459 data_buffer.append(serializeLongString(""));
1461 // Add to known objects
1462 client->m_known_objects.insert(i.getNode()->getKey(), false);
1465 obj->m_known_by_count++;
1469 SharedBuffer<u8> reply(2 + data_buffer.size());
1470 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1471 memcpy((char*)&reply[2], data_buffer.c_str(),
1472 data_buffer.size());
1474 m_con.Send(client->peer_id, 0, reply, true);
1476 dstream<<"INFO: Server: Sent object remove/add: "
1477 <<removed_objects.size()<<" removed, "
1478 <<added_objects.size()<<" added, "
1479 <<"packet size is "<<reply.getSize()<<std::endl;
1484 Collect a list of all the objects known by the clients
1485 and report it back to the environment.
1488 core::map<u16, bool> all_known_objects;
1490 for(core::map<u16, RemoteClient*>::Iterator
1491 i = m_clients.getIterator();
1492 i.atEnd() == false; i++)
1494 RemoteClient *client = i.getNode()->getValue();
1495 // Go through all known objects of client
1496 for(core::map<u16, bool>::Iterator
1497 i = client->m_known_objects.getIterator();
1498 i.atEnd()==false; i++)
1500 u16 id = i.getNode()->getKey();
1501 all_known_objects[id] = true;
1505 m_env.setKnownActiveObjects(whatever);
1511 Send object messages
1514 JMutexAutoLock envlock(m_env_mutex);
1515 JMutexAutoLock conlock(m_con_mutex);
1517 ScopeProfiler sp(&g_profiler, "Server: sending object messages");
1520 // Value = data sent by object
1521 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1523 // Get active object messages from environment
1526 ActiveObjectMessage aom = m_env.getActiveObjectMessage();
1530 core::list<ActiveObjectMessage>* message_list = NULL;
1531 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1532 n = buffered_messages.find(aom.id);
1535 message_list = new core::list<ActiveObjectMessage>;
1536 buffered_messages.insert(aom.id, message_list);
1540 message_list = n->getValue();
1542 message_list->push_back(aom);
1545 // Route data to every client
1546 for(core::map<u16, RemoteClient*>::Iterator
1547 i = m_clients.getIterator();
1548 i.atEnd()==false; i++)
1550 RemoteClient *client = i.getNode()->getValue();
1551 std::string reliable_data;
1552 std::string unreliable_data;
1553 // Go through all objects in message buffer
1554 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1555 j = buffered_messages.getIterator();
1556 j.atEnd()==false; j++)
1558 // If object is not known by client, skip it
1559 u16 id = j.getNode()->getKey();
1560 if(client->m_known_objects.find(id) == NULL)
1562 // Get message list of object
1563 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1564 // Go through every message
1565 for(core::list<ActiveObjectMessage>::Iterator
1566 k = list->begin(); k != list->end(); k++)
1568 // Compose the full new data with header
1569 ActiveObjectMessage aom = *k;
1570 std::string new_data;
1573 writeU16((u8*)&buf[0], aom.id);
1574 new_data.append(buf, 2);
1576 new_data += serializeString(aom.datastring);
1577 // Add data to buffer
1579 reliable_data += new_data;
1581 unreliable_data += new_data;
1585 reliable_data and unreliable_data are now ready.
1588 if(reliable_data.size() > 0)
1590 SharedBuffer<u8> reply(2 + reliable_data.size());
1591 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1592 memcpy((char*)&reply[2], reliable_data.c_str(),
1593 reliable_data.size());
1595 m_con.Send(client->peer_id, 0, reply, true);
1597 if(unreliable_data.size() > 0)
1599 SharedBuffer<u8> reply(2 + unreliable_data.size());
1600 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1601 memcpy((char*)&reply[2], unreliable_data.c_str(),
1602 unreliable_data.size());
1603 // Send as unreliable
1604 m_con.Send(client->peer_id, 0, reply, false);
1607 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1609 dstream<<"INFO: Server: Size of object message data: "
1610 <<"reliable: "<<reliable_data.size()
1611 <<", unreliable: "<<unreliable_data.size()
1616 // Clear buffered_messages
1617 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1618 i = buffered_messages.getIterator();
1619 i.atEnd()==false; i++)
1621 delete i.getNode()->getValue();
1625 } // enable_experimental
1628 Send queued-for-sending map edit events.
1631 while(m_unsent_map_edit_queue.size() != 0)
1633 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1635 if(event->type == MEET_ADDNODE)
1637 dstream<<"Server: MEET_ADDNODE"<<std::endl;
1638 sendAddNode(event->p, event->n, event->already_known_by_peer);
1640 else if(event->type == MEET_REMOVENODE)
1642 dstream<<"Server: MEET_REMOVENODE"<<std::endl;
1643 sendRemoveNode(event->p, event->already_known_by_peer);
1645 else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
1647 dstream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
1648 setBlockNotSent(event->p);
1650 else if(event->type == MEET_OTHER)
1652 dstream<<"WARNING: Server: MEET_OTHER not implemented"
1657 dstream<<"WARNING: Server: Unknown MapEditEvent "
1658 <<((u32)event->type)<<std::endl;
1666 Send object positions
1667 TODO: Get rid of MapBlockObjects
1670 float &counter = m_objectdata_timer;
1672 if(counter >= g_settings.getFloat("objectdata_interval"))
1674 JMutexAutoLock lock1(m_env_mutex);
1675 JMutexAutoLock lock2(m_con_mutex);
1677 ScopeProfiler sp(&g_profiler, "Server: sending mbo positions");
1679 SendObjectData(counter);
1687 TODO: Move to ServerEnvironment and utilize active block stuff
1690 //TimeTaker timer("Step node metadata");
1692 JMutexAutoLock envlock(m_env_mutex);
1693 JMutexAutoLock conlock(m_con_mutex);
1695 ScopeProfiler sp(&g_profiler, "Server: stepping node metadata");
1697 core::map<v3s16, MapBlock*> changed_blocks;
1698 m_env.getMap().nodeMetadataStep(dtime, changed_blocks);
1700 // Use setBlockNotSent
1702 for(core::map<v3s16, MapBlock*>::Iterator
1703 i = changed_blocks.getIterator();
1704 i.atEnd() == false; i++)
1706 MapBlock *block = i.getNode()->getValue();
1708 for(core::map<u16, RemoteClient*>::Iterator
1709 i = m_clients.getIterator();
1710 i.atEnd()==false; i++)
1712 RemoteClient *client = i.getNode()->getValue();
1713 client->SetBlockNotSent(block->getPos());
1719 Trigger emergethread (it somehow gets to a non-triggered but
1720 bysy state sometimes)
1723 float &counter = m_emergethread_trigger_timer;
1729 m_emergethread.trigger();
1733 // Save map, players and auth stuff
1735 float &counter = m_savemap_timer;
1737 if(counter >= g_settings.getFloat("server_map_save_interval"))
1741 ScopeProfiler sp(&g_profiler, "Server: saving stuff");
1744 if(m_authmanager.isModified())
1745 m_authmanager.save();
1748 JMutexAutoLock lock(m_env_mutex);
1749 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == true)
1751 // Save only changed parts
1752 m_env.getMap().save(true);
1754 // Delete unused sectors
1755 u32 deleted_count = m_env.getMap().deleteUnusedSectors(
1756 g_settings.getFloat("server_unload_unused_sectors_timeout"));
1757 if(deleted_count > 0)
1759 dout_server<<"Server: Unloaded "<<deleted_count
1760 <<" sectors from memory"<<std::endl;
1764 m_env.serializePlayers(m_mapsavedir);
1766 // Save environment metadata
1767 m_env.saveMeta(m_mapsavedir);
1773 void Server::Receive()
1775 DSTACK(__FUNCTION_NAME);
1776 u32 data_maxsize = 10000;
1777 Buffer<u8> data(data_maxsize);
1782 JMutexAutoLock conlock(m_con_mutex);
1783 datasize = m_con.Receive(peer_id, *data, data_maxsize);
1786 // This has to be called so that the client list gets synced
1787 // with the peer list of the connection
1788 handlePeerChanges();
1790 ProcessData(*data, datasize, peer_id);
1792 catch(con::InvalidIncomingDataException &e)
1794 derr_server<<"Server::Receive(): "
1795 "InvalidIncomingDataException: what()="
1796 <<e.what()<<std::endl;
1798 catch(con::PeerNotFoundException &e)
1800 //NOTE: This is not needed anymore
1802 // The peer has been disconnected.
1803 // Find the associated player and remove it.
1805 /*JMutexAutoLock envlock(m_env_mutex);
1807 dout_server<<"ServerThread: peer_id="<<peer_id
1808 <<" has apparently closed connection. "
1809 <<"Removing player."<<std::endl;
1811 m_env.removePlayer(peer_id);*/
1815 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1817 DSTACK(__FUNCTION_NAME);
1818 // Environment is locked first.
1819 JMutexAutoLock envlock(m_env_mutex);
1820 JMutexAutoLock conlock(m_con_mutex);
1824 peer = m_con.GetPeer(peer_id);
1826 catch(con::PeerNotFoundException &e)
1828 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1829 <<peer_id<<" not found"<<std::endl;
1833 u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1841 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1843 if(command == TOSERVER_INIT)
1845 // [0] u16 TOSERVER_INIT
1846 // [2] u8 SER_FMT_VER_HIGHEST
1847 // [3] u8[20] player_name
1848 // [23] u8[28] password <--- can be sent without this, from old versions
1850 if(datasize < 2+1+PLAYERNAME_SIZE)
1853 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1854 <<peer->id<<std::endl;
1856 // First byte after command is maximum supported
1857 // serialization version
1858 u8 client_max = data[2];
1859 u8 our_max = SER_FMT_VER_HIGHEST;
1860 // Use the highest version supported by both
1861 u8 deployed = core::min_(client_max, our_max);
1862 // If it's lower than the lowest supported, give up.
1863 if(deployed < SER_FMT_VER_LOWEST)
1864 deployed = SER_FMT_VER_INVALID;
1866 //peer->serialization_version = deployed;
1867 getClient(peer->id)->pending_serialization_version = deployed;
1869 if(deployed == SER_FMT_VER_INVALID)
1871 derr_server<<DTIME<<"Server: Cannot negotiate "
1872 "serialization version with peer "
1873 <<peer_id<<std::endl;
1882 char playername[PLAYERNAME_SIZE];
1883 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
1885 playername[i] = data[3+i];
1887 playername[PLAYERNAME_SIZE-1] = 0;
1889 if(playername[0]=='\0')
1891 derr_server<<DTIME<<"Server: Player has empty name"<<std::endl;
1892 SendAccessDenied(m_con, peer_id,
1897 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
1899 derr_server<<DTIME<<"Server: Player has invalid name"<<std::endl;
1900 SendAccessDenied(m_con, peer_id,
1901 L"Name contains unallowed characters");
1906 char password[PASSWORD_SIZE];
1907 if(datasize == 2+1+PLAYERNAME_SIZE)
1909 // old version - assume blank password
1914 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
1916 password[i] = data[23+i];
1918 password[PASSWORD_SIZE-1] = 0;
1921 std::string checkpwd;
1922 if(m_authmanager.exists(playername))
1924 checkpwd = m_authmanager.getPassword(playername);
1928 checkpwd = g_settings.get("default_password");
1931 if(password != checkpwd && checkpwd != "")
1933 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1934 <<": supplied invalid password for "
1935 <<playername<<std::endl;
1936 SendAccessDenied(m_con, peer_id, L"Invalid password");
1940 // Add player to auth manager
1941 if(m_authmanager.exists(playername) == false)
1943 derr_server<<DTIME<<"Server: adding player "<<playername
1944 <<" to auth manager"<<std::endl;
1945 m_authmanager.add(playername);
1946 m_authmanager.setPassword(playername, checkpwd);
1947 m_authmanager.setPrivs(playername,
1948 stringToPrivs(g_settings.get("default_privs")));
1949 m_authmanager.save();
1953 Player *player = emergePlayer(playername, password, peer_id);
1957 // DEBUG: Test serialization
1958 std::ostringstream test_os;
1959 player->serialize(test_os);
1960 dstream<<"Player serialization test: \""<<test_os.str()
1962 std::istringstream test_is(test_os.str());
1963 player->deSerialize(test_is);
1966 // If failed, cancel
1969 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1970 <<": failed to emerge player"<<std::endl;
1975 // If a client is already connected to the player, cancel
1976 if(player->peer_id != 0)
1978 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1979 <<" tried to connect to "
1980 "an already connected player (peer_id="
1981 <<player->peer_id<<")"<<std::endl;
1984 // Set client of player
1985 player->peer_id = peer_id;
1988 // Check if player doesn't exist
1990 throw con::InvalidIncomingDataException
1991 ("Server::ProcessData(): INIT: Player doesn't exist");
1993 /*// update name if it was supplied
1994 if(datasize >= 20+3)
1997 player->updateName((const char*)&data[3]);
2001 Answer with a TOCLIENT_INIT
2004 SharedBuffer<u8> reply(2+1+6+8);
2005 writeU16(&reply[0], TOCLIENT_INIT);
2006 writeU8(&reply[2], deployed);
2007 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
2008 writeU64(&reply[2+1+6], m_env.getServerMap().getSeed());
2011 m_con.Send(peer_id, 0, reply, true);
2015 Send complete position information
2017 SendMovePlayer(player);
2022 if(command == TOSERVER_INIT2)
2024 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
2025 <<peer->id<<std::endl;
2028 getClient(peer->id)->serialization_version
2029 = getClient(peer->id)->pending_serialization_version;
2032 Send some initialization data
2035 // Send player info to all players
2038 // Send inventory to player
2039 UpdateCrafting(peer->id);
2040 SendInventory(peer->id);
2044 Player *player = m_env.getPlayer(peer_id);
2045 SendPlayerHP(player);
2050 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2051 m_env.getTimeOfDay());
2052 m_con.Send(peer->id, 0, data, true);
2055 // Send information about server to player in chat
2056 SendChatMessage(peer_id, getStatusString());
2058 // Send information about joining in chat
2060 std::wstring name = L"unknown";
2061 Player *player = m_env.getPlayer(peer_id);
2063 name = narrow_to_wide(player->getName());
2065 std::wstring message;
2068 message += L" joined game";
2069 BroadcastChatMessage(message);
2075 if(peer_ser_ver == SER_FMT_VER_INVALID)
2077 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
2078 " serialization format invalid or not initialized."
2079 " Skipping incoming command="<<command<<std::endl;
2083 Player *player = m_env.getPlayer(peer_id);
2086 derr_server<<"Server::ProcessData(): Cancelling: "
2087 "No player for peer_id="<<peer_id
2091 if(command == TOSERVER_PLAYERPOS)
2093 if(datasize < 2+12+12+4+4)
2097 v3s32 ps = readV3S32(&data[start+2]);
2098 v3s32 ss = readV3S32(&data[start+2+12]);
2099 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2100 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2101 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2102 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2103 pitch = wrapDegrees(pitch);
2104 yaw = wrapDegrees(yaw);
2105 player->setPosition(position);
2106 player->setSpeed(speed);
2107 player->setPitch(pitch);
2108 player->setYaw(yaw);
2110 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2111 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2112 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2114 else if(command == TOSERVER_GOTBLOCKS)
2127 u16 count = data[2];
2128 for(u16 i=0; i<count; i++)
2130 if((s16)datasize < 2+1+(i+1)*6)
2131 throw con::InvalidIncomingDataException
2132 ("GOTBLOCKS length is too short");
2133 v3s16 p = readV3S16(&data[2+1+i*6]);
2134 /*dstream<<"Server: GOTBLOCKS ("
2135 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2136 RemoteClient *client = getClient(peer_id);
2137 client->GotBlock(p);
2140 else if(command == TOSERVER_DELETEDBLOCKS)
2153 u16 count = data[2];
2154 for(u16 i=0; i<count; i++)
2156 if((s16)datasize < 2+1+(i+1)*6)
2157 throw con::InvalidIncomingDataException
2158 ("DELETEDBLOCKS length is too short");
2159 v3s16 p = readV3S16(&data[2+1+i*6]);
2160 /*dstream<<"Server: DELETEDBLOCKS ("
2161 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2162 RemoteClient *client = getClient(peer_id);
2163 client->SetBlockNotSent(p);
2166 else if(command == TOSERVER_CLICK_OBJECT)
2171 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2176 [2] u8 button (0=left, 1=right)
2181 u8 button = readU8(&data[2]);
2183 p.X = readS16(&data[3]);
2184 p.Y = readS16(&data[5]);
2185 p.Z = readS16(&data[7]);
2186 s16 id = readS16(&data[9]);
2187 //u16 item_i = readU16(&data[11]);
2189 MapBlock *block = NULL;
2192 block = m_env.getMap().getBlockNoCreate(p);
2194 catch(InvalidPositionException &e)
2196 derr_server<<"CLICK_OBJECT block not found"<<std::endl;
2200 MapBlockObject *obj = block->getObject(id);
2204 derr_server<<"CLICK_OBJECT object not found"<<std::endl;
2208 //TODO: Check that object is reasonably close
2213 InventoryList *ilist = player->inventory.getList("main");
2214 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
2217 // Skip if inventory has no free space
2218 if(ilist->getUsedSlots() == ilist->getSize())
2220 dout_server<<"Player inventory has no free space"<<std::endl;
2225 Create the inventory item
2227 InventoryItem *item = NULL;
2228 // If it is an item-object, take the item from it
2229 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
2231 item = ((ItemObject*)obj)->createInventoryItem();
2233 // Else create an item of the object
2236 item = new MapBlockObjectItem
2237 (obj->getInventoryString());
2240 // Add to inventory and send inventory
2241 ilist->addItem(item);
2242 UpdateCrafting(player->peer_id);
2243 SendInventory(player->peer_id);
2246 // Remove from block
2247 block->removeObject(id);
2250 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2255 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2261 [2] u8 button (0=left, 1=right)
2265 u8 button = readU8(&data[2]);
2266 u16 id = readS16(&data[3]);
2267 u16 item_i = readU16(&data[11]);
2269 ServerActiveObject *obj = m_env.getActiveObject(id);
2273 derr_server<<"Server: CLICK_ACTIVEOBJECT: object not found"
2278 //TODO: Check that object is reasonably close
2280 // Left click, pick object up (usually)
2283 InventoryList *ilist = player->inventory.getList("main");
2284 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
2287 // Skip if inventory has no free space
2288 if(ilist->getUsedSlots() == ilist->getSize())
2290 dout_server<<"Player inventory has no free space"<<std::endl;
2294 // Skip if object has been removed
2299 Create the inventory item
2301 InventoryItem *item = obj->createPickedUpItem();
2305 // Add to inventory and send inventory
2306 ilist->addItem(item);
2307 UpdateCrafting(player->peer_id);
2308 SendInventory(player->peer_id);
2310 // Remove object from environment
2311 obj->m_removed = true;
2316 Item cannot be picked up. Punch it instead.
2319 ToolItem *titem = NULL;
2320 std::string toolname = "";
2322 InventoryList *mlist = player->inventory.getList("main");
2325 InventoryItem *item = mlist->getItem(item_i);
2326 if(item && (std::string)item->getName() == "ToolItem")
2328 titem = (ToolItem*)item;
2329 toolname = titem->getToolName();
2333 u16 wear = obj->punch(toolname);
2337 bool weared_out = titem->addWear(wear);
2339 mlist->deleteItem(item_i);
2340 SendInventory(player->peer_id);
2346 else if(command == TOSERVER_GROUND_ACTION)
2354 [3] v3s16 nodepos_undersurface
2355 [9] v3s16 nodepos_abovesurface
2360 2: stop digging (all parameters ignored)
2361 3: digging completed
2363 u8 action = readU8(&data[2]);
2365 p_under.X = readS16(&data[3]);
2366 p_under.Y = readS16(&data[5]);
2367 p_under.Z = readS16(&data[7]);
2369 p_over.X = readS16(&data[9]);
2370 p_over.Y = readS16(&data[11]);
2371 p_over.Z = readS16(&data[13]);
2372 u16 item_i = readU16(&data[15]);
2374 //TODO: Check that target is reasonably close
2382 NOTE: This can be used in the future to check if
2383 somebody is cheating, by checking the timing.
2390 else if(action == 2)
2393 RemoteClient *client = getClient(peer->id);
2394 JMutexAutoLock digmutex(client->m_dig_mutex);
2395 client->m_dig_tool_item = -1;
2400 3: Digging completed
2402 else if(action == 3)
2404 // Mandatory parameter; actually used for nothing
2405 core::map<v3s16, MapBlock*> modified_blocks;
2407 u8 material = CONTENT_IGNORE;
2408 u8 mineral = MINERAL_NONE;
2410 bool cannot_remove_node = false;
2414 MapNode n = m_env.getMap().getNode(p_under);
2416 mineral = n.getMineral();
2417 // Get material at position
2419 // If not yet cancelled
2420 if(cannot_remove_node == false)
2422 // If it's not diggable, do nothing
2423 if(content_diggable(material) == false)
2425 derr_server<<"Server: Not finishing digging: "
2426 <<"Node not diggable"
2428 cannot_remove_node = true;
2431 // If not yet cancelled
2432 if(cannot_remove_node == false)
2434 // Get node metadata
2435 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p_under);
2436 if(meta && meta->nodeRemovalDisabled() == true)
2438 derr_server<<"Server: Not finishing digging: "
2439 <<"Node metadata disables removal"
2441 cannot_remove_node = true;
2445 catch(InvalidPositionException &e)
2447 derr_server<<"Server: Not finishing digging: Node not found."
2448 <<" Adding block to emerge queue."
2450 m_emerge_queue.addBlock(peer_id,
2451 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2452 cannot_remove_node = true;
2455 // Make sure the player is allowed to do it
2456 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2458 dstream<<"Player "<<player->getName()<<" cannot remove node"
2459 <<" because privileges are "<<getPlayerPrivs(player)
2461 cannot_remove_node = true;
2465 If node can't be removed, set block to be re-sent to
2468 if(cannot_remove_node)
2470 derr_server<<"Server: Not finishing digging."<<std::endl;
2472 // Client probably has wrong data.
2473 // Set block not sent, so that client will get
2475 dstream<<"Client "<<peer_id<<" tried to dig "
2476 <<"node; but node cannot be removed."
2477 <<" setting MapBlock not sent."<<std::endl;
2478 RemoteClient *client = getClient(peer_id);
2479 v3s16 blockpos = getNodeBlockPos(p_under);
2480 client->SetBlockNotSent(blockpos);
2486 Send the removal to all other clients.
2487 - If other player is close, send REMOVENODE
2488 - Otherwise set blocks not sent
2490 core::list<u16> far_players;
2491 sendRemoveNode(p_under, peer_id, &far_players, 100);
2494 Update and send inventory
2497 if(g_settings.getBool("creative_mode") == false)
2502 InventoryList *mlist = player->inventory.getList("main");
2505 InventoryItem *item = mlist->getItem(item_i);
2506 if(item && (std::string)item->getName() == "ToolItem")
2508 ToolItem *titem = (ToolItem*)item;
2509 std::string toolname = titem->getToolName();
2511 // Get digging properties for material and tool
2512 DiggingProperties prop =
2513 getDiggingProperties(material, toolname);
2515 if(prop.diggable == false)
2517 derr_server<<"Server: WARNING: Player digged"
2518 <<" with impossible material + tool"
2519 <<" combination"<<std::endl;
2522 bool weared_out = titem->addWear(prop.wear);
2526 mlist->deleteItem(item_i);
2532 Add dug item to inventory
2535 InventoryItem *item = NULL;
2537 if(mineral != MINERAL_NONE)
2538 item = getDiggedMineralItem(mineral);
2543 std::string &dug_s = content_features(material).dug_item;
2546 std::istringstream is(dug_s, std::ios::binary);
2547 item = InventoryItem::deSerialize(is);
2553 // Add a item to inventory
2554 player->inventory.addItem("main", item);
2557 UpdateCrafting(player->peer_id);
2558 SendInventory(player->peer_id);
2564 (this takes some time so it is done after the quick stuff)
2566 m_ignore_map_edit_events = true;
2567 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2568 m_ignore_map_edit_events = false;
2571 Set blocks not sent to far players
2573 for(core::list<u16>::Iterator
2574 i = far_players.begin();
2575 i != far_players.end(); i++)
2578 RemoteClient *client = getClient(peer_id);
2581 client->SetBlocksNotSent(modified_blocks);
2588 else if(action == 1)
2591 InventoryList *ilist = player->inventory.getList("main");
2596 InventoryItem *item = ilist->getItem(item_i);
2598 // If there is no item, it is not possible to add it anywhere
2603 Handle material items
2605 if(std::string("MaterialItem") == item->getName())
2608 // Don't add a node if this is not a free space
2609 MapNode n2 = m_env.getMap().getNode(p_over);
2610 bool no_enough_privs =
2611 ((getPlayerPrivs(player) & PRIV_BUILD)==0);
2613 dstream<<"Player "<<player->getName()<<" cannot add node"
2614 <<" because privileges are "<<getPlayerPrivs(player)
2617 if(content_buildable_to(n2.d) == false
2620 // Client probably has wrong data.
2621 // Set block not sent, so that client will get
2623 dstream<<"Client "<<peer_id<<" tried to place"
2624 <<" node in invalid position; setting"
2625 <<" MapBlock not sent."<<std::endl;
2626 RemoteClient *client = getClient(peer_id);
2627 v3s16 blockpos = getNodeBlockPos(p_over);
2628 client->SetBlockNotSent(blockpos);
2632 catch(InvalidPositionException &e)
2634 derr_server<<"Server: Ignoring ADDNODE: Node not found"
2635 <<" Adding block to emerge queue."
2637 m_emerge_queue.addBlock(peer_id,
2638 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2642 // Reset build time counter
2643 getClient(peer->id)->m_time_from_building = 0.0;
2646 MaterialItem *mitem = (MaterialItem*)item;
2648 n.d = mitem->getMaterial();
2649 if(content_features(n.d).wall_mounted)
2650 n.dir = packDir(p_under - p_over);
2655 core::list<u16> far_players;
2656 sendAddNode(p_over, n, 0, &far_players, 100);
2661 InventoryList *ilist = player->inventory.getList("main");
2662 if(g_settings.getBool("creative_mode") == false && ilist)
2664 // Remove from inventory and send inventory
2665 if(mitem->getCount() == 1)
2666 ilist->deleteItem(item_i);
2670 UpdateCrafting(peer_id);
2671 SendInventory(peer_id);
2677 This takes some time so it is done after the quick stuff
2679 core::map<v3s16, MapBlock*> modified_blocks;
2680 m_ignore_map_edit_events = true;
2681 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2682 m_ignore_map_edit_events = false;
2685 Set blocks not sent to far players
2687 for(core::list<u16>::Iterator
2688 i = far_players.begin();
2689 i != far_players.end(); i++)
2692 RemoteClient *client = getClient(peer_id);
2695 client->SetBlocksNotSent(modified_blocks);
2699 Calculate special events
2702 /*if(n.d == CONTENT_MESE)
2705 for(s16 z=-1; z<=1; z++)
2706 for(s16 y=-1; y<=1; y++)
2707 for(s16 x=-1; x<=1; x++)
2714 Place other item (not a block)
2718 v3s16 blockpos = getNodeBlockPos(p_over);
2721 Check that the block is loaded so that the item
2722 can properly be added to the static list too
2724 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2727 derr_server<<"Error while placing object: "
2728 "block not found"<<std::endl;
2732 dout_server<<"Placing a miscellaneous item on map"
2735 // Calculate a position for it
2736 v3f pos = intToFloat(p_over, BS);
2738 pos.Y -= BS*0.25; // let it drop a bit
2740 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2741 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2746 ServerActiveObject *obj = item->createSAO(&m_env, 0, pos);
2750 derr_server<<"WARNING: item resulted in NULL object, "
2751 <<"not placing onto map"
2756 // Add the object to the environment
2757 m_env.addActiveObject(obj);
2759 dout_server<<"Placed object"<<std::endl;
2761 if(g_settings.getBool("creative_mode") == false)
2763 // Delete the right amount of items from the slot
2764 u16 dropcount = item->getDropCount();
2766 // Delete item if all gone
2767 if(item->getCount() <= dropcount)
2769 if(item->getCount() < dropcount)
2770 dstream<<"WARNING: Server: dropped more items"
2771 <<" than the slot contains"<<std::endl;
2773 InventoryList *ilist = player->inventory.getList("main");
2775 // Remove from inventory and send inventory
2776 ilist->deleteItem(item_i);
2778 // Else decrement it
2780 item->remove(dropcount);
2783 UpdateCrafting(peer_id);
2784 SendInventory(peer_id);
2792 Catch invalid actions
2796 derr_server<<"WARNING: Server: Invalid action "
2797 <<action<<std::endl;
2801 else if(command == TOSERVER_RELEASE)
2810 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2813 else if(command == TOSERVER_SIGNTEXT)
2815 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2824 std::string datastring((char*)&data[2], datasize-2);
2825 std::istringstream is(datastring, std::ios_base::binary);
2828 is.read((char*)buf, 6);
2829 v3s16 blockpos = readV3S16(buf);
2830 is.read((char*)buf, 2);
2831 s16 id = readS16(buf);
2832 is.read((char*)buf, 2);
2833 u16 textlen = readU16(buf);
2835 for(u16 i=0; i<textlen; i++)
2837 is.read((char*)buf, 1);
2838 text += (char)buf[0];
2841 MapBlock *block = NULL;
2844 block = m_env.getMap().getBlockNoCreate(blockpos);
2846 catch(InvalidPositionException &e)
2848 derr_server<<"Error while setting sign text: "
2849 "block not found"<<std::endl;
2853 MapBlockObject *obj = block->getObject(id);
2856 derr_server<<"Error while setting sign text: "
2857 "object not found"<<std::endl;
2861 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2863 derr_server<<"Error while setting sign text: "
2864 "object is not a sign"<<std::endl;
2868 ((SignObject*)obj)->setText(text);
2870 obj->getBlock()->setChangedFlag();
2872 else if(command == TOSERVER_SIGNNODETEXT)
2874 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2882 std::string datastring((char*)&data[2], datasize-2);
2883 std::istringstream is(datastring, std::ios_base::binary);
2886 is.read((char*)buf, 6);
2887 v3s16 p = readV3S16(buf);
2888 is.read((char*)buf, 2);
2889 u16 textlen = readU16(buf);
2891 for(u16 i=0; i<textlen; i++)
2893 is.read((char*)buf, 1);
2894 text += (char)buf[0];
2897 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2900 if(meta->typeId() != CONTENT_SIGN_WALL)
2902 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
2903 signmeta->setText(text);
2905 v3s16 blockpos = getNodeBlockPos(p);
2906 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2909 block->setChangedFlag();
2912 for(core::map<u16, RemoteClient*>::Iterator
2913 i = m_clients.getIterator();
2914 i.atEnd()==false; i++)
2916 RemoteClient *client = i.getNode()->getValue();
2917 client->SetBlockNotSent(blockpos);
2920 else if(command == TOSERVER_INVENTORY_ACTION)
2922 /*// Ignore inventory changes if in creative mode
2923 if(g_settings.getBool("creative_mode") == true)
2925 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2929 // Strip command and create a stream
2930 std::string datastring((char*)&data[2], datasize-2);
2931 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2932 std::istringstream is(datastring, std::ios_base::binary);
2934 InventoryAction *a = InventoryAction::deSerialize(is);
2939 c.current_player = player;
2942 Handle craftresult specially if not in creative mode
2944 bool disable_action = false;
2945 if(a->getType() == IACTION_MOVE
2946 && g_settings.getBool("creative_mode") == false)
2948 IMoveAction *ma = (IMoveAction*)a;
2949 if(ma->to_inv == "current_player" &&
2950 ma->from_inv == "current_player")
2952 InventoryList *rlist = player->inventory.getList("craftresult");
2954 InventoryList *clist = player->inventory.getList("craft");
2956 InventoryList *mlist = player->inventory.getList("main");
2959 Craftresult is no longer preview if something
2962 if(ma->to_list == "craftresult"
2963 && ma->from_list != "craftresult")
2965 // If it currently is a preview, remove
2967 if(player->craftresult_is_preview)
2969 rlist->deleteItem(0);
2971 player->craftresult_is_preview = false;
2974 Crafting takes place if this condition is true.
2976 if(player->craftresult_is_preview &&
2977 ma->from_list == "craftresult")
2979 player->craftresult_is_preview = false;
2980 clist->decrementMaterials(1);
2983 If the craftresult is placed on itself, move it to
2984 main inventory instead of doing the action
2986 if(ma->to_list == "craftresult"
2987 && ma->from_list == "craftresult")
2989 disable_action = true;
2991 InventoryItem *item1 = rlist->changeItem(0, NULL);
2992 mlist->addItem(item1);
2997 if(disable_action == false)
2999 // Feed action to player inventory
3007 UpdateCrafting(player->peer_id);
3008 SendInventory(player->peer_id);
3013 dstream<<"TOSERVER_INVENTORY_ACTION: "
3014 <<"InventoryAction::deSerialize() returned NULL"
3018 else if(command == TOSERVER_CHAT_MESSAGE)
3026 std::string datastring((char*)&data[2], datasize-2);
3027 std::istringstream is(datastring, std::ios_base::binary);
3030 is.read((char*)buf, 2);
3031 u16 len = readU16(buf);
3033 std::wstring message;
3034 for(u16 i=0; i<len; i++)
3036 is.read((char*)buf, 2);
3037 message += (wchar_t)readU16(buf);
3040 // Get player name of this client
3041 std::wstring name = narrow_to_wide(player->getName());
3043 // Line to send to players
3045 // Whether to send to the player that sent the line
3046 bool send_to_sender = false;
3047 // Whether to send to other players
3048 bool send_to_others = false;
3050 // Local player gets all privileges regardless of
3051 // what's set on their account.
3052 u64 privs = getPlayerPrivs(player);
3055 std::wstring commandprefix = L"/#";
3056 if(message.substr(0, commandprefix.size()) == commandprefix)
3058 line += L"Server: ";
3060 message = message.substr(commandprefix.size());
3062 ServerCommandContext *ctx = new ServerCommandContext(
3063 str_split(message, L' '),
3069 line += processServerCommand(ctx);
3070 send_to_sender = ctx->flags & 1;
3071 send_to_others = ctx->flags & 2;
3077 if(privs & PRIV_SHOUT)
3083 send_to_others = true;
3087 line += L"Server: You are not allowed to shout";
3088 send_to_sender = true;
3094 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
3097 Send the message to clients
3099 for(core::map<u16, RemoteClient*>::Iterator
3100 i = m_clients.getIterator();
3101 i.atEnd() == false; i++)
3103 // Get client and check that it is valid
3104 RemoteClient *client = i.getNode()->getValue();
3105 assert(client->peer_id == i.getNode()->getKey());
3106 if(client->serialization_version == SER_FMT_VER_INVALID)
3110 bool sender_selected = (peer_id == client->peer_id);
3111 if(sender_selected == true && send_to_sender == false)
3113 if(sender_selected == false && send_to_others == false)
3116 SendChatMessage(client->peer_id, line);
3120 else if(command == TOSERVER_DAMAGE)
3122 if(g_settings.getBool("enable_damage"))
3124 std::string datastring((char*)&data[2], datasize-2);
3125 std::istringstream is(datastring, std::ios_base::binary);
3126 u8 damage = readU8(is);
3127 if(player->hp > damage)
3129 player->hp -= damage;
3135 dstream<<"TODO: Server: TOSERVER_HP_DECREMENT: Player dies"
3138 v3f pos = findSpawnPos(m_env.getServerMap());
3139 player->setPosition(pos);
3141 SendMovePlayer(player);
3142 SendPlayerHP(player);
3144 //TODO: Throw items around
3148 SendPlayerHP(player);
3150 else if(command == TOSERVER_PASSWORD)
3153 [0] u16 TOSERVER_PASSWORD
3154 [2] u8[28] old password
3155 [30] u8[28] new password
3158 if(datasize != 2+PASSWORD_SIZE*2)
3160 /*char password[PASSWORD_SIZE];
3161 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3162 password[i] = data[2+i];
3163 password[PASSWORD_SIZE-1] = 0;*/
3165 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3173 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3175 char c = data[2+PASSWORD_SIZE+i];
3181 std::string playername = player->getName();
3183 if(m_authmanager.exists(playername) == false)
3185 dstream<<"Server: playername not found in authmanager"<<std::endl;
3186 // Wrong old password supplied!!
3187 SendChatMessage(peer_id, L"playername not found in authmanager");
3191 std::string checkpwd = m_authmanager.getPassword(playername);
3193 if(oldpwd != checkpwd)
3195 dstream<<"Server: invalid old password"<<std::endl;
3196 // Wrong old password supplied!!
3197 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
3201 m_authmanager.setPassword(playername, newpwd);
3203 dstream<<"Server: password change successful for "<<playername
3205 SendChatMessage(peer_id, L"Password change successful");
3209 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
3210 "unknown command "<<command<<std::endl;
3214 catch(SendFailedException &e)
3216 derr_server<<"Server::ProcessData(): SendFailedException: "
3222 void Server::onMapEditEvent(MapEditEvent *event)
3224 dstream<<"Server::onMapEditEvent()"<<std::endl;
3225 if(m_ignore_map_edit_events)
3227 MapEditEvent *e = event->clone();
3228 m_unsent_map_edit_queue.push_back(e);
3231 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3233 if(id == "current_player")
3235 assert(c->current_player);
3236 return &(c->current_player->inventory);
3240 std::string id0 = fn.next(":");
3242 if(id0 == "nodemeta")
3245 p.X = stoi(fn.next(","));
3246 p.Y = stoi(fn.next(","));
3247 p.Z = stoi(fn.next(","));
3248 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3250 return meta->getInventory();
3251 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3252 <<"no metadata found"<<std::endl;
3256 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3259 void Server::inventoryModified(InventoryContext *c, std::string id)
3261 if(id == "current_player")
3263 assert(c->current_player);
3265 UpdateCrafting(c->current_player->peer_id);
3266 SendInventory(c->current_player->peer_id);
3271 std::string id0 = fn.next(":");
3273 if(id0 == "nodemeta")
3276 p.X = stoi(fn.next(","));
3277 p.Y = stoi(fn.next(","));
3278 p.Z = stoi(fn.next(","));
3279 v3s16 blockpos = getNodeBlockPos(p);
3281 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3283 meta->inventoryModified();
3285 for(core::map<u16, RemoteClient*>::Iterator
3286 i = m_clients.getIterator();
3287 i.atEnd()==false; i++)
3289 RemoteClient *client = i.getNode()->getValue();
3290 client->SetBlockNotSent(blockpos);
3296 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3299 core::list<PlayerInfo> Server::getPlayerInfo()
3301 DSTACK(__FUNCTION_NAME);
3302 JMutexAutoLock envlock(m_env_mutex);
3303 JMutexAutoLock conlock(m_con_mutex);
3305 core::list<PlayerInfo> list;
3307 core::list<Player*> players = m_env.getPlayers();
3309 core::list<Player*>::Iterator i;
3310 for(i = players.begin();
3311 i != players.end(); i++)
3315 Player *player = *i;
3318 con::Peer *peer = m_con.GetPeer(player->peer_id);
3319 // Copy info from peer to info struct
3321 info.address = peer->address;
3322 info.avg_rtt = peer->avg_rtt;
3324 catch(con::PeerNotFoundException &e)
3326 // Set dummy peer info
3328 info.address = Address(0,0,0,0,0);
3332 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3333 info.position = player->getPosition();
3335 list.push_back(info);
3342 void Server::peerAdded(con::Peer *peer)
3344 DSTACK(__FUNCTION_NAME);
3345 dout_server<<"Server::peerAdded(): peer->id="
3346 <<peer->id<<std::endl;
3349 c.type = PEER_ADDED;
3350 c.peer_id = peer->id;
3352 m_peer_change_queue.push_back(c);
3355 void Server::deletingPeer(con::Peer *peer, bool timeout)
3357 DSTACK(__FUNCTION_NAME);
3358 dout_server<<"Server::deletingPeer(): peer->id="
3359 <<peer->id<<", timeout="<<timeout<<std::endl;
3362 c.type = PEER_REMOVED;
3363 c.peer_id = peer->id;
3364 c.timeout = timeout;
3365 m_peer_change_queue.push_back(c);
3372 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3374 DSTACK(__FUNCTION_NAME);
3375 std::ostringstream os(std::ios_base::binary);
3377 writeU16(os, TOCLIENT_HP);
3381 std::string s = os.str();
3382 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3384 con.Send(peer_id, 0, data, true);
3387 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3388 const std::wstring &reason)
3390 DSTACK(__FUNCTION_NAME);
3391 std::ostringstream os(std::ios_base::binary);
3393 writeU16(os, TOCLIENT_ACCESS_DENIED);
3394 os<<serializeWideString(reason);
3397 std::string s = os.str();
3398 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3400 con.Send(peer_id, 0, data, true);
3404 Non-static send methods
3407 void Server::SendObjectData(float dtime)
3409 DSTACK(__FUNCTION_NAME);
3411 core::map<v3s16, bool> stepped_blocks;
3413 for(core::map<u16, RemoteClient*>::Iterator
3414 i = m_clients.getIterator();
3415 i.atEnd() == false; i++)
3417 u16 peer_id = i.getNode()->getKey();
3418 RemoteClient *client = i.getNode()->getValue();
3419 assert(client->peer_id == peer_id);
3421 if(client->serialization_version == SER_FMT_VER_INVALID)
3424 client->SendObjectData(this, dtime, stepped_blocks);
3428 void Server::SendPlayerInfos()
3430 DSTACK(__FUNCTION_NAME);
3432 //JMutexAutoLock envlock(m_env_mutex);
3434 // Get connected players
3435 core::list<Player*> players = m_env.getPlayers(true);
3437 u32 player_count = players.getSize();
3438 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3440 SharedBuffer<u8> data(datasize);
3441 writeU16(&data[0], TOCLIENT_PLAYERINFO);
3444 core::list<Player*>::Iterator i;
3445 for(i = players.begin();
3446 i != players.end(); i++)
3448 Player *player = *i;
3450 /*dstream<<"Server sending player info for player with "
3451 "peer_id="<<player->peer_id<<std::endl;*/
3453 writeU16(&data[start], player->peer_id);
3454 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3455 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3456 start += 2+PLAYERNAME_SIZE;
3459 //JMutexAutoLock conlock(m_con_mutex);
3462 m_con.SendToAll(0, data, true);
3465 void Server::SendInventory(u16 peer_id)
3467 DSTACK(__FUNCTION_NAME);
3469 Player* player = m_env.getPlayer(peer_id);
3476 std::ostringstream os;
3477 //os.imbue(std::locale("C"));
3479 player->inventory.serialize(os);
3481 std::string s = os.str();
3483 SharedBuffer<u8> data(s.size()+2);
3484 writeU16(&data[0], TOCLIENT_INVENTORY);
3485 memcpy(&data[2], s.c_str(), s.size());
3488 m_con.Send(peer_id, 0, data, true);
3491 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3493 DSTACK(__FUNCTION_NAME);
3495 std::ostringstream os(std::ios_base::binary);
3499 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3500 os.write((char*)buf, 2);
3503 writeU16(buf, message.size());
3504 os.write((char*)buf, 2);
3507 for(u32 i=0; i<message.size(); i++)
3511 os.write((char*)buf, 2);
3515 std::string s = os.str();
3516 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3518 m_con.Send(peer_id, 0, data, true);
3521 void Server::BroadcastChatMessage(const std::wstring &message)
3523 for(core::map<u16, RemoteClient*>::Iterator
3524 i = m_clients.getIterator();
3525 i.atEnd() == false; i++)
3527 // Get client and check that it is valid
3528 RemoteClient *client = i.getNode()->getValue();
3529 assert(client->peer_id == i.getNode()->getKey());
3530 if(client->serialization_version == SER_FMT_VER_INVALID)
3533 SendChatMessage(client->peer_id, message);
3537 void Server::SendPlayerHP(Player *player)
3539 SendHP(m_con, player->peer_id, player->hp);
3542 void Server::SendMovePlayer(Player *player)
3544 DSTACK(__FUNCTION_NAME);
3545 std::ostringstream os(std::ios_base::binary);
3547 writeU16(os, TOCLIENT_MOVE_PLAYER);
3548 writeV3F1000(os, player->getPosition());
3549 writeF1000(os, player->getPitch());
3550 writeF1000(os, player->getYaw());
3553 v3f pos = player->getPosition();
3554 f32 pitch = player->getPitch();
3555 f32 yaw = player->getYaw();
3556 dstream<<"Server sending TOCLIENT_MOVE_PLAYER"
3557 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3564 std::string s = os.str();
3565 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3567 m_con.Send(player->peer_id, 0, data, true);
3570 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3571 core::list<u16> *far_players, float far_d_nodes)
3573 float maxd = far_d_nodes*BS;
3574 v3f p_f = intToFloat(p, BS);
3578 SharedBuffer<u8> reply(replysize);
3579 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3580 writeS16(&reply[2], p.X);
3581 writeS16(&reply[4], p.Y);
3582 writeS16(&reply[6], p.Z);
3584 for(core::map<u16, RemoteClient*>::Iterator
3585 i = m_clients.getIterator();
3586 i.atEnd() == false; i++)
3588 // Get client and check that it is valid
3589 RemoteClient *client = i.getNode()->getValue();
3590 assert(client->peer_id == i.getNode()->getKey());
3591 if(client->serialization_version == SER_FMT_VER_INVALID)
3594 // Don't send if it's the same one
3595 if(client->peer_id == ignore_id)
3601 Player *player = m_env.getPlayer(client->peer_id);
3604 // If player is far away, only set modified blocks not sent
3605 v3f player_pos = player->getPosition();
3606 if(player_pos.getDistanceFrom(p_f) > maxd)
3608 far_players->push_back(client->peer_id);
3615 m_con.Send(client->peer_id, 0, reply, true);
3619 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3620 core::list<u16> *far_players, float far_d_nodes)
3622 float maxd = far_d_nodes*BS;
3623 v3f p_f = intToFloat(p, BS);
3625 for(core::map<u16, RemoteClient*>::Iterator
3626 i = m_clients.getIterator();
3627 i.atEnd() == false; i++)
3629 // Get client and check that it is valid
3630 RemoteClient *client = i.getNode()->getValue();
3631 assert(client->peer_id == i.getNode()->getKey());
3632 if(client->serialization_version == SER_FMT_VER_INVALID)
3635 // Don't send if it's the same one
3636 if(client->peer_id == ignore_id)
3642 Player *player = m_env.getPlayer(client->peer_id);
3645 // If player is far away, only set modified blocks not sent
3646 v3f player_pos = player->getPosition();
3647 if(player_pos.getDistanceFrom(p_f) > maxd)
3649 far_players->push_back(client->peer_id);
3656 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3657 SharedBuffer<u8> reply(replysize);
3658 writeU16(&reply[0], TOCLIENT_ADDNODE);
3659 writeS16(&reply[2], p.X);
3660 writeS16(&reply[4], p.Y);
3661 writeS16(&reply[6], p.Z);
3662 n.serialize(&reply[8], client->serialization_version);
3665 m_con.Send(client->peer_id, 0, reply, true);
3669 void Server::setBlockNotSent(v3s16 p)
3671 for(core::map<u16, RemoteClient*>::Iterator
3672 i = m_clients.getIterator();
3673 i.atEnd()==false; i++)
3675 RemoteClient *client = i.getNode()->getValue();
3676 client->SetBlockNotSent(p);
3680 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3682 DSTACK(__FUNCTION_NAME);
3684 v3s16 p = block->getPos();
3688 bool completely_air = true;
3689 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3690 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3691 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3693 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
3695 completely_air = false;
3696 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
3701 dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
3703 dstream<<"[completely air] ";
3708 Create a packet with the block in the right format
3711 std::ostringstream os(std::ios_base::binary);
3712 block->serialize(os, ver);
3713 std::string s = os.str();
3714 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3716 u32 replysize = 8 + blockdata.getSize();
3717 SharedBuffer<u8> reply(replysize);
3718 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3719 writeS16(&reply[2], p.X);
3720 writeS16(&reply[4], p.Y);
3721 writeS16(&reply[6], p.Z);
3722 memcpy(&reply[8], *blockdata, blockdata.getSize());
3724 /*dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3725 <<": \tpacket size: "<<replysize<<std::endl;*/
3730 m_con.Send(peer_id, 1, reply, true);
3733 void Server::SendBlocks(float dtime)
3735 DSTACK(__FUNCTION_NAME);
3737 JMutexAutoLock envlock(m_env_mutex);
3738 JMutexAutoLock conlock(m_con_mutex);
3740 //TimeTaker timer("Server::SendBlocks");
3742 core::array<PrioritySortedBlockTransfer> queue;
3744 s32 total_sending = 0;
3747 ScopeProfiler sp(&g_profiler, "Server: selecting blocks for sending");
3749 for(core::map<u16, RemoteClient*>::Iterator
3750 i = m_clients.getIterator();
3751 i.atEnd() == false; i++)
3753 RemoteClient *client = i.getNode()->getValue();
3754 assert(client->peer_id == i.getNode()->getKey());
3756 total_sending += client->SendingCount();
3758 if(client->serialization_version == SER_FMT_VER_INVALID)
3761 client->GetNextBlocks(this, dtime, queue);
3766 // Lowest priority number comes first.
3767 // Lowest is most important.
3770 for(u32 i=0; i<queue.size(); i++)
3772 //TODO: Calculate limit dynamically
3773 if(total_sending >= g_settings.getS32
3774 ("max_simultaneous_block_sends_server_total"))
3777 PrioritySortedBlockTransfer q = queue[i];
3779 MapBlock *block = NULL;
3782 block = m_env.getMap().getBlockNoCreate(q.pos);
3784 catch(InvalidPositionException &e)
3789 RemoteClient *client = getClient(q.peer_id);
3791 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3793 client->SentBlock(q.pos);
3803 void Server::UpdateCrafting(u16 peer_id)
3805 DSTACK(__FUNCTION_NAME);
3807 Player* player = m_env.getPlayer(peer_id);
3811 Calculate crafting stuff
3813 if(g_settings.getBool("creative_mode") == false)
3815 InventoryList *clist = player->inventory.getList("craft");
3816 InventoryList *rlist = player->inventory.getList("craftresult");
3818 if(rlist->getUsedSlots() == 0)
3819 player->craftresult_is_preview = true;
3821 if(rlist && player->craftresult_is_preview)
3823 rlist->clearItems();
3825 if(clist && rlist && player->craftresult_is_preview)
3827 InventoryItem *items[9];
3828 for(u16 i=0; i<9; i++)
3830 items[i] = clist->getItem(i);
3833 // Get result of crafting grid
3834 InventoryItem *result = craft_get_result(items);
3836 rlist->addItem(result);
3839 } // if creative_mode == false
3842 RemoteClient* Server::getClient(u16 peer_id)
3844 DSTACK(__FUNCTION_NAME);
3845 //JMutexAutoLock lock(m_con_mutex);
3846 core::map<u16, RemoteClient*>::Node *n;
3847 n = m_clients.find(peer_id);
3848 // A client should exist for all peers
3850 return n->getValue();
3853 std::wstring Server::getStatusString()
3855 std::wostringstream os(std::ios_base::binary);
3858 os<<L"version="<<narrow_to_wide(VERSION_STRING);
3860 os<<L", uptime="<<m_uptime.get();
3861 // Information about clients
3863 for(core::map<u16, RemoteClient*>::Iterator
3864 i = m_clients.getIterator();
3865 i.atEnd() == false; i++)
3867 // Get client and check that it is valid
3868 RemoteClient *client = i.getNode()->getValue();
3869 assert(client->peer_id == i.getNode()->getKey());
3870 if(client->serialization_version == SER_FMT_VER_INVALID)
3873 Player *player = m_env.getPlayer(client->peer_id);
3874 // Get name of player
3875 std::wstring name = L"unknown";
3877 name = narrow_to_wide(player->getName());
3878 // Add name to information string
3882 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
3883 os<<" WARNING: Map saving is disabled."<<std::endl;
3887 v3f findSpawnPos(ServerMap &map)
3889 //return v3f(50,50,50)*BS;
3892 s16 groundheight = 0;
3894 // Try to find a good place a few times
3895 for(s32 i=0; i<1000; i++)
3898 // We're going to try to throw the player to this position
3899 nodepos = v2s16(-range + (myrand()%(range*2)),
3900 -range + (myrand()%(range*2)));
3901 v2s16 sectorpos = getNodeSectorPos(nodepos);
3902 // Get sector (NOTE: Don't get because it's slow)
3903 //m_env.getMap().emergeSector(sectorpos);
3904 // Get ground height at point (fallbacks to heightmap function)
3905 groundheight = map.findGroundLevel(nodepos);
3906 // Don't go underwater
3907 if(groundheight < WATER_LEVEL)
3909 //dstream<<"-> Underwater"<<std::endl;
3912 // Don't go to high places
3913 if(groundheight > WATER_LEVEL + 4)
3915 //dstream<<"-> Underwater"<<std::endl;
3919 // Found a good place
3920 //dstream<<"Searched through "<<i<<" places."<<std::endl;
3924 // If no suitable place was not found, go above water at least.
3925 if(groundheight < WATER_LEVEL)
3926 groundheight = WATER_LEVEL;
3928 return intToFloat(v3s16(
3935 Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id)
3938 Try to get an existing player
3940 Player *player = m_env.getPlayer(name);
3943 // If player is already connected, cancel
3944 if(player->peer_id != 0)
3946 dstream<<"emergePlayer(): Player already connected"<<std::endl;
3951 player->peer_id = peer_id;
3953 // Reset inventory to creative if in creative mode
3954 if(g_settings.getBool("creative_mode"))
3956 craft_set_creative_inventory(player);
3963 If player with the wanted peer_id already exists, cancel.
3965 if(m_env.getPlayer(peer_id) != NULL)
3967 dstream<<"emergePlayer(): Player with wrong name but same"
3968 " peer_id already exists"<<std::endl;
3976 player = new ServerRemotePlayer();
3977 //player->peer_id = c.peer_id;
3978 //player->peer_id = PEER_ID_INEXISTENT;
3979 player->peer_id = peer_id;
3980 player->updateName(name);
3981 m_authmanager.add(name);
3982 m_authmanager.setPassword(name, password);
3983 m_authmanager.setPrivs(name,
3984 stringToPrivs(g_settings.get("default_privs")));
3990 dstream<<"Server: Finding spawn place for player \""
3991 <<player->getName()<<"\""<<std::endl;
3993 v3f pos = findSpawnPos(m_env.getServerMap());
3995 player->setPosition(pos);
3998 Add player to environment
4001 m_env.addPlayer(player);
4004 Add stuff to inventory
4007 if(g_settings.getBool("creative_mode"))
4009 craft_set_creative_inventory(player);
4011 else if(g_settings.getBool("give_initial_stuff"))
4013 craft_give_initial_stuff(player);
4018 } // create new player
4021 void Server::handlePeerChange(PeerChange &c)
4023 JMutexAutoLock envlock(m_env_mutex);
4024 JMutexAutoLock conlock(m_con_mutex);
4026 if(c.type == PEER_ADDED)
4033 core::map<u16, RemoteClient*>::Node *n;
4034 n = m_clients.find(c.peer_id);
4035 // The client shouldn't already exist
4039 RemoteClient *client = new RemoteClient();
4040 client->peer_id = c.peer_id;
4041 m_clients.insert(client->peer_id, client);
4044 else if(c.type == PEER_REMOVED)
4051 core::map<u16, RemoteClient*>::Node *n;
4052 n = m_clients.find(c.peer_id);
4053 // The client should exist
4057 Mark objects to be not known by the client
4059 RemoteClient *client = n->getValue();
4061 for(core::map<u16, bool>::Iterator
4062 i = client->m_known_objects.getIterator();
4063 i.atEnd()==false; i++)
4066 u16 id = i.getNode()->getKey();
4067 ServerActiveObject* obj = m_env.getActiveObject(id);
4069 if(obj && obj->m_known_by_count > 0)
4070 obj->m_known_by_count--;
4073 // Collect information about leaving in chat
4074 std::wstring message;
4076 std::wstring name = L"unknown";
4077 Player *player = m_env.getPlayer(c.peer_id);
4079 name = narrow_to_wide(player->getName());
4083 message += L" left game";
4085 message += L" (timed out)";
4090 m_env.removePlayer(c.peer_id);
4093 // Set player client disconnected
4095 Player *player = m_env.getPlayer(c.peer_id);
4097 player->peer_id = 0;
4101 delete m_clients[c.peer_id];
4102 m_clients.remove(c.peer_id);
4104 // Send player info to all remaining clients
4107 // Send leave chat message to all remaining clients
4108 BroadcastChatMessage(message);
4117 void Server::handlePeerChanges()
4119 while(m_peer_change_queue.size() > 0)
4121 PeerChange c = m_peer_change_queue.pop_front();
4123 dout_server<<"Server: Handling peer change: "
4124 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4127 handlePeerChange(c);
4131 u64 Server::getPlayerPrivs(Player *player)
4135 std::string playername = player->getName();
4136 // Local player gets all privileges regardless of
4137 // what's set on their account.
4138 if(g_settings.get("name") == playername)
4144 return getPlayerAuthPrivs(playername);
4148 void dedicated_server_loop(Server &server, bool &kill)
4150 DSTACK(__FUNCTION_NAME);
4152 dstream<<DTIME<<std::endl;
4153 dstream<<"========================"<<std::endl;
4154 dstream<<"Running dedicated server"<<std::endl;
4155 dstream<<"========================"<<std::endl;
4158 IntervalLimiter m_profiler_interval;
4162 // This is kind of a hack but can be done like this
4163 // because server.step() is very light
4165 ScopeProfiler sp(&g_profiler, "dedicated server sleep");
4170 if(server.getShutdownRequested() || kill)
4172 dstream<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4179 float profiler_print_interval =
4180 g_settings.getFloat("profiler_print_interval");
4181 if(profiler_print_interval != 0)
4183 if(m_profiler_interval.step(0.030, profiler_print_interval))
4185 dstream<<"Profiler:"<<std::endl;
4186 g_profiler.print(dstream);
4194 static int counter = 0;
4200 core::list<PlayerInfo> list = server.getPlayerInfo();
4201 core::list<PlayerInfo>::Iterator i;
4202 static u32 sum_old = 0;
4203 u32 sum = PIChecksum(list);
4206 dstream<<DTIME<<"Player info:"<<std::endl;
4207 for(i=list.begin(); i!=list.end(); i++)
4209 i->PrintLine(&dstream);