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); // DEPRECATED
883 Stop collecting objects if data is already too big
885 // Sum of player and object data sizes
886 s32 sum = (s32)os.tellp() + 2 + (s32)bos.tellp();
887 // break out if data too big
888 if(sum > MAX_OBJECTDATA_SIZE)
890 goto skip_subsequent;
894 catch(InvalidPositionException &e)
897 // Add it to the emerge queue and trigger the thread.
898 // Fetch the block only if it is on disk.
900 // Grab and increment counter
901 /*SharedPtr<JMutexAutoLock> lock
902 (m_num_blocks_in_emerge_queue.getLock());
903 m_num_blocks_in_emerge_queue.m_value++;*/
905 // Add to queue as an anonymous fetch from disk
906 u8 flags = BLOCK_EMERGE_FLAG_FROMDISK;
907 server->m_emerge_queue.addBlock(0, p, flags);
908 server->m_emergethread.trigger();
916 writeU16(buf, blockcount);
917 os.write((char*)buf, 2);
919 // Write block objects
926 //dstream<<"Server: Sending object data to "<<peer_id<<std::endl;
929 std::string s = os.str();
930 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
931 // Send as unreliable
932 server->m_con.Send(peer_id, 0, data, false);
935 void RemoteClient::GotBlock(v3s16 p)
937 if(m_blocks_sending.find(p) != NULL)
938 m_blocks_sending.remove(p);
941 /*dstream<<"RemoteClient::GotBlock(): Didn't find in"
942 " m_blocks_sending"<<std::endl;*/
943 m_excess_gotblocks++;
945 m_blocks_sent.insert(p, true);
948 void RemoteClient::SentBlock(v3s16 p)
950 if(m_blocks_sending.find(p) == NULL)
951 m_blocks_sending.insert(p, 0.0);
953 dstream<<"RemoteClient::SentBlock(): Sent block"
954 " already in m_blocks_sending"<<std::endl;
957 void RemoteClient::SetBlockNotSent(v3s16 p)
959 m_nearest_unsent_d = 0;
961 if(m_blocks_sending.find(p) != NULL)
962 m_blocks_sending.remove(p);
963 if(m_blocks_sent.find(p) != NULL)
964 m_blocks_sent.remove(p);
967 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
969 m_nearest_unsent_d = 0;
971 for(core::map<v3s16, MapBlock*>::Iterator
972 i = blocks.getIterator();
973 i.atEnd()==false; i++)
975 v3s16 p = i.getNode()->getKey();
977 if(m_blocks_sending.find(p) != NULL)
978 m_blocks_sending.remove(p);
979 if(m_blocks_sent.find(p) != NULL)
980 m_blocks_sent.remove(p);
988 PlayerInfo::PlayerInfo()
994 void PlayerInfo::PrintLine(std::ostream *s)
997 (*s)<<"\""<<name<<"\" ("
998 <<(position.X/10)<<","<<(position.Y/10)
999 <<","<<(position.Z/10)<<") ";
1001 (*s)<<" avg_rtt="<<avg_rtt;
1005 u32 PIChecksum(core::list<PlayerInfo> &l)
1007 core::list<PlayerInfo>::Iterator i;
1010 for(i=l.begin(); i!=l.end(); i++)
1012 checksum += a * (i->id+1);
1013 checksum ^= 0x435aafcd;
1024 std::string mapsavedir
1026 m_env(new ServerMap(mapsavedir), this),
1027 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
1028 m_authmanager(mapsavedir+"/auth.txt"),
1030 m_emergethread(this),
1032 m_time_of_day_send_timer(0),
1034 m_mapsavedir(mapsavedir),
1035 m_shutdown_requested(false),
1036 m_ignore_map_edit_events(false),
1037 m_ignore_map_edit_events_peer_id(0)
1039 m_liquid_transform_timer = 0.0;
1040 m_print_info_timer = 0.0;
1041 m_objectdata_timer = 0.0;
1042 m_emergethread_trigger_timer = 0.0;
1043 m_savemap_timer = 0.0;
1047 m_step_dtime_mutex.Init();
1050 // Register us to receive map edit events
1051 m_env.getMap().addEventReceiver(this);
1053 // If file exists, load environment metadata
1054 if(fs::PathExists(m_mapsavedir+"/env_meta.txt"))
1056 dstream<<"Server: Loading environment metadata"<<std::endl;
1057 m_env.loadMeta(m_mapsavedir);
1061 dstream<<"Server: Loading players"<<std::endl;
1062 m_env.deSerializePlayers(m_mapsavedir);
1067 dstream<<"Server::~Server()"<<std::endl;
1070 Send shutdown message
1073 JMutexAutoLock conlock(m_con_mutex);
1075 std::wstring line = L"*** Server shutting down";
1078 Send the message to clients
1080 for(core::map<u16, RemoteClient*>::Iterator
1081 i = m_clients.getIterator();
1082 i.atEnd() == false; i++)
1084 // Get client and check that it is valid
1085 RemoteClient *client = i.getNode()->getValue();
1086 assert(client->peer_id == i.getNode()->getKey());
1087 if(client->serialization_version == SER_FMT_VER_INVALID)
1091 SendChatMessage(client->peer_id, line);
1093 catch(con::PeerNotFoundException &e)
1101 dstream<<"Server: Saving players"<<std::endl;
1102 m_env.serializePlayers(m_mapsavedir);
1105 Save environment metadata
1107 dstream<<"Server: Saving environment metadata"<<std::endl;
1108 m_env.saveMeta(m_mapsavedir);
1119 JMutexAutoLock clientslock(m_con_mutex);
1121 for(core::map<u16, RemoteClient*>::Iterator
1122 i = m_clients.getIterator();
1123 i.atEnd() == false; i++)
1126 // NOTE: These are removed by env destructor
1128 u16 peer_id = i.getNode()->getKey();
1129 JMutexAutoLock envlock(m_env_mutex);
1130 m_env.removePlayer(peer_id);
1134 delete i.getNode()->getValue();
1139 void Server::start(unsigned short port)
1141 DSTACK(__FUNCTION_NAME);
1142 // Stop thread if already running
1145 // Initialize connection
1146 m_con.setTimeoutMs(30);
1150 m_thread.setRun(true);
1153 dout_server<<"Server: Started on port "<<port<<std::endl;
1158 DSTACK(__FUNCTION_NAME);
1160 // Stop threads (set run=false first so both start stopping)
1161 m_thread.setRun(false);
1162 m_emergethread.setRun(false);
1164 m_emergethread.stop();
1166 dout_server<<"Server: Threads stopped"<<std::endl;
1169 void Server::step(float dtime)
1171 DSTACK(__FUNCTION_NAME);
1176 JMutexAutoLock lock(m_step_dtime_mutex);
1177 m_step_dtime += dtime;
1181 void Server::AsyncRunStep()
1183 DSTACK(__FUNCTION_NAME);
1187 JMutexAutoLock lock1(m_step_dtime_mutex);
1188 dtime = m_step_dtime;
1192 ScopeProfiler sp(&g_profiler, "Server: selecting and sending "
1193 "blocks to clients");
1194 // Send blocks to clients
1201 //dstream<<"Server steps "<<dtime<<std::endl;
1202 //dstream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1205 JMutexAutoLock lock1(m_step_dtime_mutex);
1206 m_step_dtime -= dtime;
1213 m_uptime.set(m_uptime.get() + dtime);
1217 Update m_time_of_day and overall game time
1220 JMutexAutoLock envlock(m_env_mutex);
1222 m_time_counter += dtime;
1223 f32 speed = g_settings.getFloat("time_speed") * 24000./(24.*3600);
1224 u32 units = (u32)(m_time_counter*speed);
1225 m_time_counter -= (f32)units / speed;
1227 m_env.setTimeOfDay((m_env.getTimeOfDay() + units) % 24000);
1229 //dstream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1232 Send to clients at constant intervals
1235 m_time_of_day_send_timer -= dtime;
1236 if(m_time_of_day_send_timer < 0.0)
1238 m_time_of_day_send_timer = g_settings.getFloat("time_send_interval");
1240 //JMutexAutoLock envlock(m_env_mutex);
1241 JMutexAutoLock conlock(m_con_mutex);
1243 for(core::map<u16, RemoteClient*>::Iterator
1244 i = m_clients.getIterator();
1245 i.atEnd() == false; i++)
1247 RemoteClient *client = i.getNode()->getValue();
1248 //Player *player = m_env.getPlayer(client->peer_id);
1250 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1251 m_env.getTimeOfDay());
1253 m_con.Send(client->peer_id, 0, data, true);
1259 // Process connection's timeouts
1260 JMutexAutoLock lock2(m_con_mutex);
1261 ScopeProfiler sp(&g_profiler, "Server: connection timeout processing");
1262 m_con.RunTimeouts(dtime);
1266 // This has to be called so that the client list gets synced
1267 // with the peer list of the connection
1268 ScopeProfiler sp(&g_profiler, "Server: peer change handling");
1269 handlePeerChanges();
1274 // This also runs Map's timers
1275 JMutexAutoLock lock(m_env_mutex);
1276 ScopeProfiler sp(&g_profiler, "Server: environment step");
1287 m_liquid_transform_timer += dtime;
1288 if(m_liquid_transform_timer >= 1.00)
1290 m_liquid_transform_timer -= 1.00;
1292 JMutexAutoLock lock(m_env_mutex);
1294 ScopeProfiler sp(&g_profiler, "Server: liquid transform");
1296 core::map<v3s16, MapBlock*> modified_blocks;
1297 m_env.getMap().transformLiquids(modified_blocks);
1302 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1303 ServerMap &map = ((ServerMap&)m_env.getMap());
1304 map.updateLighting(modified_blocks, lighting_modified_blocks);
1306 // Add blocks modified by lighting to modified_blocks
1307 for(core::map<v3s16, MapBlock*>::Iterator
1308 i = lighting_modified_blocks.getIterator();
1309 i.atEnd() == false; i++)
1311 MapBlock *block = i.getNode()->getValue();
1312 modified_blocks.insert(block->getPos(), block);
1316 Set the modified blocks unsent for all the clients
1319 JMutexAutoLock lock2(m_con_mutex);
1321 for(core::map<u16, RemoteClient*>::Iterator
1322 i = m_clients.getIterator();
1323 i.atEnd() == false; i++)
1325 RemoteClient *client = i.getNode()->getValue();
1327 if(modified_blocks.size() > 0)
1329 // Remove block from sent history
1330 client->SetBlocksNotSent(modified_blocks);
1335 // Periodically print some info
1337 float &counter = m_print_info_timer;
1343 JMutexAutoLock lock2(m_con_mutex);
1345 for(core::map<u16, RemoteClient*>::Iterator
1346 i = m_clients.getIterator();
1347 i.atEnd() == false; i++)
1349 //u16 peer_id = i.getNode()->getKey();
1350 RemoteClient *client = i.getNode()->getValue();
1351 Player *player = m_env.getPlayer(client->peer_id);
1354 std::cout<<player->getName()<<"\t";
1355 client->PrintInfo(std::cout);
1360 //if(g_settings.getBool("enable_experimental"))
1364 Check added and deleted active objects
1367 //dstream<<"Server: Checking added and deleted active objects"<<std::endl;
1368 JMutexAutoLock envlock(m_env_mutex);
1369 JMutexAutoLock conlock(m_con_mutex);
1371 ScopeProfiler sp(&g_profiler, "Server: checking added and deleted objects");
1373 // Radius inside which objects are active
1376 for(core::map<u16, RemoteClient*>::Iterator
1377 i = m_clients.getIterator();
1378 i.atEnd() == false; i++)
1380 RemoteClient *client = i.getNode()->getValue();
1381 Player *player = m_env.getPlayer(client->peer_id);
1384 // This can happen if the client timeouts somehow
1385 /*dstream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
1387 <<" has no associated player"<<std::endl;*/
1390 v3s16 pos = floatToInt(player->getPosition(), BS);
1392 core::map<u16, bool> removed_objects;
1393 core::map<u16, bool> added_objects;
1394 m_env.getRemovedActiveObjects(pos, radius,
1395 client->m_known_objects, removed_objects);
1396 m_env.getAddedActiveObjects(pos, radius,
1397 client->m_known_objects, added_objects);
1399 // Ignore if nothing happened
1400 if(removed_objects.size() == 0 && added_objects.size() == 0)
1402 //dstream<<"INFO: active objects: none changed"<<std::endl;
1406 std::string data_buffer;
1410 // Handle removed objects
1411 writeU16((u8*)buf, removed_objects.size());
1412 data_buffer.append(buf, 2);
1413 for(core::map<u16, bool>::Iterator
1414 i = removed_objects.getIterator();
1415 i.atEnd()==false; i++)
1418 u16 id = i.getNode()->getKey();
1419 ServerActiveObject* obj = m_env.getActiveObject(id);
1421 // Add to data buffer for sending
1422 writeU16((u8*)buf, i.getNode()->getKey());
1423 data_buffer.append(buf, 2);
1425 // Remove from known objects
1426 client->m_known_objects.remove(i.getNode()->getKey());
1428 if(obj && obj->m_known_by_count > 0)
1429 obj->m_known_by_count--;
1432 // Handle added objects
1433 writeU16((u8*)buf, added_objects.size());
1434 data_buffer.append(buf, 2);
1435 for(core::map<u16, bool>::Iterator
1436 i = added_objects.getIterator();
1437 i.atEnd()==false; i++)
1440 u16 id = i.getNode()->getKey();
1441 ServerActiveObject* obj = m_env.getActiveObject(id);
1444 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1446 dstream<<"WARNING: "<<__FUNCTION_NAME
1447 <<": NULL object"<<std::endl;
1449 type = obj->getType();
1451 // Add to data buffer for sending
1452 writeU16((u8*)buf, id);
1453 data_buffer.append(buf, 2);
1454 writeU8((u8*)buf, type);
1455 data_buffer.append(buf, 1);
1458 data_buffer.append(serializeLongString(
1459 obj->getClientInitializationData()));
1461 data_buffer.append(serializeLongString(""));
1463 // Add to known objects
1464 client->m_known_objects.insert(i.getNode()->getKey(), false);
1467 obj->m_known_by_count++;
1471 SharedBuffer<u8> reply(2 + data_buffer.size());
1472 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1473 memcpy((char*)&reply[2], data_buffer.c_str(),
1474 data_buffer.size());
1476 m_con.Send(client->peer_id, 0, reply, true);
1478 dstream<<"INFO: Server: Sent object remove/add: "
1479 <<removed_objects.size()<<" removed, "
1480 <<added_objects.size()<<" added, "
1481 <<"packet size is "<<reply.getSize()<<std::endl;
1486 Collect a list of all the objects known by the clients
1487 and report it back to the environment.
1490 core::map<u16, bool> all_known_objects;
1492 for(core::map<u16, RemoteClient*>::Iterator
1493 i = m_clients.getIterator();
1494 i.atEnd() == false; i++)
1496 RemoteClient *client = i.getNode()->getValue();
1497 // Go through all known objects of client
1498 for(core::map<u16, bool>::Iterator
1499 i = client->m_known_objects.getIterator();
1500 i.atEnd()==false; i++)
1502 u16 id = i.getNode()->getKey();
1503 all_known_objects[id] = true;
1507 m_env.setKnownActiveObjects(whatever);
1513 Send object messages
1516 JMutexAutoLock envlock(m_env_mutex);
1517 JMutexAutoLock conlock(m_con_mutex);
1519 ScopeProfiler sp(&g_profiler, "Server: sending object messages");
1522 // Value = data sent by object
1523 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1525 // Get active object messages from environment
1528 ActiveObjectMessage aom = m_env.getActiveObjectMessage();
1532 core::list<ActiveObjectMessage>* message_list = NULL;
1533 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1534 n = buffered_messages.find(aom.id);
1537 message_list = new core::list<ActiveObjectMessage>;
1538 buffered_messages.insert(aom.id, message_list);
1542 message_list = n->getValue();
1544 message_list->push_back(aom);
1547 // Route data to every client
1548 for(core::map<u16, RemoteClient*>::Iterator
1549 i = m_clients.getIterator();
1550 i.atEnd()==false; i++)
1552 RemoteClient *client = i.getNode()->getValue();
1553 std::string reliable_data;
1554 std::string unreliable_data;
1555 // Go through all objects in message buffer
1556 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1557 j = buffered_messages.getIterator();
1558 j.atEnd()==false; j++)
1560 // If object is not known by client, skip it
1561 u16 id = j.getNode()->getKey();
1562 if(client->m_known_objects.find(id) == NULL)
1564 // Get message list of object
1565 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1566 // Go through every message
1567 for(core::list<ActiveObjectMessage>::Iterator
1568 k = list->begin(); k != list->end(); k++)
1570 // Compose the full new data with header
1571 ActiveObjectMessage aom = *k;
1572 std::string new_data;
1575 writeU16((u8*)&buf[0], aom.id);
1576 new_data.append(buf, 2);
1578 new_data += serializeString(aom.datastring);
1579 // Add data to buffer
1581 reliable_data += new_data;
1583 unreliable_data += new_data;
1587 reliable_data and unreliable_data are now ready.
1590 if(reliable_data.size() > 0)
1592 SharedBuffer<u8> reply(2 + reliable_data.size());
1593 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1594 memcpy((char*)&reply[2], reliable_data.c_str(),
1595 reliable_data.size());
1597 m_con.Send(client->peer_id, 0, reply, true);
1599 if(unreliable_data.size() > 0)
1601 SharedBuffer<u8> reply(2 + unreliable_data.size());
1602 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1603 memcpy((char*)&reply[2], unreliable_data.c_str(),
1604 unreliable_data.size());
1605 // Send as unreliable
1606 m_con.Send(client->peer_id, 0, reply, false);
1609 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1611 dstream<<"INFO: Server: Size of object message data: "
1612 <<"reliable: "<<reliable_data.size()
1613 <<", unreliable: "<<unreliable_data.size()
1618 // Clear buffered_messages
1619 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1620 i = buffered_messages.getIterator();
1621 i.atEnd()==false; i++)
1623 delete i.getNode()->getValue();
1627 } // enable_experimental
1630 Send queued-for-sending map edit events.
1633 while(m_unsent_map_edit_queue.size() != 0)
1635 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1637 if(event->type == MEET_ADDNODE)
1639 dstream<<"Server: MEET_ADDNODE"<<std::endl;
1640 sendAddNode(event->p, event->n, event->already_known_by_peer);
1642 else if(event->type == MEET_REMOVENODE)
1644 dstream<<"Server: MEET_REMOVENODE"<<std::endl;
1645 sendRemoveNode(event->p, event->already_known_by_peer);
1647 else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
1649 dstream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
1650 setBlockNotSent(event->p);
1652 else if(event->type == MEET_OTHER)
1654 dstream<<"WARNING: Server: MEET_OTHER not implemented"
1659 dstream<<"WARNING: Server: Unknown MapEditEvent "
1660 <<((u32)event->type)<<std::endl;
1668 Send object positions
1669 TODO: Get rid of MapBlockObjects
1672 float &counter = m_objectdata_timer;
1674 if(counter >= g_settings.getFloat("objectdata_interval"))
1676 JMutexAutoLock lock1(m_env_mutex);
1677 JMutexAutoLock lock2(m_con_mutex);
1679 ScopeProfiler sp(&g_profiler, "Server: sending mbo positions");
1681 SendObjectData(counter);
1689 TODO: Move to ServerEnvironment and utilize active block stuff
1692 //TimeTaker timer("Step node metadata");
1694 JMutexAutoLock envlock(m_env_mutex);
1695 JMutexAutoLock conlock(m_con_mutex);
1697 ScopeProfiler sp(&g_profiler, "Server: stepping node metadata");
1699 core::map<v3s16, MapBlock*> changed_blocks;
1700 m_env.getMap().nodeMetadataStep(dtime, changed_blocks);
1702 // Use setBlockNotSent
1704 for(core::map<v3s16, MapBlock*>::Iterator
1705 i = changed_blocks.getIterator();
1706 i.atEnd() == false; i++)
1708 MapBlock *block = i.getNode()->getValue();
1710 for(core::map<u16, RemoteClient*>::Iterator
1711 i = m_clients.getIterator();
1712 i.atEnd()==false; i++)
1714 RemoteClient *client = i.getNode()->getValue();
1715 client->SetBlockNotSent(block->getPos());
1721 Trigger emergethread (it somehow gets to a non-triggered but
1722 bysy state sometimes)
1725 float &counter = m_emergethread_trigger_timer;
1731 m_emergethread.trigger();
1735 // Save map, players and auth stuff
1737 float &counter = m_savemap_timer;
1739 if(counter >= g_settings.getFloat("server_map_save_interval"))
1743 ScopeProfiler sp(&g_profiler, "Server: saving stuff");
1746 if(m_authmanager.isModified())
1747 m_authmanager.save();
1750 JMutexAutoLock lock(m_env_mutex);
1751 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == true)
1753 // Save only changed parts
1754 m_env.getMap().save(true);
1756 // Delete unused sectors
1757 u32 deleted_count = m_env.getMap().deleteUnusedSectors(
1758 g_settings.getFloat("server_unload_unused_sectors_timeout"));
1759 if(deleted_count > 0)
1761 dout_server<<"Server: Unloaded "<<deleted_count
1762 <<" sectors from memory"<<std::endl;
1766 m_env.serializePlayers(m_mapsavedir);
1768 // Save environment metadata
1769 m_env.saveMeta(m_mapsavedir);
1775 void Server::Receive()
1777 DSTACK(__FUNCTION_NAME);
1778 u32 data_maxsize = 10000;
1779 Buffer<u8> data(data_maxsize);
1784 JMutexAutoLock conlock(m_con_mutex);
1785 datasize = m_con.Receive(peer_id, *data, data_maxsize);
1788 // This has to be called so that the client list gets synced
1789 // with the peer list of the connection
1790 handlePeerChanges();
1792 ProcessData(*data, datasize, peer_id);
1794 catch(con::InvalidIncomingDataException &e)
1796 derr_server<<"Server::Receive(): "
1797 "InvalidIncomingDataException: what()="
1798 <<e.what()<<std::endl;
1800 catch(con::PeerNotFoundException &e)
1802 //NOTE: This is not needed anymore
1804 // The peer has been disconnected.
1805 // Find the associated player and remove it.
1807 /*JMutexAutoLock envlock(m_env_mutex);
1809 dout_server<<"ServerThread: peer_id="<<peer_id
1810 <<" has apparently closed connection. "
1811 <<"Removing player."<<std::endl;
1813 m_env.removePlayer(peer_id);*/
1817 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1819 DSTACK(__FUNCTION_NAME);
1820 // Environment is locked first.
1821 JMutexAutoLock envlock(m_env_mutex);
1822 JMutexAutoLock conlock(m_con_mutex);
1826 peer = m_con.GetPeer(peer_id);
1828 catch(con::PeerNotFoundException &e)
1830 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1831 <<peer_id<<" not found"<<std::endl;
1835 u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1843 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1845 if(command == TOSERVER_INIT)
1847 // [0] u16 TOSERVER_INIT
1848 // [2] u8 SER_FMT_VER_HIGHEST
1849 // [3] u8[20] player_name
1850 // [23] u8[28] password <--- can be sent without this, from old versions
1852 if(datasize < 2+1+PLAYERNAME_SIZE)
1855 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1856 <<peer->id<<std::endl;
1858 // First byte after command is maximum supported
1859 // serialization version
1860 u8 client_max = data[2];
1861 u8 our_max = SER_FMT_VER_HIGHEST;
1862 // Use the highest version supported by both
1863 u8 deployed = core::min_(client_max, our_max);
1864 // If it's lower than the lowest supported, give up.
1865 if(deployed < SER_FMT_VER_LOWEST)
1866 deployed = SER_FMT_VER_INVALID;
1868 //peer->serialization_version = deployed;
1869 getClient(peer->id)->pending_serialization_version = deployed;
1871 if(deployed == SER_FMT_VER_INVALID)
1873 derr_server<<DTIME<<"Server: Cannot negotiate "
1874 "serialization version with peer "
1875 <<peer_id<<std::endl;
1884 char playername[PLAYERNAME_SIZE];
1885 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
1887 playername[i] = data[3+i];
1889 playername[PLAYERNAME_SIZE-1] = 0;
1891 if(playername[0]=='\0')
1893 derr_server<<DTIME<<"Server: Player has empty name"<<std::endl;
1894 SendAccessDenied(m_con, peer_id,
1899 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
1901 derr_server<<DTIME<<"Server: Player has invalid name"<<std::endl;
1902 SendAccessDenied(m_con, peer_id,
1903 L"Name contains unallowed characters");
1908 char password[PASSWORD_SIZE];
1909 if(datasize == 2+1+PLAYERNAME_SIZE)
1911 // old version - assume blank password
1916 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
1918 password[i] = data[23+i];
1920 password[PASSWORD_SIZE-1] = 0;
1923 std::string checkpwd;
1924 if(m_authmanager.exists(playername))
1926 checkpwd = m_authmanager.getPassword(playername);
1930 checkpwd = g_settings.get("default_password");
1933 if(password != checkpwd && checkpwd != "")
1935 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1936 <<": supplied invalid password for "
1937 <<playername<<std::endl;
1938 SendAccessDenied(m_con, peer_id, L"Invalid password");
1942 // Add player to auth manager
1943 if(m_authmanager.exists(playername) == false)
1945 derr_server<<DTIME<<"Server: adding player "<<playername
1946 <<" to auth manager"<<std::endl;
1947 m_authmanager.add(playername);
1948 m_authmanager.setPassword(playername, checkpwd);
1949 m_authmanager.setPrivs(playername,
1950 stringToPrivs(g_settings.get("default_privs")));
1951 m_authmanager.save();
1955 Player *player = emergePlayer(playername, password, peer_id);
1959 // DEBUG: Test serialization
1960 std::ostringstream test_os;
1961 player->serialize(test_os);
1962 dstream<<"Player serialization test: \""<<test_os.str()
1964 std::istringstream test_is(test_os.str());
1965 player->deSerialize(test_is);
1968 // If failed, cancel
1971 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1972 <<": failed to emerge player"<<std::endl;
1977 // If a client is already connected to the player, cancel
1978 if(player->peer_id != 0)
1980 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1981 <<" tried to connect to "
1982 "an already connected player (peer_id="
1983 <<player->peer_id<<")"<<std::endl;
1986 // Set client of player
1987 player->peer_id = peer_id;
1990 // Check if player doesn't exist
1992 throw con::InvalidIncomingDataException
1993 ("Server::ProcessData(): INIT: Player doesn't exist");
1995 /*// update name if it was supplied
1996 if(datasize >= 20+3)
1999 player->updateName((const char*)&data[3]);
2003 Answer with a TOCLIENT_INIT
2006 SharedBuffer<u8> reply(2+1+6+8);
2007 writeU16(&reply[0], TOCLIENT_INIT);
2008 writeU8(&reply[2], deployed);
2009 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
2010 writeU64(&reply[2+1+6], m_env.getServerMap().getSeed());
2013 m_con.Send(peer_id, 0, reply, true);
2017 Send complete position information
2019 SendMovePlayer(player);
2024 if(command == TOSERVER_INIT2)
2026 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
2027 <<peer->id<<std::endl;
2030 getClient(peer->id)->serialization_version
2031 = getClient(peer->id)->pending_serialization_version;
2034 Send some initialization data
2037 // Send player info to all players
2040 // Send inventory to player
2041 UpdateCrafting(peer->id);
2042 SendInventory(peer->id);
2046 Player *player = m_env.getPlayer(peer_id);
2047 SendPlayerHP(player);
2052 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2053 m_env.getTimeOfDay());
2054 m_con.Send(peer->id, 0, data, true);
2057 // Send information about server to player in chat
2058 SendChatMessage(peer_id, getStatusString());
2060 // Send information about joining in chat
2062 std::wstring name = L"unknown";
2063 Player *player = m_env.getPlayer(peer_id);
2065 name = narrow_to_wide(player->getName());
2067 std::wstring message;
2070 message += L" joined game";
2071 BroadcastChatMessage(message);
2077 if(peer_ser_ver == SER_FMT_VER_INVALID)
2079 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
2080 " serialization format invalid or not initialized."
2081 " Skipping incoming command="<<command<<std::endl;
2085 Player *player = m_env.getPlayer(peer_id);
2088 derr_server<<"Server::ProcessData(): Cancelling: "
2089 "No player for peer_id="<<peer_id
2093 if(command == TOSERVER_PLAYERPOS)
2095 if(datasize < 2+12+12+4+4)
2099 v3s32 ps = readV3S32(&data[start+2]);
2100 v3s32 ss = readV3S32(&data[start+2+12]);
2101 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2102 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2103 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2104 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2105 pitch = wrapDegrees(pitch);
2106 yaw = wrapDegrees(yaw);
2107 player->setPosition(position);
2108 player->setSpeed(speed);
2109 player->setPitch(pitch);
2110 player->setYaw(yaw);
2112 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2113 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2114 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2116 else if(command == TOSERVER_GOTBLOCKS)
2129 u16 count = data[2];
2130 for(u16 i=0; i<count; i++)
2132 if((s16)datasize < 2+1+(i+1)*6)
2133 throw con::InvalidIncomingDataException
2134 ("GOTBLOCKS length is too short");
2135 v3s16 p = readV3S16(&data[2+1+i*6]);
2136 /*dstream<<"Server: GOTBLOCKS ("
2137 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2138 RemoteClient *client = getClient(peer_id);
2139 client->GotBlock(p);
2142 else if(command == TOSERVER_DELETEDBLOCKS)
2155 u16 count = data[2];
2156 for(u16 i=0; i<count; i++)
2158 if((s16)datasize < 2+1+(i+1)*6)
2159 throw con::InvalidIncomingDataException
2160 ("DELETEDBLOCKS length is too short");
2161 v3s16 p = readV3S16(&data[2+1+i*6]);
2162 /*dstream<<"Server: DELETEDBLOCKS ("
2163 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2164 RemoteClient *client = getClient(peer_id);
2165 client->SetBlockNotSent(p);
2168 else if(command == TOSERVER_CLICK_OBJECT)
2173 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2178 [2] u8 button (0=left, 1=right)
2183 u8 button = readU8(&data[2]);
2185 p.X = readS16(&data[3]);
2186 p.Y = readS16(&data[5]);
2187 p.Z = readS16(&data[7]);
2188 s16 id = readS16(&data[9]);
2189 //u16 item_i = readU16(&data[11]);
2191 MapBlock *block = NULL;
2194 block = m_env.getMap().getBlockNoCreate(p);
2196 catch(InvalidPositionException &e)
2198 derr_server<<"CLICK_OBJECT block not found"<<std::endl;
2202 MapBlockObject *obj = block->getObject(id);
2206 derr_server<<"CLICK_OBJECT object not found"<<std::endl;
2210 //TODO: Check that object is reasonably close
2215 InventoryList *ilist = player->inventory.getList("main");
2216 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
2219 // Skip if inventory has no free space
2220 if(ilist->getUsedSlots() == ilist->getSize())
2222 dout_server<<"Player inventory has no free space"<<std::endl;
2227 Create the inventory item
2229 InventoryItem *item = NULL;
2230 // If it is an item-object, take the item from it
2231 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
2233 item = ((ItemObject*)obj)->createInventoryItem();
2235 // Else create an item of the object
2238 item = new MapBlockObjectItem
2239 (obj->getInventoryString());
2242 // Add to inventory and send inventory
2243 ilist->addItem(item);
2244 UpdateCrafting(player->peer_id);
2245 SendInventory(player->peer_id);
2248 // Remove from block
2249 block->removeObject(id);
2252 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2257 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2263 [2] u8 button (0=left, 1=right)
2267 u8 button = readU8(&data[2]);
2268 u16 id = readS16(&data[3]);
2269 u16 item_i = readU16(&data[11]);
2271 ServerActiveObject *obj = m_env.getActiveObject(id);
2275 derr_server<<"Server: CLICK_ACTIVEOBJECT: object not found"
2280 //TODO: Check that object is reasonably close
2282 // Left click, pick object up (usually)
2285 InventoryList *ilist = player->inventory.getList("main");
2286 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
2289 // Skip if inventory has no free space
2290 if(ilist->getUsedSlots() == ilist->getSize())
2292 dout_server<<"Player inventory has no free space"<<std::endl;
2296 // Skip if object has been removed
2301 Create the inventory item
2303 InventoryItem *item = obj->createPickedUpItem();
2307 // Add to inventory and send inventory
2308 ilist->addItem(item);
2309 UpdateCrafting(player->peer_id);
2310 SendInventory(player->peer_id);
2312 // Remove object from environment
2313 obj->m_removed = true;
2318 Item cannot be picked up. Punch it instead.
2321 ToolItem *titem = NULL;
2322 std::string toolname = "";
2324 InventoryList *mlist = player->inventory.getList("main");
2327 InventoryItem *item = mlist->getItem(item_i);
2328 if(item && (std::string)item->getName() == "ToolItem")
2330 titem = (ToolItem*)item;
2331 toolname = titem->getToolName();
2335 u16 wear = obj->punch(toolname);
2339 bool weared_out = titem->addWear(wear);
2341 mlist->deleteItem(item_i);
2342 SendInventory(player->peer_id);
2348 else if(command == TOSERVER_GROUND_ACTION)
2356 [3] v3s16 nodepos_undersurface
2357 [9] v3s16 nodepos_abovesurface
2362 2: stop digging (all parameters ignored)
2363 3: digging completed
2365 u8 action = readU8(&data[2]);
2367 p_under.X = readS16(&data[3]);
2368 p_under.Y = readS16(&data[5]);
2369 p_under.Z = readS16(&data[7]);
2371 p_over.X = readS16(&data[9]);
2372 p_over.Y = readS16(&data[11]);
2373 p_over.Z = readS16(&data[13]);
2374 u16 item_i = readU16(&data[15]);
2376 //TODO: Check that target is reasonably close
2384 NOTE: This can be used in the future to check if
2385 somebody is cheating, by checking the timing.
2392 else if(action == 2)
2395 RemoteClient *client = getClient(peer->id);
2396 JMutexAutoLock digmutex(client->m_dig_mutex);
2397 client->m_dig_tool_item = -1;
2402 3: Digging completed
2404 else if(action == 3)
2406 // Mandatory parameter; actually used for nothing
2407 core::map<v3s16, MapBlock*> modified_blocks;
2409 u8 material = CONTENT_IGNORE;
2410 u8 mineral = MINERAL_NONE;
2412 bool cannot_remove_node = false;
2416 MapNode n = m_env.getMap().getNode(p_under);
2418 mineral = n.getMineral();
2419 // Get material at position
2421 // If not yet cancelled
2422 if(cannot_remove_node == false)
2424 // If it's not diggable, do nothing
2425 if(content_diggable(material) == false)
2427 derr_server<<"Server: Not finishing digging: "
2428 <<"Node not diggable"
2430 cannot_remove_node = true;
2433 // If not yet cancelled
2434 if(cannot_remove_node == false)
2436 // Get node metadata
2437 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p_under);
2438 if(meta && meta->nodeRemovalDisabled() == true)
2440 derr_server<<"Server: Not finishing digging: "
2441 <<"Node metadata disables removal"
2443 cannot_remove_node = true;
2447 catch(InvalidPositionException &e)
2449 derr_server<<"Server: Not finishing digging: Node not found."
2450 <<" Adding block to emerge queue."
2452 m_emerge_queue.addBlock(peer_id,
2453 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2454 cannot_remove_node = true;
2457 // Make sure the player is allowed to do it
2458 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2460 dstream<<"Player "<<player->getName()<<" cannot remove node"
2461 <<" because privileges are "<<getPlayerPrivs(player)
2463 cannot_remove_node = true;
2467 If node can't be removed, set block to be re-sent to
2470 if(cannot_remove_node)
2472 derr_server<<"Server: Not finishing digging."<<std::endl;
2474 // Client probably has wrong data.
2475 // Set block not sent, so that client will get
2477 dstream<<"Client "<<peer_id<<" tried to dig "
2478 <<"node; but node cannot be removed."
2479 <<" setting MapBlock not sent."<<std::endl;
2480 RemoteClient *client = getClient(peer_id);
2481 v3s16 blockpos = getNodeBlockPos(p_under);
2482 client->SetBlockNotSent(blockpos);
2488 Send the removal to all other clients.
2489 - If other player is close, send REMOVENODE
2490 - Otherwise set blocks not sent
2492 core::list<u16> far_players;
2493 sendRemoveNode(p_under, peer_id, &far_players, 100);
2496 Update and send inventory
2499 if(g_settings.getBool("creative_mode") == false)
2504 InventoryList *mlist = player->inventory.getList("main");
2507 InventoryItem *item = mlist->getItem(item_i);
2508 if(item && (std::string)item->getName() == "ToolItem")
2510 ToolItem *titem = (ToolItem*)item;
2511 std::string toolname = titem->getToolName();
2513 // Get digging properties for material and tool
2514 DiggingProperties prop =
2515 getDiggingProperties(material, toolname);
2517 if(prop.diggable == false)
2519 derr_server<<"Server: WARNING: Player digged"
2520 <<" with impossible material + tool"
2521 <<" combination"<<std::endl;
2524 bool weared_out = titem->addWear(prop.wear);
2528 mlist->deleteItem(item_i);
2534 Add dug item to inventory
2537 InventoryItem *item = NULL;
2539 if(mineral != MINERAL_NONE)
2540 item = getDiggedMineralItem(mineral);
2545 std::string &dug_s = content_features(material).dug_item;
2548 std::istringstream is(dug_s, std::ios::binary);
2549 item = InventoryItem::deSerialize(is);
2555 // Add a item to inventory
2556 player->inventory.addItem("main", item);
2559 UpdateCrafting(player->peer_id);
2560 SendInventory(player->peer_id);
2566 (this takes some time so it is done after the quick stuff)
2568 m_ignore_map_edit_events = true;
2569 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2570 m_ignore_map_edit_events = false;
2573 Set blocks not sent to far players
2575 for(core::list<u16>::Iterator
2576 i = far_players.begin();
2577 i != far_players.end(); i++)
2580 RemoteClient *client = getClient(peer_id);
2583 client->SetBlocksNotSent(modified_blocks);
2590 else if(action == 1)
2593 InventoryList *ilist = player->inventory.getList("main");
2598 InventoryItem *item = ilist->getItem(item_i);
2600 // If there is no item, it is not possible to add it anywhere
2605 Handle material items
2607 if(std::string("MaterialItem") == item->getName())
2610 // Don't add a node if this is not a free space
2611 MapNode n2 = m_env.getMap().getNode(p_over);
2612 bool no_enough_privs =
2613 ((getPlayerPrivs(player) & PRIV_BUILD)==0);
2615 dstream<<"Player "<<player->getName()<<" cannot add node"
2616 <<" because privileges are "<<getPlayerPrivs(player)
2619 if(content_buildable_to(n2.d) == false
2622 // Client probably has wrong data.
2623 // Set block not sent, so that client will get
2625 dstream<<"Client "<<peer_id<<" tried to place"
2626 <<" node in invalid position; setting"
2627 <<" MapBlock not sent."<<std::endl;
2628 RemoteClient *client = getClient(peer_id);
2629 v3s16 blockpos = getNodeBlockPos(p_over);
2630 client->SetBlockNotSent(blockpos);
2634 catch(InvalidPositionException &e)
2636 derr_server<<"Server: Ignoring ADDNODE: Node not found"
2637 <<" Adding block to emerge queue."
2639 m_emerge_queue.addBlock(peer_id,
2640 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2644 // Reset build time counter
2645 getClient(peer->id)->m_time_from_building = 0.0;
2648 MaterialItem *mitem = (MaterialItem*)item;
2650 n.d = mitem->getMaterial();
2651 if(content_features(n.d).wall_mounted)
2652 n.dir = packDir(p_under - p_over);
2657 core::list<u16> far_players;
2658 sendAddNode(p_over, n, 0, &far_players, 100);
2663 InventoryList *ilist = player->inventory.getList("main");
2664 if(g_settings.getBool("creative_mode") == false && ilist)
2666 // Remove from inventory and send inventory
2667 if(mitem->getCount() == 1)
2668 ilist->deleteItem(item_i);
2672 UpdateCrafting(peer_id);
2673 SendInventory(peer_id);
2679 This takes some time so it is done after the quick stuff
2681 core::map<v3s16, MapBlock*> modified_blocks;
2682 m_ignore_map_edit_events = true;
2683 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2684 m_ignore_map_edit_events = false;
2687 Set blocks not sent to far players
2689 for(core::list<u16>::Iterator
2690 i = far_players.begin();
2691 i != far_players.end(); i++)
2694 RemoteClient *client = getClient(peer_id);
2697 client->SetBlocksNotSent(modified_blocks);
2701 Calculate special events
2704 /*if(n.d == CONTENT_MESE)
2707 for(s16 z=-1; z<=1; z++)
2708 for(s16 y=-1; y<=1; y++)
2709 for(s16 x=-1; x<=1; x++)
2716 Place other item (not a block)
2720 v3s16 blockpos = getNodeBlockPos(p_over);
2723 Check that the block is loaded so that the item
2724 can properly be added to the static list too
2726 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2729 derr_server<<"Error while placing object: "
2730 "block not found"<<std::endl;
2734 dout_server<<"Placing a miscellaneous item on map"
2737 // Calculate a position for it
2738 v3f pos = intToFloat(p_over, BS);
2740 pos.Y -= BS*0.25; // let it drop a bit
2742 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2743 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2748 ServerActiveObject *obj = item->createSAO(&m_env, 0, pos);
2752 derr_server<<"WARNING: item resulted in NULL object, "
2753 <<"not placing onto map"
2758 // Add the object to the environment
2759 m_env.addActiveObject(obj);
2761 dout_server<<"Placed object"<<std::endl;
2763 if(g_settings.getBool("creative_mode") == false)
2765 // Delete the right amount of items from the slot
2766 u16 dropcount = item->getDropCount();
2768 // Delete item if all gone
2769 if(item->getCount() <= dropcount)
2771 if(item->getCount() < dropcount)
2772 dstream<<"WARNING: Server: dropped more items"
2773 <<" than the slot contains"<<std::endl;
2775 InventoryList *ilist = player->inventory.getList("main");
2777 // Remove from inventory and send inventory
2778 ilist->deleteItem(item_i);
2780 // Else decrement it
2782 item->remove(dropcount);
2785 UpdateCrafting(peer_id);
2786 SendInventory(peer_id);
2794 Catch invalid actions
2798 derr_server<<"WARNING: Server: Invalid action "
2799 <<action<<std::endl;
2803 else if(command == TOSERVER_RELEASE)
2812 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2815 else if(command == TOSERVER_SIGNTEXT)
2817 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2826 std::string datastring((char*)&data[2], datasize-2);
2827 std::istringstream is(datastring, std::ios_base::binary);
2830 is.read((char*)buf, 6);
2831 v3s16 blockpos = readV3S16(buf);
2832 is.read((char*)buf, 2);
2833 s16 id = readS16(buf);
2834 is.read((char*)buf, 2);
2835 u16 textlen = readU16(buf);
2837 for(u16 i=0; i<textlen; i++)
2839 is.read((char*)buf, 1);
2840 text += (char)buf[0];
2843 MapBlock *block = NULL;
2846 block = m_env.getMap().getBlockNoCreate(blockpos);
2848 catch(InvalidPositionException &e)
2850 derr_server<<"Error while setting sign text: "
2851 "block not found"<<std::endl;
2855 MapBlockObject *obj = block->getObject(id);
2858 derr_server<<"Error while setting sign text: "
2859 "object not found"<<std::endl;
2863 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2865 derr_server<<"Error while setting sign text: "
2866 "object is not a sign"<<std::endl;
2870 ((SignObject*)obj)->setText(text);
2872 obj->getBlock()->setChangedFlag();
2874 else if(command == TOSERVER_SIGNNODETEXT)
2876 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2884 std::string datastring((char*)&data[2], datasize-2);
2885 std::istringstream is(datastring, std::ios_base::binary);
2888 is.read((char*)buf, 6);
2889 v3s16 p = readV3S16(buf);
2890 is.read((char*)buf, 2);
2891 u16 textlen = readU16(buf);
2893 for(u16 i=0; i<textlen; i++)
2895 is.read((char*)buf, 1);
2896 text += (char)buf[0];
2899 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2902 if(meta->typeId() != CONTENT_SIGN_WALL)
2904 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
2905 signmeta->setText(text);
2907 v3s16 blockpos = getNodeBlockPos(p);
2908 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2911 block->setChangedFlag();
2914 for(core::map<u16, RemoteClient*>::Iterator
2915 i = m_clients.getIterator();
2916 i.atEnd()==false; i++)
2918 RemoteClient *client = i.getNode()->getValue();
2919 client->SetBlockNotSent(blockpos);
2922 else if(command == TOSERVER_INVENTORY_ACTION)
2924 /*// Ignore inventory changes if in creative mode
2925 if(g_settings.getBool("creative_mode") == true)
2927 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2931 // Strip command and create a stream
2932 std::string datastring((char*)&data[2], datasize-2);
2933 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2934 std::istringstream is(datastring, std::ios_base::binary);
2936 InventoryAction *a = InventoryAction::deSerialize(is);
2941 c.current_player = player;
2944 Handle craftresult specially if not in creative mode
2946 bool disable_action = false;
2947 if(a->getType() == IACTION_MOVE
2948 && g_settings.getBool("creative_mode") == false)
2950 IMoveAction *ma = (IMoveAction*)a;
2951 if(ma->to_inv == "current_player" &&
2952 ma->from_inv == "current_player")
2954 InventoryList *rlist = player->inventory.getList("craftresult");
2956 InventoryList *clist = player->inventory.getList("craft");
2958 InventoryList *mlist = player->inventory.getList("main");
2961 Craftresult is no longer preview if something
2964 if(ma->to_list == "craftresult"
2965 && ma->from_list != "craftresult")
2967 // If it currently is a preview, remove
2969 if(player->craftresult_is_preview)
2971 rlist->deleteItem(0);
2973 player->craftresult_is_preview = false;
2976 Crafting takes place if this condition is true.
2978 if(player->craftresult_is_preview &&
2979 ma->from_list == "craftresult")
2981 player->craftresult_is_preview = false;
2982 clist->decrementMaterials(1);
2985 If the craftresult is placed on itself, move it to
2986 main inventory instead of doing the action
2988 if(ma->to_list == "craftresult"
2989 && ma->from_list == "craftresult")
2991 disable_action = true;
2993 InventoryItem *item1 = rlist->changeItem(0, NULL);
2994 mlist->addItem(item1);
2999 if(disable_action == false)
3001 // Feed action to player inventory
3009 UpdateCrafting(player->peer_id);
3010 SendInventory(player->peer_id);
3015 dstream<<"TOSERVER_INVENTORY_ACTION: "
3016 <<"InventoryAction::deSerialize() returned NULL"
3020 else if(command == TOSERVER_CHAT_MESSAGE)
3028 std::string datastring((char*)&data[2], datasize-2);
3029 std::istringstream is(datastring, std::ios_base::binary);
3032 is.read((char*)buf, 2);
3033 u16 len = readU16(buf);
3035 std::wstring message;
3036 for(u16 i=0; i<len; i++)
3038 is.read((char*)buf, 2);
3039 message += (wchar_t)readU16(buf);
3042 // Get player name of this client
3043 std::wstring name = narrow_to_wide(player->getName());
3045 // Line to send to players
3047 // Whether to send to the player that sent the line
3048 bool send_to_sender = false;
3049 // Whether to send to other players
3050 bool send_to_others = false;
3052 // Local player gets all privileges regardless of
3053 // what's set on their account.
3054 u64 privs = getPlayerPrivs(player);
3057 std::wstring commandprefix = L"/#";
3058 if(message.substr(0, commandprefix.size()) == commandprefix)
3060 line += L"Server: ";
3062 message = message.substr(commandprefix.size());
3064 ServerCommandContext *ctx = new ServerCommandContext(
3065 str_split(message, L' '),
3071 line += processServerCommand(ctx);
3072 send_to_sender = ctx->flags & 1;
3073 send_to_others = ctx->flags & 2;
3079 if(privs & PRIV_SHOUT)
3085 send_to_others = true;
3089 line += L"Server: You are not allowed to shout";
3090 send_to_sender = true;
3096 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
3099 Send the message to clients
3101 for(core::map<u16, RemoteClient*>::Iterator
3102 i = m_clients.getIterator();
3103 i.atEnd() == false; i++)
3105 // Get client and check that it is valid
3106 RemoteClient *client = i.getNode()->getValue();
3107 assert(client->peer_id == i.getNode()->getKey());
3108 if(client->serialization_version == SER_FMT_VER_INVALID)
3112 bool sender_selected = (peer_id == client->peer_id);
3113 if(sender_selected == true && send_to_sender == false)
3115 if(sender_selected == false && send_to_others == false)
3118 SendChatMessage(client->peer_id, line);
3122 else if(command == TOSERVER_DAMAGE)
3124 if(g_settings.getBool("enable_damage"))
3126 std::string datastring((char*)&data[2], datasize-2);
3127 std::istringstream is(datastring, std::ios_base::binary);
3128 u8 damage = readU8(is);
3129 if(player->hp > damage)
3131 player->hp -= damage;
3137 dstream<<"TODO: Server: TOSERVER_HP_DECREMENT: Player dies"
3140 v3f pos = findSpawnPos(m_env.getServerMap());
3141 player->setPosition(pos);
3143 SendMovePlayer(player);
3144 SendPlayerHP(player);
3146 //TODO: Throw items around
3150 SendPlayerHP(player);
3152 else if(command == TOSERVER_PASSWORD)
3155 [0] u16 TOSERVER_PASSWORD
3156 [2] u8[28] old password
3157 [30] u8[28] new password
3160 if(datasize != 2+PASSWORD_SIZE*2)
3162 /*char password[PASSWORD_SIZE];
3163 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3164 password[i] = data[2+i];
3165 password[PASSWORD_SIZE-1] = 0;*/
3167 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3175 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3177 char c = data[2+PASSWORD_SIZE+i];
3183 std::string playername = player->getName();
3185 if(m_authmanager.exists(playername) == false)
3187 dstream<<"Server: playername not found in authmanager"<<std::endl;
3188 // Wrong old password supplied!!
3189 SendChatMessage(peer_id, L"playername not found in authmanager");
3193 std::string checkpwd = m_authmanager.getPassword(playername);
3195 if(oldpwd != checkpwd)
3197 dstream<<"Server: invalid old password"<<std::endl;
3198 // Wrong old password supplied!!
3199 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
3203 m_authmanager.setPassword(playername, newpwd);
3205 dstream<<"Server: password change successful for "<<playername
3207 SendChatMessage(peer_id, L"Password change successful");
3211 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
3212 "unknown command "<<command<<std::endl;
3216 catch(SendFailedException &e)
3218 derr_server<<"Server::ProcessData(): SendFailedException: "
3224 void Server::onMapEditEvent(MapEditEvent *event)
3226 dstream<<"Server::onMapEditEvent()"<<std::endl;
3227 if(m_ignore_map_edit_events)
3229 MapEditEvent *e = event->clone();
3230 m_unsent_map_edit_queue.push_back(e);
3233 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3235 if(id == "current_player")
3237 assert(c->current_player);
3238 return &(c->current_player->inventory);
3242 std::string id0 = fn.next(":");
3244 if(id0 == "nodemeta")
3247 p.X = stoi(fn.next(","));
3248 p.Y = stoi(fn.next(","));
3249 p.Z = stoi(fn.next(","));
3250 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3252 return meta->getInventory();
3253 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3254 <<"no metadata found"<<std::endl;
3258 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3261 void Server::inventoryModified(InventoryContext *c, std::string id)
3263 if(id == "current_player")
3265 assert(c->current_player);
3267 UpdateCrafting(c->current_player->peer_id);
3268 SendInventory(c->current_player->peer_id);
3273 std::string id0 = fn.next(":");
3275 if(id0 == "nodemeta")
3278 p.X = stoi(fn.next(","));
3279 p.Y = stoi(fn.next(","));
3280 p.Z = stoi(fn.next(","));
3281 v3s16 blockpos = getNodeBlockPos(p);
3283 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3285 meta->inventoryModified();
3287 for(core::map<u16, RemoteClient*>::Iterator
3288 i = m_clients.getIterator();
3289 i.atEnd()==false; i++)
3291 RemoteClient *client = i.getNode()->getValue();
3292 client->SetBlockNotSent(blockpos);
3298 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3301 core::list<PlayerInfo> Server::getPlayerInfo()
3303 DSTACK(__FUNCTION_NAME);
3304 JMutexAutoLock envlock(m_env_mutex);
3305 JMutexAutoLock conlock(m_con_mutex);
3307 core::list<PlayerInfo> list;
3309 core::list<Player*> players = m_env.getPlayers();
3311 core::list<Player*>::Iterator i;
3312 for(i = players.begin();
3313 i != players.end(); i++)
3317 Player *player = *i;
3320 con::Peer *peer = m_con.GetPeer(player->peer_id);
3321 // Copy info from peer to info struct
3323 info.address = peer->address;
3324 info.avg_rtt = peer->avg_rtt;
3326 catch(con::PeerNotFoundException &e)
3328 // Set dummy peer info
3330 info.address = Address(0,0,0,0,0);
3334 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3335 info.position = player->getPosition();
3337 list.push_back(info);
3344 void Server::peerAdded(con::Peer *peer)
3346 DSTACK(__FUNCTION_NAME);
3347 dout_server<<"Server::peerAdded(): peer->id="
3348 <<peer->id<<std::endl;
3351 c.type = PEER_ADDED;
3352 c.peer_id = peer->id;
3354 m_peer_change_queue.push_back(c);
3357 void Server::deletingPeer(con::Peer *peer, bool timeout)
3359 DSTACK(__FUNCTION_NAME);
3360 dout_server<<"Server::deletingPeer(): peer->id="
3361 <<peer->id<<", timeout="<<timeout<<std::endl;
3364 c.type = PEER_REMOVED;
3365 c.peer_id = peer->id;
3366 c.timeout = timeout;
3367 m_peer_change_queue.push_back(c);
3374 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3376 DSTACK(__FUNCTION_NAME);
3377 std::ostringstream os(std::ios_base::binary);
3379 writeU16(os, TOCLIENT_HP);
3383 std::string s = os.str();
3384 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3386 con.Send(peer_id, 0, data, true);
3389 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3390 const std::wstring &reason)
3392 DSTACK(__FUNCTION_NAME);
3393 std::ostringstream os(std::ios_base::binary);
3395 writeU16(os, TOCLIENT_ACCESS_DENIED);
3396 os<<serializeWideString(reason);
3399 std::string s = os.str();
3400 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3402 con.Send(peer_id, 0, data, true);
3406 Non-static send methods
3409 void Server::SendObjectData(float dtime)
3411 DSTACK(__FUNCTION_NAME);
3413 core::map<v3s16, bool> stepped_blocks;
3415 for(core::map<u16, RemoteClient*>::Iterator
3416 i = m_clients.getIterator();
3417 i.atEnd() == false; i++)
3419 u16 peer_id = i.getNode()->getKey();
3420 RemoteClient *client = i.getNode()->getValue();
3421 assert(client->peer_id == peer_id);
3423 if(client->serialization_version == SER_FMT_VER_INVALID)
3426 client->SendObjectData(this, dtime, stepped_blocks);
3430 void Server::SendPlayerInfos()
3432 DSTACK(__FUNCTION_NAME);
3434 //JMutexAutoLock envlock(m_env_mutex);
3436 // Get connected players
3437 core::list<Player*> players = m_env.getPlayers(true);
3439 u32 player_count = players.getSize();
3440 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3442 SharedBuffer<u8> data(datasize);
3443 writeU16(&data[0], TOCLIENT_PLAYERINFO);
3446 core::list<Player*>::Iterator i;
3447 for(i = players.begin();
3448 i != players.end(); i++)
3450 Player *player = *i;
3452 /*dstream<<"Server sending player info for player with "
3453 "peer_id="<<player->peer_id<<std::endl;*/
3455 writeU16(&data[start], player->peer_id);
3456 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3457 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3458 start += 2+PLAYERNAME_SIZE;
3461 //JMutexAutoLock conlock(m_con_mutex);
3464 m_con.SendToAll(0, data, true);
3467 void Server::SendInventory(u16 peer_id)
3469 DSTACK(__FUNCTION_NAME);
3471 Player* player = m_env.getPlayer(peer_id);
3478 std::ostringstream os;
3479 //os.imbue(std::locale("C"));
3481 player->inventory.serialize(os);
3483 std::string s = os.str();
3485 SharedBuffer<u8> data(s.size()+2);
3486 writeU16(&data[0], TOCLIENT_INVENTORY);
3487 memcpy(&data[2], s.c_str(), s.size());
3490 m_con.Send(peer_id, 0, data, true);
3493 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3495 DSTACK(__FUNCTION_NAME);
3497 std::ostringstream os(std::ios_base::binary);
3501 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3502 os.write((char*)buf, 2);
3505 writeU16(buf, message.size());
3506 os.write((char*)buf, 2);
3509 for(u32 i=0; i<message.size(); i++)
3513 os.write((char*)buf, 2);
3517 std::string s = os.str();
3518 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3520 m_con.Send(peer_id, 0, data, true);
3523 void Server::BroadcastChatMessage(const std::wstring &message)
3525 for(core::map<u16, RemoteClient*>::Iterator
3526 i = m_clients.getIterator();
3527 i.atEnd() == false; i++)
3529 // Get client and check that it is valid
3530 RemoteClient *client = i.getNode()->getValue();
3531 assert(client->peer_id == i.getNode()->getKey());
3532 if(client->serialization_version == SER_FMT_VER_INVALID)
3535 SendChatMessage(client->peer_id, message);
3539 void Server::SendPlayerHP(Player *player)
3541 SendHP(m_con, player->peer_id, player->hp);
3544 void Server::SendMovePlayer(Player *player)
3546 DSTACK(__FUNCTION_NAME);
3547 std::ostringstream os(std::ios_base::binary);
3549 writeU16(os, TOCLIENT_MOVE_PLAYER);
3550 writeV3F1000(os, player->getPosition());
3551 writeF1000(os, player->getPitch());
3552 writeF1000(os, player->getYaw());
3555 v3f pos = player->getPosition();
3556 f32 pitch = player->getPitch();
3557 f32 yaw = player->getYaw();
3558 dstream<<"Server sending TOCLIENT_MOVE_PLAYER"
3559 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3566 std::string s = os.str();
3567 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3569 m_con.Send(player->peer_id, 0, data, true);
3572 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3573 core::list<u16> *far_players, float far_d_nodes)
3575 float maxd = far_d_nodes*BS;
3576 v3f p_f = intToFloat(p, BS);
3580 SharedBuffer<u8> reply(replysize);
3581 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3582 writeS16(&reply[2], p.X);
3583 writeS16(&reply[4], p.Y);
3584 writeS16(&reply[6], p.Z);
3586 for(core::map<u16, RemoteClient*>::Iterator
3587 i = m_clients.getIterator();
3588 i.atEnd() == false; i++)
3590 // Get client and check that it is valid
3591 RemoteClient *client = i.getNode()->getValue();
3592 assert(client->peer_id == i.getNode()->getKey());
3593 if(client->serialization_version == SER_FMT_VER_INVALID)
3596 // Don't send if it's the same one
3597 if(client->peer_id == ignore_id)
3603 Player *player = m_env.getPlayer(client->peer_id);
3606 // If player is far away, only set modified blocks not sent
3607 v3f player_pos = player->getPosition();
3608 if(player_pos.getDistanceFrom(p_f) > maxd)
3610 far_players->push_back(client->peer_id);
3617 m_con.Send(client->peer_id, 0, reply, true);
3621 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3622 core::list<u16> *far_players, float far_d_nodes)
3624 float maxd = far_d_nodes*BS;
3625 v3f p_f = intToFloat(p, BS);
3627 for(core::map<u16, RemoteClient*>::Iterator
3628 i = m_clients.getIterator();
3629 i.atEnd() == false; i++)
3631 // Get client and check that it is valid
3632 RemoteClient *client = i.getNode()->getValue();
3633 assert(client->peer_id == i.getNode()->getKey());
3634 if(client->serialization_version == SER_FMT_VER_INVALID)
3637 // Don't send if it's the same one
3638 if(client->peer_id == ignore_id)
3644 Player *player = m_env.getPlayer(client->peer_id);
3647 // If player is far away, only set modified blocks not sent
3648 v3f player_pos = player->getPosition();
3649 if(player_pos.getDistanceFrom(p_f) > maxd)
3651 far_players->push_back(client->peer_id);
3658 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3659 SharedBuffer<u8> reply(replysize);
3660 writeU16(&reply[0], TOCLIENT_ADDNODE);
3661 writeS16(&reply[2], p.X);
3662 writeS16(&reply[4], p.Y);
3663 writeS16(&reply[6], p.Z);
3664 n.serialize(&reply[8], client->serialization_version);
3667 m_con.Send(client->peer_id, 0, reply, true);
3671 void Server::setBlockNotSent(v3s16 p)
3673 for(core::map<u16, RemoteClient*>::Iterator
3674 i = m_clients.getIterator();
3675 i.atEnd()==false; i++)
3677 RemoteClient *client = i.getNode()->getValue();
3678 client->SetBlockNotSent(p);
3682 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3684 DSTACK(__FUNCTION_NAME);
3686 v3s16 p = block->getPos();
3690 bool completely_air = true;
3691 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3692 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3693 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3695 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
3697 completely_air = false;
3698 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
3703 dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
3705 dstream<<"[completely air] ";
3710 Create a packet with the block in the right format
3713 std::ostringstream os(std::ios_base::binary);
3714 block->serialize(os, ver);
3715 std::string s = os.str();
3716 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3718 u32 replysize = 8 + blockdata.getSize();
3719 SharedBuffer<u8> reply(replysize);
3720 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3721 writeS16(&reply[2], p.X);
3722 writeS16(&reply[4], p.Y);
3723 writeS16(&reply[6], p.Z);
3724 memcpy(&reply[8], *blockdata, blockdata.getSize());
3726 /*dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3727 <<": \tpacket size: "<<replysize<<std::endl;*/
3732 m_con.Send(peer_id, 1, reply, true);
3735 void Server::SendBlocks(float dtime)
3737 DSTACK(__FUNCTION_NAME);
3739 JMutexAutoLock envlock(m_env_mutex);
3740 JMutexAutoLock conlock(m_con_mutex);
3742 //TimeTaker timer("Server::SendBlocks");
3744 core::array<PrioritySortedBlockTransfer> queue;
3746 s32 total_sending = 0;
3749 ScopeProfiler sp(&g_profiler, "Server: selecting blocks for sending");
3751 for(core::map<u16, RemoteClient*>::Iterator
3752 i = m_clients.getIterator();
3753 i.atEnd() == false; i++)
3755 RemoteClient *client = i.getNode()->getValue();
3756 assert(client->peer_id == i.getNode()->getKey());
3758 total_sending += client->SendingCount();
3760 if(client->serialization_version == SER_FMT_VER_INVALID)
3763 client->GetNextBlocks(this, dtime, queue);
3768 // Lowest priority number comes first.
3769 // Lowest is most important.
3772 for(u32 i=0; i<queue.size(); i++)
3774 //TODO: Calculate limit dynamically
3775 if(total_sending >= g_settings.getS32
3776 ("max_simultaneous_block_sends_server_total"))
3779 PrioritySortedBlockTransfer q = queue[i];
3781 MapBlock *block = NULL;
3784 block = m_env.getMap().getBlockNoCreate(q.pos);
3786 catch(InvalidPositionException &e)
3791 RemoteClient *client = getClient(q.peer_id);
3793 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3795 client->SentBlock(q.pos);
3805 void Server::UpdateCrafting(u16 peer_id)
3807 DSTACK(__FUNCTION_NAME);
3809 Player* player = m_env.getPlayer(peer_id);
3813 Calculate crafting stuff
3815 if(g_settings.getBool("creative_mode") == false)
3817 InventoryList *clist = player->inventory.getList("craft");
3818 InventoryList *rlist = player->inventory.getList("craftresult");
3820 if(rlist->getUsedSlots() == 0)
3821 player->craftresult_is_preview = true;
3823 if(rlist && player->craftresult_is_preview)
3825 rlist->clearItems();
3827 if(clist && rlist && player->craftresult_is_preview)
3829 InventoryItem *items[9];
3830 for(u16 i=0; i<9; i++)
3832 items[i] = clist->getItem(i);
3835 // Get result of crafting grid
3836 InventoryItem *result = craft_get_result(items);
3838 rlist->addItem(result);
3841 } // if creative_mode == false
3844 RemoteClient* Server::getClient(u16 peer_id)
3846 DSTACK(__FUNCTION_NAME);
3847 //JMutexAutoLock lock(m_con_mutex);
3848 core::map<u16, RemoteClient*>::Node *n;
3849 n = m_clients.find(peer_id);
3850 // A client should exist for all peers
3852 return n->getValue();
3855 std::wstring Server::getStatusString()
3857 std::wostringstream os(std::ios_base::binary);
3860 os<<L"version="<<narrow_to_wide(VERSION_STRING);
3862 os<<L", uptime="<<m_uptime.get();
3863 // Information about clients
3865 for(core::map<u16, RemoteClient*>::Iterator
3866 i = m_clients.getIterator();
3867 i.atEnd() == false; i++)
3869 // Get client and check that it is valid
3870 RemoteClient *client = i.getNode()->getValue();
3871 assert(client->peer_id == i.getNode()->getKey());
3872 if(client->serialization_version == SER_FMT_VER_INVALID)
3875 Player *player = m_env.getPlayer(client->peer_id);
3876 // Get name of player
3877 std::wstring name = L"unknown";
3879 name = narrow_to_wide(player->getName());
3880 // Add name to information string
3884 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
3885 os<<" WARNING: Map saving is disabled."<<std::endl;
3889 v3f findSpawnPos(ServerMap &map)
3891 //return v3f(50,50,50)*BS;
3894 s16 groundheight = 0;
3896 // Try to find a good place a few times
3897 for(s32 i=0; i<1000; i++)
3900 // We're going to try to throw the player to this position
3901 nodepos = v2s16(-range + (myrand()%(range*2)),
3902 -range + (myrand()%(range*2)));
3903 v2s16 sectorpos = getNodeSectorPos(nodepos);
3904 // Get sector (NOTE: Don't get because it's slow)
3905 //m_env.getMap().emergeSector(sectorpos);
3906 // Get ground height at point (fallbacks to heightmap function)
3907 groundheight = map.findGroundLevel(nodepos);
3908 // Don't go underwater
3909 if(groundheight < WATER_LEVEL)
3911 //dstream<<"-> Underwater"<<std::endl;
3914 // Don't go to high places
3915 if(groundheight > WATER_LEVEL + 4)
3917 //dstream<<"-> Underwater"<<std::endl;
3921 // Found a good place
3922 //dstream<<"Searched through "<<i<<" places."<<std::endl;
3926 // If no suitable place was not found, go above water at least.
3927 if(groundheight < WATER_LEVEL)
3928 groundheight = WATER_LEVEL;
3930 return intToFloat(v3s16(
3937 Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id)
3940 Try to get an existing player
3942 Player *player = m_env.getPlayer(name);
3945 // If player is already connected, cancel
3946 if(player->peer_id != 0)
3948 dstream<<"emergePlayer(): Player already connected"<<std::endl;
3953 player->peer_id = peer_id;
3955 // Reset inventory to creative if in creative mode
3956 if(g_settings.getBool("creative_mode"))
3958 craft_set_creative_inventory(player);
3965 If player with the wanted peer_id already exists, cancel.
3967 if(m_env.getPlayer(peer_id) != NULL)
3969 dstream<<"emergePlayer(): Player with wrong name but same"
3970 " peer_id already exists"<<std::endl;
3978 player = new ServerRemotePlayer();
3979 //player->peer_id = c.peer_id;
3980 //player->peer_id = PEER_ID_INEXISTENT;
3981 player->peer_id = peer_id;
3982 player->updateName(name);
3983 m_authmanager.add(name);
3984 m_authmanager.setPassword(name, password);
3985 m_authmanager.setPrivs(name,
3986 stringToPrivs(g_settings.get("default_privs")));
3992 dstream<<"Server: Finding spawn place for player \""
3993 <<player->getName()<<"\""<<std::endl;
3995 v3f pos = findSpawnPos(m_env.getServerMap());
3997 player->setPosition(pos);
4000 Add player to environment
4003 m_env.addPlayer(player);
4006 Add stuff to inventory
4009 if(g_settings.getBool("creative_mode"))
4011 craft_set_creative_inventory(player);
4013 else if(g_settings.getBool("give_initial_stuff"))
4015 craft_give_initial_stuff(player);
4020 } // create new player
4023 void Server::handlePeerChange(PeerChange &c)
4025 JMutexAutoLock envlock(m_env_mutex);
4026 JMutexAutoLock conlock(m_con_mutex);
4028 if(c.type == PEER_ADDED)
4035 core::map<u16, RemoteClient*>::Node *n;
4036 n = m_clients.find(c.peer_id);
4037 // The client shouldn't already exist
4041 RemoteClient *client = new RemoteClient();
4042 client->peer_id = c.peer_id;
4043 m_clients.insert(client->peer_id, client);
4046 else if(c.type == PEER_REMOVED)
4053 core::map<u16, RemoteClient*>::Node *n;
4054 n = m_clients.find(c.peer_id);
4055 // The client should exist
4059 Mark objects to be not known by the client
4061 RemoteClient *client = n->getValue();
4063 for(core::map<u16, bool>::Iterator
4064 i = client->m_known_objects.getIterator();
4065 i.atEnd()==false; i++)
4068 u16 id = i.getNode()->getKey();
4069 ServerActiveObject* obj = m_env.getActiveObject(id);
4071 if(obj && obj->m_known_by_count > 0)
4072 obj->m_known_by_count--;
4075 // Collect information about leaving in chat
4076 std::wstring message;
4078 std::wstring name = L"unknown";
4079 Player *player = m_env.getPlayer(c.peer_id);
4081 name = narrow_to_wide(player->getName());
4085 message += L" left game";
4087 message += L" (timed out)";
4092 m_env.removePlayer(c.peer_id);
4095 // Set player client disconnected
4097 Player *player = m_env.getPlayer(c.peer_id);
4099 player->peer_id = 0;
4103 delete m_clients[c.peer_id];
4104 m_clients.remove(c.peer_id);
4106 // Send player info to all remaining clients
4109 // Send leave chat message to all remaining clients
4110 BroadcastChatMessage(message);
4119 void Server::handlePeerChanges()
4121 while(m_peer_change_queue.size() > 0)
4123 PeerChange c = m_peer_change_queue.pop_front();
4125 dout_server<<"Server: Handling peer change: "
4126 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4129 handlePeerChange(c);
4133 u64 Server::getPlayerPrivs(Player *player)
4137 std::string playername = player->getName();
4138 // Local player gets all privileges regardless of
4139 // what's set on their account.
4140 if(g_settings.get("name") == playername)
4146 return getPlayerAuthPrivs(playername);
4150 void dedicated_server_loop(Server &server, bool &kill)
4152 DSTACK(__FUNCTION_NAME);
4154 dstream<<DTIME<<std::endl;
4155 dstream<<"========================"<<std::endl;
4156 dstream<<"Running dedicated server"<<std::endl;
4157 dstream<<"========================"<<std::endl;
4160 IntervalLimiter m_profiler_interval;
4164 // This is kind of a hack but can be done like this
4165 // because server.step() is very light
4167 ScopeProfiler sp(&g_profiler, "dedicated server sleep");
4172 if(server.getShutdownRequested() || kill)
4174 dstream<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4181 float profiler_print_interval =
4182 g_settings.getFloat("profiler_print_interval");
4183 if(profiler_print_interval != 0)
4185 if(m_profiler_interval.step(0.030, profiler_print_interval))
4187 dstream<<"Profiler:"<<std::endl;
4188 g_profiler.print(dstream);
4196 static int counter = 0;
4202 core::list<PlayerInfo> list = server.getPlayerInfo();
4203 core::list<PlayerInfo>::Iterator i;
4204 static u32 sum_old = 0;
4205 u32 sum = PIChecksum(list);
4208 dstream<<DTIME<<"Player info:"<<std::endl;
4209 for(i=list.begin(); i!=list.end(); i++)
4211 i->PrintLine(&dstream);