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);
3194 meta->setText(text);
3196 actionstream<<player->getName()<<" writes \""<<text<<"\" to sign"
3197 <<" at "<<PP(p)<<std::endl;
3199 v3s16 blockpos = getNodeBlockPos(p);
3200 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3203 block->raiseModified(MOD_STATE_WRITE_NEEDED,
3207 setBlockNotSent(blockpos);
3209 else if(command == TOSERVER_INVENTORY_ACTION)
3211 /*// Ignore inventory changes if in creative mode
3212 if(g_settings->getBool("creative_mode") == true)
3214 infostream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
3218 // Strip command and create a stream
3219 std::string datastring((char*)&data[2], datasize-2);
3220 infostream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
3221 std::istringstream is(datastring, std::ios_base::binary);
3223 InventoryAction *a = InventoryAction::deSerialize(is);
3228 c.current_player = player;
3231 Handle craftresult specially if not in creative mode
3233 bool disable_action = false;
3234 if(a->getType() == IACTION_MOVE
3235 && g_settings->getBool("creative_mode") == false)
3237 IMoveAction *ma = (IMoveAction*)a;
3238 if(ma->to_inv == "current_player" &&
3239 ma->from_inv == "current_player")
3241 InventoryList *rlist = player->inventory.getList("craftresult");
3243 InventoryList *clist = player->inventory.getList("craft");
3245 InventoryList *mlist = player->inventory.getList("main");
3248 Craftresult is no longer preview if something
3251 if(ma->to_list == "craftresult"
3252 && ma->from_list != "craftresult")
3254 // If it currently is a preview, remove
3256 if(player->craftresult_is_preview)
3258 rlist->deleteItem(0);
3260 player->craftresult_is_preview = false;
3263 Crafting takes place if this condition is true.
3265 if(player->craftresult_is_preview &&
3266 ma->from_list == "craftresult")
3268 player->craftresult_is_preview = false;
3269 clist->decrementMaterials(1);
3271 /* Print out action */
3272 InventoryList *list =
3273 player->inventory.getList("craftresult");
3275 InventoryItem *item = list->getItem(0);
3276 std::string itemname = "NULL";
3278 itemname = item->getName();
3279 actionstream<<player->getName()<<" crafts "
3280 <<itemname<<std::endl;
3283 If the craftresult is placed on itself, move it to
3284 main inventory instead of doing the action
3286 if(ma->to_list == "craftresult"
3287 && ma->from_list == "craftresult")
3289 disable_action = true;
3291 InventoryItem *item1 = rlist->changeItem(0, NULL);
3292 mlist->addItem(item1);
3295 // Disallow moving items if not allowed to build
3296 else if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
3298 disable_action = true;
3300 // if it's a locking chest, only allow the owner or server admins to move items
3301 else if (ma->from_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
3303 Strfnd fn(ma->from_inv);
3304 std::string id0 = fn.next(":");
3305 if(id0 == "nodemeta")
3308 p.X = stoi(fn.next(","));
3309 p.Y = stoi(fn.next(","));
3310 p.Z = stoi(fn.next(","));
3311 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3312 if(meta->getOwner() != ""){
3313 if(meta->getOwner() != player->getName())
3314 disable_action = true;
3318 else if (ma->to_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
3320 Strfnd fn(ma->to_inv);
3321 std::string id0 = fn.next(":");
3322 if(id0 == "nodemeta")
3325 p.X = stoi(fn.next(","));
3326 p.Y = stoi(fn.next(","));
3327 p.Z = stoi(fn.next(","));
3328 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3329 if(meta->getOwner() != ""){
3330 if(meta->getOwner() != player->getName())
3331 disable_action = true;
3337 if(a->getType() == IACTION_DROP)
3339 IDropAction *da = (IDropAction*)a;
3340 // Disallow dropping items if not allowed to build
3341 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
3343 disable_action = true;
3345 // if it's a locking chest, only allow the owner or server admins to drop items
3346 else if (da->from_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
3348 Strfnd fn(da->from_inv);
3349 std::string id0 = fn.next(":");
3350 if(id0 == "nodemeta")
3353 p.X = stoi(fn.next(","));
3354 p.Y = stoi(fn.next(","));
3355 p.Z = stoi(fn.next(","));
3356 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3357 if(meta->getOwner() != ""){
3358 if(meta->getOwner() != player->getName())
3359 disable_action = true;
3365 if(disable_action == false)
3367 // Feed action to player inventory
3368 a->apply(&c, this, m_env);
3373 UpdateCrafting(player->peer_id);
3374 SendInventory(player->peer_id);
3382 infostream<<"TOSERVER_INVENTORY_ACTION: "
3383 <<"InventoryAction::deSerialize() returned NULL"
3387 else if(command == TOSERVER_CHAT_MESSAGE)
3395 std::string datastring((char*)&data[2], datasize-2);
3396 std::istringstream is(datastring, std::ios_base::binary);
3399 is.read((char*)buf, 2);
3400 u16 len = readU16(buf);
3402 std::wstring message;
3403 for(u16 i=0; i<len; i++)
3405 is.read((char*)buf, 2);
3406 message += (wchar_t)readU16(buf);
3409 // Get player name of this client
3410 std::wstring name = narrow_to_wide(player->getName());
3413 bool ate = scriptapi_on_chat_message(m_lua, player->getName(),
3414 wide_to_narrow(message));
3415 // If script ate the message, don't proceed
3419 // Line to send to players
3421 // Whether to send to the player that sent the line
3422 bool send_to_sender = false;
3423 // Whether to send to other players
3424 bool send_to_others = false;
3426 // Local player gets all privileges regardless of
3427 // what's set on their account.
3428 u64 privs = getPlayerPrivs(player);
3431 if(message[0] == L'/')
3433 size_t strip_size = 1;
3434 if (message[1] == L'#') // support old-style commans
3436 message = message.substr(strip_size);
3438 WStrfnd f1(message);
3439 f1.next(L" "); // Skip over /#whatever
3440 std::wstring paramstring = f1.next(L"");
3442 ServerCommandContext *ctx = new ServerCommandContext(
3443 str_split(message, L' '),
3450 std::wstring reply(processServerCommand(ctx));
3451 send_to_sender = ctx->flags & SEND_TO_SENDER;
3452 send_to_others = ctx->flags & SEND_TO_OTHERS;
3454 if (ctx->flags & SEND_NO_PREFIX)
3457 line += L"Server: " + reply;
3464 if(privs & PRIV_SHOUT)
3470 send_to_others = true;
3474 line += L"Server: You are not allowed to shout";
3475 send_to_sender = true;
3482 actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
3485 Send the message to clients
3487 for(core::map<u16, RemoteClient*>::Iterator
3488 i = m_clients.getIterator();
3489 i.atEnd() == false; i++)
3491 // Get client and check that it is valid
3492 RemoteClient *client = i.getNode()->getValue();
3493 assert(client->peer_id == i.getNode()->getKey());
3494 if(client->serialization_version == SER_FMT_VER_INVALID)
3498 bool sender_selected = (peer_id == client->peer_id);
3499 if(sender_selected == true && send_to_sender == false)
3501 if(sender_selected == false && send_to_others == false)
3504 SendChatMessage(client->peer_id, line);
3508 else if(command == TOSERVER_DAMAGE)
3510 std::string datastring((char*)&data[2], datasize-2);
3511 std::istringstream is(datastring, std::ios_base::binary);
3512 u8 damage = readU8(is);
3514 if(g_settings->getBool("enable_damage"))
3516 actionstream<<player->getName()<<" damaged by "
3517 <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
3520 HandlePlayerHP(player, damage);
3524 SendPlayerHP(player);
3527 else if(command == TOSERVER_PASSWORD)
3530 [0] u16 TOSERVER_PASSWORD
3531 [2] u8[28] old password
3532 [30] u8[28] new password
3535 if(datasize != 2+PASSWORD_SIZE*2)
3537 /*char password[PASSWORD_SIZE];
3538 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3539 password[i] = data[2+i];
3540 password[PASSWORD_SIZE-1] = 0;*/
3542 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3550 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3552 char c = data[2+PASSWORD_SIZE+i];
3558 infostream<<"Server: Client requests a password change from "
3559 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
3561 std::string playername = player->getName();
3563 if(m_authmanager.exists(playername) == false)
3565 infostream<<"Server: playername not found in authmanager"<<std::endl;
3566 // Wrong old password supplied!!
3567 SendChatMessage(peer_id, L"playername not found in authmanager");
3571 std::string checkpwd = m_authmanager.getPassword(playername);
3573 if(oldpwd != checkpwd)
3575 infostream<<"Server: invalid old password"<<std::endl;
3576 // Wrong old password supplied!!
3577 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
3581 actionstream<<player->getName()<<" changes password"<<std::endl;
3583 m_authmanager.setPassword(playername, newpwd);
3585 infostream<<"Server: password change successful for "<<playername
3587 SendChatMessage(peer_id, L"Password change successful");
3589 else if(command == TOSERVER_PLAYERITEM)
3594 u16 item = readU16(&data[2]);
3595 player->wieldItem(item);
3596 SendWieldedItem(player);
3598 else if(command == TOSERVER_RESPAWN)
3603 RespawnPlayer(player);
3605 actionstream<<player->getName()<<" respawns at "
3606 <<PP(player->getPosition()/BS)<<std::endl;
3610 infostream<<"Server::ProcessData(): Ignoring "
3611 "unknown command "<<command<<std::endl;
3615 catch(SendFailedException &e)
3617 errorstream<<"Server::ProcessData(): SendFailedException: "
3623 void Server::onMapEditEvent(MapEditEvent *event)
3625 //infostream<<"Server::onMapEditEvent()"<<std::endl;
3626 if(m_ignore_map_edit_events)
3628 MapEditEvent *e = event->clone();
3629 m_unsent_map_edit_queue.push_back(e);
3632 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3634 if(id == "current_player")
3636 assert(c->current_player);
3637 return &(c->current_player->inventory);
3641 std::string id0 = fn.next(":");
3643 if(id0 == "nodemeta")
3646 p.X = stoi(fn.next(","));
3647 p.Y = stoi(fn.next(","));
3648 p.Z = stoi(fn.next(","));
3649 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3651 return meta->getInventory();
3652 infostream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3653 <<"no metadata found"<<std::endl;
3657 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3660 void Server::inventoryModified(InventoryContext *c, std::string id)
3662 if(id == "current_player")
3664 assert(c->current_player);
3666 UpdateCrafting(c->current_player->peer_id);
3667 SendInventory(c->current_player->peer_id);
3672 std::string id0 = fn.next(":");
3674 if(id0 == "nodemeta")
3677 p.X = stoi(fn.next(","));
3678 p.Y = stoi(fn.next(","));
3679 p.Z = stoi(fn.next(","));
3680 v3s16 blockpos = getNodeBlockPos(p);
3682 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3684 meta->inventoryModified();
3686 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3688 block->raiseModified(MOD_STATE_WRITE_NEEDED);
3690 setBlockNotSent(blockpos);
3695 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3698 core::list<PlayerInfo> Server::getPlayerInfo()
3700 DSTACK(__FUNCTION_NAME);
3701 JMutexAutoLock envlock(m_env_mutex);
3702 JMutexAutoLock conlock(m_con_mutex);
3704 core::list<PlayerInfo> list;
3706 core::list<Player*> players = m_env->getPlayers();
3708 core::list<Player*>::Iterator i;
3709 for(i = players.begin();
3710 i != players.end(); i++)
3714 Player *player = *i;
3717 // Copy info from connection to info struct
3718 info.id = player->peer_id;
3719 info.address = m_con.GetPeerAddress(player->peer_id);
3720 info.avg_rtt = m_con.GetPeerAvgRTT(player->peer_id);
3722 catch(con::PeerNotFoundException &e)
3724 // Set dummy peer info
3726 info.address = Address(0,0,0,0,0);
3730 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3731 info.position = player->getPosition();
3733 list.push_back(info);
3740 void Server::peerAdded(con::Peer *peer)
3742 DSTACK(__FUNCTION_NAME);
3743 infostream<<"Server::peerAdded(): peer->id="
3744 <<peer->id<<std::endl;
3747 c.type = PEER_ADDED;
3748 c.peer_id = peer->id;
3750 m_peer_change_queue.push_back(c);
3753 void Server::deletingPeer(con::Peer *peer, bool timeout)
3755 DSTACK(__FUNCTION_NAME);
3756 infostream<<"Server::deletingPeer(): peer->id="
3757 <<peer->id<<", timeout="<<timeout<<std::endl;
3760 c.type = PEER_REMOVED;
3761 c.peer_id = peer->id;
3762 c.timeout = timeout;
3763 m_peer_change_queue.push_back(c);
3770 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3772 DSTACK(__FUNCTION_NAME);
3773 std::ostringstream os(std::ios_base::binary);
3775 writeU16(os, TOCLIENT_HP);
3779 std::string s = os.str();
3780 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3782 con.Send(peer_id, 0, data, true);
3785 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3786 const std::wstring &reason)
3788 DSTACK(__FUNCTION_NAME);
3789 std::ostringstream os(std::ios_base::binary);
3791 writeU16(os, TOCLIENT_ACCESS_DENIED);
3792 os<<serializeWideString(reason);
3795 std::string s = os.str();
3796 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3798 con.Send(peer_id, 0, data, true);
3801 void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
3802 bool set_camera_point_target, v3f camera_point_target)
3804 DSTACK(__FUNCTION_NAME);
3805 std::ostringstream os(std::ios_base::binary);
3807 writeU16(os, TOCLIENT_DEATHSCREEN);
3808 writeU8(os, set_camera_point_target);
3809 writeV3F1000(os, camera_point_target);
3812 std::string s = os.str();
3813 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3815 con.Send(peer_id, 0, data, true);
3818 void Server::SendToolDef(con::Connection &con, u16 peer_id,
3819 IToolDefManager *tooldef)
3821 DSTACK(__FUNCTION_NAME);
3822 std::ostringstream os(std::ios_base::binary);
3826 u32 length of the next item
3827 serialized ToolDefManager
3829 writeU16(os, TOCLIENT_TOOLDEF);
3830 std::ostringstream tmp_os(std::ios::binary);
3831 tooldef->serialize(tmp_os);
3832 os<<serializeLongString(tmp_os.str());
3835 std::string s = os.str();
3836 infostream<<"Server::SendToolDef(): Sending tool definitions: size="
3837 <<s.size()<<std::endl;
3838 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3840 con.Send(peer_id, 0, data, true);
3843 void Server::SendNodeDef(con::Connection &con, u16 peer_id,
3844 INodeDefManager *nodedef)
3846 DSTACK(__FUNCTION_NAME);
3847 std::ostringstream os(std::ios_base::binary);
3851 u32 length of the next item
3852 serialized NodeDefManager
3854 writeU16(os, TOCLIENT_NODEDEF);
3855 std::ostringstream tmp_os(std::ios::binary);
3856 nodedef->serialize(tmp_os);
3857 os<<serializeLongString(tmp_os.str());
3860 std::string s = os.str();
3861 infostream<<"Server::SendNodeDef(): Sending node definitions: size="
3862 <<s.size()<<std::endl;
3863 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3865 con.Send(peer_id, 0, data, true);
3869 Non-static send methods
3872 void Server::SendObjectData(float dtime)
3874 DSTACK(__FUNCTION_NAME);
3876 core::map<v3s16, bool> stepped_blocks;
3878 for(core::map<u16, RemoteClient*>::Iterator
3879 i = m_clients.getIterator();
3880 i.atEnd() == false; i++)
3882 u16 peer_id = i.getNode()->getKey();
3883 RemoteClient *client = i.getNode()->getValue();
3884 assert(client->peer_id == peer_id);
3886 if(client->serialization_version == SER_FMT_VER_INVALID)
3889 client->SendObjectData(this, dtime, stepped_blocks);
3893 void Server::SendPlayerInfos()
3895 DSTACK(__FUNCTION_NAME);
3897 //JMutexAutoLock envlock(m_env_mutex);
3899 // Get connected players
3900 core::list<Player*> players = m_env->getPlayers(true);
3902 u32 player_count = players.getSize();
3903 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3905 SharedBuffer<u8> data(datasize);
3906 writeU16(&data[0], TOCLIENT_PLAYERINFO);
3909 core::list<Player*>::Iterator i;
3910 for(i = players.begin();
3911 i != players.end(); i++)
3913 Player *player = *i;
3915 /*infostream<<"Server sending player info for player with "
3916 "peer_id="<<player->peer_id<<std::endl;*/
3918 writeU16(&data[start], player->peer_id);
3919 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3920 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3921 start += 2+PLAYERNAME_SIZE;
3924 //JMutexAutoLock conlock(m_con_mutex);
3927 m_con.SendToAll(0, data, true);
3930 void Server::SendInventory(u16 peer_id)
3932 DSTACK(__FUNCTION_NAME);
3934 Player* player = m_env->getPlayer(peer_id);
3941 std::ostringstream os;
3942 //os.imbue(std::locale("C"));
3944 player->inventory.serialize(os);
3946 std::string s = os.str();
3948 SharedBuffer<u8> data(s.size()+2);
3949 writeU16(&data[0], TOCLIENT_INVENTORY);
3950 memcpy(&data[2], s.c_str(), s.size());
3953 m_con.Send(peer_id, 0, data, true);
3956 std::string getWieldedItemString(const Player *player)
3958 const InventoryItem *item = player->getWieldItem();
3960 return std::string("");
3961 std::ostringstream os(std::ios_base::binary);
3962 item->serialize(os);
3966 void Server::SendWieldedItem(const Player* player)
3968 DSTACK(__FUNCTION_NAME);
3972 std::ostringstream os(std::ios_base::binary);
3974 writeU16(os, TOCLIENT_PLAYERITEM);
3976 writeU16(os, player->peer_id);
3977 os<<serializeString(getWieldedItemString(player));
3980 std::string s = os.str();
3981 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3983 m_con.SendToAll(0, data, true);
3986 void Server::SendPlayerItems()
3988 DSTACK(__FUNCTION_NAME);
3990 std::ostringstream os(std::ios_base::binary);
3991 core::list<Player *> players = m_env->getPlayers(true);
3993 writeU16(os, TOCLIENT_PLAYERITEM);
3994 writeU16(os, players.size());
3995 core::list<Player *>::Iterator i;
3996 for(i = players.begin(); i != players.end(); ++i)
3999 writeU16(os, p->peer_id);
4000 os<<serializeString(getWieldedItemString(p));
4004 std::string s = os.str();
4005 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4007 m_con.SendToAll(0, data, true);
4010 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
4012 DSTACK(__FUNCTION_NAME);
4014 std::ostringstream os(std::ios_base::binary);
4018 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
4019 os.write((char*)buf, 2);
4022 writeU16(buf, message.size());
4023 os.write((char*)buf, 2);
4026 for(u32 i=0; i<message.size(); i++)
4030 os.write((char*)buf, 2);
4034 std::string s = os.str();
4035 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4037 m_con.Send(peer_id, 0, data, true);
4040 void Server::BroadcastChatMessage(const std::wstring &message)
4042 for(core::map<u16, RemoteClient*>::Iterator
4043 i = m_clients.getIterator();
4044 i.atEnd() == false; i++)
4046 // Get client and check that it is valid
4047 RemoteClient *client = i.getNode()->getValue();
4048 assert(client->peer_id == i.getNode()->getKey());
4049 if(client->serialization_version == SER_FMT_VER_INVALID)
4052 SendChatMessage(client->peer_id, message);
4056 void Server::SendPlayerHP(Player *player)
4058 SendHP(m_con, player->peer_id, player->hp);
4061 void Server::SendMovePlayer(Player *player)
4063 DSTACK(__FUNCTION_NAME);
4064 std::ostringstream os(std::ios_base::binary);
4066 writeU16(os, TOCLIENT_MOVE_PLAYER);
4067 writeV3F1000(os, player->getPosition());
4068 writeF1000(os, player->getPitch());
4069 writeF1000(os, player->getYaw());
4072 v3f pos = player->getPosition();
4073 f32 pitch = player->getPitch();
4074 f32 yaw = player->getYaw();
4075 infostream<<"Server sending TOCLIENT_MOVE_PLAYER"
4076 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
4083 std::string s = os.str();
4084 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4086 m_con.Send(player->peer_id, 0, data, true);
4089 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
4090 core::list<u16> *far_players, float far_d_nodes)
4092 float maxd = far_d_nodes*BS;
4093 v3f p_f = intToFloat(p, BS);
4097 SharedBuffer<u8> reply(replysize);
4098 writeU16(&reply[0], TOCLIENT_REMOVENODE);
4099 writeS16(&reply[2], p.X);
4100 writeS16(&reply[4], p.Y);
4101 writeS16(&reply[6], p.Z);
4103 for(core::map<u16, RemoteClient*>::Iterator
4104 i = m_clients.getIterator();
4105 i.atEnd() == false; i++)
4107 // Get client and check that it is valid
4108 RemoteClient *client = i.getNode()->getValue();
4109 assert(client->peer_id == i.getNode()->getKey());
4110 if(client->serialization_version == SER_FMT_VER_INVALID)
4113 // Don't send if it's the same one
4114 if(client->peer_id == ignore_id)
4120 Player *player = m_env->getPlayer(client->peer_id);
4123 // If player is far away, only set modified blocks not sent
4124 v3f player_pos = player->getPosition();
4125 if(player_pos.getDistanceFrom(p_f) > maxd)
4127 far_players->push_back(client->peer_id);
4134 m_con.Send(client->peer_id, 0, reply, true);
4138 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
4139 core::list<u16> *far_players, float far_d_nodes)
4141 float maxd = far_d_nodes*BS;
4142 v3f p_f = intToFloat(p, BS);
4144 for(core::map<u16, RemoteClient*>::Iterator
4145 i = m_clients.getIterator();
4146 i.atEnd() == false; i++)
4148 // Get client and check that it is valid
4149 RemoteClient *client = i.getNode()->getValue();
4150 assert(client->peer_id == i.getNode()->getKey());
4151 if(client->serialization_version == SER_FMT_VER_INVALID)
4154 // Don't send if it's the same one
4155 if(client->peer_id == ignore_id)
4161 Player *player = m_env->getPlayer(client->peer_id);
4164 // If player is far away, only set modified blocks not sent
4165 v3f player_pos = player->getPosition();
4166 if(player_pos.getDistanceFrom(p_f) > maxd)
4168 far_players->push_back(client->peer_id);
4175 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
4176 SharedBuffer<u8> reply(replysize);
4177 writeU16(&reply[0], TOCLIENT_ADDNODE);
4178 writeS16(&reply[2], p.X);
4179 writeS16(&reply[4], p.Y);
4180 writeS16(&reply[6], p.Z);
4181 n.serialize(&reply[8], client->serialization_version);
4184 m_con.Send(client->peer_id, 0, reply, true);
4188 void Server::setBlockNotSent(v3s16 p)
4190 for(core::map<u16, RemoteClient*>::Iterator
4191 i = m_clients.getIterator();
4192 i.atEnd()==false; i++)
4194 RemoteClient *client = i.getNode()->getValue();
4195 client->SetBlockNotSent(p);
4199 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
4201 DSTACK(__FUNCTION_NAME);
4203 v3s16 p = block->getPos();
4207 bool completely_air = true;
4208 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4209 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4210 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4212 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
4214 completely_air = false;
4215 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
4220 infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
4222 infostream<<"[completely air] ";
4223 infostream<<std::endl;
4227 Create a packet with the block in the right format
4230 std::ostringstream os(std::ios_base::binary);
4231 block->serialize(os, ver);
4232 std::string s = os.str();
4233 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
4235 u32 replysize = 8 + blockdata.getSize();
4236 SharedBuffer<u8> reply(replysize);
4237 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
4238 writeS16(&reply[2], p.X);
4239 writeS16(&reply[4], p.Y);
4240 writeS16(&reply[6], p.Z);
4241 memcpy(&reply[8], *blockdata, blockdata.getSize());
4243 /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4244 <<": \tpacket size: "<<replysize<<std::endl;*/
4249 m_con.Send(peer_id, 1, reply, true);
4252 void Server::SendBlocks(float dtime)
4254 DSTACK(__FUNCTION_NAME);
4256 JMutexAutoLock envlock(m_env_mutex);
4257 JMutexAutoLock conlock(m_con_mutex);
4259 //TimeTaker timer("Server::SendBlocks");
4261 core::array<PrioritySortedBlockTransfer> queue;
4263 s32 total_sending = 0;
4266 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
4268 for(core::map<u16, RemoteClient*>::Iterator
4269 i = m_clients.getIterator();
4270 i.atEnd() == false; i++)
4272 RemoteClient *client = i.getNode()->getValue();
4273 assert(client->peer_id == i.getNode()->getKey());
4275 total_sending += client->SendingCount();
4277 if(client->serialization_version == SER_FMT_VER_INVALID)
4280 client->GetNextBlocks(this, dtime, queue);
4285 // Lowest priority number comes first.
4286 // Lowest is most important.
4289 for(u32 i=0; i<queue.size(); i++)
4291 //TODO: Calculate limit dynamically
4292 if(total_sending >= g_settings->getS32
4293 ("max_simultaneous_block_sends_server_total"))
4296 PrioritySortedBlockTransfer q = queue[i];
4298 MapBlock *block = NULL;
4301 block = m_env->getMap().getBlockNoCreate(q.pos);
4303 catch(InvalidPositionException &e)
4308 RemoteClient *client = getClient(q.peer_id);
4310 SendBlockNoLock(q.peer_id, block, client->serialization_version);
4312 client->SentBlock(q.pos);
4318 struct SendableTexture
4324 SendableTexture(const std::string &name_="", const std::string path_="",
4325 const std::string &data_=""):
4332 void Server::SendTextures(u16 peer_id)
4334 DSTACK(__FUNCTION_NAME);
4336 infostream<<"Server::SendTextures(): Sending textures to client"<<std::endl;
4340 // Put 5kB in one bunch (this is not accurate)
4341 u32 bytes_per_bunch = 5000;
4343 core::array< core::list<SendableTexture> > texture_bunches;
4344 texture_bunches.push_back(core::list<SendableTexture>());
4346 u32 texture_size_bunch_total = 0;
4347 core::list<ModSpec> mods = getMods(m_modspaths);
4348 for(core::list<ModSpec>::Iterator i = mods.begin();
4349 i != mods.end(); i++){
4351 std::string texturepath = mod.path + DIR_DELIM + "textures";
4352 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(texturepath);
4353 for(u32 j=0; j<dirlist.size(); j++){
4354 if(dirlist[j].dir) // Ignode dirs
4356 std::string tname = dirlist[j].name;
4357 std::string tpath = texturepath + DIR_DELIM + tname;
4359 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
4360 if(fis.good() == false){
4361 errorstream<<"Server::SendTextures(): Could not open \""
4362 <<tname<<"\" for reading"<<std::endl;
4365 std::ostringstream tmp_os(std::ios_base::binary);
4369 fis.read(buf, 1024);
4370 std::streamsize len = fis.gcount();
4371 tmp_os.write(buf, len);
4372 texture_size_bunch_total += len;
4381 errorstream<<"Server::SendTextures(): Failed to read \""
4382 <<tname<<"\""<<std::endl;
4385 /*infostream<<"Server::SendTextures(): Loaded \""
4386 <<tname<<"\""<<std::endl;*/
4388 texture_bunches[texture_bunches.size()-1].push_back(
4389 SendableTexture(tname, tpath, tmp_os.str()));
4391 // Start next bunch if got enough data
4392 if(texture_size_bunch_total >= bytes_per_bunch){
4393 texture_bunches.push_back(core::list<SendableTexture>());
4394 texture_size_bunch_total = 0;
4399 /* Create and send packets */
4401 u32 num_bunches = texture_bunches.size();
4402 for(u32 i=0; i<num_bunches; i++)
4406 u16 total number of texture bunches
4407 u16 index of this bunch
4408 u32 number of textures in this bunch
4416 std::ostringstream os(std::ios_base::binary);
4418 writeU16(os, TOCLIENT_TEXTURES);
4419 writeU16(os, num_bunches);
4421 writeU32(os, texture_bunches[i].size());
4423 for(core::list<SendableTexture>::Iterator
4424 j = texture_bunches[i].begin();
4425 j != texture_bunches[i].end(); j++){
4426 os<<serializeString(j->name);
4427 os<<serializeLongString(j->data);
4431 std::string s = os.str();
4432 infostream<<"Server::SendTextures(): bunch "<<i<<"/"<<num_bunches
4433 <<" textures="<<texture_bunches[i].size()
4434 <<" size=" <<s.size()<<std::endl;
4435 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4437 m_con.Send(peer_id, 0, data, true);
4445 void Server::HandlePlayerHP(Player *player, s16 damage)
4447 if(player->hp > damage)
4449 player->hp -= damage;
4450 SendPlayerHP(player);
4454 infostream<<"Server::HandlePlayerHP(): Player "
4455 <<player->getName()<<" dies"<<std::endl;
4459 //TODO: Throw items around
4461 // Handle players that are not connected
4462 if(player->peer_id == PEER_ID_INEXISTENT){
4463 RespawnPlayer(player);
4467 SendPlayerHP(player);
4469 RemoteClient *client = getClient(player->peer_id);
4470 if(client->net_proto_version >= 3)
4472 SendDeathscreen(m_con, player->peer_id, false, v3f(0,0,0));
4476 RespawnPlayer(player);
4481 void Server::RespawnPlayer(Player *player)
4484 ServerRemotePlayer *srp = (ServerRemotePlayer*)player;
4485 bool repositioned = scriptapi_on_respawnplayer(m_lua, srp);
4487 v3f pos = findSpawnPos(m_env->getServerMap());
4488 player->setPosition(pos);
4489 srp->m_last_good_position = pos;
4490 srp->m_last_good_position_age = 0;
4492 SendMovePlayer(player);
4493 SendPlayerHP(player);
4496 void Server::UpdateCrafting(u16 peer_id)
4498 DSTACK(__FUNCTION_NAME);
4500 Player* player = m_env->getPlayer(peer_id);
4504 Calculate crafting stuff
4506 if(g_settings->getBool("creative_mode") == false)
4508 InventoryList *clist = player->inventory.getList("craft");
4509 InventoryList *rlist = player->inventory.getList("craftresult");
4511 if(rlist && rlist->getUsedSlots() == 0)
4512 player->craftresult_is_preview = true;
4514 if(rlist && player->craftresult_is_preview)
4516 rlist->clearItems();
4518 if(clist && rlist && player->craftresult_is_preview)
4520 // Get result of crafting grid
4522 std::vector<InventoryItem*> items;
4523 for(u16 i=0; i<9; i++){
4524 if(clist->getItem(i) == NULL)
4525 items.push_back(NULL);
4527 items.push_back(clist->getItem(i)->clone());
4529 CraftPointerInput cpi(3, items);
4531 InventoryItem *result = m_craftdef->getCraftResult(cpi, this);
4532 //InventoryItem *result = craft_get_result(items, this);
4534 rlist->addItem(result);
4537 } // if creative_mode == false
4540 RemoteClient* Server::getClient(u16 peer_id)
4542 DSTACK(__FUNCTION_NAME);
4543 //JMutexAutoLock lock(m_con_mutex);
4544 core::map<u16, RemoteClient*>::Node *n;
4545 n = m_clients.find(peer_id);
4546 // A client should exist for all peers
4548 return n->getValue();
4551 std::wstring Server::getStatusString()
4553 std::wostringstream os(std::ios_base::binary);
4556 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4558 os<<L", uptime="<<m_uptime.get();
4559 // Information about clients
4561 for(core::map<u16, RemoteClient*>::Iterator
4562 i = m_clients.getIterator();
4563 i.atEnd() == false; i++)
4565 // Get client and check that it is valid
4566 RemoteClient *client = i.getNode()->getValue();
4567 assert(client->peer_id == i.getNode()->getKey());
4568 if(client->serialization_version == SER_FMT_VER_INVALID)
4571 Player *player = m_env->getPlayer(client->peer_id);
4572 // Get name of player
4573 std::wstring name = L"unknown";
4575 name = narrow_to_wide(player->getName());
4576 // Add name to information string
4580 if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
4581 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4582 if(g_settings->get("motd") != "")
4583 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4587 // Saves g_settings to configpath given at initialization
4588 void Server::saveConfig()
4590 if(m_configpath != "")
4591 g_settings->updateConfigFile(m_configpath.c_str());
4594 void Server::notifyPlayer(const char *name, const std::wstring msg)
4596 Player *player = m_env->getPlayer(name);
4599 SendChatMessage(player->peer_id, std::wstring(L"Server: -!- ")+msg);
4602 void Server::notifyPlayers(const std::wstring msg)
4604 BroadcastChatMessage(msg);
4607 void Server::queueBlockEmerge(v3s16 blockpos, bool allow_generate)
4611 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
4612 m_emerge_queue.addBlock(PEER_ID_INEXISTENT, blockpos, flags);
4615 // IGameDef interface
4617 IToolDefManager* Server::getToolDefManager()
4621 INodeDefManager* Server::getNodeDefManager()
4625 ICraftDefManager* Server::getCraftDefManager()
4629 ITextureSource* Server::getTextureSource()
4633 u16 Server::allocateUnknownNodeId(const std::string &name)
4635 return m_nodedef->allocateDummy(name);
4638 IWritableToolDefManager* Server::getWritableToolDefManager()
4642 IWritableNodeDefManager* Server::getWritableNodeDefManager()
4646 IWritableCraftDefManager* Server::getWritableCraftDefManager()
4651 v3f findSpawnPos(ServerMap &map)
4653 //return v3f(50,50,50)*BS;
4658 nodepos = v2s16(0,0);
4663 // Try to find a good place a few times
4664 for(s32 i=0; i<1000; i++)
4667 // We're going to try to throw the player to this position
4668 v2s16 nodepos2d = v2s16(-range + (myrand()%(range*2)),
4669 -range + (myrand()%(range*2)));
4670 //v2s16 sectorpos = getNodeSectorPos(nodepos2d);
4671 // Get ground height at point (fallbacks to heightmap function)
4672 s16 groundheight = map.findGroundLevel(nodepos2d);
4673 // Don't go underwater
4674 if(groundheight < WATER_LEVEL)
4676 //infostream<<"-> Underwater"<<std::endl;
4679 // Don't go to high places
4680 if(groundheight > WATER_LEVEL + 4)
4682 //infostream<<"-> Underwater"<<std::endl;
4686 nodepos = v3s16(nodepos2d.X, groundheight-2, nodepos2d.Y);
4687 bool is_good = false;
4689 for(s32 i=0; i<10; i++){
4690 v3s16 blockpos = getNodeBlockPos(nodepos);
4691 map.emergeBlock(blockpos, true);
4692 MapNode n = map.getNodeNoEx(nodepos);
4693 if(n.getContent() == CONTENT_AIR){
4704 // Found a good place
4705 //infostream<<"Searched through "<<i<<" places."<<std::endl;
4711 return intToFloat(nodepos, BS);
4714 Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id)
4717 Try to get an existing player
4719 Player *player = m_env->getPlayer(name);
4722 // If player is already connected, cancel
4723 if(player->peer_id != 0)
4725 infostream<<"emergePlayer(): Player already connected"<<std::endl;
4730 player->peer_id = peer_id;
4732 // Reset inventory to creative if in creative mode
4733 if(g_settings->getBool("creative_mode"))
4735 // Warning: double code below
4736 // Backup actual inventory
4737 player->inventory_backup = new Inventory();
4738 *(player->inventory_backup) = player->inventory;
4739 // Set creative inventory
4740 craft_set_creative_inventory(player, this);
4747 If player with the wanted peer_id already exists, cancel.
4749 if(m_env->getPlayer(peer_id) != NULL)
4751 infostream<<"emergePlayer(): Player with wrong name but same"
4752 " peer_id already exists"<<std::endl;
4760 // Add authentication stuff
4761 m_authmanager.add(name);
4762 m_authmanager.setPassword(name, password);
4763 m_authmanager.setPrivs(name,
4764 stringToPrivs(g_settings->get("default_privs")));
4766 /* Set player position */
4768 infostream<<"Server: Finding spawn place for player \""
4769 <<name<<"\""<<std::endl;
4771 v3f pos = findSpawnPos(m_env->getServerMap());
4773 player = new ServerRemotePlayer(m_env, pos, peer_id, name);
4775 /* Add player to environment */
4776 m_env->addPlayer(player);
4779 ServerRemotePlayer *srp = (ServerRemotePlayer*)player;
4780 scriptapi_on_newplayer(m_lua, srp);
4782 /* Add stuff to inventory */
4783 if(g_settings->getBool("creative_mode"))
4785 // Warning: double code above
4786 // Backup actual inventory
4787 player->inventory_backup = new Inventory();
4788 *(player->inventory_backup) = player->inventory;
4789 // Set creative inventory
4790 craft_set_creative_inventory(player, this);
4795 } // create new player
4798 void Server::handlePeerChange(PeerChange &c)
4800 JMutexAutoLock envlock(m_env_mutex);
4801 JMutexAutoLock conlock(m_con_mutex);
4803 if(c.type == PEER_ADDED)
4810 core::map<u16, RemoteClient*>::Node *n;
4811 n = m_clients.find(c.peer_id);
4812 // The client shouldn't already exist
4816 RemoteClient *client = new RemoteClient();
4817 client->peer_id = c.peer_id;
4818 m_clients.insert(client->peer_id, client);
4821 else if(c.type == PEER_REMOVED)
4828 core::map<u16, RemoteClient*>::Node *n;
4829 n = m_clients.find(c.peer_id);
4830 // The client should exist
4834 Mark objects to be not known by the client
4836 RemoteClient *client = n->getValue();
4838 for(core::map<u16, bool>::Iterator
4839 i = client->m_known_objects.getIterator();
4840 i.atEnd()==false; i++)
4843 u16 id = i.getNode()->getKey();
4844 ServerActiveObject* obj = m_env->getActiveObject(id);
4846 if(obj && obj->m_known_by_count > 0)
4847 obj->m_known_by_count--;
4850 // Collect information about leaving in chat
4851 std::wstring message;
4853 Player *player = m_env->getPlayer(c.peer_id);
4856 std::wstring name = narrow_to_wide(player->getName());
4859 message += L" left game";
4861 message += L" (timed out)";
4867 m_env->removePlayer(c.peer_id);
4870 // Set player client disconnected
4872 Player *player = m_env->getPlayer(c.peer_id);
4874 player->peer_id = 0;
4881 std::ostringstream os(std::ios_base::binary);
4882 for(core::map<u16, RemoteClient*>::Iterator
4883 i = m_clients.getIterator();
4884 i.atEnd() == false; i++)
4886 RemoteClient *client = i.getNode()->getValue();
4887 assert(client->peer_id == i.getNode()->getKey());
4888 if(client->serialization_version == SER_FMT_VER_INVALID)
4891 Player *player = m_env->getPlayer(client->peer_id);
4894 // Get name of player
4895 os<<player->getName()<<" ";
4898 actionstream<<player->getName()<<" "
4899 <<(c.timeout?"times out.":"leaves game.")
4900 <<" List of players: "
4901 <<os.str()<<std::endl;
4906 delete m_clients[c.peer_id];
4907 m_clients.remove(c.peer_id);
4909 // Send player info to all remaining clients
4912 // Send leave chat message to all remaining clients
4913 BroadcastChatMessage(message);
4922 void Server::handlePeerChanges()
4924 while(m_peer_change_queue.size() > 0)
4926 PeerChange c = m_peer_change_queue.pop_front();
4928 infostream<<"Server: Handling peer change: "
4929 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4932 handlePeerChange(c);
4936 u64 Server::getPlayerPrivs(Player *player)
4940 std::string playername = player->getName();
4941 // Local player gets all privileges regardless of
4942 // what's set on their account.
4943 if(g_settings->get("name") == playername)
4949 return getPlayerAuthPrivs(playername);
4953 void dedicated_server_loop(Server &server, bool &kill)
4955 DSTACK(__FUNCTION_NAME);
4957 infostream<<DTIME<<std::endl;
4958 infostream<<"========================"<<std::endl;
4959 infostream<<"Running dedicated server"<<std::endl;
4960 infostream<<"========================"<<std::endl;
4961 infostream<<std::endl;
4963 IntervalLimiter m_profiler_interval;
4967 // This is kind of a hack but can be done like this
4968 // because server.step() is very light
4970 ScopeProfiler sp(g_profiler, "dedicated server sleep");
4975 if(server.getShutdownRequested() || kill)
4977 infostream<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4984 float profiler_print_interval =
4985 g_settings->getFloat("profiler_print_interval");
4986 if(profiler_print_interval != 0)
4988 if(m_profiler_interval.step(0.030, profiler_print_interval))
4990 infostream<<"Profiler:"<<std::endl;
4991 g_profiler->print(infostream);
4992 g_profiler->clear();
4999 static int counter = 0;
5005 core::list<PlayerInfo> list = server.getPlayerInfo();
5006 core::list<PlayerInfo>::Iterator i;
5007 static u32 sum_old = 0;
5008 u32 sum = PIChecksum(list);
5011 infostream<<DTIME<<"Player info:"<<std::endl;
5012 for(i=list.begin(); i!=list.end(); i++)
5014 i->PrintLine(&infostream);