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.
24 #include "clientserver.h"
26 #include "jmutexautolock.h"
28 #include "constants.h"
30 #include "materials.h"
33 #include "servercommand.h"
35 #include "content_mapnode.h"
36 #include "content_craft.h"
37 #include "content_nodemeta.h"
39 #include "serverobject.h"
44 #include "scriptapi.h"
49 #include "content_abm.h"
51 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
53 #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
55 class MapEditEventIgnorer
58 MapEditEventIgnorer(bool *flag):
67 ~MapEditEventIgnorer()
80 void * ServerThread::Thread()
84 log_register_thread("ServerThread");
86 DSTACK(__FUNCTION_NAME);
88 BEGIN_DEBUG_EXCEPTION_HANDLER
93 //TimeTaker timer("AsyncRunStep() + Receive()");
96 //TimeTaker timer("AsyncRunStep()");
97 m_server->AsyncRunStep();
100 //infostream<<"Running m_server->Receive()"<<std::endl;
103 catch(con::NoIncomingDataException &e)
106 catch(con::PeerNotFoundException &e)
108 infostream<<"Server: PeerNotFoundException"<<std::endl;
112 END_DEBUG_EXCEPTION_HANDLER(errorstream)
117 void * EmergeThread::Thread()
121 log_register_thread("EmergeThread");
123 DSTACK(__FUNCTION_NAME);
125 BEGIN_DEBUG_EXCEPTION_HANDLER
127 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
130 Get block info from queue, emerge them and send them
133 After queue is empty, exit.
137 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
141 SharedPtr<QueuedBlockEmerge> q(qptr);
147 Do not generate over-limit
149 if(blockpos_over_limit(p))
152 //infostream<<"EmergeThread::Thread(): running"<<std::endl;
154 //TimeTaker timer("block emerge");
157 Try to emerge it from somewhere.
159 If it is only wanted as optional, only loading from disk
164 Check if any peer wants it as non-optional. In that case it
167 Also decrement the emerge queue count in clients.
170 bool only_from_disk = true;
173 core::map<u16, u8>::Iterator i;
174 for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
176 //u16 peer_id = i.getNode()->getKey();
179 u8 flags = i.getNode()->getValue();
180 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
181 only_from_disk = false;
186 if(enable_mapgen_debug_info)
187 infostream<<"EmergeThread: p="
188 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
189 <<"only_from_disk="<<only_from_disk<<std::endl;
191 ServerMap &map = ((ServerMap&)m_server->m_env->getMap());
193 MapBlock *block = NULL;
194 bool got_block = true;
195 core::map<v3s16, MapBlock*> modified_blocks;
198 Try to fetch block from memory or disk.
199 If not found and asked to generate, initialize generator.
202 bool started_generate = false;
203 mapgen::BlockMakeData data;
206 JMutexAutoLock envlock(m_server->m_env_mutex);
208 // Load sector if it isn't loaded
209 if(map.getSectorNoGenerateNoEx(p2d) == NULL)
210 map.loadSectorMeta(p2d);
212 // Attempt to load block
213 block = map.getBlockNoCreateNoEx(p);
214 if(!block || block->isDummy() || !block->isGenerated())
216 if(enable_mapgen_debug_info)
217 infostream<<"EmergeThread: not in memory, "
218 <<"attempting to load from disk"<<std::endl;
220 block = map.loadBlock(p);
223 // If could not load and allowed to generate, start generation
224 // inside this same envlock
225 if(only_from_disk == false &&
226 (block == NULL || block->isGenerated() == false)){
227 if(enable_mapgen_debug_info)
228 infostream<<"EmergeThread: generating"<<std::endl;
229 started_generate = true;
231 map.initBlockMake(&data, p);
236 If generator was initialized, generate now when envlock is free.
241 ScopeProfiler sp(g_profiler, "EmergeThread: mapgen::make_block",
243 TimeTaker t("mapgen::make_block()");
245 mapgen::make_block(&data);
247 if(enable_mapgen_debug_info == false)
248 t.stop(true); // Hide output
252 // Lock environment again to access the map
253 JMutexAutoLock envlock(m_server->m_env_mutex);
255 ScopeProfiler sp(g_profiler, "EmergeThread: after "
256 "mapgen::make_block (envlock)", SPT_AVG);
258 // Blit data back on map, update lighting, add mobs and
259 // whatever this does
260 map.finishBlockMake(&data, modified_blocks);
263 block = map.getBlockNoCreateNoEx(p);
266 Do some post-generate stuff
269 v3s16 minp = block->getPos()*MAP_BLOCKSIZE;
270 v3s16 maxp = minp + v3s16(1,1,1)*(MAP_BLOCKSIZE-1);
271 scriptapi_environment_on_generated(m_server->m_lua,
274 if(enable_mapgen_debug_info)
275 infostream<<"EmergeThread: ended up with: "
276 <<analyze_block(block)<<std::endl;
279 Ignore map edit events, they will not need to be
280 sent to anybody because the block hasn't been sent
283 MapEditEventIgnorer ign(&m_server->m_ignore_map_edit_events);
285 // Activate objects and stuff
286 m_server->m_env->activateBlock(block, 0);
294 Set sent status of modified blocks on clients
297 // NOTE: Server's clients are also behind the connection mutex
298 JMutexAutoLock lock(m_server->m_con_mutex);
301 Add the originally fetched block to the modified list
305 modified_blocks.insert(p, block);
309 Set the modified blocks unsent for all the clients
312 for(core::map<u16, RemoteClient*>::Iterator
313 i = m_server->m_clients.getIterator();
314 i.atEnd() == false; i++)
316 RemoteClient *client = i.getNode()->getValue();
318 if(modified_blocks.size() > 0)
320 // Remove block from sent history
321 client->SetBlocksNotSent(modified_blocks);
327 END_DEBUG_EXCEPTION_HANDLER(errorstream)
329 log_deregister_thread();
334 void RemoteClient::GetNextBlocks(Server *server, float dtime,
335 core::array<PrioritySortedBlockTransfer> &dest)
337 DSTACK(__FUNCTION_NAME);
340 TimeTaker timer("RemoteClient::GetNextBlocks", &timer_result);*/
343 m_nothing_to_send_pause_timer -= dtime;
344 m_nearest_unsent_reset_timer += dtime;
346 if(m_nothing_to_send_pause_timer >= 0)
351 // Won't send anything if already sending
352 if(m_blocks_sending.size() >= g_settings->getU16
353 ("max_simultaneous_block_sends_per_client"))
355 //infostream<<"Not sending any blocks, Queue full."<<std::endl;
359 //TimeTaker timer("RemoteClient::GetNextBlocks");
361 Player *player = server->m_env->getPlayer(peer_id);
363 assert(player != NULL);
365 v3f playerpos = player->getPosition();
366 v3f playerspeed = player->getSpeed();
367 v3f playerspeeddir(0,0,0);
368 if(playerspeed.getLength() > 1.0*BS)
369 playerspeeddir = playerspeed / playerspeed.getLength();
370 // Predict to next block
371 v3f playerpos_predicted = playerpos + playerspeeddir*MAP_BLOCKSIZE*BS;
373 v3s16 center_nodepos = floatToInt(playerpos_predicted, BS);
375 v3s16 center = getNodeBlockPos(center_nodepos);
377 // Camera position and direction
378 v3f camera_pos = player->getEyePosition();
379 v3f camera_dir = v3f(0,0,1);
380 camera_dir.rotateYZBy(player->getPitch());
381 camera_dir.rotateXZBy(player->getYaw());
383 /*infostream<<"camera_dir=("<<camera_dir.X<<","<<camera_dir.Y<<","
384 <<camera_dir.Z<<")"<<std::endl;*/
387 Get the starting value of the block finder radius.
390 if(m_last_center != center)
392 m_nearest_unsent_d = 0;
393 m_last_center = center;
396 /*infostream<<"m_nearest_unsent_reset_timer="
397 <<m_nearest_unsent_reset_timer<<std::endl;*/
399 // Reset periodically to workaround for some bugs or stuff
400 if(m_nearest_unsent_reset_timer > 20.0)
402 m_nearest_unsent_reset_timer = 0;
403 m_nearest_unsent_d = 0;
404 //infostream<<"Resetting m_nearest_unsent_d for "
405 // <<server->getPlayerName(peer_id)<<std::endl;
408 //s16 last_nearest_unsent_d = m_nearest_unsent_d;
409 s16 d_start = m_nearest_unsent_d;
411 //infostream<<"d_start="<<d_start<<std::endl;
413 u16 max_simul_sends_setting = g_settings->getU16
414 ("max_simultaneous_block_sends_per_client");
415 u16 max_simul_sends_usually = max_simul_sends_setting;
418 Check the time from last addNode/removeNode.
420 Decrease send rate if player is building stuff.
422 m_time_from_building += dtime;
423 if(m_time_from_building < g_settings->getFloat(
424 "full_block_send_enable_min_time_from_building"))
426 max_simul_sends_usually
427 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
431 Number of blocks sending + number of blocks selected for sending
433 u32 num_blocks_selected = m_blocks_sending.size();
436 next time d will be continued from the d from which the nearest
437 unsent block was found this time.
439 This is because not necessarily any of the blocks found this
440 time are actually sent.
442 s32 new_nearest_unsent_d = -1;
444 s16 d_max = g_settings->getS16("max_block_send_distance");
445 s16 d_max_gen = g_settings->getS16("max_block_generate_distance");
447 // Don't loop very much at a time
448 s16 max_d_increment_at_time = 2;
449 if(d_max > d_start + max_d_increment_at_time)
450 d_max = d_start + max_d_increment_at_time;
451 /*if(d_max_gen > d_start+2)
452 d_max_gen = d_start+2;*/
454 //infostream<<"Starting from "<<d_start<<std::endl;
456 s32 nearest_emerged_d = -1;
457 s32 nearest_emergefull_d = -1;
458 s32 nearest_sent_d = -1;
459 bool queue_is_full = false;
462 for(d = d_start; d <= d_max; d++)
464 /*errorstream<<"checking d="<<d<<" for "
465 <<server->getPlayerName(peer_id)<<std::endl;*/
466 //infostream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
469 If m_nearest_unsent_d was changed by the EmergeThread
470 (it can change it to 0 through SetBlockNotSent),
472 Else update m_nearest_unsent_d
474 /*if(m_nearest_unsent_d != last_nearest_unsent_d)
476 d = m_nearest_unsent_d;
477 last_nearest_unsent_d = m_nearest_unsent_d;
481 Get the border/face dot coordinates of a "d-radiused"
484 core::list<v3s16> list;
485 getFacePositions(list, d);
487 core::list<v3s16>::Iterator li;
488 for(li=list.begin(); li!=list.end(); li++)
490 v3s16 p = *li + center;
494 - Don't allow too many simultaneous transfers
495 - EXCEPT when the blocks are very close
497 Also, don't send blocks that are already flying.
500 // Start with the usual maximum
501 u16 max_simul_dynamic = max_simul_sends_usually;
503 // If block is very close, allow full maximum
504 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
505 max_simul_dynamic = max_simul_sends_setting;
507 // Don't select too many blocks for sending
508 if(num_blocks_selected >= max_simul_dynamic)
510 queue_is_full = true;
511 goto queue_full_break;
514 // Don't send blocks that are currently being transferred
515 if(m_blocks_sending.find(p) != NULL)
521 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
522 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
523 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
524 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
525 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
526 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
529 // If this is true, inexistent block will be made from scratch
530 bool generate = d <= d_max_gen;
533 /*// Limit the generating area vertically to 2/3
534 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
537 // Limit the send area vertically to 1/2
538 if(abs(p.Y - center.Y) > d_max / 2)
544 If block is far away, don't generate it unless it is
550 // Block center y in nodes
551 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
552 // Don't generate if it's very high or very low
553 if(y < -64 || y > 64)
557 v2s16 p2d_nodes_center(
561 // Get ground height in nodes
562 s16 gh = server->m_env->getServerMap().findGroundLevel(
565 // If differs a lot, don't generate
566 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
568 // Actually, don't even send it
574 //infostream<<"d="<<d<<std::endl;
577 Don't generate or send if not in sight
578 FIXME This only works if the client uses a small enough
579 FOV setting. The default of 72 degrees is fine.
582 float camera_fov = (72.0*PI/180) * 4./3.;
583 if(isBlockInSight(p, camera_pos, camera_dir, camera_fov, 10000*BS) == false)
589 Don't send already sent blocks
592 if(m_blocks_sent.find(p) != NULL)
599 Check if map has this block
601 MapBlock *block = server->m_env->getMap().getBlockNoCreateNoEx(p);
603 bool surely_not_found_on_disk = false;
604 bool block_is_invalid = false;
607 // Reset usage timer, this block will be of use in the future.
608 block->resetUsageTimer();
610 // Block is dummy if data doesn't exist.
611 // It means it has been not found from disk and not generated
614 surely_not_found_on_disk = true;
617 // Block is valid if lighting is up-to-date and data exists
618 if(block->isValid() == false)
620 block_is_invalid = true;
623 /*if(block->isFullyGenerated() == false)
625 block_is_invalid = true;
630 ServerMap *map = (ServerMap*)(&server->m_env->getMap());
631 v2s16 chunkpos = map->sector_to_chunk(p2d);
632 if(map->chunkNonVolatile(chunkpos) == false)
633 block_is_invalid = true;
635 if(block->isGenerated() == false)
636 block_is_invalid = true;
639 If block is not close, don't send it unless it is near
642 Block is near ground level if night-time mesh
643 differs from day-time mesh.
647 if(block->dayNightDiffed() == false)
654 If block has been marked to not exist on disk (dummy)
655 and generating new ones is not wanted, skip block.
657 if(generate == false && surely_not_found_on_disk == true)
664 Add inexistent block to emerge queue.
666 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
668 //TODO: Get value from somewhere
669 // Allow only one block in emerge queue
670 //if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
671 // Allow two blocks in queue per client
672 //if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
674 // Make it more responsive when needing to generate stuff
675 if(surely_not_found_on_disk)
677 if(server->m_emerge_queue.peerItemCount(peer_id) < max_emerge)
679 //infostream<<"Adding block to emerge queue"<<std::endl;
681 // Add it to the emerge queue and trigger the thread
684 if(generate == false)
685 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
687 server->m_emerge_queue.addBlock(peer_id, p, flags);
688 server->m_emergethread.trigger();
690 if(nearest_emerged_d == -1)
691 nearest_emerged_d = d;
693 if(nearest_emergefull_d == -1)
694 nearest_emergefull_d = d;
701 if(nearest_sent_d == -1)
705 Add block to send queue
708 /*errorstream<<"sending from d="<<d<<" to "
709 <<server->getPlayerName(peer_id)<<std::endl;*/
711 PrioritySortedBlockTransfer q((float)d, p, peer_id);
715 num_blocks_selected += 1;
720 //infostream<<"Stopped at "<<d<<std::endl;
722 // If nothing was found for sending and nothing was queued for
723 // emerging, continue next time browsing from here
724 if(nearest_emerged_d != -1){
725 new_nearest_unsent_d = nearest_emerged_d;
726 } else if(nearest_emergefull_d != -1){
727 new_nearest_unsent_d = nearest_emergefull_d;
729 if(d > g_settings->getS16("max_block_send_distance")){
730 new_nearest_unsent_d = 0;
731 m_nothing_to_send_pause_timer = 2.0;
732 /*infostream<<"GetNextBlocks(): d wrapped around for "
733 <<server->getPlayerName(peer_id)
734 <<"; setting to 0 and pausing"<<std::endl;*/
736 if(nearest_sent_d != -1)
737 new_nearest_unsent_d = nearest_sent_d;
739 new_nearest_unsent_d = d;
743 if(new_nearest_unsent_d != -1)
744 m_nearest_unsent_d = new_nearest_unsent_d;
746 /*timer_result = timer.stop(true);
747 if(timer_result != 0)
748 infostream<<"GetNextBlocks duration: "<<timer_result<<" (!=0)"<<std::endl;*/
751 void RemoteClient::SendObjectData(
754 core::map<v3s16, bool> &stepped_blocks
757 DSTACK(__FUNCTION_NAME);
759 // Can't send anything without knowing version
760 if(serialization_version == SER_FMT_VER_INVALID)
762 infostream<<"RemoteClient::SendObjectData(): Not sending, no version."
768 Send a TOCLIENT_OBJECTDATA packet.
772 u16 number of player positions
784 std::ostringstream os(std::ios_base::binary);
788 writeU16(buf, TOCLIENT_OBJECTDATA);
789 os.write((char*)buf, 2);
792 Get and write player data
795 // Get connected players
796 core::list<Player*> players = server->m_env->getPlayers(true);
798 // Write player count
799 u16 playercount = players.size();
800 writeU16(buf, playercount);
801 os.write((char*)buf, 2);
803 core::list<Player*>::Iterator i;
804 for(i = players.begin();
805 i != players.end(); i++)
809 v3f pf = player->getPosition();
810 v3f sf = player->getSpeed();
812 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
813 v3s32 speed_i (sf.X*100, sf.Y*100, sf.Z*100);
814 s32 pitch_i (player->getPitch() * 100);
815 s32 yaw_i (player->getYaw() * 100);
817 writeU16(buf, player->peer_id);
818 os.write((char*)buf, 2);
819 writeV3S32(buf, position_i);
820 os.write((char*)buf, 12);
821 writeV3S32(buf, speed_i);
822 os.write((char*)buf, 12);
823 writeS32(buf, pitch_i);
824 os.write((char*)buf, 4);
825 writeS32(buf, yaw_i);
826 os.write((char*)buf, 4);
830 Get and write object data (dummy, for compatibility)
835 os.write((char*)buf, 2);
841 //infostream<<"Server: Sending object data to "<<peer_id<<std::endl;
844 std::string s = os.str();
845 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
846 // Send as unreliable
847 server->m_con.Send(peer_id, 0, data, false);
850 void RemoteClient::GotBlock(v3s16 p)
852 if(m_blocks_sending.find(p) != NULL)
853 m_blocks_sending.remove(p);
856 /*infostream<<"RemoteClient::GotBlock(): Didn't find in"
857 " m_blocks_sending"<<std::endl;*/
858 m_excess_gotblocks++;
860 m_blocks_sent.insert(p, true);
863 void RemoteClient::SentBlock(v3s16 p)
865 if(m_blocks_sending.find(p) == NULL)
866 m_blocks_sending.insert(p, 0.0);
868 infostream<<"RemoteClient::SentBlock(): Sent block"
869 " already in m_blocks_sending"<<std::endl;
872 void RemoteClient::SetBlockNotSent(v3s16 p)
874 m_nearest_unsent_d = 0;
876 if(m_blocks_sending.find(p) != NULL)
877 m_blocks_sending.remove(p);
878 if(m_blocks_sent.find(p) != NULL)
879 m_blocks_sent.remove(p);
882 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
884 m_nearest_unsent_d = 0;
886 for(core::map<v3s16, MapBlock*>::Iterator
887 i = blocks.getIterator();
888 i.atEnd()==false; i++)
890 v3s16 p = i.getNode()->getKey();
892 if(m_blocks_sending.find(p) != NULL)
893 m_blocks_sending.remove(p);
894 if(m_blocks_sent.find(p) != NULL)
895 m_blocks_sent.remove(p);
903 PlayerInfo::PlayerInfo()
909 void PlayerInfo::PrintLine(std::ostream *s)
912 (*s)<<"\""<<name<<"\" ("
913 <<(position.X/10)<<","<<(position.Y/10)
914 <<","<<(position.Z/10)<<") ";
916 (*s)<<" avg_rtt="<<avg_rtt;
920 u32 PIChecksum(core::list<PlayerInfo> &l)
922 core::list<PlayerInfo>::Iterator i;
925 for(i=l.begin(); i!=l.end(); i++)
927 checksum += a * (i->id+1);
928 checksum ^= 0x435aafcd;
942 std::set<std::string> depends;
943 std::set<std::string> unsatisfied_depends;
945 ModSpec(const std::string &name_="", const std::string path_="",
946 const std::set<std::string> &depends_=std::set<std::string>()):
950 unsatisfied_depends(depends_)
954 // Get a dependency-sorted list of ModSpecs
955 static core::list<ModSpec> getMods(core::list<std::string> &modspaths)
957 std::queue<ModSpec> mods_satisfied;
958 core::list<ModSpec> mods_unsorted;
959 core::list<ModSpec> mods_sorted;
960 for(core::list<std::string>::Iterator i = modspaths.begin();
961 i != modspaths.end(); i++){
962 std::string modspath = *i;
963 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(modspath);
964 for(u32 j=0; j<dirlist.size(); j++){
967 std::string modname = dirlist[j].name;
968 std::string modpath = modspath + DIR_DELIM + modname;
969 std::set<std::string> depends;
970 std::ifstream is((modpath+DIR_DELIM+"depends.txt").c_str(),
971 std::ios_base::binary);
974 std::getline(is, dep);
979 ModSpec spec(modname, modpath, depends);
980 mods_unsorted.push_back(spec);
982 mods_satisfied.push(spec);
985 // Sort by depencencies
986 while(!mods_satisfied.empty()){
987 ModSpec mod = mods_satisfied.front();
988 mods_satisfied.pop();
989 mods_sorted.push_back(mod);
990 for(core::list<ModSpec>::Iterator i = mods_unsorted.begin();
991 i != mods_unsorted.end(); i++){
993 if(mod2.unsatisfied_depends.empty())
995 mod2.unsatisfied_depends.erase(mod.name);
996 if(!mod2.unsatisfied_depends.empty())
998 mods_satisfied.push(mod2);
1001 // Check unsatisfied dependencies
1002 for(core::list<ModSpec>::Iterator i = mods_unsorted.begin();
1003 i != mods_unsorted.end(); i++){
1005 if(mod.unsatisfied_depends.empty())
1007 errorstream<<"mod \""<<mod.name
1008 <<"\" has unsatisfied dependencies:";
1009 for(std::set<std::string>::iterator
1010 i = mod.unsatisfied_depends.begin();
1011 i != mod.unsatisfied_depends.end(); i++){
1012 errorstream<<" \""<<(*i)<<"\"";
1014 errorstream<<". Loading nevertheless."<<std::endl;
1015 mods_sorted.push_back(mod);
1025 std::string mapsavedir,
1026 std::string configpath
1029 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
1030 m_authmanager(mapsavedir+DIR_DELIM+"auth.txt"),
1031 m_banmanager(mapsavedir+DIR_DELIM+"ipban.txt"),
1033 m_toolmgr(createToolDefManager()),
1034 m_nodedef(createNodeDefManager()),
1035 m_craftdef(createCraftDefManager()),
1037 m_emergethread(this),
1039 m_time_of_day_send_timer(0),
1041 m_mapsavedir(mapsavedir),
1042 m_configpath(configpath),
1043 m_shutdown_requested(false),
1044 m_ignore_map_edit_events(false),
1045 m_ignore_map_edit_events_peer_id(0)
1047 m_liquid_transform_timer = 0.0;
1048 m_print_info_timer = 0.0;
1049 m_objectdata_timer = 0.0;
1050 m_emergethread_trigger_timer = 0.0;
1051 m_savemap_timer = 0.0;
1055 m_step_dtime_mutex.Init();
1058 JMutexAutoLock envlock(m_env_mutex);
1059 JMutexAutoLock conlock(m_con_mutex);
1061 infostream<<"m_nodedef="<<m_nodedef<<std::endl;
1063 // Path to builtin.lua
1064 std::string builtinpath = porting::path_data + DIR_DELIM + "builtin.lua";
1065 // Add default global mod path
1066 m_modspaths.push_back(porting::path_data + DIR_DELIM + "mods");
1068 // Initialize scripting
1070 infostream<<"Server: Initializing scripting"<<std::endl;
1071 m_lua = script_init();
1074 scriptapi_export(m_lua, this);
1075 // Load and run builtin.lua
1076 infostream<<"Server: Loading builtin Lua stuff from \""<<builtinpath
1078 bool success = script_load(m_lua, builtinpath.c_str());
1080 errorstream<<"Server: Failed to load and run "
1081 <<builtinpath<<std::endl;
1084 // Load and run "mod" scripts
1085 core::list<ModSpec> mods = getMods(m_modspaths);
1086 for(core::list<ModSpec>::Iterator i = mods.begin();
1087 i != mods.end(); i++){
1089 infostream<<"Server: Loading mod \""<<mod.name<<"\""<<std::endl;
1090 std::string scriptpath = mod.path + DIR_DELIM + "init.lua";
1091 bool success = script_load(m_lua, scriptpath.c_str());
1093 errorstream<<"Server: Failed to load and run "
1094 <<scriptpath<<std::endl;
1099 // Initialize Environment
1101 m_env = new ServerEnvironment(new ServerMap(mapsavedir, this), m_lua,
1104 // Give environment reference to scripting api
1105 scriptapi_add_environment(m_lua, m_env);
1107 // Register us to receive map edit events
1108 m_env->getMap().addEventReceiver(this);
1110 // If file exists, load environment metadata
1111 if(fs::PathExists(m_mapsavedir+DIR_DELIM+"env_meta.txt"))
1113 infostream<<"Server: Loading environment metadata"<<std::endl;
1114 m_env->loadMeta(m_mapsavedir);
1118 infostream<<"Server: Loading players"<<std::endl;
1119 m_env->deSerializePlayers(m_mapsavedir);
1122 Add some test ActiveBlockModifiers to environment
1124 add_legacy_abms(m_env, m_nodedef);
1129 infostream<<"Server::~Server()"<<std::endl;
1132 Send shutdown message
1135 JMutexAutoLock conlock(m_con_mutex);
1137 std::wstring line = L"*** Server shutting down";
1140 Send the message to clients
1142 for(core::map<u16, RemoteClient*>::Iterator
1143 i = m_clients.getIterator();
1144 i.atEnd() == false; i++)
1146 // Get client and check that it is valid
1147 RemoteClient *client = i.getNode()->getValue();
1148 assert(client->peer_id == i.getNode()->getKey());
1149 if(client->serialization_version == SER_FMT_VER_INVALID)
1153 SendChatMessage(client->peer_id, line);
1155 catch(con::PeerNotFoundException &e)
1161 JMutexAutoLock envlock(m_env_mutex);
1166 infostream<<"Server: Saving players"<<std::endl;
1167 m_env->serializePlayers(m_mapsavedir);
1170 Save environment metadata
1172 infostream<<"Server: Saving environment metadata"<<std::endl;
1173 m_env->saveMeta(m_mapsavedir);
1185 JMutexAutoLock clientslock(m_con_mutex);
1187 for(core::map<u16, RemoteClient*>::Iterator
1188 i = m_clients.getIterator();
1189 i.atEnd() == false; i++)
1192 // NOTE: These are removed by env destructor
1194 u16 peer_id = i.getNode()->getKey();
1195 JMutexAutoLock envlock(m_env_mutex);
1196 m_env->removePlayer(peer_id);
1200 delete i.getNode()->getValue();
1204 // Delete Environment
1210 // Deinitialize scripting
1211 infostream<<"Server: Deinitializing scripting"<<std::endl;
1212 script_deinit(m_lua);
1215 void Server::start(unsigned short port)
1217 DSTACK(__FUNCTION_NAME);
1218 // Stop thread if already running
1221 // Initialize connection
1222 m_con.SetTimeoutMs(30);
1226 m_thread.setRun(true);
1229 infostream<<"Server: Started on port "<<port<<std::endl;
1234 DSTACK(__FUNCTION_NAME);
1236 infostream<<"Server: Stopping and waiting threads"<<std::endl;
1238 // Stop threads (set run=false first so both start stopping)
1239 m_thread.setRun(false);
1240 m_emergethread.setRun(false);
1242 m_emergethread.stop();
1244 infostream<<"Server: Threads stopped"<<std::endl;
1247 void Server::step(float dtime)
1249 DSTACK(__FUNCTION_NAME);
1254 JMutexAutoLock lock(m_step_dtime_mutex);
1255 m_step_dtime += dtime;
1259 void Server::AsyncRunStep()
1261 DSTACK(__FUNCTION_NAME);
1263 g_profiler->add("Server::AsyncRunStep (num)", 1);
1267 JMutexAutoLock lock1(m_step_dtime_mutex);
1268 dtime = m_step_dtime;
1272 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
1273 // Send blocks to clients
1280 g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
1282 //infostream<<"Server steps "<<dtime<<std::endl;
1283 //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1286 JMutexAutoLock lock1(m_step_dtime_mutex);
1287 m_step_dtime -= dtime;
1294 m_uptime.set(m_uptime.get() + dtime);
1298 // Process connection's timeouts
1299 JMutexAutoLock lock2(m_con_mutex);
1300 ScopeProfiler sp(g_profiler, "Server: connection timeout processing");
1301 m_con.RunTimeouts(dtime);
1305 // This has to be called so that the client list gets synced
1306 // with the peer list of the connection
1307 handlePeerChanges();
1311 Update m_time_of_day and overall game time
1314 JMutexAutoLock envlock(m_env_mutex);
1316 m_time_counter += dtime;
1317 f32 speed = g_settings->getFloat("time_speed") * 24000./(24.*3600);
1318 u32 units = (u32)(m_time_counter*speed);
1319 m_time_counter -= (f32)units / speed;
1321 m_env->setTimeOfDay((m_env->getTimeOfDay() + units) % 24000);
1323 //infostream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1326 Send to clients at constant intervals
1329 m_time_of_day_send_timer -= dtime;
1330 if(m_time_of_day_send_timer < 0.0)
1332 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
1334 //JMutexAutoLock envlock(m_env_mutex);
1335 JMutexAutoLock conlock(m_con_mutex);
1337 for(core::map<u16, RemoteClient*>::Iterator
1338 i = m_clients.getIterator();
1339 i.atEnd() == false; i++)
1341 RemoteClient *client = i.getNode()->getValue();
1342 //Player *player = m_env->getPlayer(client->peer_id);
1344 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1345 m_env->getTimeOfDay());
1347 m_con.Send(client->peer_id, 0, data, true);
1353 JMutexAutoLock lock(m_env_mutex);
1355 ScopeProfiler sp(g_profiler, "SEnv step");
1356 ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
1360 const float map_timer_and_unload_dtime = 2.92;
1361 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
1363 JMutexAutoLock lock(m_env_mutex);
1364 // Run Map's timers and unload unused data
1365 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
1366 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
1367 g_settings->getFloat("server_unload_unused_data_timeout"));
1375 Check player movements
1377 NOTE: Actually the server should handle player physics like the
1378 client does and compare player's position to what is calculated
1379 on our side. This is required when eg. players fly due to an
1383 JMutexAutoLock lock(m_env_mutex);
1384 JMutexAutoLock lock2(m_con_mutex);
1386 //float player_max_speed = BS * 4.0; // Normal speed
1387 float player_max_speed = BS * 20; // Fast speed
1388 float player_max_speed_up = BS * 20;
1390 player_max_speed *= 2.5; // Tolerance
1391 player_max_speed_up *= 2.5;
1393 for(core::map<u16, RemoteClient*>::Iterator
1394 i = m_clients.getIterator();
1395 i.atEnd() == false; i++)
1397 RemoteClient *client = i.getNode()->getValue();
1398 ServerRemotePlayer *player =
1399 (ServerRemotePlayer*)m_env->getPlayer(client->peer_id);
1402 player->m_last_good_position_age += dtime;
1403 if(player->m_last_good_position_age >= 2.0){
1404 float age = player->m_last_good_position_age;
1405 v3f diff = (player->getPosition() - player->m_last_good_position);
1406 float d_vert = diff.Y;
1408 float d_horiz = diff.getLength();
1409 /*infostream<<player->getName()<<"'s horizontal speed is "
1410 <<(d_horiz/age)<<std::endl;*/
1411 if(d_horiz <= age * player_max_speed &&
1412 (d_vert < 0 || d_vert < age * player_max_speed_up)){
1413 player->m_last_good_position = player->getPosition();
1415 actionstream<<"Player "<<player->getName()
1416 <<" moved too fast; resetting position"
1418 player->setPosition(player->m_last_good_position);
1419 SendMovePlayer(player);
1421 player->m_last_good_position_age = 0;
1426 /* Transform liquids */
1427 m_liquid_transform_timer += dtime;
1428 if(m_liquid_transform_timer >= 1.00)
1430 m_liquid_transform_timer -= 1.00;
1432 JMutexAutoLock lock(m_env_mutex);
1434 ScopeProfiler sp(g_profiler, "Server: liquid transform");
1436 core::map<v3s16, MapBlock*> modified_blocks;
1437 m_env->getMap().transformLiquids(modified_blocks);
1442 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1443 ServerMap &map = ((ServerMap&)m_env->getMap());
1444 map.updateLighting(modified_blocks, lighting_modified_blocks);
1446 // Add blocks modified by lighting to modified_blocks
1447 for(core::map<v3s16, MapBlock*>::Iterator
1448 i = lighting_modified_blocks.getIterator();
1449 i.atEnd() == false; i++)
1451 MapBlock *block = i.getNode()->getValue();
1452 modified_blocks.insert(block->getPos(), block);
1456 Set the modified blocks unsent for all the clients
1459 JMutexAutoLock lock2(m_con_mutex);
1461 for(core::map<u16, RemoteClient*>::Iterator
1462 i = m_clients.getIterator();
1463 i.atEnd() == false; i++)
1465 RemoteClient *client = i.getNode()->getValue();
1467 if(modified_blocks.size() > 0)
1469 // Remove block from sent history
1470 client->SetBlocksNotSent(modified_blocks);
1475 // Periodically print some info
1477 float &counter = m_print_info_timer;
1483 JMutexAutoLock lock2(m_con_mutex);
1485 if(m_clients.size() != 0)
1486 infostream<<"Players:"<<std::endl;
1487 for(core::map<u16, RemoteClient*>::Iterator
1488 i = m_clients.getIterator();
1489 i.atEnd() == false; i++)
1491 //u16 peer_id = i.getNode()->getKey();
1492 RemoteClient *client = i.getNode()->getValue();
1493 Player *player = m_env->getPlayer(client->peer_id);
1496 infostream<<"* "<<player->getName()<<"\t";
1497 client->PrintInfo(infostream);
1502 //if(g_settings->getBool("enable_experimental"))
1506 Check added and deleted active objects
1509 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
1510 JMutexAutoLock envlock(m_env_mutex);
1511 JMutexAutoLock conlock(m_con_mutex);
1513 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
1515 // Radius inside which objects are active
1516 s16 radius = g_settings->getS16("active_object_send_range_blocks");
1517 radius *= MAP_BLOCKSIZE;
1519 for(core::map<u16, RemoteClient*>::Iterator
1520 i = m_clients.getIterator();
1521 i.atEnd() == false; i++)
1523 RemoteClient *client = i.getNode()->getValue();
1524 Player *player = m_env->getPlayer(client->peer_id);
1527 // This can happen if the client timeouts somehow
1528 /*infostream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
1530 <<" has no associated player"<<std::endl;*/
1533 v3s16 pos = floatToInt(player->getPosition(), BS);
1535 core::map<u16, bool> removed_objects;
1536 core::map<u16, bool> added_objects;
1537 m_env->getRemovedActiveObjects(pos, radius,
1538 client->m_known_objects, removed_objects);
1539 m_env->getAddedActiveObjects(pos, radius,
1540 client->m_known_objects, added_objects);
1542 // Ignore if nothing happened
1543 if(removed_objects.size() == 0 && added_objects.size() == 0)
1545 //infostream<<"active objects: none changed"<<std::endl;
1549 std::string data_buffer;
1553 // Handle removed objects
1554 writeU16((u8*)buf, removed_objects.size());
1555 data_buffer.append(buf, 2);
1556 for(core::map<u16, bool>::Iterator
1557 i = removed_objects.getIterator();
1558 i.atEnd()==false; i++)
1561 u16 id = i.getNode()->getKey();
1562 ServerActiveObject* obj = m_env->getActiveObject(id);
1564 // Add to data buffer for sending
1565 writeU16((u8*)buf, i.getNode()->getKey());
1566 data_buffer.append(buf, 2);
1568 // Remove from known objects
1569 client->m_known_objects.remove(i.getNode()->getKey());
1571 if(obj && obj->m_known_by_count > 0)
1572 obj->m_known_by_count--;
1575 // Handle added objects
1576 writeU16((u8*)buf, added_objects.size());
1577 data_buffer.append(buf, 2);
1578 for(core::map<u16, bool>::Iterator
1579 i = added_objects.getIterator();
1580 i.atEnd()==false; i++)
1583 u16 id = i.getNode()->getKey();
1584 ServerActiveObject* obj = m_env->getActiveObject(id);
1587 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1589 infostream<<"WARNING: "<<__FUNCTION_NAME
1590 <<": NULL object"<<std::endl;
1592 type = obj->getType();
1594 // Add to data buffer for sending
1595 writeU16((u8*)buf, id);
1596 data_buffer.append(buf, 2);
1597 writeU8((u8*)buf, type);
1598 data_buffer.append(buf, 1);
1601 data_buffer.append(serializeLongString(
1602 obj->getClientInitializationData()));
1604 data_buffer.append(serializeLongString(""));
1606 // Add to known objects
1607 client->m_known_objects.insert(i.getNode()->getKey(), false);
1610 obj->m_known_by_count++;
1614 SharedBuffer<u8> reply(2 + data_buffer.size());
1615 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1616 memcpy((char*)&reply[2], data_buffer.c_str(),
1617 data_buffer.size());
1619 m_con.Send(client->peer_id, 0, reply, true);
1621 infostream<<"Server: Sent object remove/add: "
1622 <<removed_objects.size()<<" removed, "
1623 <<added_objects.size()<<" added, "
1624 <<"packet size is "<<reply.getSize()<<std::endl;
1629 Collect a list of all the objects known by the clients
1630 and report it back to the environment.
1633 core::map<u16, bool> all_known_objects;
1635 for(core::map<u16, RemoteClient*>::Iterator
1636 i = m_clients.getIterator();
1637 i.atEnd() == false; i++)
1639 RemoteClient *client = i.getNode()->getValue();
1640 // Go through all known objects of client
1641 for(core::map<u16, bool>::Iterator
1642 i = client->m_known_objects.getIterator();
1643 i.atEnd()==false; i++)
1645 u16 id = i.getNode()->getKey();
1646 all_known_objects[id] = true;
1650 m_env->setKnownActiveObjects(whatever);
1656 Send object messages
1659 JMutexAutoLock envlock(m_env_mutex);
1660 JMutexAutoLock conlock(m_con_mutex);
1662 //ScopeProfiler sp(g_profiler, "Server: sending object messages");
1665 // Value = data sent by object
1666 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1668 // Get active object messages from environment
1671 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
1675 core::list<ActiveObjectMessage>* message_list = NULL;
1676 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1677 n = buffered_messages.find(aom.id);
1680 message_list = new core::list<ActiveObjectMessage>;
1681 buffered_messages.insert(aom.id, message_list);
1685 message_list = n->getValue();
1687 message_list->push_back(aom);
1690 // Route data to every client
1691 for(core::map<u16, RemoteClient*>::Iterator
1692 i = m_clients.getIterator();
1693 i.atEnd()==false; i++)
1695 RemoteClient *client = i.getNode()->getValue();
1696 std::string reliable_data;
1697 std::string unreliable_data;
1698 // Go through all objects in message buffer
1699 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1700 j = buffered_messages.getIterator();
1701 j.atEnd()==false; j++)
1703 // If object is not known by client, skip it
1704 u16 id = j.getNode()->getKey();
1705 if(client->m_known_objects.find(id) == NULL)
1707 // Get message list of object
1708 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1709 // Go through every message
1710 for(core::list<ActiveObjectMessage>::Iterator
1711 k = list->begin(); k != list->end(); k++)
1713 // Compose the full new data with header
1714 ActiveObjectMessage aom = *k;
1715 std::string new_data;
1718 writeU16((u8*)&buf[0], aom.id);
1719 new_data.append(buf, 2);
1721 new_data += serializeString(aom.datastring);
1722 // Add data to buffer
1724 reliable_data += new_data;
1726 unreliable_data += new_data;
1730 reliable_data and unreliable_data are now ready.
1733 if(reliable_data.size() > 0)
1735 SharedBuffer<u8> reply(2 + reliable_data.size());
1736 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1737 memcpy((char*)&reply[2], reliable_data.c_str(),
1738 reliable_data.size());
1740 m_con.Send(client->peer_id, 0, reply, true);
1742 if(unreliable_data.size() > 0)
1744 SharedBuffer<u8> reply(2 + unreliable_data.size());
1745 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1746 memcpy((char*)&reply[2], unreliable_data.c_str(),
1747 unreliable_data.size());
1748 // Send as unreliable
1749 m_con.Send(client->peer_id, 0, reply, false);
1752 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1754 infostream<<"Server: Size of object message data: "
1755 <<"reliable: "<<reliable_data.size()
1756 <<", unreliable: "<<unreliable_data.size()
1761 // Clear buffered_messages
1762 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1763 i = buffered_messages.getIterator();
1764 i.atEnd()==false; i++)
1766 delete i.getNode()->getValue();
1770 } // enable_experimental
1773 Send queued-for-sending map edit events.
1776 // Don't send too many at a time
1779 // Single change sending is disabled if queue size is not small
1780 bool disable_single_change_sending = false;
1781 if(m_unsent_map_edit_queue.size() >= 4)
1782 disable_single_change_sending = true;
1784 bool got_any_events = false;
1786 // We'll log the amount of each
1789 while(m_unsent_map_edit_queue.size() != 0)
1791 got_any_events = true;
1793 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1795 // Players far away from the change are stored here.
1796 // Instead of sending the changes, MapBlocks are set not sent
1798 core::list<u16> far_players;
1800 if(event->type == MEET_ADDNODE)
1802 //infostream<<"Server: MEET_ADDNODE"<<std::endl;
1803 prof.add("MEET_ADDNODE", 1);
1804 if(disable_single_change_sending)
1805 sendAddNode(event->p, event->n, event->already_known_by_peer,
1808 sendAddNode(event->p, event->n, event->already_known_by_peer,
1811 else if(event->type == MEET_REMOVENODE)
1813 //infostream<<"Server: MEET_REMOVENODE"<<std::endl;
1814 prof.add("MEET_REMOVENODE", 1);
1815 if(disable_single_change_sending)
1816 sendRemoveNode(event->p, event->already_known_by_peer,
1819 sendRemoveNode(event->p, event->already_known_by_peer,
1822 else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
1824 infostream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
1825 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
1826 setBlockNotSent(event->p);
1828 else if(event->type == MEET_OTHER)
1830 infostream<<"Server: MEET_OTHER"<<std::endl;
1831 prof.add("MEET_OTHER", 1);
1832 for(core::map<v3s16, bool>::Iterator
1833 i = event->modified_blocks.getIterator();
1834 i.atEnd()==false; i++)
1836 v3s16 p = i.getNode()->getKey();
1842 prof.add("unknown", 1);
1843 infostream<<"WARNING: Server: Unknown MapEditEvent "
1844 <<((u32)event->type)<<std::endl;
1848 Set blocks not sent to far players
1850 if(far_players.size() > 0)
1852 // Convert list format to that wanted by SetBlocksNotSent
1853 core::map<v3s16, MapBlock*> modified_blocks2;
1854 for(core::map<v3s16, bool>::Iterator
1855 i = event->modified_blocks.getIterator();
1856 i.atEnd()==false; i++)
1858 v3s16 p = i.getNode()->getKey();
1859 modified_blocks2.insert(p,
1860 m_env->getMap().getBlockNoCreateNoEx(p));
1862 // Set blocks not sent
1863 for(core::list<u16>::Iterator
1864 i = far_players.begin();
1865 i != far_players.end(); i++)
1868 RemoteClient *client = getClient(peer_id);
1871 client->SetBlocksNotSent(modified_blocks2);
1877 /*// Don't send too many at a time
1879 if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
1885 infostream<<"Server: MapEditEvents:"<<std::endl;
1886 prof.print(infostream);
1892 Send object positions
1895 float &counter = m_objectdata_timer;
1897 if(counter >= g_settings->getFloat("objectdata_interval"))
1899 JMutexAutoLock lock1(m_env_mutex);
1900 JMutexAutoLock lock2(m_con_mutex);
1902 //ScopeProfiler sp(g_profiler, "Server: sending player positions");
1904 SendObjectData(counter);
1911 Trigger emergethread (it somehow gets to a non-triggered but
1912 bysy state sometimes)
1915 float &counter = m_emergethread_trigger_timer;
1921 m_emergethread.trigger();
1925 // Save map, players and auth stuff
1927 float &counter = m_savemap_timer;
1929 if(counter >= g_settings->getFloat("server_map_save_interval"))
1933 ScopeProfiler sp(g_profiler, "Server: saving stuff");
1936 if(m_authmanager.isModified())
1937 m_authmanager.save();
1940 if(m_banmanager.isModified())
1941 m_banmanager.save();
1944 JMutexAutoLock lock(m_env_mutex);
1946 // Save changed parts of map
1947 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
1950 m_env->serializePlayers(m_mapsavedir);
1952 // Save environment metadata
1953 m_env->saveMeta(m_mapsavedir);
1958 void Server::Receive()
1960 DSTACK(__FUNCTION_NAME);
1961 SharedBuffer<u8> data;
1966 JMutexAutoLock conlock(m_con_mutex);
1967 datasize = m_con.Receive(peer_id, data);
1970 // This has to be called so that the client list gets synced
1971 // with the peer list of the connection
1972 handlePeerChanges();
1974 ProcessData(*data, datasize, peer_id);
1976 catch(con::InvalidIncomingDataException &e)
1978 infostream<<"Server::Receive(): "
1979 "InvalidIncomingDataException: what()="
1980 <<e.what()<<std::endl;
1982 catch(con::PeerNotFoundException &e)
1984 //NOTE: This is not needed anymore
1986 // The peer has been disconnected.
1987 // Find the associated player and remove it.
1989 /*JMutexAutoLock envlock(m_env_mutex);
1991 infostream<<"ServerThread: peer_id="<<peer_id
1992 <<" has apparently closed connection. "
1993 <<"Removing player."<<std::endl;
1995 m_env->removePlayer(peer_id);*/
1999 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
2001 DSTACK(__FUNCTION_NAME);
2002 // Environment is locked first.
2003 JMutexAutoLock envlock(m_env_mutex);
2004 JMutexAutoLock conlock(m_con_mutex);
2007 Address address = m_con.GetPeerAddress(peer_id);
2009 // drop player if is ip is banned
2010 if(m_banmanager.isIpBanned(address.serializeString())){
2011 SendAccessDenied(m_con, peer_id,
2012 L"Your ip is banned. Banned name was "
2013 +narrow_to_wide(m_banmanager.getBanName(
2014 address.serializeString())));
2015 m_con.DeletePeer(peer_id);
2019 catch(con::PeerNotFoundException &e)
2021 infostream<<"Server::ProcessData(): Cancelling: peer "
2022 <<peer_id<<" not found"<<std::endl;
2026 u8 peer_ser_ver = getClient(peer_id)->serialization_version;
2034 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
2036 if(command == TOSERVER_INIT)
2038 // [0] u16 TOSERVER_INIT
2039 // [2] u8 SER_FMT_VER_HIGHEST
2040 // [3] u8[20] player_name
2041 // [23] u8[28] password <--- can be sent without this, from old versions
2043 if(datasize < 2+1+PLAYERNAME_SIZE)
2046 infostream<<"Server: Got TOSERVER_INIT from "
2047 <<peer_id<<std::endl;
2049 // First byte after command is maximum supported
2050 // serialization version
2051 u8 client_max = data[2];
2052 u8 our_max = SER_FMT_VER_HIGHEST;
2053 // Use the highest version supported by both
2054 u8 deployed = core::min_(client_max, our_max);
2055 // If it's lower than the lowest supported, give up.
2056 if(deployed < SER_FMT_VER_LOWEST)
2057 deployed = SER_FMT_VER_INVALID;
2059 //peer->serialization_version = deployed;
2060 getClient(peer_id)->pending_serialization_version = deployed;
2062 if(deployed == SER_FMT_VER_INVALID)
2064 infostream<<"Server: Cannot negotiate "
2065 "serialization version with peer "
2066 <<peer_id<<std::endl;
2067 SendAccessDenied(m_con, peer_id,
2068 L"Your client is too old (map format)");
2073 Read and check network protocol version
2076 u16 net_proto_version = 0;
2077 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
2079 net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
2082 getClient(peer_id)->net_proto_version = net_proto_version;
2084 if(net_proto_version == 0)
2086 SendAccessDenied(m_con, peer_id,
2087 L"Your client is too old. Please upgrade.");
2091 /* Uhh... this should actually be a warning but let's do it like this */
2092 if(g_settings->getBool("strict_protocol_version_checking"))
2094 if(net_proto_version < PROTOCOL_VERSION)
2096 SendAccessDenied(m_con, peer_id,
2097 L"Your client is too old. Please upgrade.");
2107 char playername[PLAYERNAME_SIZE];
2108 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
2110 playername[i] = data[3+i];
2112 playername[PLAYERNAME_SIZE-1] = 0;
2114 if(playername[0]=='\0')
2116 infostream<<"Server: Player has empty name"<<std::endl;
2117 SendAccessDenied(m_con, peer_id,
2122 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
2124 infostream<<"Server: Player has invalid name"<<std::endl;
2125 SendAccessDenied(m_con, peer_id,
2126 L"Name contains unallowed characters");
2131 char password[PASSWORD_SIZE];
2132 if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
2134 // old version - assume blank password
2139 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2141 password[i] = data[23+i];
2143 password[PASSWORD_SIZE-1] = 0;
2146 std::string checkpwd;
2147 if(m_authmanager.exists(playername))
2149 checkpwd = m_authmanager.getPassword(playername);
2153 checkpwd = g_settings->get("default_password");
2156 /*infostream<<"Server: Client gave password '"<<password
2157 <<"', the correct one is '"<<checkpwd<<"'"<<std::endl;*/
2159 if(password != checkpwd && m_authmanager.exists(playername))
2161 infostream<<"Server: peer_id="<<peer_id
2162 <<": supplied invalid password for "
2163 <<playername<<std::endl;
2164 SendAccessDenied(m_con, peer_id, L"Invalid password");
2168 // Add player to auth manager
2169 if(m_authmanager.exists(playername) == false)
2171 infostream<<"Server: adding player "<<playername
2172 <<" to auth manager"<<std::endl;
2173 m_authmanager.add(playername);
2174 m_authmanager.setPassword(playername, checkpwd);
2175 m_authmanager.setPrivs(playername,
2176 stringToPrivs(g_settings->get("default_privs")));
2177 m_authmanager.save();
2180 // Enforce user limit.
2181 // Don't enforce for users that have some admin right
2182 if(m_clients.size() >= g_settings->getU16("max_users") &&
2183 (m_authmanager.getPrivs(playername)
2184 & (PRIV_SERVER|PRIV_BAN|PRIV_PRIVS)) == 0 &&
2185 playername != g_settings->get("name"))
2187 SendAccessDenied(m_con, peer_id, L"Too many users.");
2192 Player *player = emergePlayer(playername, password, peer_id);
2194 // If failed, cancel
2197 infostream<<"Server: peer_id="<<peer_id
2198 <<": failed to emerge player"<<std::endl;
2203 Answer with a TOCLIENT_INIT
2206 SharedBuffer<u8> reply(2+1+6+8);
2207 writeU16(&reply[0], TOCLIENT_INIT);
2208 writeU8(&reply[2], deployed);
2209 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
2210 writeU64(&reply[2+1+6], m_env->getServerMap().getSeed());
2213 m_con.Send(peer_id, 0, reply, true);
2217 Send complete position information
2219 SendMovePlayer(player);
2224 if(command == TOSERVER_INIT2)
2226 infostream<<"Server: Got TOSERVER_INIT2 from "
2227 <<peer_id<<std::endl;
2230 getClient(peer_id)->serialization_version
2231 = getClient(peer_id)->pending_serialization_version;
2234 Send some initialization data
2237 // Send tool definitions
2238 SendToolDef(m_con, peer_id, m_toolmgr);
2240 // Send node definitions
2241 SendNodeDef(m_con, peer_id, m_nodedef);
2244 SendTextures(peer_id);
2246 // Send player info to all players
2249 // Send inventory to player
2250 UpdateCrafting(peer_id);
2251 SendInventory(peer_id);
2253 // Send player items to all players
2256 Player *player = m_env->getPlayer(peer_id);
2259 SendPlayerHP(player);
2263 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2264 m_env->getTimeOfDay());
2265 m_con.Send(peer_id, 0, data, true);
2268 // Send information about server to player in chat
2269 SendChatMessage(peer_id, getStatusString());
2271 // Send information about joining in chat
2273 std::wstring name = L"unknown";
2274 Player *player = m_env->getPlayer(peer_id);
2276 name = narrow_to_wide(player->getName());
2278 std::wstring message;
2281 message += L" joined game";
2282 BroadcastChatMessage(message);
2285 // Warnings about protocol version can be issued here
2286 if(getClient(peer_id)->net_proto_version < PROTOCOL_VERSION)
2288 SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT IS OLD AND MAY WORK PROPERLY WITH THIS SERVER");
2292 Check HP, respawn if necessary
2294 HandlePlayerHP(player, 0);
2300 std::ostringstream os(std::ios_base::binary);
2301 for(core::map<u16, RemoteClient*>::Iterator
2302 i = m_clients.getIterator();
2303 i.atEnd() == false; i++)
2305 RemoteClient *client = i.getNode()->getValue();
2306 assert(client->peer_id == i.getNode()->getKey());
2307 if(client->serialization_version == SER_FMT_VER_INVALID)
2310 Player *player = m_env->getPlayer(client->peer_id);
2313 // Get name of player
2314 os<<player->getName()<<" ";
2317 actionstream<<player->getName()<<" joins game. List of players: "
2318 <<os.str()<<std::endl;
2324 if(peer_ser_ver == SER_FMT_VER_INVALID)
2326 infostream<<"Server::ProcessData(): Cancelling: Peer"
2327 " serialization format invalid or not initialized."
2328 " Skipping incoming command="<<command<<std::endl;
2332 Player *player = m_env->getPlayer(peer_id);
2335 infostream<<"Server::ProcessData(): Cancelling: "
2336 "No player for peer_id="<<peer_id
2340 if(command == TOSERVER_PLAYERPOS)
2342 if(datasize < 2+12+12+4+4)
2346 v3s32 ps = readV3S32(&data[start+2]);
2347 v3s32 ss = readV3S32(&data[start+2+12]);
2348 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2349 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2350 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2351 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2352 pitch = wrapDegrees(pitch);
2353 yaw = wrapDegrees(yaw);
2355 player->setPosition(position);
2356 player->setSpeed(speed);
2357 player->setPitch(pitch);
2358 player->setYaw(yaw);
2360 /*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2361 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2362 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2364 else if(command == TOSERVER_GOTBLOCKS)
2377 u16 count = data[2];
2378 for(u16 i=0; i<count; i++)
2380 if((s16)datasize < 2+1+(i+1)*6)
2381 throw con::InvalidIncomingDataException
2382 ("GOTBLOCKS length is too short");
2383 v3s16 p = readV3S16(&data[2+1+i*6]);
2384 /*infostream<<"Server: GOTBLOCKS ("
2385 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2386 RemoteClient *client = getClient(peer_id);
2387 client->GotBlock(p);
2390 else if(command == TOSERVER_DELETEDBLOCKS)
2403 u16 count = data[2];
2404 for(u16 i=0; i<count; i++)
2406 if((s16)datasize < 2+1+(i+1)*6)
2407 throw con::InvalidIncomingDataException
2408 ("DELETEDBLOCKS length is too short");
2409 v3s16 p = readV3S16(&data[2+1+i*6]);
2410 /*infostream<<"Server: DELETEDBLOCKS ("
2411 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2412 RemoteClient *client = getClient(peer_id);
2413 client->SetBlockNotSent(p);
2416 else if(command == TOSERVER_CLICK_OBJECT)
2418 infostream<<"Server: CLICK_OBJECT not supported anymore"<<std::endl;
2421 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2426 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2432 [2] u8 button (0=left, 1=right)
2436 u8 button = readU8(&data[2]);
2437 u16 id = readS16(&data[3]);
2438 u16 item_i = readU16(&data[5]);
2440 ServerActiveObject *obj = m_env->getActiveObject(id);
2444 infostream<<"Server: CLICK_ACTIVEOBJECT: object not found"
2449 // Skip if object has been removed
2453 //TODO: Check that object is reasonably close
2455 // Get ServerRemotePlayer
2456 ServerRemotePlayer *srp = (ServerRemotePlayer*)player;
2458 // Update wielded item
2459 srp->wieldItem(item_i);
2461 // Left click, pick/punch
2464 actionstream<<player->getName()<<" punches object "
2465 <<obj->getId()<<std::endl;
2472 Try creating inventory item
2474 InventoryItem *item = obj->createPickedUpItem();
2478 InventoryList *ilist = player->inventory.getList("main");
2481 actionstream<<player->getName()<<" picked up "
2482 <<item->getName()<<std::endl;
2483 if(g_settings->getBool("creative_mode") == false)
2485 // Skip if inventory has no free space
2486 if(ilist->roomForItem(item) == false)
2488 infostream<<"Player inventory has no free space"<<std::endl;
2492 // Add to inventory and send inventory
2493 ilist->addItem(item);
2494 UpdateCrafting(player->peer_id);
2495 SendInventory(player->peer_id);
2498 // Remove object from environment
2499 obj->m_removed = true;
2505 Item cannot be picked up. Punch it instead.
2508 actionstream<<player->getName()<<" punches object "
2509 <<obj->getId()<<std::endl;
2511 ToolItem *titem = NULL;
2512 std::string toolname = "";
2514 InventoryList *mlist = player->inventory.getList("main");
2517 InventoryItem *item = mlist->getItem(item_i);
2518 if(item && (std::string)item->getName() == "ToolItem")
2520 titem = (ToolItem*)item;
2521 toolname = titem->getToolName();
2525 v3f playerpos = player->getPosition();
2526 v3f objpos = obj->getBasePosition();
2527 v3f dir = (objpos - playerpos).normalize();
2529 u16 wear = obj->punch(toolname, dir, player->getName());
2533 bool weared_out = titem->addWear(wear);
2535 mlist->deleteItem(item_i);
2536 SendInventory(player->peer_id);
2541 // Right click, do something with object
2544 actionstream<<player->getName()<<" right clicks object "
2545 <<obj->getId()<<std::endl;
2548 obj->rightClick(srp);
2552 Update player state to client
2554 SendPlayerHP(player);
2555 UpdateCrafting(player->peer_id);
2556 SendInventory(player->peer_id);
2558 else if(command == TOSERVER_GROUND_ACTION)
2566 [3] v3s16 nodepos_undersurface
2567 [9] v3s16 nodepos_abovesurface
2572 2: stop digging (all parameters ignored)
2573 3: digging completed
2575 u8 action = readU8(&data[2]);
2577 p_under.X = readS16(&data[3]);
2578 p_under.Y = readS16(&data[5]);
2579 p_under.Z = readS16(&data[7]);
2581 p_over.X = readS16(&data[9]);
2582 p_over.Y = readS16(&data[11]);
2583 p_over.Z = readS16(&data[13]);
2584 u16 item_i = readU16(&data[15]);
2586 ServerRemotePlayer *srp = (ServerRemotePlayer*)player;
2589 Check that target is reasonably close
2591 if(action != 2) // action 2 has always position (0,0,0)
2593 v3f np_f = intToFloat(p_under, BS);
2594 float max_d = BS * 10; // Just some large enough value
2595 float d = srp->m_last_good_position.getDistanceFrom(np_f);
2597 actionstream<<"Player "<<player->getName()
2598 <<" tried to access node from too far: "
2599 <<"d="<<d<<", max_d="<<max_d
2600 <<". ignoring."<<std::endl;
2601 // Re-send block to revert change on client-side
2602 RemoteClient *client = getClient(peer_id);
2603 v3s16 blockpos = getNodeBlockPos(p_under);
2604 client->SetBlockNotSent(blockpos);
2616 NOTE: This can be used in the future to check if
2617 somebody is cheating, by checking the timing.
2619 bool cannot_punch_node = false;
2621 MapNode n(CONTENT_IGNORE);
2625 n = m_env->getMap().getNode(p_under);
2627 catch(InvalidPositionException &e)
2629 infostream<<"Server: Not punching: Node not found."
2630 <<" Adding block to emerge queue."
2632 m_emerge_queue.addBlock(peer_id,
2633 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2634 cannot_punch_node = true;
2637 if(cannot_punch_node)
2643 scriptapi_environment_on_punchnode(m_lua, p_under, n, srp);
2650 else if(action == 2)
2653 RemoteClient *client = getClient(peer_id);
2654 JMutexAutoLock digmutex(client->m_dig_mutex);
2655 client->m_dig_tool_item = -1;
2660 3: Digging completed
2662 else if(action == 3)
2664 // Mandatory parameter; actually used for nothing
2665 core::map<v3s16, MapBlock*> modified_blocks;
2667 content_t material = CONTENT_IGNORE;
2668 u8 mineral = MINERAL_NONE;
2670 bool cannot_remove_node = false;
2672 MapNode n(CONTENT_IGNORE);
2675 n = m_env->getMap().getNode(p_under);
2677 mineral = n.getMineral(m_nodedef);
2678 // Get material at position
2679 material = n.getContent();
2680 // If not yet cancelled
2681 if(cannot_remove_node == false)
2683 // If it's not diggable, do nothing
2684 if(m_nodedef->get(material).diggable == false)
2686 infostream<<"Server: Not finishing digging: "
2687 <<"Node not diggable"
2689 cannot_remove_node = true;
2692 // If not yet cancelled
2693 if(cannot_remove_node == false)
2695 // Get node metadata
2696 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p_under);
2697 if(meta && meta->nodeRemovalDisabled() == true)
2699 infostream<<"Server: Not finishing digging: "
2700 <<"Node metadata disables removal"
2702 cannot_remove_node = true;
2706 catch(InvalidPositionException &e)
2708 infostream<<"Server: Not finishing digging: Node not found."
2709 <<" Adding block to emerge queue."
2711 m_emerge_queue.addBlock(peer_id,
2712 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2713 cannot_remove_node = true;
2716 // Make sure the player is allowed to do it
2717 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2719 infostream<<"Player "<<player->getName()<<" cannot remove node"
2720 <<" because privileges are "<<getPlayerPrivs(player)
2722 cannot_remove_node = true;
2726 If node can't be removed, set block to be re-sent to
2729 if(cannot_remove_node)
2731 infostream<<"Server: Not finishing digging."<<std::endl;
2733 // Client probably has wrong data.
2734 // Set block not sent, so that client will get
2736 infostream<<"Client "<<peer_id<<" tried to dig "
2737 <<"node; but node cannot be removed."
2738 <<" setting MapBlock not sent."<<std::endl;
2739 RemoteClient *client = getClient(peer_id);
2740 v3s16 blockpos = getNodeBlockPos(p_under);
2741 client->SetBlockNotSent(blockpos);
2746 actionstream<<player->getName()<<" digs "<<PP(p_under)
2747 <<", gets material "<<(int)material<<", mineral "
2748 <<(int)mineral<<std::endl;
2751 Send the removal to all close-by players.
2752 - If other player is close, send REMOVENODE
2753 - Otherwise set blocks not sent
2755 core::list<u16> far_players;
2756 sendRemoveNode(p_under, peer_id, &far_players, 30);
2759 Update and send inventory
2762 if(g_settings->getBool("creative_mode") == false)
2767 InventoryList *mlist = player->inventory.getList("main");
2770 InventoryItem *item = mlist->getItem(item_i);
2771 if(item && (std::string)item->getName() == "ToolItem")
2773 ToolItem *titem = (ToolItem*)item;
2774 std::string toolname = titem->getToolName();
2776 // Get digging properties for material and tool
2777 ToolDiggingProperties tp =
2778 m_toolmgr->getDiggingProperties(toolname);
2779 DiggingProperties prop =
2780 getDiggingProperties(material, &tp, m_nodedef);
2782 if(prop.diggable == false)
2784 infostream<<"Server: WARNING: Player digged"
2785 <<" with impossible material + tool"
2786 <<" combination"<<std::endl;
2789 bool weared_out = titem->addWear(prop.wear);
2793 mlist->deleteItem(item_i);
2799 Add dug item to inventory
2802 InventoryItem *item = NULL;
2804 if(mineral != MINERAL_NONE)
2805 item = getDiggedMineralItem(mineral, this);
2810 const std::string &dug_s = m_nodedef->get(material).dug_item;
2813 std::istringstream is(dug_s, std::ios::binary);
2814 item = InventoryItem::deSerialize(is, this);
2820 // Add a item to inventory
2821 player->inventory.addItem("main", item);
2824 UpdateCrafting(player->peer_id);
2825 SendInventory(player->peer_id);
2830 if(mineral != MINERAL_NONE)
2831 item = getDiggedMineralItem(mineral, this);
2836 const std::string &extra_dug_s = m_nodedef->get(material).extra_dug_item;
2837 s32 extra_rarity = m_nodedef->get(material).extra_dug_item_rarity;
2838 if(extra_dug_s != "" && extra_rarity != 0
2839 && myrand() % extra_rarity == 0)
2841 std::istringstream is(extra_dug_s, std::ios::binary);
2842 item = InventoryItem::deSerialize(is, this);
2848 // Add a item to inventory
2849 player->inventory.addItem("main", item);
2852 UpdateCrafting(player->peer_id);
2853 SendInventory(player->peer_id);
2859 (this takes some time so it is done after the quick stuff)
2862 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
2864 m_env->getMap().removeNodeAndUpdate(p_under, modified_blocks);
2867 Set blocks not sent to far players
2869 for(core::list<u16>::Iterator
2870 i = far_players.begin();
2871 i != far_players.end(); i++)
2874 RemoteClient *client = getClient(peer_id);
2877 client->SetBlocksNotSent(modified_blocks);
2883 scriptapi_environment_on_dignode(m_lua, p_under, n, srp);
2889 else if(action == 1)
2892 InventoryList *ilist = player->inventory.getList("main");
2897 InventoryItem *item = ilist->getItem(item_i);
2899 // If there is no item, it is not possible to add it anywhere
2904 Handle material items
2906 if(std::string("MaterialItem") == item->getName())
2909 // Don't add a node if this is not a free space
2910 MapNode n2 = m_env->getMap().getNode(p_over);
2911 bool no_enough_privs =
2912 ((getPlayerPrivs(player) & PRIV_BUILD)==0);
2914 infostream<<"Player "<<player->getName()<<" cannot add node"
2915 <<" because privileges are "<<getPlayerPrivs(player)
2918 if(m_nodedef->get(n2).buildable_to == false
2921 // Client probably has wrong data.
2922 // Set block not sent, so that client will get
2924 infostream<<"Client "<<peer_id<<" tried to place"
2925 <<" node in invalid position; setting"
2926 <<" MapBlock not sent."<<std::endl;
2927 RemoteClient *client = getClient(peer_id);
2928 v3s16 blockpos = getNodeBlockPos(p_over);
2929 client->SetBlockNotSent(blockpos);
2933 catch(InvalidPositionException &e)
2935 infostream<<"Server: Ignoring ADDNODE: Node not found"
2936 <<" Adding block to emerge queue."
2938 m_emerge_queue.addBlock(peer_id,
2939 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2943 // Reset build time counter
2944 getClient(peer_id)->m_time_from_building = 0.0;
2947 MaterialItem *mitem = (MaterialItem*)item;
2949 n.setContent(mitem->getMaterial());
2951 actionstream<<player->getName()<<" places material "
2952 <<(int)mitem->getMaterial()
2953 <<" at "<<PP(p_under)<<std::endl;
2955 // Calculate direction for wall mounted stuff
2956 if(m_nodedef->get(n).wall_mounted)
2957 n.param2 = packDir(p_under - p_over);
2959 // Calculate the direction for furnaces and chests and stuff
2960 if(m_nodedef->get(n).param_type == CPT_FACEDIR_SIMPLE)
2962 v3f playerpos = player->getPosition();
2963 v3f blockpos = intToFloat(p_over, BS) - playerpos;
2964 blockpos = blockpos.normalize();
2966 if (fabs(blockpos.X) > fabs(blockpos.Z)) {
2980 Send to all close-by players
2982 core::list<u16> far_players;
2983 sendAddNode(p_over, n, 0, &far_players, 30);
2988 InventoryList *ilist = player->inventory.getList("main");
2989 if(g_settings->getBool("creative_mode") == false && ilist)
2991 // Remove from inventory and send inventory
2992 if(mitem->getCount() == 1)
2993 ilist->deleteItem(item_i);
2997 UpdateCrafting(peer_id);
2998 SendInventory(peer_id);
3004 This takes some time so it is done after the quick stuff
3006 core::map<v3s16, MapBlock*> modified_blocks;
3008 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
3010 std::string p_name = std::string(player->getName());
3011 m_env->getMap().addNodeAndUpdate(p_over, n, modified_blocks, p_name);
3014 Set blocks not sent to far players
3016 for(core::list<u16>::Iterator
3017 i = far_players.begin();
3018 i != far_players.end(); i++)
3021 RemoteClient *client = getClient(peer_id);
3024 client->SetBlocksNotSent(modified_blocks);
3030 scriptapi_environment_on_placenode(m_lua, p_over, n, srp);
3033 Calculate special events
3036 /*if(n.d == LEGN(m_nodedef, "CONTENT_MESE"))
3039 for(s16 z=-1; z<=1; z++)
3040 for(s16 y=-1; y<=1; y++)
3041 for(s16 x=-1; x<=1; x++)
3048 Place other item (not a block)
3052 v3s16 blockpos = getNodeBlockPos(p_over);
3055 Check that the block is loaded so that the item
3056 can properly be added to the static list too
3058 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3061 infostream<<"Error while placing object: "
3062 "block not found"<<std::endl;
3067 If in creative mode, item dropping is disabled unless
3068 player has build privileges
3070 if(g_settings->getBool("creative_mode") &&
3071 (getPlayerPrivs(player) & PRIV_BUILD) == 0)
3073 infostream<<"Not allowing player to drop item: "
3074 "creative mode and no build privs"<<std::endl;
3078 // Calculate a position for it
3079 v3f pos = intToFloat(p_over, BS);
3081 /*pos.Y -= BS*0.25; // let it drop a bit
3083 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
3084 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;*/
3089 ServerActiveObject *obj = item->createSAO(m_env, pos);
3093 infostream<<"WARNING: item resulted in NULL object, "
3094 <<"not placing onto map"
3099 actionstream<<player->getName()<<" places "<<item->getName()
3100 <<" at "<<PP(p_over)<<std::endl;
3102 // Add the object to the environment
3103 m_env->addActiveObject(obj);
3105 infostream<<"Placed object"<<std::endl;
3107 if(g_settings->getBool("creative_mode") == false)
3109 // Delete the right amount of items from the slot
3110 u16 dropcount = item->getDropCount();
3112 // Delete item if all gone
3113 if(item->getCount() <= dropcount)
3115 if(item->getCount() < dropcount)
3116 infostream<<"WARNING: Server: dropped more items"
3117 <<" than the slot contains"<<std::endl;
3119 InventoryList *ilist = player->inventory.getList("main");
3121 // Remove from inventory and send inventory
3122 ilist->deleteItem(item_i);
3124 // Else decrement it
3126 item->remove(dropcount);
3129 UpdateCrafting(peer_id);
3130 SendInventory(peer_id);
3138 Catch invalid actions
3142 infostream<<"WARNING: Server: Invalid action "
3143 <<action<<std::endl;
3147 else if(command == TOSERVER_RELEASE)
3156 infostream<<"TOSERVER_RELEASE ignored"<<std::endl;
3159 else if(command == TOSERVER_SIGNTEXT)
3161 infostream<<"Server: TOSERVER_SIGNTEXT not supported anymore"
3165 else if(command == TOSERVER_SIGNNODETEXT)
3167 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
3175 std::string datastring((char*)&data[2], datasize-2);
3176 std::istringstream is(datastring, std::ios_base::binary);
3179 is.read((char*)buf, 6);
3180 v3s16 p = readV3S16(buf);
3181 is.read((char*)buf, 2);
3182 u16 textlen = readU16(buf);
3184 for(u16 i=0; i<textlen; i++)
3186 is.read((char*)buf, 1);
3187 text += (char)buf[0];
3190 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3193 if(meta->typeId() != LEGN(m_nodedef, "CONTENT_SIGN_WALL"))
3195 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
3196 signmeta->setText(text);
3198 actionstream<<player->getName()<<" writes \""<<text<<"\" to sign "
3199 <<" at "<<PP(p)<<std::endl;
3201 v3s16 blockpos = getNodeBlockPos(p);
3202 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3205 block->raiseModified(MOD_STATE_WRITE_NEEDED,
3209 setBlockNotSent(blockpos);
3211 else if(command == TOSERVER_INVENTORY_ACTION)
3213 /*// Ignore inventory changes if in creative mode
3214 if(g_settings->getBool("creative_mode") == true)
3216 infostream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
3220 // Strip command and create a stream
3221 std::string datastring((char*)&data[2], datasize-2);
3222 infostream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
3223 std::istringstream is(datastring, std::ios_base::binary);
3225 InventoryAction *a = InventoryAction::deSerialize(is);
3230 c.current_player = player;
3233 Handle craftresult specially if not in creative mode
3235 bool disable_action = false;
3236 if(a->getType() == IACTION_MOVE
3237 && g_settings->getBool("creative_mode") == false)
3239 IMoveAction *ma = (IMoveAction*)a;
3240 if(ma->to_inv == "current_player" &&
3241 ma->from_inv == "current_player")
3243 InventoryList *rlist = player->inventory.getList("craftresult");
3245 InventoryList *clist = player->inventory.getList("craft");
3247 InventoryList *mlist = player->inventory.getList("main");
3250 Craftresult is no longer preview if something
3253 if(ma->to_list == "craftresult"
3254 && ma->from_list != "craftresult")
3256 // If it currently is a preview, remove
3258 if(player->craftresult_is_preview)
3260 rlist->deleteItem(0);
3262 player->craftresult_is_preview = false;
3265 Crafting takes place if this condition is true.
3267 if(player->craftresult_is_preview &&
3268 ma->from_list == "craftresult")
3270 player->craftresult_is_preview = false;
3271 clist->decrementMaterials(1);
3273 /* Print out action */
3274 InventoryList *list =
3275 player->inventory.getList("craftresult");
3277 InventoryItem *item = list->getItem(0);
3278 std::string itemname = "NULL";
3280 itemname = item->getName();
3281 actionstream<<player->getName()<<" crafts "
3282 <<itemname<<std::endl;
3285 If the craftresult is placed on itself, move it to
3286 main inventory instead of doing the action
3288 if(ma->to_list == "craftresult"
3289 && ma->from_list == "craftresult")
3291 disable_action = true;
3293 InventoryItem *item1 = rlist->changeItem(0, NULL);
3294 mlist->addItem(item1);
3297 // Disallow moving items if not allowed to build
3298 else if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
3300 disable_action = true;
3302 // if it's a locking chest, only allow the owner or server admins to move items
3303 else if (ma->from_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
3305 Strfnd fn(ma->from_inv);
3306 std::string id0 = fn.next(":");
3307 if(id0 == "nodemeta")
3310 p.X = stoi(fn.next(","));
3311 p.Y = stoi(fn.next(","));
3312 p.Z = stoi(fn.next(","));
3313 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3314 if(meta && meta->typeId() == LEGN(m_nodedef, "CONTENT_LOCKABLE_CHEST")) {
3315 LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
3316 if (lcm->getOwner() != player->getName())
3317 disable_action = true;
3321 else if (ma->to_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
3323 Strfnd fn(ma->to_inv);
3324 std::string id0 = fn.next(":");
3325 if(id0 == "nodemeta")
3328 p.X = stoi(fn.next(","));
3329 p.Y = stoi(fn.next(","));
3330 p.Z = stoi(fn.next(","));
3331 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3332 if(meta && meta->typeId() == LEGN(m_nodedef, "CONTENT_LOCKABLE_CHEST")) {
3333 LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
3334 if (lcm->getOwner() != player->getName())
3335 disable_action = true;
3341 if(a->getType() == IACTION_DROP)
3343 IDropAction *da = (IDropAction*)a;
3344 // Disallow dropping items if not allowed to build
3345 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
3347 disable_action = true;
3349 // if it's a locking chest, only allow the owner or server admins to drop items
3350 else if (da->from_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
3352 Strfnd fn(da->from_inv);
3353 std::string id0 = fn.next(":");
3354 if(id0 == "nodemeta")
3357 p.X = stoi(fn.next(","));
3358 p.Y = stoi(fn.next(","));
3359 p.Z = stoi(fn.next(","));
3360 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3361 if(meta && meta->typeId() == LEGN(m_nodedef, "CONTENT_LOCKABLE_CHEST")) {
3362 LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
3363 if (lcm->getOwner() != player->getName())
3364 disable_action = true;
3370 if(disable_action == false)
3372 // Feed action to player inventory
3373 a->apply(&c, this, m_env);
3378 UpdateCrafting(player->peer_id);
3379 SendInventory(player->peer_id);
3387 infostream<<"TOSERVER_INVENTORY_ACTION: "
3388 <<"InventoryAction::deSerialize() returned NULL"
3392 else if(command == TOSERVER_CHAT_MESSAGE)
3400 std::string datastring((char*)&data[2], datasize-2);
3401 std::istringstream is(datastring, std::ios_base::binary);
3404 is.read((char*)buf, 2);
3405 u16 len = readU16(buf);
3407 std::wstring message;
3408 for(u16 i=0; i<len; i++)
3410 is.read((char*)buf, 2);
3411 message += (wchar_t)readU16(buf);
3414 // Get player name of this client
3415 std::wstring name = narrow_to_wide(player->getName());
3418 bool ate = scriptapi_on_chat_message(m_lua, player->getName(),
3419 wide_to_narrow(message));
3420 // If script ate the message, don't proceed
3424 // Line to send to players
3426 // Whether to send to the player that sent the line
3427 bool send_to_sender = false;
3428 // Whether to send to other players
3429 bool send_to_others = false;
3431 // Local player gets all privileges regardless of
3432 // what's set on their account.
3433 u64 privs = getPlayerPrivs(player);
3436 if(message[0] == L'/')
3438 size_t strip_size = 1;
3439 if (message[1] == L'#') // support old-style commans
3441 message = message.substr(strip_size);
3443 WStrfnd f1(message);
3444 f1.next(L" "); // Skip over /#whatever
3445 std::wstring paramstring = f1.next(L"");
3447 ServerCommandContext *ctx = new ServerCommandContext(
3448 str_split(message, L' '),
3455 std::wstring reply(processServerCommand(ctx));
3456 send_to_sender = ctx->flags & SEND_TO_SENDER;
3457 send_to_others = ctx->flags & SEND_TO_OTHERS;
3459 if (ctx->flags & SEND_NO_PREFIX)
3462 line += L"Server: " + reply;
3469 if(privs & PRIV_SHOUT)
3475 send_to_others = true;
3479 line += L"Server: You are not allowed to shout";
3480 send_to_sender = true;
3487 actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
3490 Send the message to clients
3492 for(core::map<u16, RemoteClient*>::Iterator
3493 i = m_clients.getIterator();
3494 i.atEnd() == false; i++)
3496 // Get client and check that it is valid
3497 RemoteClient *client = i.getNode()->getValue();
3498 assert(client->peer_id == i.getNode()->getKey());
3499 if(client->serialization_version == SER_FMT_VER_INVALID)
3503 bool sender_selected = (peer_id == client->peer_id);
3504 if(sender_selected == true && send_to_sender == false)
3506 if(sender_selected == false && send_to_others == false)
3509 SendChatMessage(client->peer_id, line);
3513 else if(command == TOSERVER_DAMAGE)
3515 std::string datastring((char*)&data[2], datasize-2);
3516 std::istringstream is(datastring, std::ios_base::binary);
3517 u8 damage = readU8(is);
3519 if(g_settings->getBool("enable_damage"))
3521 actionstream<<player->getName()<<" damaged by "
3522 <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
3525 HandlePlayerHP(player, damage);
3529 SendPlayerHP(player);
3532 else if(command == TOSERVER_PASSWORD)
3535 [0] u16 TOSERVER_PASSWORD
3536 [2] u8[28] old password
3537 [30] u8[28] new password
3540 if(datasize != 2+PASSWORD_SIZE*2)
3542 /*char password[PASSWORD_SIZE];
3543 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3544 password[i] = data[2+i];
3545 password[PASSWORD_SIZE-1] = 0;*/
3547 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3555 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3557 char c = data[2+PASSWORD_SIZE+i];
3563 infostream<<"Server: Client requests a password change from "
3564 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
3566 std::string playername = player->getName();
3568 if(m_authmanager.exists(playername) == false)
3570 infostream<<"Server: playername not found in authmanager"<<std::endl;
3571 // Wrong old password supplied!!
3572 SendChatMessage(peer_id, L"playername not found in authmanager");
3576 std::string checkpwd = m_authmanager.getPassword(playername);
3578 if(oldpwd != checkpwd)
3580 infostream<<"Server: invalid old password"<<std::endl;
3581 // Wrong old password supplied!!
3582 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
3586 actionstream<<player->getName()<<" changes password"<<std::endl;
3588 m_authmanager.setPassword(playername, newpwd);
3590 infostream<<"Server: password change successful for "<<playername
3592 SendChatMessage(peer_id, L"Password change successful");
3594 else if(command == TOSERVER_PLAYERITEM)
3599 u16 item = readU16(&data[2]);
3600 player->wieldItem(item);
3601 SendWieldedItem(player);
3603 else if(command == TOSERVER_RESPAWN)
3608 RespawnPlayer(player);
3610 actionstream<<player->getName()<<" respawns at "
3611 <<PP(player->getPosition()/BS)<<std::endl;
3615 infostream<<"Server::ProcessData(): Ignoring "
3616 "unknown command "<<command<<std::endl;
3620 catch(SendFailedException &e)
3622 errorstream<<"Server::ProcessData(): SendFailedException: "
3628 void Server::onMapEditEvent(MapEditEvent *event)
3630 //infostream<<"Server::onMapEditEvent()"<<std::endl;
3631 if(m_ignore_map_edit_events)
3633 MapEditEvent *e = event->clone();
3634 m_unsent_map_edit_queue.push_back(e);
3637 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3639 if(id == "current_player")
3641 assert(c->current_player);
3642 return &(c->current_player->inventory);
3646 std::string id0 = fn.next(":");
3648 if(id0 == "nodemeta")
3651 p.X = stoi(fn.next(","));
3652 p.Y = stoi(fn.next(","));
3653 p.Z = stoi(fn.next(","));
3654 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3656 return meta->getInventory();
3657 infostream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3658 <<"no metadata found"<<std::endl;
3662 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3665 void Server::inventoryModified(InventoryContext *c, std::string id)
3667 if(id == "current_player")
3669 assert(c->current_player);
3671 UpdateCrafting(c->current_player->peer_id);
3672 SendInventory(c->current_player->peer_id);
3677 std::string id0 = fn.next(":");
3679 if(id0 == "nodemeta")
3682 p.X = stoi(fn.next(","));
3683 p.Y = stoi(fn.next(","));
3684 p.Z = stoi(fn.next(","));
3685 v3s16 blockpos = getNodeBlockPos(p);
3687 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3689 meta->inventoryModified();
3691 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3693 block->raiseModified(MOD_STATE_WRITE_NEEDED);
3695 setBlockNotSent(blockpos);
3700 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3703 core::list<PlayerInfo> Server::getPlayerInfo()
3705 DSTACK(__FUNCTION_NAME);
3706 JMutexAutoLock envlock(m_env_mutex);
3707 JMutexAutoLock conlock(m_con_mutex);
3709 core::list<PlayerInfo> list;
3711 core::list<Player*> players = m_env->getPlayers();
3713 core::list<Player*>::Iterator i;
3714 for(i = players.begin();
3715 i != players.end(); i++)
3719 Player *player = *i;
3722 // Copy info from connection to info struct
3723 info.id = player->peer_id;
3724 info.address = m_con.GetPeerAddress(player->peer_id);
3725 info.avg_rtt = m_con.GetPeerAvgRTT(player->peer_id);
3727 catch(con::PeerNotFoundException &e)
3729 // Set dummy peer info
3731 info.address = Address(0,0,0,0,0);
3735 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3736 info.position = player->getPosition();
3738 list.push_back(info);
3745 void Server::peerAdded(con::Peer *peer)
3747 DSTACK(__FUNCTION_NAME);
3748 infostream<<"Server::peerAdded(): peer->id="
3749 <<peer->id<<std::endl;
3752 c.type = PEER_ADDED;
3753 c.peer_id = peer->id;
3755 m_peer_change_queue.push_back(c);
3758 void Server::deletingPeer(con::Peer *peer, bool timeout)
3760 DSTACK(__FUNCTION_NAME);
3761 infostream<<"Server::deletingPeer(): peer->id="
3762 <<peer->id<<", timeout="<<timeout<<std::endl;
3765 c.type = PEER_REMOVED;
3766 c.peer_id = peer->id;
3767 c.timeout = timeout;
3768 m_peer_change_queue.push_back(c);
3775 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3777 DSTACK(__FUNCTION_NAME);
3778 std::ostringstream os(std::ios_base::binary);
3780 writeU16(os, TOCLIENT_HP);
3784 std::string s = os.str();
3785 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3787 con.Send(peer_id, 0, data, true);
3790 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3791 const std::wstring &reason)
3793 DSTACK(__FUNCTION_NAME);
3794 std::ostringstream os(std::ios_base::binary);
3796 writeU16(os, TOCLIENT_ACCESS_DENIED);
3797 os<<serializeWideString(reason);
3800 std::string s = os.str();
3801 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3803 con.Send(peer_id, 0, data, true);
3806 void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
3807 bool set_camera_point_target, v3f camera_point_target)
3809 DSTACK(__FUNCTION_NAME);
3810 std::ostringstream os(std::ios_base::binary);
3812 writeU16(os, TOCLIENT_DEATHSCREEN);
3813 writeU8(os, set_camera_point_target);
3814 writeV3F1000(os, camera_point_target);
3817 std::string s = os.str();
3818 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3820 con.Send(peer_id, 0, data, true);
3823 void Server::SendToolDef(con::Connection &con, u16 peer_id,
3824 IToolDefManager *tooldef)
3826 DSTACK(__FUNCTION_NAME);
3827 std::ostringstream os(std::ios_base::binary);
3831 u32 length of the next item
3832 serialized ToolDefManager
3834 writeU16(os, TOCLIENT_TOOLDEF);
3835 std::ostringstream tmp_os(std::ios::binary);
3836 tooldef->serialize(tmp_os);
3837 os<<serializeLongString(tmp_os.str());
3840 std::string s = os.str();
3841 infostream<<"Server::SendToolDef(): Sending tool definitions: size="
3842 <<s.size()<<std::endl;
3843 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3845 con.Send(peer_id, 0, data, true);
3848 void Server::SendNodeDef(con::Connection &con, u16 peer_id,
3849 INodeDefManager *nodedef)
3851 DSTACK(__FUNCTION_NAME);
3852 std::ostringstream os(std::ios_base::binary);
3856 u32 length of the next item
3857 serialized NodeDefManager
3859 writeU16(os, TOCLIENT_NODEDEF);
3860 std::ostringstream tmp_os(std::ios::binary);
3861 nodedef->serialize(tmp_os);
3862 os<<serializeLongString(tmp_os.str());
3865 std::string s = os.str();
3866 infostream<<"Server::SendNodeDef(): Sending node definitions: size="
3867 <<s.size()<<std::endl;
3868 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3870 con.Send(peer_id, 0, data, true);
3874 Non-static send methods
3877 void Server::SendObjectData(float dtime)
3879 DSTACK(__FUNCTION_NAME);
3881 core::map<v3s16, bool> stepped_blocks;
3883 for(core::map<u16, RemoteClient*>::Iterator
3884 i = m_clients.getIterator();
3885 i.atEnd() == false; i++)
3887 u16 peer_id = i.getNode()->getKey();
3888 RemoteClient *client = i.getNode()->getValue();
3889 assert(client->peer_id == peer_id);
3891 if(client->serialization_version == SER_FMT_VER_INVALID)
3894 client->SendObjectData(this, dtime, stepped_blocks);
3898 void Server::SendPlayerInfos()
3900 DSTACK(__FUNCTION_NAME);
3902 //JMutexAutoLock envlock(m_env_mutex);
3904 // Get connected players
3905 core::list<Player*> players = m_env->getPlayers(true);
3907 u32 player_count = players.getSize();
3908 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3910 SharedBuffer<u8> data(datasize);
3911 writeU16(&data[0], TOCLIENT_PLAYERINFO);
3914 core::list<Player*>::Iterator i;
3915 for(i = players.begin();
3916 i != players.end(); i++)
3918 Player *player = *i;
3920 /*infostream<<"Server sending player info for player with "
3921 "peer_id="<<player->peer_id<<std::endl;*/
3923 writeU16(&data[start], player->peer_id);
3924 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3925 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3926 start += 2+PLAYERNAME_SIZE;
3929 //JMutexAutoLock conlock(m_con_mutex);
3932 m_con.SendToAll(0, data, true);
3935 void Server::SendInventory(u16 peer_id)
3937 DSTACK(__FUNCTION_NAME);
3939 Player* player = m_env->getPlayer(peer_id);
3946 std::ostringstream os;
3947 //os.imbue(std::locale("C"));
3949 player->inventory.serialize(os);
3951 std::string s = os.str();
3953 SharedBuffer<u8> data(s.size()+2);
3954 writeU16(&data[0], TOCLIENT_INVENTORY);
3955 memcpy(&data[2], s.c_str(), s.size());
3958 m_con.Send(peer_id, 0, data, true);
3961 std::string getWieldedItemString(const Player *player)
3963 const InventoryItem *item = player->getWieldItem();
3965 return std::string("");
3966 std::ostringstream os(std::ios_base::binary);
3967 item->serialize(os);
3971 void Server::SendWieldedItem(const Player* player)
3973 DSTACK(__FUNCTION_NAME);
3977 std::ostringstream os(std::ios_base::binary);
3979 writeU16(os, TOCLIENT_PLAYERITEM);
3981 writeU16(os, player->peer_id);
3982 os<<serializeString(getWieldedItemString(player));
3985 std::string s = os.str();
3986 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3988 m_con.SendToAll(0, data, true);
3991 void Server::SendPlayerItems()
3993 DSTACK(__FUNCTION_NAME);
3995 std::ostringstream os(std::ios_base::binary);
3996 core::list<Player *> players = m_env->getPlayers(true);
3998 writeU16(os, TOCLIENT_PLAYERITEM);
3999 writeU16(os, players.size());
4000 core::list<Player *>::Iterator i;
4001 for(i = players.begin(); i != players.end(); ++i)
4004 writeU16(os, p->peer_id);
4005 os<<serializeString(getWieldedItemString(p));
4009 std::string s = os.str();
4010 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4012 m_con.SendToAll(0, data, true);
4015 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
4017 DSTACK(__FUNCTION_NAME);
4019 std::ostringstream os(std::ios_base::binary);
4023 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
4024 os.write((char*)buf, 2);
4027 writeU16(buf, message.size());
4028 os.write((char*)buf, 2);
4031 for(u32 i=0; i<message.size(); i++)
4035 os.write((char*)buf, 2);
4039 std::string s = os.str();
4040 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4042 m_con.Send(peer_id, 0, data, true);
4045 void Server::BroadcastChatMessage(const std::wstring &message)
4047 for(core::map<u16, RemoteClient*>::Iterator
4048 i = m_clients.getIterator();
4049 i.atEnd() == false; i++)
4051 // Get client and check that it is valid
4052 RemoteClient *client = i.getNode()->getValue();
4053 assert(client->peer_id == i.getNode()->getKey());
4054 if(client->serialization_version == SER_FMT_VER_INVALID)
4057 SendChatMessage(client->peer_id, message);
4061 void Server::SendPlayerHP(Player *player)
4063 SendHP(m_con, player->peer_id, player->hp);
4066 void Server::SendMovePlayer(Player *player)
4068 DSTACK(__FUNCTION_NAME);
4069 std::ostringstream os(std::ios_base::binary);
4071 writeU16(os, TOCLIENT_MOVE_PLAYER);
4072 writeV3F1000(os, player->getPosition());
4073 writeF1000(os, player->getPitch());
4074 writeF1000(os, player->getYaw());
4077 v3f pos = player->getPosition();
4078 f32 pitch = player->getPitch();
4079 f32 yaw = player->getYaw();
4080 infostream<<"Server sending TOCLIENT_MOVE_PLAYER"
4081 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
4088 std::string s = os.str();
4089 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4091 m_con.Send(player->peer_id, 0, data, true);
4094 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
4095 core::list<u16> *far_players, float far_d_nodes)
4097 float maxd = far_d_nodes*BS;
4098 v3f p_f = intToFloat(p, BS);
4102 SharedBuffer<u8> reply(replysize);
4103 writeU16(&reply[0], TOCLIENT_REMOVENODE);
4104 writeS16(&reply[2], p.X);
4105 writeS16(&reply[4], p.Y);
4106 writeS16(&reply[6], p.Z);
4108 for(core::map<u16, RemoteClient*>::Iterator
4109 i = m_clients.getIterator();
4110 i.atEnd() == false; i++)
4112 // Get client and check that it is valid
4113 RemoteClient *client = i.getNode()->getValue();
4114 assert(client->peer_id == i.getNode()->getKey());
4115 if(client->serialization_version == SER_FMT_VER_INVALID)
4118 // Don't send if it's the same one
4119 if(client->peer_id == ignore_id)
4125 Player *player = m_env->getPlayer(client->peer_id);
4128 // If player is far away, only set modified blocks not sent
4129 v3f player_pos = player->getPosition();
4130 if(player_pos.getDistanceFrom(p_f) > maxd)
4132 far_players->push_back(client->peer_id);
4139 m_con.Send(client->peer_id, 0, reply, true);
4143 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
4144 core::list<u16> *far_players, float far_d_nodes)
4146 float maxd = far_d_nodes*BS;
4147 v3f p_f = intToFloat(p, BS);
4149 for(core::map<u16, RemoteClient*>::Iterator
4150 i = m_clients.getIterator();
4151 i.atEnd() == false; i++)
4153 // Get client and check that it is valid
4154 RemoteClient *client = i.getNode()->getValue();
4155 assert(client->peer_id == i.getNode()->getKey());
4156 if(client->serialization_version == SER_FMT_VER_INVALID)
4159 // Don't send if it's the same one
4160 if(client->peer_id == ignore_id)
4166 Player *player = m_env->getPlayer(client->peer_id);
4169 // If player is far away, only set modified blocks not sent
4170 v3f player_pos = player->getPosition();
4171 if(player_pos.getDistanceFrom(p_f) > maxd)
4173 far_players->push_back(client->peer_id);
4180 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
4181 SharedBuffer<u8> reply(replysize);
4182 writeU16(&reply[0], TOCLIENT_ADDNODE);
4183 writeS16(&reply[2], p.X);
4184 writeS16(&reply[4], p.Y);
4185 writeS16(&reply[6], p.Z);
4186 n.serialize(&reply[8], client->serialization_version);
4189 m_con.Send(client->peer_id, 0, reply, true);
4193 void Server::setBlockNotSent(v3s16 p)
4195 for(core::map<u16, RemoteClient*>::Iterator
4196 i = m_clients.getIterator();
4197 i.atEnd()==false; i++)
4199 RemoteClient *client = i.getNode()->getValue();
4200 client->SetBlockNotSent(p);
4204 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
4206 DSTACK(__FUNCTION_NAME);
4208 v3s16 p = block->getPos();
4212 bool completely_air = true;
4213 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4214 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4215 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4217 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
4219 completely_air = false;
4220 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
4225 infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
4227 infostream<<"[completely air] ";
4228 infostream<<std::endl;
4232 Create a packet with the block in the right format
4235 std::ostringstream os(std::ios_base::binary);
4236 block->serialize(os, ver);
4237 std::string s = os.str();
4238 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
4240 u32 replysize = 8 + blockdata.getSize();
4241 SharedBuffer<u8> reply(replysize);
4242 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
4243 writeS16(&reply[2], p.X);
4244 writeS16(&reply[4], p.Y);
4245 writeS16(&reply[6], p.Z);
4246 memcpy(&reply[8], *blockdata, blockdata.getSize());
4248 /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4249 <<": \tpacket size: "<<replysize<<std::endl;*/
4254 m_con.Send(peer_id, 1, reply, true);
4257 void Server::SendBlocks(float dtime)
4259 DSTACK(__FUNCTION_NAME);
4261 JMutexAutoLock envlock(m_env_mutex);
4262 JMutexAutoLock conlock(m_con_mutex);
4264 //TimeTaker timer("Server::SendBlocks");
4266 core::array<PrioritySortedBlockTransfer> queue;
4268 s32 total_sending = 0;
4271 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
4273 for(core::map<u16, RemoteClient*>::Iterator
4274 i = m_clients.getIterator();
4275 i.atEnd() == false; i++)
4277 RemoteClient *client = i.getNode()->getValue();
4278 assert(client->peer_id == i.getNode()->getKey());
4280 total_sending += client->SendingCount();
4282 if(client->serialization_version == SER_FMT_VER_INVALID)
4285 client->GetNextBlocks(this, dtime, queue);
4290 // Lowest priority number comes first.
4291 // Lowest is most important.
4294 for(u32 i=0; i<queue.size(); i++)
4296 //TODO: Calculate limit dynamically
4297 if(total_sending >= g_settings->getS32
4298 ("max_simultaneous_block_sends_server_total"))
4301 PrioritySortedBlockTransfer q = queue[i];
4303 MapBlock *block = NULL;
4306 block = m_env->getMap().getBlockNoCreate(q.pos);
4308 catch(InvalidPositionException &e)
4313 RemoteClient *client = getClient(q.peer_id);
4315 SendBlockNoLock(q.peer_id, block, client->serialization_version);
4317 client->SentBlock(q.pos);
4323 struct SendableTexture
4329 SendableTexture(const std::string &name_="", const std::string path_="",
4330 const std::string &data_=""):
4337 void Server::SendTextures(u16 peer_id)
4339 DSTACK(__FUNCTION_NAME);
4341 infostream<<"Server::SendTextures(): Sending textures to client"<<std::endl;
4345 // Put 5kB in one bunch (this is not accurate)
4346 u32 bytes_per_bunch = 5000;
4348 core::array< core::list<SendableTexture> > texture_bunches;
4349 texture_bunches.push_back(core::list<SendableTexture>());
4351 u32 texture_size_bunch_total = 0;
4352 core::list<ModSpec> mods = getMods(m_modspaths);
4353 for(core::list<ModSpec>::Iterator i = mods.begin();
4354 i != mods.end(); i++){
4356 std::string texturepath = mod.path + DIR_DELIM + "textures";
4357 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(texturepath);
4358 for(u32 j=0; j<dirlist.size(); j++){
4359 if(dirlist[j].dir) // Ignode dirs
4361 std::string tname = dirlist[j].name;
4362 std::string tpath = texturepath + DIR_DELIM + tname;
4364 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
4365 if(fis.good() == false){
4366 errorstream<<"Server::SendTextures(): Could not open \""
4367 <<tname<<"\" for reading"<<std::endl;
4370 std::ostringstream tmp_os(std::ios_base::binary);
4374 fis.read(buf, 1024);
4375 std::streamsize len = fis.gcount();
4376 tmp_os.write(buf, len);
4377 texture_size_bunch_total += len;
4386 errorstream<<"Server::SendTextures(): Failed to read \""
4387 <<tname<<"\""<<std::endl;
4390 /*infostream<<"Server::SendTextures(): Loaded \""
4391 <<tname<<"\""<<std::endl;*/
4393 texture_bunches[texture_bunches.size()-1].push_back(
4394 SendableTexture(tname, tpath, tmp_os.str()));
4396 // Start next bunch if got enough data
4397 if(texture_size_bunch_total >= bytes_per_bunch){
4398 texture_bunches.push_back(core::list<SendableTexture>());
4399 texture_size_bunch_total = 0;
4404 /* Create and send packets */
4406 u32 num_bunches = texture_bunches.size();
4407 for(u32 i=0; i<num_bunches; i++)
4411 u16 total number of texture bunches
4412 u16 index of this bunch
4413 u32 number of textures in this bunch
4421 std::ostringstream os(std::ios_base::binary);
4423 writeU16(os, TOCLIENT_TEXTURES);
4424 writeU16(os, num_bunches);
4426 writeU32(os, texture_bunches[i].size());
4428 for(core::list<SendableTexture>::Iterator
4429 j = texture_bunches[i].begin();
4430 j != texture_bunches[i].end(); j++){
4431 os<<serializeString(j->name);
4432 os<<serializeLongString(j->data);
4436 std::string s = os.str();
4437 infostream<<"Server::SendTextures(): bunch "<<i<<"/"<<num_bunches
4438 <<" textures="<<texture_bunches[i].size()
4439 <<" size=" <<s.size()<<std::endl;
4440 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4442 m_con.Send(peer_id, 0, data, true);
4450 void Server::HandlePlayerHP(Player *player, s16 damage)
4452 if(player->hp > damage)
4454 player->hp -= damage;
4455 SendPlayerHP(player);
4459 infostream<<"Server::HandlePlayerHP(): Player "
4460 <<player->getName()<<" dies"<<std::endl;
4464 //TODO: Throw items around
4466 // Handle players that are not connected
4467 if(player->peer_id == PEER_ID_INEXISTENT){
4468 RespawnPlayer(player);
4472 SendPlayerHP(player);
4474 RemoteClient *client = getClient(player->peer_id);
4475 if(client->net_proto_version >= 3)
4477 SendDeathscreen(m_con, player->peer_id, false, v3f(0,0,0));
4481 RespawnPlayer(player);
4486 void Server::RespawnPlayer(Player *player)
4489 ServerRemotePlayer *srp = (ServerRemotePlayer*)player;
4490 bool repositioned = scriptapi_on_respawnplayer(m_lua, srp);
4492 v3f pos = findSpawnPos(m_env->getServerMap());
4493 player->setPosition(pos);
4494 srp->m_last_good_position = pos;
4495 srp->m_last_good_position_age = 0;
4497 SendMovePlayer(player);
4498 SendPlayerHP(player);
4501 void Server::UpdateCrafting(u16 peer_id)
4503 DSTACK(__FUNCTION_NAME);
4505 Player* player = m_env->getPlayer(peer_id);
4509 Calculate crafting stuff
4511 if(g_settings->getBool("creative_mode") == false)
4513 InventoryList *clist = player->inventory.getList("craft");
4514 InventoryList *rlist = player->inventory.getList("craftresult");
4516 if(rlist && rlist->getUsedSlots() == 0)
4517 player->craftresult_is_preview = true;
4519 if(rlist && player->craftresult_is_preview)
4521 rlist->clearItems();
4523 if(clist && rlist && player->craftresult_is_preview)
4525 // Get result of crafting grid
4527 std::vector<InventoryItem*> items;
4528 for(u16 i=0; i<9; i++){
4529 if(clist->getItem(i) == NULL)
4530 items.push_back(NULL);
4532 items.push_back(clist->getItem(i)->clone());
4534 CraftPointerInput cpi(3, items);
4536 InventoryItem *result = m_craftdef->getCraftResult(cpi, this);
4537 //InventoryItem *result = craft_get_result(items, this);
4539 rlist->addItem(result);
4542 } // if creative_mode == false
4545 RemoteClient* Server::getClient(u16 peer_id)
4547 DSTACK(__FUNCTION_NAME);
4548 //JMutexAutoLock lock(m_con_mutex);
4549 core::map<u16, RemoteClient*>::Node *n;
4550 n = m_clients.find(peer_id);
4551 // A client should exist for all peers
4553 return n->getValue();
4556 std::wstring Server::getStatusString()
4558 std::wostringstream os(std::ios_base::binary);
4561 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4563 os<<L", uptime="<<m_uptime.get();
4564 // Information about clients
4566 for(core::map<u16, RemoteClient*>::Iterator
4567 i = m_clients.getIterator();
4568 i.atEnd() == false; i++)
4570 // Get client and check that it is valid
4571 RemoteClient *client = i.getNode()->getValue();
4572 assert(client->peer_id == i.getNode()->getKey());
4573 if(client->serialization_version == SER_FMT_VER_INVALID)
4576 Player *player = m_env->getPlayer(client->peer_id);
4577 // Get name of player
4578 std::wstring name = L"unknown";
4580 name = narrow_to_wide(player->getName());
4581 // Add name to information string
4585 if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
4586 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4587 if(g_settings->get("motd") != "")
4588 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4592 // Saves g_settings to configpath given at initialization
4593 void Server::saveConfig()
4595 if(m_configpath != "")
4596 g_settings->updateConfigFile(m_configpath.c_str());
4599 void Server::notifyPlayer(const char *name, const std::wstring msg)
4601 Player *player = m_env->getPlayer(name);
4604 SendChatMessage(player->peer_id, std::wstring(L"Server: -!- ")+msg);
4607 void Server::notifyPlayers(const std::wstring msg)
4609 BroadcastChatMessage(msg);
4612 void Server::queueBlockEmerge(v3s16 blockpos, bool allow_generate)
4616 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
4617 m_emerge_queue.addBlock(PEER_ID_INEXISTENT, blockpos, flags);
4620 // IGameDef interface
4622 IToolDefManager* Server::getToolDefManager()
4626 INodeDefManager* Server::getNodeDefManager()
4630 ICraftDefManager* Server::getCraftDefManager()
4634 ITextureSource* Server::getTextureSource()
4638 u16 Server::allocateUnknownNodeId(const std::string &name)
4640 return m_nodedef->allocateDummy(name);
4643 IWritableToolDefManager* Server::getWritableToolDefManager()
4647 IWritableNodeDefManager* Server::getWritableNodeDefManager()
4651 IWritableCraftDefManager* Server::getWritableCraftDefManager()
4656 v3f findSpawnPos(ServerMap &map)
4658 //return v3f(50,50,50)*BS;
4663 nodepos = v2s16(0,0);
4668 // Try to find a good place a few times
4669 for(s32 i=0; i<1000; i++)
4672 // We're going to try to throw the player to this position
4673 v2s16 nodepos2d = v2s16(-range + (myrand()%(range*2)),
4674 -range + (myrand()%(range*2)));
4675 //v2s16 sectorpos = getNodeSectorPos(nodepos2d);
4676 // Get ground height at point (fallbacks to heightmap function)
4677 s16 groundheight = map.findGroundLevel(nodepos2d);
4678 // Don't go underwater
4679 if(groundheight < WATER_LEVEL)
4681 //infostream<<"-> Underwater"<<std::endl;
4684 // Don't go to high places
4685 if(groundheight > WATER_LEVEL + 4)
4687 //infostream<<"-> Underwater"<<std::endl;
4691 nodepos = v3s16(nodepos2d.X, groundheight-2, nodepos2d.Y);
4692 bool is_good = false;
4694 for(s32 i=0; i<10; i++){
4695 v3s16 blockpos = getNodeBlockPos(nodepos);
4696 map.emergeBlock(blockpos, true);
4697 MapNode n = map.getNodeNoEx(nodepos);
4698 if(n.getContent() == CONTENT_AIR){
4709 // Found a good place
4710 //infostream<<"Searched through "<<i<<" places."<<std::endl;
4716 return intToFloat(nodepos, BS);
4719 Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id)
4722 Try to get an existing player
4724 Player *player = m_env->getPlayer(name);
4727 // If player is already connected, cancel
4728 if(player->peer_id != 0)
4730 infostream<<"emergePlayer(): Player already connected"<<std::endl;
4735 player->peer_id = peer_id;
4737 // Reset inventory to creative if in creative mode
4738 if(g_settings->getBool("creative_mode"))
4740 // Warning: double code below
4741 // Backup actual inventory
4742 player->inventory_backup = new Inventory();
4743 *(player->inventory_backup) = player->inventory;
4744 // Set creative inventory
4745 craft_set_creative_inventory(player, this);
4752 If player with the wanted peer_id already exists, cancel.
4754 if(m_env->getPlayer(peer_id) != NULL)
4756 infostream<<"emergePlayer(): Player with wrong name but same"
4757 " peer_id already exists"<<std::endl;
4765 // Add authentication stuff
4766 m_authmanager.add(name);
4767 m_authmanager.setPassword(name, password);
4768 m_authmanager.setPrivs(name,
4769 stringToPrivs(g_settings->get("default_privs")));
4771 /* Set player position */
4773 infostream<<"Server: Finding spawn place for player \""
4774 <<name<<"\""<<std::endl;
4776 v3f pos = findSpawnPos(m_env->getServerMap());
4778 player = new ServerRemotePlayer(m_env, pos, peer_id, name);
4780 /* Add player to environment */
4781 m_env->addPlayer(player);
4784 ServerRemotePlayer *srp = (ServerRemotePlayer*)player;
4785 scriptapi_on_newplayer(m_lua, srp);
4787 /* Add stuff to inventory */
4788 if(g_settings->getBool("creative_mode"))
4790 // Warning: double code above
4791 // Backup actual inventory
4792 player->inventory_backup = new Inventory();
4793 *(player->inventory_backup) = player->inventory;
4794 // Set creative inventory
4795 craft_set_creative_inventory(player, this);
4800 } // create new player
4803 void Server::handlePeerChange(PeerChange &c)
4805 JMutexAutoLock envlock(m_env_mutex);
4806 JMutexAutoLock conlock(m_con_mutex);
4808 if(c.type == PEER_ADDED)
4815 core::map<u16, RemoteClient*>::Node *n;
4816 n = m_clients.find(c.peer_id);
4817 // The client shouldn't already exist
4821 RemoteClient *client = new RemoteClient();
4822 client->peer_id = c.peer_id;
4823 m_clients.insert(client->peer_id, client);
4826 else if(c.type == PEER_REMOVED)
4833 core::map<u16, RemoteClient*>::Node *n;
4834 n = m_clients.find(c.peer_id);
4835 // The client should exist
4839 Mark objects to be not known by the client
4841 RemoteClient *client = n->getValue();
4843 for(core::map<u16, bool>::Iterator
4844 i = client->m_known_objects.getIterator();
4845 i.atEnd()==false; i++)
4848 u16 id = i.getNode()->getKey();
4849 ServerActiveObject* obj = m_env->getActiveObject(id);
4851 if(obj && obj->m_known_by_count > 0)
4852 obj->m_known_by_count--;
4855 // Collect information about leaving in chat
4856 std::wstring message;
4858 Player *player = m_env->getPlayer(c.peer_id);
4861 std::wstring name = narrow_to_wide(player->getName());
4864 message += L" left game";
4866 message += L" (timed out)";
4872 m_env->removePlayer(c.peer_id);
4875 // Set player client disconnected
4877 Player *player = m_env->getPlayer(c.peer_id);
4879 player->peer_id = 0;
4886 std::ostringstream os(std::ios_base::binary);
4887 for(core::map<u16, RemoteClient*>::Iterator
4888 i = m_clients.getIterator();
4889 i.atEnd() == false; i++)
4891 RemoteClient *client = i.getNode()->getValue();
4892 assert(client->peer_id == i.getNode()->getKey());
4893 if(client->serialization_version == SER_FMT_VER_INVALID)
4896 Player *player = m_env->getPlayer(client->peer_id);
4899 // Get name of player
4900 os<<player->getName()<<" ";
4903 actionstream<<player->getName()<<" "
4904 <<(c.timeout?"times out.":"leaves game.")
4905 <<" List of players: "
4906 <<os.str()<<std::endl;
4911 delete m_clients[c.peer_id];
4912 m_clients.remove(c.peer_id);
4914 // Send player info to all remaining clients
4917 // Send leave chat message to all remaining clients
4918 BroadcastChatMessage(message);
4927 void Server::handlePeerChanges()
4929 while(m_peer_change_queue.size() > 0)
4931 PeerChange c = m_peer_change_queue.pop_front();
4933 infostream<<"Server: Handling peer change: "
4934 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4937 handlePeerChange(c);
4941 u64 Server::getPlayerPrivs(Player *player)
4945 std::string playername = player->getName();
4946 // Local player gets all privileges regardless of
4947 // what's set on their account.
4948 if(g_settings->get("name") == playername)
4954 return getPlayerAuthPrivs(playername);
4958 void dedicated_server_loop(Server &server, bool &kill)
4960 DSTACK(__FUNCTION_NAME);
4962 infostream<<DTIME<<std::endl;
4963 infostream<<"========================"<<std::endl;
4964 infostream<<"Running dedicated server"<<std::endl;
4965 infostream<<"========================"<<std::endl;
4966 infostream<<std::endl;
4968 IntervalLimiter m_profiler_interval;
4972 // This is kind of a hack but can be done like this
4973 // because server.step() is very light
4975 ScopeProfiler sp(g_profiler, "dedicated server sleep");
4980 if(server.getShutdownRequested() || kill)
4982 infostream<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4989 float profiler_print_interval =
4990 g_settings->getFloat("profiler_print_interval");
4991 if(profiler_print_interval != 0)
4993 if(m_profiler_interval.step(0.030, profiler_print_interval))
4995 infostream<<"Profiler:"<<std::endl;
4996 g_profiler->print(infostream);
4997 g_profiler->clear();
5004 static int counter = 0;
5010 core::list<PlayerInfo> list = server.getPlayerInfo();
5011 core::list<PlayerInfo>::Iterator i;
5012 static u32 sum_old = 0;
5013 u32 sum = PIChecksum(list);
5016 infostream<<DTIME<<"Player info:"<<std::endl;
5017 for(i=list.begin(); i!=list.end(); i++)
5019 i->PrintLine(&infostream);