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 #include "serverobject.h"
43 #include "scriptapi.h"
48 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
50 #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
52 class MapEditEventIgnorer
55 MapEditEventIgnorer(bool *flag):
64 ~MapEditEventIgnorer()
77 void * ServerThread::Thread()
81 log_register_thread("ServerThread");
83 DSTACK(__FUNCTION_NAME);
85 BEGIN_DEBUG_EXCEPTION_HANDLER
90 //TimeTaker timer("AsyncRunStep() + Receive()");
93 //TimeTaker timer("AsyncRunStep()");
94 m_server->AsyncRunStep();
97 //infostream<<"Running m_server->Receive()"<<std::endl;
100 catch(con::NoIncomingDataException &e)
103 catch(con::PeerNotFoundException &e)
105 infostream<<"Server: PeerNotFoundException"<<std::endl;
109 END_DEBUG_EXCEPTION_HANDLER(errorstream)
114 void * EmergeThread::Thread()
118 log_register_thread("EmergeThread");
120 DSTACK(__FUNCTION_NAME);
122 BEGIN_DEBUG_EXCEPTION_HANDLER
124 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
127 Get block info from queue, emerge them and send them
130 After queue is empty, exit.
134 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
138 SharedPtr<QueuedBlockEmerge> q(qptr);
144 Do not generate over-limit
146 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
147 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
148 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
149 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
150 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
151 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
154 //infostream<<"EmergeThread::Thread(): running"<<std::endl;
156 //TimeTaker timer("block emerge");
159 Try to emerge it from somewhere.
161 If it is only wanted as optional, only loading from disk
166 Check if any peer wants it as non-optional. In that case it
169 Also decrement the emerge queue count in clients.
172 bool only_from_disk = true;
175 core::map<u16, u8>::Iterator i;
176 for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
178 //u16 peer_id = i.getNode()->getKey();
181 u8 flags = i.getNode()->getValue();
182 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
183 only_from_disk = false;
188 if(enable_mapgen_debug_info)
189 infostream<<"EmergeThread: p="
190 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
191 <<"only_from_disk="<<only_from_disk<<std::endl;
193 ServerMap &map = ((ServerMap&)m_server->m_env->getMap());
195 //core::map<v3s16, MapBlock*> changed_blocks;
196 //core::map<v3s16, MapBlock*> lighting_invalidated_blocks;
198 MapBlock *block = NULL;
199 bool got_block = true;
200 core::map<v3s16, MapBlock*> modified_blocks;
203 Fetch block from map or generate a single block
206 JMutexAutoLock envlock(m_server->m_env_mutex);
208 // Load sector if it isn't loaded
209 if(map.getSectorNoGenerateNoEx(p2d) == NULL)
210 //map.loadSectorFull(p2d);
211 map.loadSectorMeta(p2d);
213 block = map.getBlockNoCreateNoEx(p);
214 if(!block || block->isDummy() || !block->isGenerated())
216 if(enable_mapgen_debug_info)
217 infostream<<"EmergeThread: not in memory, loading"<<std::endl;
219 // Get, load or create sector
220 /*ServerMapSector *sector =
221 (ServerMapSector*)map.createSector(p2d);*/
223 // Load/generate block
225 /*block = map.emergeBlock(p, sector, changed_blocks,
226 lighting_invalidated_blocks);*/
228 block = map.loadBlock(p);
230 if(only_from_disk == false)
232 if(block == NULL || block->isGenerated() == false)
234 if(enable_mapgen_debug_info)
235 infostream<<"EmergeThread: generating"<<std::endl;
236 block = map.generateBlock(p, modified_blocks);
240 if(enable_mapgen_debug_info)
241 infostream<<"EmergeThread: ended up with: "
242 <<analyze_block(block)<<std::endl;
251 Ignore map edit events, they will not need to be
252 sent to anybody because the block hasn't been sent
255 MapEditEventIgnorer ign(&m_server->m_ignore_map_edit_events);
257 // Activate objects and stuff
258 m_server->m_env->activateBlock(block, 3600);
263 /*if(block->getLightingExpired()){
264 lighting_invalidated_blocks[block->getPos()] = block;
268 // TODO: Some additional checking and lighting updating,
273 JMutexAutoLock envlock(m_server->m_env_mutex);
278 Collect a list of blocks that have been modified in
279 addition to the fetched one.
283 if(lighting_invalidated_blocks.size() > 0)
285 /*infostream<<"lighting "<<lighting_invalidated_blocks.size()
286 <<" blocks"<<std::endl;*/
288 // 50-100ms for single block generation
289 //TimeTaker timer("** EmergeThread updateLighting");
291 // Update lighting without locking the environment mutex,
292 // add modified blocks to changed blocks
293 map.updateLighting(lighting_invalidated_blocks, modified_blocks);
296 // Add all from changed_blocks to modified_blocks
297 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
298 i.atEnd() == false; i++)
300 MapBlock *block = i.getNode()->getValue();
301 modified_blocks.insert(block->getPos(), block);
305 // If we got no block, there should be no invalidated blocks
308 //assert(lighting_invalidated_blocks.size() == 0);
314 Set sent status of modified blocks on clients
317 // NOTE: Server's clients are also behind the connection mutex
318 JMutexAutoLock lock(m_server->m_con_mutex);
321 Add the originally fetched block to the modified list
325 modified_blocks.insert(p, block);
329 Set the modified blocks unsent for all the clients
332 for(core::map<u16, RemoteClient*>::Iterator
333 i = m_server->m_clients.getIterator();
334 i.atEnd() == false; i++)
336 RemoteClient *client = i.getNode()->getValue();
338 if(modified_blocks.size() > 0)
340 // Remove block from sent history
341 client->SetBlocksNotSent(modified_blocks);
347 END_DEBUG_EXCEPTION_HANDLER(errorstream)
352 void RemoteClient::GetNextBlocks(Server *server, float dtime,
353 core::array<PrioritySortedBlockTransfer> &dest)
355 DSTACK(__FUNCTION_NAME);
358 TimeTaker timer("RemoteClient::GetNextBlocks", &timer_result);*/
361 m_nothing_to_send_pause_timer -= dtime;
362 m_nearest_unsent_reset_timer += dtime;
364 if(m_nothing_to_send_pause_timer >= 0)
369 // Won't send anything if already sending
370 if(m_blocks_sending.size() >= g_settings->getU16
371 ("max_simultaneous_block_sends_per_client"))
373 //infostream<<"Not sending any blocks, Queue full."<<std::endl;
377 //TimeTaker timer("RemoteClient::GetNextBlocks");
379 Player *player = server->m_env->getPlayer(peer_id);
381 assert(player != NULL);
383 v3f playerpos = player->getPosition();
384 v3f playerspeed = player->getSpeed();
385 v3f playerspeeddir(0,0,0);
386 if(playerspeed.getLength() > 1.0*BS)
387 playerspeeddir = playerspeed / playerspeed.getLength();
388 // Predict to next block
389 v3f playerpos_predicted = playerpos + playerspeeddir*MAP_BLOCKSIZE*BS;
391 v3s16 center_nodepos = floatToInt(playerpos_predicted, BS);
393 v3s16 center = getNodeBlockPos(center_nodepos);
395 // Camera position and direction
396 v3f camera_pos = player->getEyePosition();
397 v3f camera_dir = v3f(0,0,1);
398 camera_dir.rotateYZBy(player->getPitch());
399 camera_dir.rotateXZBy(player->getYaw());
401 /*infostream<<"camera_dir=("<<camera_dir.X<<","<<camera_dir.Y<<","
402 <<camera_dir.Z<<")"<<std::endl;*/
405 Get the starting value of the block finder radius.
408 if(m_last_center != center)
410 m_nearest_unsent_d = 0;
411 m_last_center = center;
414 /*infostream<<"m_nearest_unsent_reset_timer="
415 <<m_nearest_unsent_reset_timer<<std::endl;*/
417 // Reset periodically to workaround for some bugs or stuff
418 if(m_nearest_unsent_reset_timer > 20.0)
420 m_nearest_unsent_reset_timer = 0;
421 m_nearest_unsent_d = 0;
422 //infostream<<"Resetting m_nearest_unsent_d for "
423 // <<server->getPlayerName(peer_id)<<std::endl;
426 //s16 last_nearest_unsent_d = m_nearest_unsent_d;
427 s16 d_start = m_nearest_unsent_d;
429 //infostream<<"d_start="<<d_start<<std::endl;
431 u16 max_simul_sends_setting = g_settings->getU16
432 ("max_simultaneous_block_sends_per_client");
433 u16 max_simul_sends_usually = max_simul_sends_setting;
436 Check the time from last addNode/removeNode.
438 Decrease send rate if player is building stuff.
440 m_time_from_building += dtime;
441 if(m_time_from_building < g_settings->getFloat(
442 "full_block_send_enable_min_time_from_building"))
444 max_simul_sends_usually
445 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
449 Number of blocks sending + number of blocks selected for sending
451 u32 num_blocks_selected = m_blocks_sending.size();
454 next time d will be continued from the d from which the nearest
455 unsent block was found this time.
457 This is because not necessarily any of the blocks found this
458 time are actually sent.
460 s32 new_nearest_unsent_d = -1;
462 s16 d_max = g_settings->getS16("max_block_send_distance");
463 s16 d_max_gen = g_settings->getS16("max_block_generate_distance");
465 // Don't loop very much at a time
466 s16 max_d_increment_at_time = 2;
467 if(d_max > d_start + max_d_increment_at_time)
468 d_max = d_start + max_d_increment_at_time;
469 /*if(d_max_gen > d_start+2)
470 d_max_gen = d_start+2;*/
472 //infostream<<"Starting from "<<d_start<<std::endl;
474 s32 nearest_emerged_d = -1;
475 s32 nearest_emergefull_d = -1;
476 s32 nearest_sent_d = -1;
477 bool queue_is_full = false;
480 for(d = d_start; d <= d_max; d++)
482 /*errorstream<<"checking d="<<d<<" for "
483 <<server->getPlayerName(peer_id)<<std::endl;*/
484 //infostream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
487 If m_nearest_unsent_d was changed by the EmergeThread
488 (it can change it to 0 through SetBlockNotSent),
490 Else update m_nearest_unsent_d
492 /*if(m_nearest_unsent_d != last_nearest_unsent_d)
494 d = m_nearest_unsent_d;
495 last_nearest_unsent_d = m_nearest_unsent_d;
499 Get the border/face dot coordinates of a "d-radiused"
502 core::list<v3s16> list;
503 getFacePositions(list, d);
505 core::list<v3s16>::Iterator li;
506 for(li=list.begin(); li!=list.end(); li++)
508 v3s16 p = *li + center;
512 - Don't allow too many simultaneous transfers
513 - EXCEPT when the blocks are very close
515 Also, don't send blocks that are already flying.
518 // Start with the usual maximum
519 u16 max_simul_dynamic = max_simul_sends_usually;
521 // If block is very close, allow full maximum
522 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
523 max_simul_dynamic = max_simul_sends_setting;
525 // Don't select too many blocks for sending
526 if(num_blocks_selected >= max_simul_dynamic)
528 queue_is_full = true;
529 goto queue_full_break;
532 // Don't send blocks that are currently being transferred
533 if(m_blocks_sending.find(p) != NULL)
539 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
540 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
541 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
542 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
543 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
544 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
547 // If this is true, inexistent block will be made from scratch
548 bool generate = d <= d_max_gen;
551 /*// Limit the generating area vertically to 2/3
552 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
555 // Limit the send area vertically to 1/2
556 if(abs(p.Y - center.Y) > d_max / 2)
562 If block is far away, don't generate it unless it is
568 // Block center y in nodes
569 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
570 // Don't generate if it's very high or very low
571 if(y < -64 || y > 64)
575 v2s16 p2d_nodes_center(
579 // Get ground height in nodes
580 s16 gh = server->m_env->getServerMap().findGroundLevel(
583 // If differs a lot, don't generate
584 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
586 // Actually, don't even send it
592 //infostream<<"d="<<d<<std::endl;
595 Don't generate or send if not in sight
596 FIXME This only works if the client uses a small enough
597 FOV setting. The default of 72 degrees is fine.
600 float camera_fov = (72.0*PI/180) * 4./3.;
601 if(isBlockInSight(p, camera_pos, camera_dir, camera_fov, 10000*BS) == false)
607 Don't send already sent blocks
610 if(m_blocks_sent.find(p) != NULL)
617 Check if map has this block
619 MapBlock *block = server->m_env->getMap().getBlockNoCreateNoEx(p);
621 bool surely_not_found_on_disk = false;
622 bool block_is_invalid = false;
625 // Reset usage timer, this block will be of use in the future.
626 block->resetUsageTimer();
628 // Block is dummy if data doesn't exist.
629 // It means it has been not found from disk and not generated
632 surely_not_found_on_disk = true;
635 // Block is valid if lighting is up-to-date and data exists
636 if(block->isValid() == false)
638 block_is_invalid = true;
641 /*if(block->isFullyGenerated() == false)
643 block_is_invalid = true;
648 ServerMap *map = (ServerMap*)(&server->m_env->getMap());
649 v2s16 chunkpos = map->sector_to_chunk(p2d);
650 if(map->chunkNonVolatile(chunkpos) == false)
651 block_is_invalid = true;
653 if(block->isGenerated() == false)
654 block_is_invalid = true;
657 If block is not close, don't send it unless it is near
660 Block is near ground level if night-time mesh
661 differs from day-time mesh.
665 if(block->dayNightDiffed() == false)
672 If block has been marked to not exist on disk (dummy)
673 and generating new ones is not wanted, skip block.
675 if(generate == false && surely_not_found_on_disk == true)
682 Add inexistent block to emerge queue.
684 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
686 //TODO: Get value from somewhere
687 // Allow only one block in emerge queue
688 //if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
689 // Allow two blocks in queue per client
690 //if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
692 // Make it more responsive when needing to generate stuff
693 if(surely_not_found_on_disk)
695 if(server->m_emerge_queue.peerItemCount(peer_id) < max_emerge)
697 //infostream<<"Adding block to emerge queue"<<std::endl;
699 // Add it to the emerge queue and trigger the thread
702 if(generate == false)
703 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
705 server->m_emerge_queue.addBlock(peer_id, p, flags);
706 server->m_emergethread.trigger();
708 if(nearest_emerged_d == -1)
709 nearest_emerged_d = d;
711 if(nearest_emergefull_d == -1)
712 nearest_emergefull_d = d;
719 if(nearest_sent_d == -1)
723 Add block to send queue
726 /*errorstream<<"sending from d="<<d<<" to "
727 <<server->getPlayerName(peer_id)<<std::endl;*/
729 PrioritySortedBlockTransfer q((float)d, p, peer_id);
733 num_blocks_selected += 1;
738 //infostream<<"Stopped at "<<d<<std::endl;
740 // If nothing was found for sending and nothing was queued for
741 // emerging, continue next time browsing from here
742 if(nearest_emerged_d != -1){
743 new_nearest_unsent_d = nearest_emerged_d;
744 } else if(nearest_emergefull_d != -1){
745 new_nearest_unsent_d = nearest_emergefull_d;
747 if(d > g_settings->getS16("max_block_send_distance")){
748 new_nearest_unsent_d = 0;
749 m_nothing_to_send_pause_timer = 2.0;
750 /*infostream<<"GetNextBlocks(): d wrapped around for "
751 <<server->getPlayerName(peer_id)
752 <<"; setting to 0 and pausing"<<std::endl;*/
754 if(nearest_sent_d != -1)
755 new_nearest_unsent_d = nearest_sent_d;
757 new_nearest_unsent_d = d;
761 if(new_nearest_unsent_d != -1)
762 m_nearest_unsent_d = new_nearest_unsent_d;
764 /*timer_result = timer.stop(true);
765 if(timer_result != 0)
766 infostream<<"GetNextBlocks duration: "<<timer_result<<" (!=0)"<<std::endl;*/
769 void RemoteClient::SendObjectData(
772 core::map<v3s16, bool> &stepped_blocks
775 DSTACK(__FUNCTION_NAME);
777 // Can't send anything without knowing version
778 if(serialization_version == SER_FMT_VER_INVALID)
780 infostream<<"RemoteClient::SendObjectData(): Not sending, no version."
786 Send a TOCLIENT_OBJECTDATA packet.
790 u16 number of player positions
802 std::ostringstream os(std::ios_base::binary);
806 writeU16(buf, TOCLIENT_OBJECTDATA);
807 os.write((char*)buf, 2);
810 Get and write player data
813 // Get connected players
814 core::list<Player*> players = server->m_env->getPlayers(true);
816 // Write player count
817 u16 playercount = players.size();
818 writeU16(buf, playercount);
819 os.write((char*)buf, 2);
821 core::list<Player*>::Iterator i;
822 for(i = players.begin();
823 i != players.end(); i++)
827 v3f pf = player->getPosition();
828 v3f sf = player->getSpeed();
830 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
831 v3s32 speed_i (sf.X*100, sf.Y*100, sf.Z*100);
832 s32 pitch_i (player->getPitch() * 100);
833 s32 yaw_i (player->getYaw() * 100);
835 writeU16(buf, player->peer_id);
836 os.write((char*)buf, 2);
837 writeV3S32(buf, position_i);
838 os.write((char*)buf, 12);
839 writeV3S32(buf, speed_i);
840 os.write((char*)buf, 12);
841 writeS32(buf, pitch_i);
842 os.write((char*)buf, 4);
843 writeS32(buf, yaw_i);
844 os.write((char*)buf, 4);
848 Get and write object data (dummy, for compatibility)
853 os.write((char*)buf, 2);
859 //infostream<<"Server: Sending object data to "<<peer_id<<std::endl;
862 std::string s = os.str();
863 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
864 // Send as unreliable
865 server->m_con.Send(peer_id, 0, data, false);
868 void RemoteClient::GotBlock(v3s16 p)
870 if(m_blocks_sending.find(p) != NULL)
871 m_blocks_sending.remove(p);
874 /*infostream<<"RemoteClient::GotBlock(): Didn't find in"
875 " m_blocks_sending"<<std::endl;*/
876 m_excess_gotblocks++;
878 m_blocks_sent.insert(p, true);
881 void RemoteClient::SentBlock(v3s16 p)
883 if(m_blocks_sending.find(p) == NULL)
884 m_blocks_sending.insert(p, 0.0);
886 infostream<<"RemoteClient::SentBlock(): Sent block"
887 " already in m_blocks_sending"<<std::endl;
890 void RemoteClient::SetBlockNotSent(v3s16 p)
892 m_nearest_unsent_d = 0;
894 if(m_blocks_sending.find(p) != NULL)
895 m_blocks_sending.remove(p);
896 if(m_blocks_sent.find(p) != NULL)
897 m_blocks_sent.remove(p);
900 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
902 m_nearest_unsent_d = 0;
904 for(core::map<v3s16, MapBlock*>::Iterator
905 i = blocks.getIterator();
906 i.atEnd()==false; i++)
908 v3s16 p = i.getNode()->getKey();
910 if(m_blocks_sending.find(p) != NULL)
911 m_blocks_sending.remove(p);
912 if(m_blocks_sent.find(p) != NULL)
913 m_blocks_sent.remove(p);
921 PlayerInfo::PlayerInfo()
927 void PlayerInfo::PrintLine(std::ostream *s)
930 (*s)<<"\""<<name<<"\" ("
931 <<(position.X/10)<<","<<(position.Y/10)
932 <<","<<(position.Z/10)<<") ";
934 (*s)<<" avg_rtt="<<avg_rtt;
938 u32 PIChecksum(core::list<PlayerInfo> &l)
940 core::list<PlayerInfo>::Iterator i;
943 for(i=l.begin(); i!=l.end(); i++)
945 checksum += a * (i->id+1);
946 checksum ^= 0x435aafcd;
957 ModSpec(const std::string &name_="", const std::string path_=""):
963 static core::list<ModSpec> getMods(core::list<std::string> &modspaths)
965 core::list<ModSpec> mods;
966 for(core::list<std::string>::Iterator i = modspaths.begin();
967 i != modspaths.end(); i++){
968 std::string modspath = *i;
969 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(modspath);
970 for(u32 j=0; j<dirlist.size(); j++){
973 std::string modname = dirlist[j].name;
974 std::string modpath = modspath + DIR_DELIM + modname;
975 mods.push_back(ModSpec(modname, modpath));
986 std::string mapsavedir,
987 std::string configpath
990 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
991 m_authmanager(mapsavedir+DIR_DELIM+"auth.txt"),
992 m_banmanager(mapsavedir+DIR_DELIM+"ipban.txt"),
994 m_toolmgr(createToolDefManager()),
995 m_nodedef(createNodeDefManager()),
996 m_craftdef(createCraftDefManager()),
998 m_emergethread(this),
1000 m_time_of_day_send_timer(0),
1002 m_mapsavedir(mapsavedir),
1003 m_configpath(configpath),
1004 m_shutdown_requested(false),
1005 m_ignore_map_edit_events(false),
1006 m_ignore_map_edit_events_peer_id(0)
1008 m_liquid_transform_timer = 0.0;
1009 m_print_info_timer = 0.0;
1010 m_objectdata_timer = 0.0;
1011 m_emergethread_trigger_timer = 0.0;
1012 m_savemap_timer = 0.0;
1016 m_step_dtime_mutex.Init();
1019 JMutexAutoLock envlock(m_env_mutex);
1020 JMutexAutoLock conlock(m_con_mutex);
1022 infostream<<"m_nodedef="<<m_nodedef<<std::endl;
1024 // Path to builtin.lua
1025 std::string builtinpath = porting::path_data + DIR_DELIM + "builtin.lua";
1026 // Add default global mod path
1027 m_modspaths.push_back(porting::path_data + DIR_DELIM + "mods");
1029 // Initialize scripting
1031 infostream<<"Server: Initializing scripting"<<std::endl;
1032 m_lua = script_init();
1035 scriptapi_export(m_lua, this);
1036 // Load and run builtin.lua
1037 infostream<<"Server: Loading builtin Lua stuff from \""<<builtinpath
1039 bool success = script_load(m_lua, builtinpath.c_str());
1041 errorstream<<"Server: Failed to load and run "
1042 <<builtinpath<<std::endl;
1045 // Load and run "mod" scripts
1046 core::list<ModSpec> mods = getMods(m_modspaths);
1047 for(core::list<ModSpec>::Iterator i = mods.begin();
1048 i != mods.end(); i++){
1050 infostream<<"Server: Loading mod \""<<mod.name<<"\""<<std::endl;
1051 std::string scriptpath = mod.path + DIR_DELIM + "init.lua";
1052 bool success = script_load(m_lua, scriptpath.c_str());
1054 errorstream<<"Server: Failed to load and run "
1055 <<scriptpath<<std::endl;
1060 // Initialize Environment
1062 m_env = new ServerEnvironment(new ServerMap(mapsavedir, this), m_lua, this);
1064 // Give environment reference to scripting api
1065 scriptapi_add_environment(m_lua, m_env);
1067 // Register us to receive map edit events
1068 m_env->getMap().addEventReceiver(this);
1070 // If file exists, load environment metadata
1071 if(fs::PathExists(m_mapsavedir+DIR_DELIM+"env_meta.txt"))
1073 infostream<<"Server: Loading environment metadata"<<std::endl;
1074 m_env->loadMeta(m_mapsavedir);
1078 infostream<<"Server: Loading players"<<std::endl;
1079 m_env->deSerializePlayers(m_mapsavedir);
1084 infostream<<"Server::~Server()"<<std::endl;
1087 Send shutdown message
1090 JMutexAutoLock conlock(m_con_mutex);
1092 std::wstring line = L"*** Server shutting down";
1095 Send the message to clients
1097 for(core::map<u16, RemoteClient*>::Iterator
1098 i = m_clients.getIterator();
1099 i.atEnd() == false; i++)
1101 // Get client and check that it is valid
1102 RemoteClient *client = i.getNode()->getValue();
1103 assert(client->peer_id == i.getNode()->getKey());
1104 if(client->serialization_version == SER_FMT_VER_INVALID)
1108 SendChatMessage(client->peer_id, line);
1110 catch(con::PeerNotFoundException &e)
1116 JMutexAutoLock envlock(m_env_mutex);
1121 infostream<<"Server: Saving players"<<std::endl;
1122 m_env->serializePlayers(m_mapsavedir);
1125 Save environment metadata
1127 infostream<<"Server: Saving environment metadata"<<std::endl;
1128 m_env->saveMeta(m_mapsavedir);
1140 JMutexAutoLock clientslock(m_con_mutex);
1142 for(core::map<u16, RemoteClient*>::Iterator
1143 i = m_clients.getIterator();
1144 i.atEnd() == false; i++)
1147 // NOTE: These are removed by env destructor
1149 u16 peer_id = i.getNode()->getKey();
1150 JMutexAutoLock envlock(m_env_mutex);
1151 m_env->removePlayer(peer_id);
1155 delete i.getNode()->getValue();
1159 // Delete Environment
1165 // Deinitialize scripting
1166 infostream<<"Server: Deinitializing scripting"<<std::endl;
1167 script_deinit(m_lua);
1170 void Server::start(unsigned short port)
1172 DSTACK(__FUNCTION_NAME);
1173 // Stop thread if already running
1176 // Initialize connection
1177 m_con.SetTimeoutMs(30);
1181 m_thread.setRun(true);
1184 infostream<<"Server: Started on port "<<port<<std::endl;
1189 DSTACK(__FUNCTION_NAME);
1191 infostream<<"Server: Stopping and waiting threads"<<std::endl;
1193 // Stop threads (set run=false first so both start stopping)
1194 m_thread.setRun(false);
1195 m_emergethread.setRun(false);
1197 m_emergethread.stop();
1199 infostream<<"Server: Threads stopped"<<std::endl;
1202 void Server::step(float dtime)
1204 DSTACK(__FUNCTION_NAME);
1209 JMutexAutoLock lock(m_step_dtime_mutex);
1210 m_step_dtime += dtime;
1214 void Server::AsyncRunStep()
1216 DSTACK(__FUNCTION_NAME);
1218 g_profiler->add("Server::AsyncRunStep (num)", 1);
1222 JMutexAutoLock lock1(m_step_dtime_mutex);
1223 dtime = m_step_dtime;
1227 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
1228 // Send blocks to clients
1235 g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
1237 //infostream<<"Server steps "<<dtime<<std::endl;
1238 //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1241 JMutexAutoLock lock1(m_step_dtime_mutex);
1242 m_step_dtime -= dtime;
1249 m_uptime.set(m_uptime.get() + dtime);
1253 // Process connection's timeouts
1254 JMutexAutoLock lock2(m_con_mutex);
1255 ScopeProfiler sp(g_profiler, "Server: connection timeout processing");
1256 m_con.RunTimeouts(dtime);
1260 // This has to be called so that the client list gets synced
1261 // with the peer list of the connection
1262 handlePeerChanges();
1266 Update m_time_of_day and overall game time
1269 JMutexAutoLock envlock(m_env_mutex);
1271 m_time_counter += dtime;
1272 f32 speed = g_settings->getFloat("time_speed") * 24000./(24.*3600);
1273 u32 units = (u32)(m_time_counter*speed);
1274 m_time_counter -= (f32)units / speed;
1276 m_env->setTimeOfDay((m_env->getTimeOfDay() + units) % 24000);
1278 //infostream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1281 Send to clients at constant intervals
1284 m_time_of_day_send_timer -= dtime;
1285 if(m_time_of_day_send_timer < 0.0)
1287 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
1289 //JMutexAutoLock envlock(m_env_mutex);
1290 JMutexAutoLock conlock(m_con_mutex);
1292 for(core::map<u16, RemoteClient*>::Iterator
1293 i = m_clients.getIterator();
1294 i.atEnd() == false; i++)
1296 RemoteClient *client = i.getNode()->getValue();
1297 //Player *player = m_env->getPlayer(client->peer_id);
1299 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1300 m_env->getTimeOfDay());
1302 m_con.Send(client->peer_id, 0, data, true);
1308 JMutexAutoLock lock(m_env_mutex);
1310 ScopeProfiler sp(g_profiler, "SEnv step");
1311 ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
1315 const float map_timer_and_unload_dtime = 2.92;
1316 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
1318 JMutexAutoLock lock(m_env_mutex);
1319 // Run Map's timers and unload unused data
1320 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
1321 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
1322 g_settings->getFloat("server_unload_unused_data_timeout"));
1330 Check player movements
1332 NOTE: Actually the server should handle player physics like the
1333 client does and compare player's position to what is calculated
1334 on our side. This is required when eg. players fly due to an
1338 JMutexAutoLock lock(m_env_mutex);
1339 JMutexAutoLock lock2(m_con_mutex);
1341 //float player_max_speed = BS * 4.0; // Normal speed
1342 float player_max_speed = BS * 20; // Fast speed
1343 float player_max_speed_up = BS * 20;
1345 player_max_speed *= 1.7; // Tolerance
1346 player_max_speed_up *= 1.7;
1348 for(core::map<u16, RemoteClient*>::Iterator
1349 i = m_clients.getIterator();
1350 i.atEnd() == false; i++)
1352 RemoteClient *client = i.getNode()->getValue();
1353 ServerRemotePlayer *player =
1354 (ServerRemotePlayer*)m_env->getPlayer(client->peer_id);
1357 player->m_last_good_position_age += dtime;
1358 if(player->m_last_good_position_age >= 2.0){
1359 float age = player->m_last_good_position_age;
1360 v3f diff = (player->getPosition() - player->m_last_good_position);
1361 float d_vert = diff.Y;
1363 float d_horiz = diff.getLength();
1364 /*infostream<<player->getName()<<"'s horizontal speed is "
1365 <<(d_horiz/age)<<std::endl;*/
1366 if(d_horiz <= age * player_max_speed &&
1367 (d_vert < 0 || d_vert < age * player_max_speed_up)){
1368 player->m_last_good_position = player->getPosition();
1370 actionstream<<"Player "<<player->getName()
1371 <<" moved too fast; resetting position"
1373 player->setPosition(player->m_last_good_position);
1374 SendMovePlayer(player);
1376 player->m_last_good_position_age = 0;
1381 /* Transform liquids */
1382 m_liquid_transform_timer += dtime;
1383 if(m_liquid_transform_timer >= 1.00)
1385 m_liquid_transform_timer -= 1.00;
1387 JMutexAutoLock lock(m_env_mutex);
1389 ScopeProfiler sp(g_profiler, "Server: liquid transform");
1391 core::map<v3s16, MapBlock*> modified_blocks;
1392 m_env->getMap().transformLiquids(modified_blocks);
1397 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1398 ServerMap &map = ((ServerMap&)m_env->getMap());
1399 map.updateLighting(modified_blocks, lighting_modified_blocks);
1401 // Add blocks modified by lighting to modified_blocks
1402 for(core::map<v3s16, MapBlock*>::Iterator
1403 i = lighting_modified_blocks.getIterator();
1404 i.atEnd() == false; i++)
1406 MapBlock *block = i.getNode()->getValue();
1407 modified_blocks.insert(block->getPos(), block);
1411 Set the modified blocks unsent for all the clients
1414 JMutexAutoLock lock2(m_con_mutex);
1416 for(core::map<u16, RemoteClient*>::Iterator
1417 i = m_clients.getIterator();
1418 i.atEnd() == false; i++)
1420 RemoteClient *client = i.getNode()->getValue();
1422 if(modified_blocks.size() > 0)
1424 // Remove block from sent history
1425 client->SetBlocksNotSent(modified_blocks);
1430 // Periodically print some info
1432 float &counter = m_print_info_timer;
1438 JMutexAutoLock lock2(m_con_mutex);
1440 if(m_clients.size() != 0)
1441 infostream<<"Players:"<<std::endl;
1442 for(core::map<u16, RemoteClient*>::Iterator
1443 i = m_clients.getIterator();
1444 i.atEnd() == false; i++)
1446 //u16 peer_id = i.getNode()->getKey();
1447 RemoteClient *client = i.getNode()->getValue();
1448 Player *player = m_env->getPlayer(client->peer_id);
1451 infostream<<"* "<<player->getName()<<"\t";
1452 client->PrintInfo(infostream);
1457 //if(g_settings->getBool("enable_experimental"))
1461 Check added and deleted active objects
1464 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
1465 JMutexAutoLock envlock(m_env_mutex);
1466 JMutexAutoLock conlock(m_con_mutex);
1468 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
1470 // Radius inside which objects are active
1471 s16 radius = g_settings->getS16("active_object_send_range_blocks");
1472 radius *= MAP_BLOCKSIZE;
1474 for(core::map<u16, RemoteClient*>::Iterator
1475 i = m_clients.getIterator();
1476 i.atEnd() == false; i++)
1478 RemoteClient *client = i.getNode()->getValue();
1479 Player *player = m_env->getPlayer(client->peer_id);
1482 // This can happen if the client timeouts somehow
1483 /*infostream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
1485 <<" has no associated player"<<std::endl;*/
1488 v3s16 pos = floatToInt(player->getPosition(), BS);
1490 core::map<u16, bool> removed_objects;
1491 core::map<u16, bool> added_objects;
1492 m_env->getRemovedActiveObjects(pos, radius,
1493 client->m_known_objects, removed_objects);
1494 m_env->getAddedActiveObjects(pos, radius,
1495 client->m_known_objects, added_objects);
1497 // Ignore if nothing happened
1498 if(removed_objects.size() == 0 && added_objects.size() == 0)
1500 //infostream<<"active objects: none changed"<<std::endl;
1504 std::string data_buffer;
1508 // Handle removed objects
1509 writeU16((u8*)buf, removed_objects.size());
1510 data_buffer.append(buf, 2);
1511 for(core::map<u16, bool>::Iterator
1512 i = removed_objects.getIterator();
1513 i.atEnd()==false; i++)
1516 u16 id = i.getNode()->getKey();
1517 ServerActiveObject* obj = m_env->getActiveObject(id);
1519 // Add to data buffer for sending
1520 writeU16((u8*)buf, i.getNode()->getKey());
1521 data_buffer.append(buf, 2);
1523 // Remove from known objects
1524 client->m_known_objects.remove(i.getNode()->getKey());
1526 if(obj && obj->m_known_by_count > 0)
1527 obj->m_known_by_count--;
1530 // Handle added objects
1531 writeU16((u8*)buf, added_objects.size());
1532 data_buffer.append(buf, 2);
1533 for(core::map<u16, bool>::Iterator
1534 i = added_objects.getIterator();
1535 i.atEnd()==false; i++)
1538 u16 id = i.getNode()->getKey();
1539 ServerActiveObject* obj = m_env->getActiveObject(id);
1542 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1544 infostream<<"WARNING: "<<__FUNCTION_NAME
1545 <<": NULL object"<<std::endl;
1547 type = obj->getType();
1549 // Add to data buffer for sending
1550 writeU16((u8*)buf, id);
1551 data_buffer.append(buf, 2);
1552 writeU8((u8*)buf, type);
1553 data_buffer.append(buf, 1);
1556 data_buffer.append(serializeLongString(
1557 obj->getClientInitializationData()));
1559 data_buffer.append(serializeLongString(""));
1561 // Add to known objects
1562 client->m_known_objects.insert(i.getNode()->getKey(), false);
1565 obj->m_known_by_count++;
1569 SharedBuffer<u8> reply(2 + data_buffer.size());
1570 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1571 memcpy((char*)&reply[2], data_buffer.c_str(),
1572 data_buffer.size());
1574 m_con.Send(client->peer_id, 0, reply, true);
1576 infostream<<"Server: Sent object remove/add: "
1577 <<removed_objects.size()<<" removed, "
1578 <<added_objects.size()<<" added, "
1579 <<"packet size is "<<reply.getSize()<<std::endl;
1584 Collect a list of all the objects known by the clients
1585 and report it back to the environment.
1588 core::map<u16, bool> all_known_objects;
1590 for(core::map<u16, RemoteClient*>::Iterator
1591 i = m_clients.getIterator();
1592 i.atEnd() == false; i++)
1594 RemoteClient *client = i.getNode()->getValue();
1595 // Go through all known objects of client
1596 for(core::map<u16, bool>::Iterator
1597 i = client->m_known_objects.getIterator();
1598 i.atEnd()==false; i++)
1600 u16 id = i.getNode()->getKey();
1601 all_known_objects[id] = true;
1605 m_env->setKnownActiveObjects(whatever);
1611 Send object messages
1614 JMutexAutoLock envlock(m_env_mutex);
1615 JMutexAutoLock conlock(m_con_mutex);
1617 //ScopeProfiler sp(g_profiler, "Server: sending object messages");
1620 // Value = data sent by object
1621 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1623 // Get active object messages from environment
1626 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
1630 core::list<ActiveObjectMessage>* message_list = NULL;
1631 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1632 n = buffered_messages.find(aom.id);
1635 message_list = new core::list<ActiveObjectMessage>;
1636 buffered_messages.insert(aom.id, message_list);
1640 message_list = n->getValue();
1642 message_list->push_back(aom);
1645 // Route data to every client
1646 for(core::map<u16, RemoteClient*>::Iterator
1647 i = m_clients.getIterator();
1648 i.atEnd()==false; i++)
1650 RemoteClient *client = i.getNode()->getValue();
1651 std::string reliable_data;
1652 std::string unreliable_data;
1653 // Go through all objects in message buffer
1654 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1655 j = buffered_messages.getIterator();
1656 j.atEnd()==false; j++)
1658 // If object is not known by client, skip it
1659 u16 id = j.getNode()->getKey();
1660 if(client->m_known_objects.find(id) == NULL)
1662 // Get message list of object
1663 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1664 // Go through every message
1665 for(core::list<ActiveObjectMessage>::Iterator
1666 k = list->begin(); k != list->end(); k++)
1668 // Compose the full new data with header
1669 ActiveObjectMessage aom = *k;
1670 std::string new_data;
1673 writeU16((u8*)&buf[0], aom.id);
1674 new_data.append(buf, 2);
1676 new_data += serializeString(aom.datastring);
1677 // Add data to buffer
1679 reliable_data += new_data;
1681 unreliable_data += new_data;
1685 reliable_data and unreliable_data are now ready.
1688 if(reliable_data.size() > 0)
1690 SharedBuffer<u8> reply(2 + reliable_data.size());
1691 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1692 memcpy((char*)&reply[2], reliable_data.c_str(),
1693 reliable_data.size());
1695 m_con.Send(client->peer_id, 0, reply, true);
1697 if(unreliable_data.size() > 0)
1699 SharedBuffer<u8> reply(2 + unreliable_data.size());
1700 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1701 memcpy((char*)&reply[2], unreliable_data.c_str(),
1702 unreliable_data.size());
1703 // Send as unreliable
1704 m_con.Send(client->peer_id, 0, reply, false);
1707 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1709 infostream<<"Server: Size of object message data: "
1710 <<"reliable: "<<reliable_data.size()
1711 <<", unreliable: "<<unreliable_data.size()
1716 // Clear buffered_messages
1717 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1718 i = buffered_messages.getIterator();
1719 i.atEnd()==false; i++)
1721 delete i.getNode()->getValue();
1725 } // enable_experimental
1728 Send queued-for-sending map edit events.
1731 // Don't send too many at a time
1734 // Single change sending is disabled if queue size is not small
1735 bool disable_single_change_sending = false;
1736 if(m_unsent_map_edit_queue.size() >= 4)
1737 disable_single_change_sending = true;
1739 bool got_any_events = false;
1741 // We'll log the amount of each
1744 while(m_unsent_map_edit_queue.size() != 0)
1746 got_any_events = true;
1748 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1750 // Players far away from the change are stored here.
1751 // Instead of sending the changes, MapBlocks are set not sent
1753 core::list<u16> far_players;
1755 if(event->type == MEET_ADDNODE)
1757 //infostream<<"Server: MEET_ADDNODE"<<std::endl;
1758 prof.add("MEET_ADDNODE", 1);
1759 if(disable_single_change_sending)
1760 sendAddNode(event->p, event->n, event->already_known_by_peer,
1763 sendAddNode(event->p, event->n, event->already_known_by_peer,
1766 else if(event->type == MEET_REMOVENODE)
1768 //infostream<<"Server: MEET_REMOVENODE"<<std::endl;
1769 prof.add("MEET_REMOVENODE", 1);
1770 if(disable_single_change_sending)
1771 sendRemoveNode(event->p, event->already_known_by_peer,
1774 sendRemoveNode(event->p, event->already_known_by_peer,
1777 else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
1779 infostream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
1780 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
1781 setBlockNotSent(event->p);
1783 else if(event->type == MEET_OTHER)
1785 infostream<<"Server: MEET_OTHER"<<std::endl;
1786 prof.add("MEET_OTHER", 1);
1787 for(core::map<v3s16, bool>::Iterator
1788 i = event->modified_blocks.getIterator();
1789 i.atEnd()==false; i++)
1791 v3s16 p = i.getNode()->getKey();
1797 prof.add("unknown", 1);
1798 infostream<<"WARNING: Server: Unknown MapEditEvent "
1799 <<((u32)event->type)<<std::endl;
1803 Set blocks not sent to far players
1805 if(far_players.size() > 0)
1807 // Convert list format to that wanted by SetBlocksNotSent
1808 core::map<v3s16, MapBlock*> modified_blocks2;
1809 for(core::map<v3s16, bool>::Iterator
1810 i = event->modified_blocks.getIterator();
1811 i.atEnd()==false; i++)
1813 v3s16 p = i.getNode()->getKey();
1814 modified_blocks2.insert(p,
1815 m_env->getMap().getBlockNoCreateNoEx(p));
1817 // Set blocks not sent
1818 for(core::list<u16>::Iterator
1819 i = far_players.begin();
1820 i != far_players.end(); i++)
1823 RemoteClient *client = getClient(peer_id);
1826 client->SetBlocksNotSent(modified_blocks2);
1832 /*// Don't send too many at a time
1834 if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
1840 infostream<<"Server: MapEditEvents:"<<std::endl;
1841 prof.print(infostream);
1847 Send object positions
1850 float &counter = m_objectdata_timer;
1852 if(counter >= g_settings->getFloat("objectdata_interval"))
1854 JMutexAutoLock lock1(m_env_mutex);
1855 JMutexAutoLock lock2(m_con_mutex);
1857 //ScopeProfiler sp(g_profiler, "Server: sending player positions");
1859 SendObjectData(counter);
1866 Trigger emergethread (it somehow gets to a non-triggered but
1867 bysy state sometimes)
1870 float &counter = m_emergethread_trigger_timer;
1876 m_emergethread.trigger();
1880 // Save map, players and auth stuff
1882 float &counter = m_savemap_timer;
1884 if(counter >= g_settings->getFloat("server_map_save_interval"))
1888 ScopeProfiler sp(g_profiler, "Server: saving stuff");
1891 if(m_authmanager.isModified())
1892 m_authmanager.save();
1895 if(m_banmanager.isModified())
1896 m_banmanager.save();
1899 JMutexAutoLock lock(m_env_mutex);
1901 /*// Unload unused data (delete from memory)
1902 m_env->getMap().unloadUnusedData(
1903 g_settings->getFloat("server_unload_unused_sectors_timeout"));
1905 /*u32 deleted_count = m_env->getMap().unloadUnusedData(
1906 g_settings->getFloat("server_unload_unused_sectors_timeout"));
1909 // Save only changed parts
1910 m_env->getMap().save(true);
1912 /*if(deleted_count > 0)
1914 infostream<<"Server: Unloaded "<<deleted_count
1915 <<" blocks from memory"<<std::endl;
1919 m_env->serializePlayers(m_mapsavedir);
1921 // Save environment metadata
1922 m_env->saveMeta(m_mapsavedir);
1927 void Server::Receive()
1929 DSTACK(__FUNCTION_NAME);
1930 SharedBuffer<u8> data;
1935 JMutexAutoLock conlock(m_con_mutex);
1936 datasize = m_con.Receive(peer_id, data);
1939 // This has to be called so that the client list gets synced
1940 // with the peer list of the connection
1941 handlePeerChanges();
1943 ProcessData(*data, datasize, peer_id);
1945 catch(con::InvalidIncomingDataException &e)
1947 infostream<<"Server::Receive(): "
1948 "InvalidIncomingDataException: what()="
1949 <<e.what()<<std::endl;
1951 catch(con::PeerNotFoundException &e)
1953 //NOTE: This is not needed anymore
1955 // The peer has been disconnected.
1956 // Find the associated player and remove it.
1958 /*JMutexAutoLock envlock(m_env_mutex);
1960 infostream<<"ServerThread: peer_id="<<peer_id
1961 <<" has apparently closed connection. "
1962 <<"Removing player."<<std::endl;
1964 m_env->removePlayer(peer_id);*/
1968 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1970 DSTACK(__FUNCTION_NAME);
1971 // Environment is locked first.
1972 JMutexAutoLock envlock(m_env_mutex);
1973 JMutexAutoLock conlock(m_con_mutex);
1976 Address address = m_con.GetPeerAddress(peer_id);
1978 // drop player if is ip is banned
1979 if(m_banmanager.isIpBanned(address.serializeString())){
1980 SendAccessDenied(m_con, peer_id,
1981 L"Your ip is banned. Banned name was "
1982 +narrow_to_wide(m_banmanager.getBanName(
1983 address.serializeString())));
1984 m_con.DeletePeer(peer_id);
1988 catch(con::PeerNotFoundException &e)
1990 infostream<<"Server::ProcessData(): Cancelling: peer "
1991 <<peer_id<<" not found"<<std::endl;
1995 u8 peer_ser_ver = getClient(peer_id)->serialization_version;
2003 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
2005 if(command == TOSERVER_INIT)
2007 // [0] u16 TOSERVER_INIT
2008 // [2] u8 SER_FMT_VER_HIGHEST
2009 // [3] u8[20] player_name
2010 // [23] u8[28] password <--- can be sent without this, from old versions
2012 if(datasize < 2+1+PLAYERNAME_SIZE)
2015 infostream<<"Server: Got TOSERVER_INIT from "
2016 <<peer_id<<std::endl;
2018 // First byte after command is maximum supported
2019 // serialization version
2020 u8 client_max = data[2];
2021 u8 our_max = SER_FMT_VER_HIGHEST;
2022 // Use the highest version supported by both
2023 u8 deployed = core::min_(client_max, our_max);
2024 // If it's lower than the lowest supported, give up.
2025 if(deployed < SER_FMT_VER_LOWEST)
2026 deployed = SER_FMT_VER_INVALID;
2028 //peer->serialization_version = deployed;
2029 getClient(peer_id)->pending_serialization_version = deployed;
2031 if(deployed == SER_FMT_VER_INVALID)
2033 infostream<<"Server: Cannot negotiate "
2034 "serialization version with peer "
2035 <<peer_id<<std::endl;
2036 SendAccessDenied(m_con, peer_id,
2037 L"Your client is too old (map format)");
2042 Read and check network protocol version
2045 u16 net_proto_version = 0;
2046 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
2048 net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
2051 getClient(peer_id)->net_proto_version = net_proto_version;
2053 if(net_proto_version == 0)
2055 SendAccessDenied(m_con, peer_id,
2056 L"Your client is too old. Please upgrade.");
2060 /* Uhh... this should actually be a warning but let's do it like this */
2061 if(g_settings->getBool("strict_protocol_version_checking"))
2063 if(net_proto_version < PROTOCOL_VERSION)
2065 SendAccessDenied(m_con, peer_id,
2066 L"Your client is too old. Please upgrade.");
2076 char playername[PLAYERNAME_SIZE];
2077 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
2079 playername[i] = data[3+i];
2081 playername[PLAYERNAME_SIZE-1] = 0;
2083 if(playername[0]=='\0')
2085 infostream<<"Server: Player has empty name"<<std::endl;
2086 SendAccessDenied(m_con, peer_id,
2091 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
2093 infostream<<"Server: Player has invalid name"<<std::endl;
2094 SendAccessDenied(m_con, peer_id,
2095 L"Name contains unallowed characters");
2100 char password[PASSWORD_SIZE];
2101 if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
2103 // old version - assume blank password
2108 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2110 password[i] = data[23+i];
2112 password[PASSWORD_SIZE-1] = 0;
2115 std::string checkpwd;
2116 if(m_authmanager.exists(playername))
2118 checkpwd = m_authmanager.getPassword(playername);
2122 checkpwd = g_settings->get("default_password");
2125 /*infostream<<"Server: Client gave password '"<<password
2126 <<"', the correct one is '"<<checkpwd<<"'"<<std::endl;*/
2128 if(password != checkpwd && m_authmanager.exists(playername))
2130 infostream<<"Server: peer_id="<<peer_id
2131 <<": supplied invalid password for "
2132 <<playername<<std::endl;
2133 SendAccessDenied(m_con, peer_id, L"Invalid password");
2137 // Add player to auth manager
2138 if(m_authmanager.exists(playername) == false)
2140 infostream<<"Server: adding player "<<playername
2141 <<" to auth manager"<<std::endl;
2142 m_authmanager.add(playername);
2143 m_authmanager.setPassword(playername, checkpwd);
2144 m_authmanager.setPrivs(playername,
2145 stringToPrivs(g_settings->get("default_privs")));
2146 m_authmanager.save();
2149 // Enforce user limit.
2150 // Don't enforce for users that have some admin right
2151 if(m_clients.size() >= g_settings->getU16("max_users") &&
2152 (m_authmanager.getPrivs(playername)
2153 & (PRIV_SERVER|PRIV_BAN|PRIV_PRIVS)) == 0 &&
2154 playername != g_settings->get("name"))
2156 SendAccessDenied(m_con, peer_id, L"Too many users.");
2161 Player *player = emergePlayer(playername, password, peer_id);
2163 // If failed, cancel
2166 infostream<<"Server: peer_id="<<peer_id
2167 <<": failed to emerge player"<<std::endl;
2172 Answer with a TOCLIENT_INIT
2175 SharedBuffer<u8> reply(2+1+6+8);
2176 writeU16(&reply[0], TOCLIENT_INIT);
2177 writeU8(&reply[2], deployed);
2178 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
2179 writeU64(&reply[2+1+6], m_env->getServerMap().getSeed());
2182 m_con.Send(peer_id, 0, reply, true);
2186 Send complete position information
2188 SendMovePlayer(player);
2193 if(command == TOSERVER_INIT2)
2195 infostream<<"Server: Got TOSERVER_INIT2 from "
2196 <<peer_id<<std::endl;
2199 getClient(peer_id)->serialization_version
2200 = getClient(peer_id)->pending_serialization_version;
2203 Send some initialization data
2206 // Send tool definitions
2207 SendToolDef(m_con, peer_id, m_toolmgr);
2209 // Send node definitions
2210 SendNodeDef(m_con, peer_id, m_nodedef);
2213 SendTextures(peer_id);
2215 // Send player info to all players
2218 // Send inventory to player
2219 UpdateCrafting(peer_id);
2220 SendInventory(peer_id);
2222 // Send player items to all players
2225 Player *player = m_env->getPlayer(peer_id);
2228 SendPlayerHP(player);
2232 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2233 m_env->getTimeOfDay());
2234 m_con.Send(peer_id, 0, data, true);
2237 // Send information about server to player in chat
2238 SendChatMessage(peer_id, getStatusString());
2240 // Send information about joining in chat
2242 std::wstring name = L"unknown";
2243 Player *player = m_env->getPlayer(peer_id);
2245 name = narrow_to_wide(player->getName());
2247 std::wstring message;
2250 message += L" joined game";
2251 BroadcastChatMessage(message);
2254 // Warnings about protocol version can be issued here
2255 if(getClient(peer_id)->net_proto_version < PROTOCOL_VERSION)
2257 SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT IS OLD AND MAY WORK PROPERLY WITH THIS SERVER");
2261 Check HP, respawn if necessary
2263 HandlePlayerHP(player, 0);
2269 std::ostringstream os(std::ios_base::binary);
2270 for(core::map<u16, RemoteClient*>::Iterator
2271 i = m_clients.getIterator();
2272 i.atEnd() == false; i++)
2274 RemoteClient *client = i.getNode()->getValue();
2275 assert(client->peer_id == i.getNode()->getKey());
2276 if(client->serialization_version == SER_FMT_VER_INVALID)
2279 Player *player = m_env->getPlayer(client->peer_id);
2282 // Get name of player
2283 os<<player->getName()<<" ";
2286 actionstream<<player->getName()<<" joins game. List of players: "
2287 <<os.str()<<std::endl;
2293 if(peer_ser_ver == SER_FMT_VER_INVALID)
2295 infostream<<"Server::ProcessData(): Cancelling: Peer"
2296 " serialization format invalid or not initialized."
2297 " Skipping incoming command="<<command<<std::endl;
2301 Player *player = m_env->getPlayer(peer_id);
2304 infostream<<"Server::ProcessData(): Cancelling: "
2305 "No player for peer_id="<<peer_id
2309 if(command == TOSERVER_PLAYERPOS)
2311 if(datasize < 2+12+12+4+4)
2315 v3s32 ps = readV3S32(&data[start+2]);
2316 v3s32 ss = readV3S32(&data[start+2+12]);
2317 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2318 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2319 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2320 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2321 pitch = wrapDegrees(pitch);
2322 yaw = wrapDegrees(yaw);
2324 player->setPosition(position);
2325 player->setSpeed(speed);
2326 player->setPitch(pitch);
2327 player->setYaw(yaw);
2329 /*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2330 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2331 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2333 else if(command == TOSERVER_GOTBLOCKS)
2346 u16 count = data[2];
2347 for(u16 i=0; i<count; i++)
2349 if((s16)datasize < 2+1+(i+1)*6)
2350 throw con::InvalidIncomingDataException
2351 ("GOTBLOCKS length is too short");
2352 v3s16 p = readV3S16(&data[2+1+i*6]);
2353 /*infostream<<"Server: GOTBLOCKS ("
2354 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2355 RemoteClient *client = getClient(peer_id);
2356 client->GotBlock(p);
2359 else if(command == TOSERVER_DELETEDBLOCKS)
2372 u16 count = data[2];
2373 for(u16 i=0; i<count; i++)
2375 if((s16)datasize < 2+1+(i+1)*6)
2376 throw con::InvalidIncomingDataException
2377 ("DELETEDBLOCKS length is too short");
2378 v3s16 p = readV3S16(&data[2+1+i*6]);
2379 /*infostream<<"Server: DELETEDBLOCKS ("
2380 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2381 RemoteClient *client = getClient(peer_id);
2382 client->SetBlockNotSent(p);
2385 else if(command == TOSERVER_CLICK_OBJECT)
2387 infostream<<"Server: CLICK_OBJECT not supported anymore"<<std::endl;
2390 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2395 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2401 [2] u8 button (0=left, 1=right)
2405 u8 button = readU8(&data[2]);
2406 u16 id = readS16(&data[3]);
2407 u16 item_i = readU16(&data[5]);
2409 ServerActiveObject *obj = m_env->getActiveObject(id);
2413 infostream<<"Server: CLICK_ACTIVEOBJECT: object not found"
2418 // Skip if object has been removed
2422 //TODO: Check that object is reasonably close
2424 // Get ServerRemotePlayer
2425 ServerRemotePlayer *srp = (ServerRemotePlayer*)player;
2427 // Update wielded item
2428 srp->wieldItem(item_i);
2430 // Left click, pick/punch
2433 actionstream<<player->getName()<<" punches object "
2434 <<obj->getId()<<std::endl;
2441 Try creating inventory item
2443 InventoryItem *item = obj->createPickedUpItem();
2447 InventoryList *ilist = player->inventory.getList("main");
2450 actionstream<<player->getName()<<" picked up "
2451 <<item->getName()<<std::endl;
2452 if(g_settings->getBool("creative_mode") == false)
2454 // Skip if inventory has no free space
2455 if(ilist->roomForItem(item) == false)
2457 infostream<<"Player inventory has no free space"<<std::endl;
2461 // Add to inventory and send inventory
2462 ilist->addItem(item);
2463 UpdateCrafting(player->peer_id);
2464 SendInventory(player->peer_id);
2467 // Remove object from environment
2468 obj->m_removed = true;
2474 Item cannot be picked up. Punch it instead.
2477 actionstream<<player->getName()<<" punches object "
2478 <<obj->getId()<<std::endl;
2480 ToolItem *titem = NULL;
2481 std::string toolname = "";
2483 InventoryList *mlist = player->inventory.getList("main");
2486 InventoryItem *item = mlist->getItem(item_i);
2487 if(item && (std::string)item->getName() == "ToolItem")
2489 titem = (ToolItem*)item;
2490 toolname = titem->getToolName();
2494 v3f playerpos = player->getPosition();
2495 v3f objpos = obj->getBasePosition();
2496 v3f dir = (objpos - playerpos).normalize();
2498 u16 wear = obj->punch(toolname, dir, player->getName());
2502 bool weared_out = titem->addWear(wear);
2504 mlist->deleteItem(item_i);
2505 SendInventory(player->peer_id);
2510 // Right click, do something with object
2513 actionstream<<player->getName()<<" right clicks object "
2514 <<obj->getId()<<std::endl;
2517 obj->rightClick(srp);
2521 Update player state to client
2523 SendPlayerHP(player);
2524 UpdateCrafting(player->peer_id);
2525 SendInventory(player->peer_id);
2527 else if(command == TOSERVER_GROUND_ACTION)
2535 [3] v3s16 nodepos_undersurface
2536 [9] v3s16 nodepos_abovesurface
2541 2: stop digging (all parameters ignored)
2542 3: digging completed
2544 u8 action = readU8(&data[2]);
2546 p_under.X = readS16(&data[3]);
2547 p_under.Y = readS16(&data[5]);
2548 p_under.Z = readS16(&data[7]);
2550 p_over.X = readS16(&data[9]);
2551 p_over.Y = readS16(&data[11]);
2552 p_over.Z = readS16(&data[13]);
2553 u16 item_i = readU16(&data[15]);
2555 ServerRemotePlayer *srp = (ServerRemotePlayer*)player;
2558 Check that target is reasonably close
2561 v3f np_f = intToFloat(p_under, BS);
2562 float max_d = BS * 8; // Just some large enough value
2563 float d = srp->m_last_good_position.getDistanceFrom(np_f);
2565 actionstream<<"Player "<<player->getName()
2566 <<" tried to access node from too far: "
2567 <<"d="<<d<<", max_d="<<max_d
2568 <<". ignoring."<<std::endl;
2569 // Re-send block to revert change on client-side
2570 RemoteClient *client = getClient(peer_id);
2571 v3s16 blockpos = getNodeBlockPos(p_under);
2572 client->SetBlockNotSent(blockpos);
2584 NOTE: This can be used in the future to check if
2585 somebody is cheating, by checking the timing.
2587 bool cannot_punch_node = false;
2589 MapNode n(CONTENT_IGNORE);
2593 n = m_env->getMap().getNode(p_under);
2595 catch(InvalidPositionException &e)
2597 infostream<<"Server: Not punching: Node not found."
2598 <<" Adding block to emerge queue."
2600 m_emerge_queue.addBlock(peer_id,
2601 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2602 cannot_punch_node = true;
2605 if(cannot_punch_node)
2611 scriptapi_environment_on_punchnode(m_lua, p_under, n, srp);
2618 else if(action == 2)
2621 RemoteClient *client = getClient(peer_id);
2622 JMutexAutoLock digmutex(client->m_dig_mutex);
2623 client->m_dig_tool_item = -1;
2628 3: Digging completed
2630 else if(action == 3)
2632 // Mandatory parameter; actually used for nothing
2633 core::map<v3s16, MapBlock*> modified_blocks;
2635 content_t material = CONTENT_IGNORE;
2636 u8 mineral = MINERAL_NONE;
2638 bool cannot_remove_node = false;
2640 MapNode n(CONTENT_IGNORE);
2643 n = m_env->getMap().getNode(p_under);
2645 mineral = n.getMineral(m_nodedef);
2646 // Get material at position
2647 material = n.getContent();
2648 // If not yet cancelled
2649 if(cannot_remove_node == false)
2651 // If it's not diggable, do nothing
2652 if(m_nodedef->get(material).diggable == false)
2654 infostream<<"Server: Not finishing digging: "
2655 <<"Node not diggable"
2657 cannot_remove_node = true;
2660 // If not yet cancelled
2661 if(cannot_remove_node == false)
2663 // Get node metadata
2664 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p_under);
2665 if(meta && meta->nodeRemovalDisabled() == true)
2667 infostream<<"Server: Not finishing digging: "
2668 <<"Node metadata disables removal"
2670 cannot_remove_node = true;
2674 catch(InvalidPositionException &e)
2676 infostream<<"Server: Not finishing digging: Node not found."
2677 <<" Adding block to emerge queue."
2679 m_emerge_queue.addBlock(peer_id,
2680 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2681 cannot_remove_node = true;
2684 // Make sure the player is allowed to do it
2685 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2687 infostream<<"Player "<<player->getName()<<" cannot remove node"
2688 <<" because privileges are "<<getPlayerPrivs(player)
2690 cannot_remove_node = true;
2694 If node can't be removed, set block to be re-sent to
2697 if(cannot_remove_node)
2699 infostream<<"Server: Not finishing digging."<<std::endl;
2701 // Client probably has wrong data.
2702 // Set block not sent, so that client will get
2704 infostream<<"Client "<<peer_id<<" tried to dig "
2705 <<"node; but node cannot be removed."
2706 <<" setting MapBlock not sent."<<std::endl;
2707 RemoteClient *client = getClient(peer_id);
2708 v3s16 blockpos = getNodeBlockPos(p_under);
2709 client->SetBlockNotSent(blockpos);
2714 actionstream<<player->getName()<<" digs "<<PP(p_under)
2715 <<", gets material "<<(int)material<<", mineral "
2716 <<(int)mineral<<std::endl;
2719 Send the removal to all close-by players.
2720 - If other player is close, send REMOVENODE
2721 - Otherwise set blocks not sent
2723 core::list<u16> far_players;
2724 sendRemoveNode(p_under, peer_id, &far_players, 30);
2727 Update and send inventory
2730 if(g_settings->getBool("creative_mode") == false)
2735 InventoryList *mlist = player->inventory.getList("main");
2738 InventoryItem *item = mlist->getItem(item_i);
2739 if(item && (std::string)item->getName() == "ToolItem")
2741 ToolItem *titem = (ToolItem*)item;
2742 std::string toolname = titem->getToolName();
2744 // Get digging properties for material and tool
2745 ToolDiggingProperties tp =
2746 m_toolmgr->getDiggingProperties(toolname);
2747 DiggingProperties prop =
2748 getDiggingProperties(material, &tp, m_nodedef);
2750 if(prop.diggable == false)
2752 infostream<<"Server: WARNING: Player digged"
2753 <<" with impossible material + tool"
2754 <<" combination"<<std::endl;
2757 bool weared_out = titem->addWear(prop.wear);
2761 mlist->deleteItem(item_i);
2767 Add dug item to inventory
2770 InventoryItem *item = NULL;
2772 if(mineral != MINERAL_NONE)
2773 item = getDiggedMineralItem(mineral, this);
2778 const std::string &dug_s = m_nodedef->get(material).dug_item;
2781 std::istringstream is(dug_s, std::ios::binary);
2782 item = InventoryItem::deSerialize(is, this);
2788 // Add a item to inventory
2789 player->inventory.addItem("main", item);
2792 UpdateCrafting(player->peer_id);
2793 SendInventory(player->peer_id);
2798 if(mineral != MINERAL_NONE)
2799 item = getDiggedMineralItem(mineral, this);
2804 const std::string &extra_dug_s = m_nodedef->get(material).extra_dug_item;
2805 s32 extra_rarity = m_nodedef->get(material).extra_dug_item_rarity;
2806 if(extra_dug_s != "" && extra_rarity != 0
2807 && myrand() % extra_rarity == 0)
2809 std::istringstream is(extra_dug_s, std::ios::binary);
2810 item = InventoryItem::deSerialize(is, this);
2816 // Add a item to inventory
2817 player->inventory.addItem("main", item);
2820 UpdateCrafting(player->peer_id);
2821 SendInventory(player->peer_id);
2827 (this takes some time so it is done after the quick stuff)
2830 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
2832 m_env->getMap().removeNodeAndUpdate(p_under, modified_blocks);
2835 Set blocks not sent to far players
2837 for(core::list<u16>::Iterator
2838 i = far_players.begin();
2839 i != far_players.end(); i++)
2842 RemoteClient *client = getClient(peer_id);
2845 client->SetBlocksNotSent(modified_blocks);
2851 scriptapi_environment_on_dignode(m_lua, p_under, n, srp);
2857 else if(action == 1)
2860 InventoryList *ilist = player->inventory.getList("main");
2865 InventoryItem *item = ilist->getItem(item_i);
2867 // If there is no item, it is not possible to add it anywhere
2872 Handle material items
2874 if(std::string("MaterialItem") == item->getName())
2877 // Don't add a node if this is not a free space
2878 MapNode n2 = m_env->getMap().getNode(p_over);
2879 bool no_enough_privs =
2880 ((getPlayerPrivs(player) & PRIV_BUILD)==0);
2882 infostream<<"Player "<<player->getName()<<" cannot add node"
2883 <<" because privileges are "<<getPlayerPrivs(player)
2886 if(m_nodedef->get(n2).buildable_to == false
2889 // Client probably has wrong data.
2890 // Set block not sent, so that client will get
2892 infostream<<"Client "<<peer_id<<" tried to place"
2893 <<" node in invalid position; setting"
2894 <<" MapBlock not sent."<<std::endl;
2895 RemoteClient *client = getClient(peer_id);
2896 v3s16 blockpos = getNodeBlockPos(p_over);
2897 client->SetBlockNotSent(blockpos);
2901 catch(InvalidPositionException &e)
2903 infostream<<"Server: Ignoring ADDNODE: Node not found"
2904 <<" Adding block to emerge queue."
2906 m_emerge_queue.addBlock(peer_id,
2907 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2911 // Reset build time counter
2912 getClient(peer_id)->m_time_from_building = 0.0;
2915 MaterialItem *mitem = (MaterialItem*)item;
2917 n.setContent(mitem->getMaterial());
2919 actionstream<<player->getName()<<" places material "
2920 <<(int)mitem->getMaterial()
2921 <<" at "<<PP(p_under)<<std::endl;
2923 // Calculate direction for wall mounted stuff
2924 if(m_nodedef->get(n).wall_mounted)
2925 n.param2 = packDir(p_under - p_over);
2927 // Calculate the direction for furnaces and chests and stuff
2928 if(m_nodedef->get(n).param_type == CPT_FACEDIR_SIMPLE)
2930 v3f playerpos = player->getPosition();
2931 v3f blockpos = intToFloat(p_over, BS) - playerpos;
2932 blockpos = blockpos.normalize();
2934 if (fabs(blockpos.X) > fabs(blockpos.Z)) {
2948 Send to all close-by players
2950 core::list<u16> far_players;
2951 sendAddNode(p_over, n, 0, &far_players, 30);
2956 InventoryList *ilist = player->inventory.getList("main");
2957 if(g_settings->getBool("creative_mode") == false && ilist)
2959 // Remove from inventory and send inventory
2960 if(mitem->getCount() == 1)
2961 ilist->deleteItem(item_i);
2965 UpdateCrafting(peer_id);
2966 SendInventory(peer_id);
2972 This takes some time so it is done after the quick stuff
2974 core::map<v3s16, MapBlock*> modified_blocks;
2976 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
2978 std::string p_name = std::string(player->getName());
2979 m_env->getMap().addNodeAndUpdate(p_over, n, modified_blocks, p_name);
2982 Set blocks not sent to far players
2984 for(core::list<u16>::Iterator
2985 i = far_players.begin();
2986 i != far_players.end(); i++)
2989 RemoteClient *client = getClient(peer_id);
2992 client->SetBlocksNotSent(modified_blocks);
2998 scriptapi_environment_on_placenode(m_lua, p_over, n, srp);
3001 Calculate special events
3004 /*if(n.d == LEGN(m_nodedef, "CONTENT_MESE"))
3007 for(s16 z=-1; z<=1; z++)
3008 for(s16 y=-1; y<=1; y++)
3009 for(s16 x=-1; x<=1; x++)
3016 Place other item (not a block)
3020 v3s16 blockpos = getNodeBlockPos(p_over);
3023 Check that the block is loaded so that the item
3024 can properly be added to the static list too
3026 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3029 infostream<<"Error while placing object: "
3030 "block not found"<<std::endl;
3035 If in creative mode, item dropping is disabled unless
3036 player has build privileges
3038 if(g_settings->getBool("creative_mode") &&
3039 (getPlayerPrivs(player) & PRIV_BUILD) == 0)
3041 infostream<<"Not allowing player to drop item: "
3042 "creative mode and no build privs"<<std::endl;
3046 // Calculate a position for it
3047 v3f pos = intToFloat(p_over, BS);
3049 /*pos.Y -= BS*0.25; // let it drop a bit
3051 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
3052 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;*/
3057 ServerActiveObject *obj = item->createSAO(m_env, 0, pos);
3061 infostream<<"WARNING: item resulted in NULL object, "
3062 <<"not placing onto map"
3067 actionstream<<player->getName()<<" places "<<item->getName()
3068 <<" at "<<PP(p_over)<<std::endl;
3070 // Add the object to the environment
3071 m_env->addActiveObject(obj);
3073 infostream<<"Placed object"<<std::endl;
3075 if(g_settings->getBool("creative_mode") == false)
3077 // Delete the right amount of items from the slot
3078 u16 dropcount = item->getDropCount();
3080 // Delete item if all gone
3081 if(item->getCount() <= dropcount)
3083 if(item->getCount() < dropcount)
3084 infostream<<"WARNING: Server: dropped more items"
3085 <<" than the slot contains"<<std::endl;
3087 InventoryList *ilist = player->inventory.getList("main");
3089 // Remove from inventory and send inventory
3090 ilist->deleteItem(item_i);
3092 // Else decrement it
3094 item->remove(dropcount);
3097 UpdateCrafting(peer_id);
3098 SendInventory(peer_id);
3106 Catch invalid actions
3110 infostream<<"WARNING: Server: Invalid action "
3111 <<action<<std::endl;
3115 else if(command == TOSERVER_RELEASE)
3124 infostream<<"TOSERVER_RELEASE ignored"<<std::endl;
3127 else if(command == TOSERVER_SIGNTEXT)
3129 infostream<<"Server: TOSERVER_SIGNTEXT not supported anymore"
3133 else if(command == TOSERVER_SIGNNODETEXT)
3135 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
3143 std::string datastring((char*)&data[2], datasize-2);
3144 std::istringstream is(datastring, std::ios_base::binary);
3147 is.read((char*)buf, 6);
3148 v3s16 p = readV3S16(buf);
3149 is.read((char*)buf, 2);
3150 u16 textlen = readU16(buf);
3152 for(u16 i=0; i<textlen; i++)
3154 is.read((char*)buf, 1);
3155 text += (char)buf[0];
3158 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3161 if(meta->typeId() != LEGN(m_nodedef, "CONTENT_SIGN_WALL"))
3163 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
3164 signmeta->setText(text);
3166 actionstream<<player->getName()<<" writes \""<<text<<"\" to sign "
3167 <<" at "<<PP(p)<<std::endl;
3169 v3s16 blockpos = getNodeBlockPos(p);
3170 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3173 block->raiseModified(MOD_STATE_WRITE_NEEDED,
3177 setBlockNotSent(blockpos);
3179 else if(command == TOSERVER_INVENTORY_ACTION)
3181 /*// Ignore inventory changes if in creative mode
3182 if(g_settings->getBool("creative_mode") == true)
3184 infostream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
3188 // Strip command and create a stream
3189 std::string datastring((char*)&data[2], datasize-2);
3190 infostream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
3191 std::istringstream is(datastring, std::ios_base::binary);
3193 InventoryAction *a = InventoryAction::deSerialize(is);
3198 c.current_player = player;
3201 Handle craftresult specially if not in creative mode
3203 bool disable_action = false;
3204 if(a->getType() == IACTION_MOVE
3205 && g_settings->getBool("creative_mode") == false)
3207 IMoveAction *ma = (IMoveAction*)a;
3208 if(ma->to_inv == "current_player" &&
3209 ma->from_inv == "current_player")
3211 InventoryList *rlist = player->inventory.getList("craftresult");
3213 InventoryList *clist = player->inventory.getList("craft");
3215 InventoryList *mlist = player->inventory.getList("main");
3218 Craftresult is no longer preview if something
3221 if(ma->to_list == "craftresult"
3222 && ma->from_list != "craftresult")
3224 // If it currently is a preview, remove
3226 if(player->craftresult_is_preview)
3228 rlist->deleteItem(0);
3230 player->craftresult_is_preview = false;
3233 Crafting takes place if this condition is true.
3235 if(player->craftresult_is_preview &&
3236 ma->from_list == "craftresult")
3238 player->craftresult_is_preview = false;
3239 clist->decrementMaterials(1);
3241 /* Print out action */
3242 InventoryList *list =
3243 player->inventory.getList("craftresult");
3245 InventoryItem *item = list->getItem(0);
3246 std::string itemname = "NULL";
3248 itemname = item->getName();
3249 actionstream<<player->getName()<<" crafts "
3250 <<itemname<<std::endl;
3253 If the craftresult is placed on itself, move it to
3254 main inventory instead of doing the action
3256 if(ma->to_list == "craftresult"
3257 && ma->from_list == "craftresult")
3259 disable_action = true;
3261 InventoryItem *item1 = rlist->changeItem(0, NULL);
3262 mlist->addItem(item1);
3265 // Disallow moving items if not allowed to build
3266 else if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
3270 // if it's a locking chest, only allow the owner or server admins to move items
3271 else if (ma->from_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
3273 Strfnd fn(ma->from_inv);
3274 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 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3282 if(meta && meta->typeId() == LEGN(m_nodedef, "CONTENT_LOCKABLE_CHEST")) {
3283 LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
3284 if (lcm->getOwner() != player->getName())
3289 else if (ma->to_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
3291 Strfnd fn(ma->to_inv);
3292 std::string id0 = fn.next(":");
3293 if(id0 == "nodemeta")
3296 p.X = stoi(fn.next(","));
3297 p.Y = stoi(fn.next(","));
3298 p.Z = stoi(fn.next(","));
3299 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3300 if(meta && meta->typeId() == LEGN(m_nodedef, "CONTENT_LOCKABLE_CHEST")) {
3301 LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
3302 if (lcm->getOwner() != player->getName())
3309 if(disable_action == false)
3311 // Feed action to player inventory
3319 UpdateCrafting(player->peer_id);
3320 SendInventory(player->peer_id);
3325 infostream<<"TOSERVER_INVENTORY_ACTION: "
3326 <<"InventoryAction::deSerialize() returned NULL"
3330 else if(command == TOSERVER_CHAT_MESSAGE)
3338 std::string datastring((char*)&data[2], datasize-2);
3339 std::istringstream is(datastring, std::ios_base::binary);
3342 is.read((char*)buf, 2);
3343 u16 len = readU16(buf);
3345 std::wstring message;
3346 for(u16 i=0; i<len; i++)
3348 is.read((char*)buf, 2);
3349 message += (wchar_t)readU16(buf);
3352 // Get player name of this client
3353 std::wstring name = narrow_to_wide(player->getName());
3355 // Line to send to players
3357 // Whether to send to the player that sent the line
3358 bool send_to_sender = false;
3359 // Whether to send to other players
3360 bool send_to_others = false;
3362 // Local player gets all privileges regardless of
3363 // what's set on their account.
3364 u64 privs = getPlayerPrivs(player);
3367 if(message[0] == L'/')
3369 size_t strip_size = 1;
3370 if (message[1] == L'#') // support old-style commans
3372 message = message.substr(strip_size);
3374 WStrfnd f1(message);
3375 f1.next(L" "); // Skip over /#whatever
3376 std::wstring paramstring = f1.next(L"");
3378 ServerCommandContext *ctx = new ServerCommandContext(
3379 str_split(message, L' '),
3386 std::wstring reply(processServerCommand(ctx));
3387 send_to_sender = ctx->flags & SEND_TO_SENDER;
3388 send_to_others = ctx->flags & SEND_TO_OTHERS;
3390 if (ctx->flags & SEND_NO_PREFIX)
3393 line += L"Server: " + reply;
3400 if(privs & PRIV_SHOUT)
3406 send_to_others = true;
3410 line += L"Server: You are not allowed to shout";
3411 send_to_sender = true;
3418 actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
3421 Send the message to clients
3423 for(core::map<u16, RemoteClient*>::Iterator
3424 i = m_clients.getIterator();
3425 i.atEnd() == false; i++)
3427 // Get client and check that it is valid
3428 RemoteClient *client = i.getNode()->getValue();
3429 assert(client->peer_id == i.getNode()->getKey());
3430 if(client->serialization_version == SER_FMT_VER_INVALID)
3434 bool sender_selected = (peer_id == client->peer_id);
3435 if(sender_selected == true && send_to_sender == false)
3437 if(sender_selected == false && send_to_others == false)
3440 SendChatMessage(client->peer_id, line);
3444 else if(command == TOSERVER_DAMAGE)
3446 std::string datastring((char*)&data[2], datasize-2);
3447 std::istringstream is(datastring, std::ios_base::binary);
3448 u8 damage = readU8(is);
3450 if(g_settings->getBool("enable_damage"))
3452 actionstream<<player->getName()<<" damaged by "
3453 <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
3456 HandlePlayerHP(player, damage);
3460 SendPlayerHP(player);
3463 else if(command == TOSERVER_PASSWORD)
3466 [0] u16 TOSERVER_PASSWORD
3467 [2] u8[28] old password
3468 [30] u8[28] new password
3471 if(datasize != 2+PASSWORD_SIZE*2)
3473 /*char password[PASSWORD_SIZE];
3474 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3475 password[i] = data[2+i];
3476 password[PASSWORD_SIZE-1] = 0;*/
3478 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3486 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3488 char c = data[2+PASSWORD_SIZE+i];
3494 infostream<<"Server: Client requests a password change from "
3495 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
3497 std::string playername = player->getName();
3499 if(m_authmanager.exists(playername) == false)
3501 infostream<<"Server: playername not found in authmanager"<<std::endl;
3502 // Wrong old password supplied!!
3503 SendChatMessage(peer_id, L"playername not found in authmanager");
3507 std::string checkpwd = m_authmanager.getPassword(playername);
3509 if(oldpwd != checkpwd)
3511 infostream<<"Server: invalid old password"<<std::endl;
3512 // Wrong old password supplied!!
3513 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
3517 actionstream<<player->getName()<<" changes password"<<std::endl;
3519 m_authmanager.setPassword(playername, newpwd);
3521 infostream<<"Server: password change successful for "<<playername
3523 SendChatMessage(peer_id, L"Password change successful");
3525 else if(command == TOSERVER_PLAYERITEM)
3530 u16 item = readU16(&data[2]);
3531 player->wieldItem(item);
3532 SendWieldedItem(player);
3534 else if(command == TOSERVER_RESPAWN)
3539 RespawnPlayer(player);
3541 actionstream<<player->getName()<<" respawns at "
3542 <<PP(player->getPosition()/BS)<<std::endl;
3546 infostream<<"Server::ProcessData(): Ignoring "
3547 "unknown command "<<command<<std::endl;
3551 catch(SendFailedException &e)
3553 errorstream<<"Server::ProcessData(): SendFailedException: "
3559 void Server::onMapEditEvent(MapEditEvent *event)
3561 //infostream<<"Server::onMapEditEvent()"<<std::endl;
3562 if(m_ignore_map_edit_events)
3564 MapEditEvent *e = event->clone();
3565 m_unsent_map_edit_queue.push_back(e);
3568 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3570 if(id == "current_player")
3572 assert(c->current_player);
3573 return &(c->current_player->inventory);
3577 std::string id0 = fn.next(":");
3579 if(id0 == "nodemeta")
3582 p.X = stoi(fn.next(","));
3583 p.Y = stoi(fn.next(","));
3584 p.Z = stoi(fn.next(","));
3585 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3587 return meta->getInventory();
3588 infostream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3589 <<"no metadata found"<<std::endl;
3593 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3596 void Server::inventoryModified(InventoryContext *c, std::string id)
3598 if(id == "current_player")
3600 assert(c->current_player);
3602 UpdateCrafting(c->current_player->peer_id);
3603 SendInventory(c->current_player->peer_id);
3608 std::string id0 = fn.next(":");
3610 if(id0 == "nodemeta")
3613 p.X = stoi(fn.next(","));
3614 p.Y = stoi(fn.next(","));
3615 p.Z = stoi(fn.next(","));
3616 v3s16 blockpos = getNodeBlockPos(p);
3618 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3620 meta->inventoryModified();
3622 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3624 block->raiseModified(MOD_STATE_WRITE_NEEDED);
3626 setBlockNotSent(blockpos);
3631 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3634 core::list<PlayerInfo> Server::getPlayerInfo()
3636 DSTACK(__FUNCTION_NAME);
3637 JMutexAutoLock envlock(m_env_mutex);
3638 JMutexAutoLock conlock(m_con_mutex);
3640 core::list<PlayerInfo> list;
3642 core::list<Player*> players = m_env->getPlayers();
3644 core::list<Player*>::Iterator i;
3645 for(i = players.begin();
3646 i != players.end(); i++)
3650 Player *player = *i;
3653 // Copy info from connection to info struct
3654 info.id = player->peer_id;
3655 info.address = m_con.GetPeerAddress(player->peer_id);
3656 info.avg_rtt = m_con.GetPeerAvgRTT(player->peer_id);
3658 catch(con::PeerNotFoundException &e)
3660 // Set dummy peer info
3662 info.address = Address(0,0,0,0,0);
3666 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3667 info.position = player->getPosition();
3669 list.push_back(info);
3676 void Server::peerAdded(con::Peer *peer)
3678 DSTACK(__FUNCTION_NAME);
3679 infostream<<"Server::peerAdded(): peer->id="
3680 <<peer->id<<std::endl;
3683 c.type = PEER_ADDED;
3684 c.peer_id = peer->id;
3686 m_peer_change_queue.push_back(c);
3689 void Server::deletingPeer(con::Peer *peer, bool timeout)
3691 DSTACK(__FUNCTION_NAME);
3692 infostream<<"Server::deletingPeer(): peer->id="
3693 <<peer->id<<", timeout="<<timeout<<std::endl;
3696 c.type = PEER_REMOVED;
3697 c.peer_id = peer->id;
3698 c.timeout = timeout;
3699 m_peer_change_queue.push_back(c);
3706 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3708 DSTACK(__FUNCTION_NAME);
3709 std::ostringstream os(std::ios_base::binary);
3711 writeU16(os, TOCLIENT_HP);
3715 std::string s = os.str();
3716 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3718 con.Send(peer_id, 0, data, true);
3721 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3722 const std::wstring &reason)
3724 DSTACK(__FUNCTION_NAME);
3725 std::ostringstream os(std::ios_base::binary);
3727 writeU16(os, TOCLIENT_ACCESS_DENIED);
3728 os<<serializeWideString(reason);
3731 std::string s = os.str();
3732 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3734 con.Send(peer_id, 0, data, true);
3737 void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
3738 bool set_camera_point_target, v3f camera_point_target)
3740 DSTACK(__FUNCTION_NAME);
3741 std::ostringstream os(std::ios_base::binary);
3743 writeU16(os, TOCLIENT_DEATHSCREEN);
3744 writeU8(os, set_camera_point_target);
3745 writeV3F1000(os, camera_point_target);
3748 std::string s = os.str();
3749 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3751 con.Send(peer_id, 0, data, true);
3754 void Server::SendToolDef(con::Connection &con, u16 peer_id,
3755 IToolDefManager *tooldef)
3757 DSTACK(__FUNCTION_NAME);
3758 std::ostringstream os(std::ios_base::binary);
3762 u32 length of the next item
3763 serialized ToolDefManager
3765 writeU16(os, TOCLIENT_TOOLDEF);
3766 std::ostringstream tmp_os(std::ios::binary);
3767 tooldef->serialize(tmp_os);
3768 os<<serializeLongString(tmp_os.str());
3771 std::string s = os.str();
3772 infostream<<"Server::SendToolDef(): Sending tool definitions: size="
3773 <<s.size()<<std::endl;
3774 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3776 con.Send(peer_id, 0, data, true);
3779 void Server::SendNodeDef(con::Connection &con, u16 peer_id,
3780 INodeDefManager *nodedef)
3782 DSTACK(__FUNCTION_NAME);
3783 std::ostringstream os(std::ios_base::binary);
3787 u32 length of the next item
3788 serialized NodeDefManager
3790 writeU16(os, TOCLIENT_NODEDEF);
3791 std::ostringstream tmp_os(std::ios::binary);
3792 nodedef->serialize(tmp_os);
3793 os<<serializeLongString(tmp_os.str());
3796 std::string s = os.str();
3797 infostream<<"Server::SendNodeDef(): Sending node definitions: size="
3798 <<s.size()<<std::endl;
3799 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3801 con.Send(peer_id, 0, data, true);
3805 Non-static send methods
3808 void Server::SendObjectData(float dtime)
3810 DSTACK(__FUNCTION_NAME);
3812 core::map<v3s16, bool> stepped_blocks;
3814 for(core::map<u16, RemoteClient*>::Iterator
3815 i = m_clients.getIterator();
3816 i.atEnd() == false; i++)
3818 u16 peer_id = i.getNode()->getKey();
3819 RemoteClient *client = i.getNode()->getValue();
3820 assert(client->peer_id == peer_id);
3822 if(client->serialization_version == SER_FMT_VER_INVALID)
3825 client->SendObjectData(this, dtime, stepped_blocks);
3829 void Server::SendPlayerInfos()
3831 DSTACK(__FUNCTION_NAME);
3833 //JMutexAutoLock envlock(m_env_mutex);
3835 // Get connected players
3836 core::list<Player*> players = m_env->getPlayers(true);
3838 u32 player_count = players.getSize();
3839 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3841 SharedBuffer<u8> data(datasize);
3842 writeU16(&data[0], TOCLIENT_PLAYERINFO);
3845 core::list<Player*>::Iterator i;
3846 for(i = players.begin();
3847 i != players.end(); i++)
3849 Player *player = *i;
3851 /*infostream<<"Server sending player info for player with "
3852 "peer_id="<<player->peer_id<<std::endl;*/
3854 writeU16(&data[start], player->peer_id);
3855 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3856 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3857 start += 2+PLAYERNAME_SIZE;
3860 //JMutexAutoLock conlock(m_con_mutex);
3863 m_con.SendToAll(0, data, true);
3866 void Server::SendInventory(u16 peer_id)
3868 DSTACK(__FUNCTION_NAME);
3870 Player* player = m_env->getPlayer(peer_id);
3877 std::ostringstream os;
3878 //os.imbue(std::locale("C"));
3880 player->inventory.serialize(os);
3882 std::string s = os.str();
3884 SharedBuffer<u8> data(s.size()+2);
3885 writeU16(&data[0], TOCLIENT_INVENTORY);
3886 memcpy(&data[2], s.c_str(), s.size());
3889 m_con.Send(peer_id, 0, data, true);
3892 std::string getWieldedItemString(const Player *player)
3894 const InventoryItem *item = player->getWieldItem();
3896 return std::string("");
3897 std::ostringstream os(std::ios_base::binary);
3898 item->serialize(os);
3902 void Server::SendWieldedItem(const Player* player)
3904 DSTACK(__FUNCTION_NAME);
3908 std::ostringstream os(std::ios_base::binary);
3910 writeU16(os, TOCLIENT_PLAYERITEM);
3912 writeU16(os, player->peer_id);
3913 os<<serializeString(getWieldedItemString(player));
3916 std::string s = os.str();
3917 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3919 m_con.SendToAll(0, data, true);
3922 void Server::SendPlayerItems()
3924 DSTACK(__FUNCTION_NAME);
3926 std::ostringstream os(std::ios_base::binary);
3927 core::list<Player *> players = m_env->getPlayers(true);
3929 writeU16(os, TOCLIENT_PLAYERITEM);
3930 writeU16(os, players.size());
3931 core::list<Player *>::Iterator i;
3932 for(i = players.begin(); i != players.end(); ++i)
3935 writeU16(os, p->peer_id);
3936 os<<serializeString(getWieldedItemString(p));
3940 std::string s = os.str();
3941 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3943 m_con.SendToAll(0, data, true);
3946 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3948 DSTACK(__FUNCTION_NAME);
3950 std::ostringstream os(std::ios_base::binary);
3954 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3955 os.write((char*)buf, 2);
3958 writeU16(buf, message.size());
3959 os.write((char*)buf, 2);
3962 for(u32 i=0; i<message.size(); i++)
3966 os.write((char*)buf, 2);
3970 std::string s = os.str();
3971 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3973 m_con.Send(peer_id, 0, data, true);
3976 void Server::BroadcastChatMessage(const std::wstring &message)
3978 for(core::map<u16, RemoteClient*>::Iterator
3979 i = m_clients.getIterator();
3980 i.atEnd() == false; i++)
3982 // Get client and check that it is valid
3983 RemoteClient *client = i.getNode()->getValue();
3984 assert(client->peer_id == i.getNode()->getKey());
3985 if(client->serialization_version == SER_FMT_VER_INVALID)
3988 SendChatMessage(client->peer_id, message);
3992 void Server::SendPlayerHP(Player *player)
3994 SendHP(m_con, player->peer_id, player->hp);
3997 void Server::SendMovePlayer(Player *player)
3999 DSTACK(__FUNCTION_NAME);
4000 std::ostringstream os(std::ios_base::binary);
4002 writeU16(os, TOCLIENT_MOVE_PLAYER);
4003 writeV3F1000(os, player->getPosition());
4004 writeF1000(os, player->getPitch());
4005 writeF1000(os, player->getYaw());
4008 v3f pos = player->getPosition();
4009 f32 pitch = player->getPitch();
4010 f32 yaw = player->getYaw();
4011 infostream<<"Server sending TOCLIENT_MOVE_PLAYER"
4012 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
4019 std::string s = os.str();
4020 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4022 m_con.Send(player->peer_id, 0, data, true);
4025 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
4026 core::list<u16> *far_players, float far_d_nodes)
4028 float maxd = far_d_nodes*BS;
4029 v3f p_f = intToFloat(p, BS);
4033 SharedBuffer<u8> reply(replysize);
4034 writeU16(&reply[0], TOCLIENT_REMOVENODE);
4035 writeS16(&reply[2], p.X);
4036 writeS16(&reply[4], p.Y);
4037 writeS16(&reply[6], p.Z);
4039 for(core::map<u16, RemoteClient*>::Iterator
4040 i = m_clients.getIterator();
4041 i.atEnd() == false; i++)
4043 // Get client and check that it is valid
4044 RemoteClient *client = i.getNode()->getValue();
4045 assert(client->peer_id == i.getNode()->getKey());
4046 if(client->serialization_version == SER_FMT_VER_INVALID)
4049 // Don't send if it's the same one
4050 if(client->peer_id == ignore_id)
4056 Player *player = m_env->getPlayer(client->peer_id);
4059 // If player is far away, only set modified blocks not sent
4060 v3f player_pos = player->getPosition();
4061 if(player_pos.getDistanceFrom(p_f) > maxd)
4063 far_players->push_back(client->peer_id);
4070 m_con.Send(client->peer_id, 0, reply, true);
4074 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
4075 core::list<u16> *far_players, float far_d_nodes)
4077 float maxd = far_d_nodes*BS;
4078 v3f p_f = intToFloat(p, BS);
4080 for(core::map<u16, RemoteClient*>::Iterator
4081 i = m_clients.getIterator();
4082 i.atEnd() == false; i++)
4084 // Get client and check that it is valid
4085 RemoteClient *client = i.getNode()->getValue();
4086 assert(client->peer_id == i.getNode()->getKey());
4087 if(client->serialization_version == SER_FMT_VER_INVALID)
4090 // Don't send if it's the same one
4091 if(client->peer_id == ignore_id)
4097 Player *player = m_env->getPlayer(client->peer_id);
4100 // If player is far away, only set modified blocks not sent
4101 v3f player_pos = player->getPosition();
4102 if(player_pos.getDistanceFrom(p_f) > maxd)
4104 far_players->push_back(client->peer_id);
4111 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
4112 SharedBuffer<u8> reply(replysize);
4113 writeU16(&reply[0], TOCLIENT_ADDNODE);
4114 writeS16(&reply[2], p.X);
4115 writeS16(&reply[4], p.Y);
4116 writeS16(&reply[6], p.Z);
4117 n.serialize(&reply[8], client->serialization_version);
4120 m_con.Send(client->peer_id, 0, reply, true);
4124 void Server::setBlockNotSent(v3s16 p)
4126 for(core::map<u16, RemoteClient*>::Iterator
4127 i = m_clients.getIterator();
4128 i.atEnd()==false; i++)
4130 RemoteClient *client = i.getNode()->getValue();
4131 client->SetBlockNotSent(p);
4135 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
4137 DSTACK(__FUNCTION_NAME);
4139 v3s16 p = block->getPos();
4143 bool completely_air = true;
4144 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4145 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4146 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4148 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
4150 completely_air = false;
4151 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
4156 infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
4158 infostream<<"[completely air] ";
4159 infostream<<std::endl;
4163 Create a packet with the block in the right format
4166 std::ostringstream os(std::ios_base::binary);
4167 block->serialize(os, ver);
4168 std::string s = os.str();
4169 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
4171 u32 replysize = 8 + blockdata.getSize();
4172 SharedBuffer<u8> reply(replysize);
4173 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
4174 writeS16(&reply[2], p.X);
4175 writeS16(&reply[4], p.Y);
4176 writeS16(&reply[6], p.Z);
4177 memcpy(&reply[8], *blockdata, blockdata.getSize());
4179 /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4180 <<": \tpacket size: "<<replysize<<std::endl;*/
4185 m_con.Send(peer_id, 1, reply, true);
4188 void Server::SendBlocks(float dtime)
4190 DSTACK(__FUNCTION_NAME);
4192 JMutexAutoLock envlock(m_env_mutex);
4193 JMutexAutoLock conlock(m_con_mutex);
4195 //TimeTaker timer("Server::SendBlocks");
4197 core::array<PrioritySortedBlockTransfer> queue;
4199 s32 total_sending = 0;
4202 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
4204 for(core::map<u16, RemoteClient*>::Iterator
4205 i = m_clients.getIterator();
4206 i.atEnd() == false; i++)
4208 RemoteClient *client = i.getNode()->getValue();
4209 assert(client->peer_id == i.getNode()->getKey());
4211 total_sending += client->SendingCount();
4213 if(client->serialization_version == SER_FMT_VER_INVALID)
4216 client->GetNextBlocks(this, dtime, queue);
4221 // Lowest priority number comes first.
4222 // Lowest is most important.
4225 for(u32 i=0; i<queue.size(); i++)
4227 //TODO: Calculate limit dynamically
4228 if(total_sending >= g_settings->getS32
4229 ("max_simultaneous_block_sends_server_total"))
4232 PrioritySortedBlockTransfer q = queue[i];
4234 MapBlock *block = NULL;
4237 block = m_env->getMap().getBlockNoCreate(q.pos);
4239 catch(InvalidPositionException &e)
4244 RemoteClient *client = getClient(q.peer_id);
4246 SendBlockNoLock(q.peer_id, block, client->serialization_version);
4248 client->SentBlock(q.pos);
4254 struct SendableTexture
4260 SendableTexture(const std::string &name_="", const std::string path_="",
4261 const std::string &data_=""):
4268 void Server::SendTextures(u16 peer_id)
4270 DSTACK(__FUNCTION_NAME);
4272 infostream<<"Server::SendTextures(): Sending textures to client"<<std::endl;
4276 // Put 5kB in one bunch (this is not accurate)
4277 u32 bytes_per_bunch = 5000;
4279 core::array< core::list<SendableTexture> > texture_bunches;
4280 texture_bunches.push_back(core::list<SendableTexture>());
4282 u32 texture_size_bunch_total = 0;
4283 core::list<ModSpec> mods = getMods(m_modspaths);
4284 for(core::list<ModSpec>::Iterator i = mods.begin();
4285 i != mods.end(); i++){
4287 std::string texturepath = mod.path + DIR_DELIM + "textures";
4288 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(texturepath);
4289 for(u32 j=0; j<dirlist.size(); j++){
4290 if(dirlist[j].dir) // Ignode dirs
4292 std::string tname = dirlist[j].name;
4293 std::string tpath = texturepath + DIR_DELIM + tname;
4295 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
4296 if(fis.good() == false){
4297 errorstream<<"Server::SendTextures(): Could not open \""
4298 <<tname<<"\" for reading"<<std::endl;
4301 std::ostringstream tmp_os(std::ios_base::binary);
4305 fis.read(buf, 1024);
4306 std::streamsize len = fis.gcount();
4307 tmp_os.write(buf, len);
4308 texture_size_bunch_total += len;
4317 errorstream<<"Server::SendTextures(): Failed to read \""
4318 <<tname<<"\""<<std::endl;
4321 /*infostream<<"Server::SendTextures(): Loaded \""
4322 <<tname<<"\""<<std::endl;*/
4324 texture_bunches[texture_bunches.size()-1].push_back(
4325 SendableTexture(tname, tpath, tmp_os.str()));
4327 // Start next bunch if got enough data
4328 if(texture_size_bunch_total >= bytes_per_bunch){
4329 texture_bunches.push_back(core::list<SendableTexture>());
4330 texture_size_bunch_total = 0;
4335 /* Create and send packets */
4337 u32 num_bunches = texture_bunches.size();
4338 for(u32 i=0; i<num_bunches; i++)
4342 u16 total number of texture bunches
4343 u16 index of this bunch
4344 u32 number of textures in this bunch
4352 std::ostringstream os(std::ios_base::binary);
4354 writeU16(os, TOCLIENT_TEXTURES);
4355 writeU16(os, num_bunches);
4357 writeU32(os, texture_bunches[i].size());
4359 for(core::list<SendableTexture>::Iterator
4360 j = texture_bunches[i].begin();
4361 j != texture_bunches[i].end(); j++){
4362 os<<serializeString(j->name);
4363 os<<serializeLongString(j->data);
4367 std::string s = os.str();
4368 infostream<<"Server::SendTextures(): bunch "<<i<<"/"<<num_bunches
4369 <<" textures="<<texture_bunches[i].size()
4370 <<" size=" <<s.size()<<std::endl;
4371 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4373 m_con.Send(peer_id, 0, data, true);
4381 void Server::HandlePlayerHP(Player *player, s16 damage)
4383 if(player->hp > damage)
4385 player->hp -= damage;
4386 SendPlayerHP(player);
4390 infostream<<"Server::HandlePlayerHP(): Player "
4391 <<player->getName()<<" dies"<<std::endl;
4395 //TODO: Throw items around
4397 // Handle players that are not connected
4398 if(player->peer_id == PEER_ID_INEXISTENT){
4399 RespawnPlayer(player);
4403 SendPlayerHP(player);
4405 RemoteClient *client = getClient(player->peer_id);
4406 if(client->net_proto_version >= 3)
4408 SendDeathscreen(m_con, player->peer_id, false, v3f(0,0,0));
4412 RespawnPlayer(player);
4417 void Server::RespawnPlayer(Player *player)
4420 ServerRemotePlayer *srp = (ServerRemotePlayer*)player;
4421 bool repositioned = scriptapi_on_respawnplayer(m_lua, srp);
4423 v3f pos = findSpawnPos(m_env->getServerMap());
4424 player->setPosition(pos);
4425 srp->m_last_good_position = pos;
4426 srp->m_last_good_position_age = 0;
4428 SendMovePlayer(player);
4429 SendPlayerHP(player);
4432 void Server::UpdateCrafting(u16 peer_id)
4434 DSTACK(__FUNCTION_NAME);
4436 Player* player = m_env->getPlayer(peer_id);
4440 Calculate crafting stuff
4442 if(g_settings->getBool("creative_mode") == false)
4444 InventoryList *clist = player->inventory.getList("craft");
4445 InventoryList *rlist = player->inventory.getList("craftresult");
4447 if(rlist && rlist->getUsedSlots() == 0)
4448 player->craftresult_is_preview = true;
4450 if(rlist && player->craftresult_is_preview)
4452 rlist->clearItems();
4454 if(clist && rlist && player->craftresult_is_preview)
4456 // Get result of crafting grid
4458 std::vector<InventoryItem*> items;
4459 for(u16 i=0; i<9; i++){
4460 if(clist->getItem(i) == NULL)
4461 items.push_back(NULL);
4463 items.push_back(clist->getItem(i)->clone());
4465 CraftPointerInput cpi(3, items);
4467 InventoryItem *result = m_craftdef->getCraftResult(cpi, this);
4468 //InventoryItem *result = craft_get_result(items, this);
4470 rlist->addItem(result);
4473 } // if creative_mode == false
4476 RemoteClient* Server::getClient(u16 peer_id)
4478 DSTACK(__FUNCTION_NAME);
4479 //JMutexAutoLock lock(m_con_mutex);
4480 core::map<u16, RemoteClient*>::Node *n;
4481 n = m_clients.find(peer_id);
4482 // A client should exist for all peers
4484 return n->getValue();
4487 std::wstring Server::getStatusString()
4489 std::wostringstream os(std::ios_base::binary);
4492 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4494 os<<L", uptime="<<m_uptime.get();
4495 // Information about clients
4497 for(core::map<u16, RemoteClient*>::Iterator
4498 i = m_clients.getIterator();
4499 i.atEnd() == false; i++)
4501 // Get client and check that it is valid
4502 RemoteClient *client = i.getNode()->getValue();
4503 assert(client->peer_id == i.getNode()->getKey());
4504 if(client->serialization_version == SER_FMT_VER_INVALID)
4507 Player *player = m_env->getPlayer(client->peer_id);
4508 // Get name of player
4509 std::wstring name = L"unknown";
4511 name = narrow_to_wide(player->getName());
4512 // Add name to information string
4516 if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
4517 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4518 if(g_settings->get("motd") != "")
4519 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4523 // Saves g_settings to configpath given at initialization
4524 void Server::saveConfig()
4526 if(m_configpath != "")
4527 g_settings->updateConfigFile(m_configpath.c_str());
4530 void Server::notifyPlayer(const char *name, const std::wstring msg)
4532 Player *player = m_env->getPlayer(name);
4535 SendChatMessage(player->peer_id, std::wstring(L"Server: -!- ")+msg);
4538 void Server::notifyPlayers(const std::wstring msg)
4540 BroadcastChatMessage(msg);
4543 // IGameDef interface
4545 IToolDefManager* Server::getToolDefManager()
4549 INodeDefManager* Server::getNodeDefManager()
4553 ICraftDefManager* Server::getCraftDefManager()
4557 ITextureSource* Server::getTextureSource()
4561 u16 Server::allocateUnknownNodeId(const std::string &name)
4563 return m_nodedef->allocateDummy(name);
4566 IWritableToolDefManager* Server::getWritableToolDefManager()
4570 IWritableNodeDefManager* Server::getWritableNodeDefManager()
4574 IWritableCraftDefManager* Server::getWritableCraftDefManager()
4579 v3f findSpawnPos(ServerMap &map)
4581 //return v3f(50,50,50)*BS;
4586 nodepos = v2s16(0,0);
4591 // Try to find a good place a few times
4592 for(s32 i=0; i<1000; i++)
4595 // We're going to try to throw the player to this position
4596 v2s16 nodepos2d = v2s16(-range + (myrand()%(range*2)),
4597 -range + (myrand()%(range*2)));
4598 //v2s16 sectorpos = getNodeSectorPos(nodepos2d);
4599 // Get ground height at point (fallbacks to heightmap function)
4600 s16 groundheight = map.findGroundLevel(nodepos2d);
4601 // Don't go underwater
4602 if(groundheight < WATER_LEVEL)
4604 //infostream<<"-> Underwater"<<std::endl;
4607 // Don't go to high places
4608 if(groundheight > WATER_LEVEL + 4)
4610 //infostream<<"-> Underwater"<<std::endl;
4614 nodepos = v3s16(nodepos2d.X, groundheight-2, nodepos2d.Y);
4615 bool is_good = false;
4617 for(s32 i=0; i<10; i++){
4618 v3s16 blockpos = getNodeBlockPos(nodepos);
4619 map.emergeBlock(blockpos, true);
4620 MapNode n = map.getNodeNoEx(nodepos);
4621 if(n.getContent() == CONTENT_AIR){
4632 // Found a good place
4633 //infostream<<"Searched through "<<i<<" places."<<std::endl;
4639 return intToFloat(nodepos, BS);
4642 Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id)
4645 Try to get an existing player
4647 Player *player = m_env->getPlayer(name);
4650 // If player is already connected, cancel
4651 if(player->peer_id != 0)
4653 infostream<<"emergePlayer(): Player already connected"<<std::endl;
4658 player->peer_id = peer_id;
4660 // Reset inventory to creative if in creative mode
4661 if(g_settings->getBool("creative_mode"))
4663 // Warning: double code below
4664 // Backup actual inventory
4665 player->inventory_backup = new Inventory();
4666 *(player->inventory_backup) = player->inventory;
4667 // Set creative inventory
4668 craft_set_creative_inventory(player, this);
4675 If player with the wanted peer_id already exists, cancel.
4677 if(m_env->getPlayer(peer_id) != NULL)
4679 infostream<<"emergePlayer(): Player with wrong name but same"
4680 " peer_id already exists"<<std::endl;
4688 // Add authentication stuff
4689 m_authmanager.add(name);
4690 m_authmanager.setPassword(name, password);
4691 m_authmanager.setPrivs(name,
4692 stringToPrivs(g_settings->get("default_privs")));
4694 /* Set player position */
4696 infostream<<"Server: Finding spawn place for player \""
4697 <<name<<"\""<<std::endl;
4699 v3f pos = findSpawnPos(m_env->getServerMap());
4701 player = new ServerRemotePlayer(m_env, pos, peer_id, name);
4703 /* Add player to environment */
4704 m_env->addPlayer(player);
4707 ServerRemotePlayer *srp = (ServerRemotePlayer*)player;
4708 scriptapi_on_newplayer(m_lua, srp);
4710 /* Add stuff to inventory */
4711 if(g_settings->getBool("creative_mode"))
4713 // Warning: double code above
4714 // Backup actual inventory
4715 player->inventory_backup = new Inventory();
4716 *(player->inventory_backup) = player->inventory;
4717 // Set creative inventory
4718 craft_set_creative_inventory(player, this);
4723 } // create new player
4726 void Server::handlePeerChange(PeerChange &c)
4728 JMutexAutoLock envlock(m_env_mutex);
4729 JMutexAutoLock conlock(m_con_mutex);
4731 if(c.type == PEER_ADDED)
4738 core::map<u16, RemoteClient*>::Node *n;
4739 n = m_clients.find(c.peer_id);
4740 // The client shouldn't already exist
4744 RemoteClient *client = new RemoteClient();
4745 client->peer_id = c.peer_id;
4746 m_clients.insert(client->peer_id, client);
4749 else if(c.type == PEER_REMOVED)
4756 core::map<u16, RemoteClient*>::Node *n;
4757 n = m_clients.find(c.peer_id);
4758 // The client should exist
4762 Mark objects to be not known by the client
4764 RemoteClient *client = n->getValue();
4766 for(core::map<u16, bool>::Iterator
4767 i = client->m_known_objects.getIterator();
4768 i.atEnd()==false; i++)
4771 u16 id = i.getNode()->getKey();
4772 ServerActiveObject* obj = m_env->getActiveObject(id);
4774 if(obj && obj->m_known_by_count > 0)
4775 obj->m_known_by_count--;
4778 // Collect information about leaving in chat
4779 std::wstring message;
4781 Player *player = m_env->getPlayer(c.peer_id);
4784 std::wstring name = narrow_to_wide(player->getName());
4787 message += L" left game";
4789 message += L" (timed out)";
4795 m_env->removePlayer(c.peer_id);
4798 // Set player client disconnected
4800 Player *player = m_env->getPlayer(c.peer_id);
4802 player->peer_id = 0;
4809 std::ostringstream os(std::ios_base::binary);
4810 for(core::map<u16, RemoteClient*>::Iterator
4811 i = m_clients.getIterator();
4812 i.atEnd() == false; i++)
4814 RemoteClient *client = i.getNode()->getValue();
4815 assert(client->peer_id == i.getNode()->getKey());
4816 if(client->serialization_version == SER_FMT_VER_INVALID)
4819 Player *player = m_env->getPlayer(client->peer_id);
4822 // Get name of player
4823 os<<player->getName()<<" ";
4826 actionstream<<player->getName()<<" "
4827 <<(c.timeout?"times out.":"leaves game.")
4828 <<" List of players: "
4829 <<os.str()<<std::endl;
4834 delete m_clients[c.peer_id];
4835 m_clients.remove(c.peer_id);
4837 // Send player info to all remaining clients
4840 // Send leave chat message to all remaining clients
4841 BroadcastChatMessage(message);
4850 void Server::handlePeerChanges()
4852 while(m_peer_change_queue.size() > 0)
4854 PeerChange c = m_peer_change_queue.pop_front();
4856 infostream<<"Server: Handling peer change: "
4857 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4860 handlePeerChange(c);
4864 u64 Server::getPlayerPrivs(Player *player)
4868 std::string playername = player->getName();
4869 // Local player gets all privileges regardless of
4870 // what's set on their account.
4871 if(g_settings->get("name") == playername)
4877 return getPlayerAuthPrivs(playername);
4881 void dedicated_server_loop(Server &server, bool &kill)
4883 DSTACK(__FUNCTION_NAME);
4885 infostream<<DTIME<<std::endl;
4886 infostream<<"========================"<<std::endl;
4887 infostream<<"Running dedicated server"<<std::endl;
4888 infostream<<"========================"<<std::endl;
4889 infostream<<std::endl;
4891 IntervalLimiter m_profiler_interval;
4895 // This is kind of a hack but can be done like this
4896 // because server.step() is very light
4898 ScopeProfiler sp(g_profiler, "dedicated server sleep");
4903 if(server.getShutdownRequested() || kill)
4905 infostream<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4912 float profiler_print_interval =
4913 g_settings->getFloat("profiler_print_interval");
4914 if(profiler_print_interval != 0)
4916 if(m_profiler_interval.step(0.030, profiler_print_interval))
4918 infostream<<"Profiler:"<<std::endl;
4919 g_profiler->print(infostream);
4920 g_profiler->clear();
4927 static int counter = 0;
4933 core::list<PlayerInfo> list = server.getPlayerInfo();
4934 core::list<PlayerInfo>::Iterator i;
4935 static u32 sum_old = 0;
4936 u32 sum = PIChecksum(list);
4939 infostream<<DTIME<<"Player info:"<<std::endl;
4940 for(i=list.begin(); i!=list.end(); i++)
4942 i->PrintLine(&infostream);