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"
48 #include "craftitemdef.h"
50 #include "content_abm.h"
51 #include "content_sao.h" // For PlayerSAO
53 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
55 #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
57 class MapEditEventIgnorer
60 MapEditEventIgnorer(bool *flag):
69 ~MapEditEventIgnorer()
82 void * ServerThread::Thread()
86 log_register_thread("ServerThread");
88 DSTACK(__FUNCTION_NAME);
90 BEGIN_DEBUG_EXCEPTION_HANDLER
95 //TimeTaker timer("AsyncRunStep() + Receive()");
98 //TimeTaker timer("AsyncRunStep()");
99 m_server->AsyncRunStep();
102 //infostream<<"Running m_server->Receive()"<<std::endl;
105 catch(con::NoIncomingDataException &e)
108 catch(con::PeerNotFoundException &e)
110 infostream<<"Server: PeerNotFoundException"<<std::endl;
114 END_DEBUG_EXCEPTION_HANDLER(errorstream)
119 void * EmergeThread::Thread()
123 log_register_thread("EmergeThread");
125 DSTACK(__FUNCTION_NAME);
127 BEGIN_DEBUG_EXCEPTION_HANDLER
129 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
132 Get block info from queue, emerge them and send them
135 After queue is empty, exit.
139 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
143 SharedPtr<QueuedBlockEmerge> q(qptr);
149 Do not generate over-limit
151 if(blockpos_over_limit(p))
154 //infostream<<"EmergeThread::Thread(): running"<<std::endl;
156 //TimeTaker timer("block emerge");
159 Try to emerge it from somewhere.
161 If it is only wanted as optional, only loading from disk
166 Check if any peer wants it as non-optional. In that case it
169 Also decrement the emerge queue count in clients.
172 bool only_from_disk = true;
175 core::map<u16, u8>::Iterator i;
176 for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
178 //u16 peer_id = i.getNode()->getKey();
181 u8 flags = i.getNode()->getValue();
182 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
183 only_from_disk = false;
188 if(enable_mapgen_debug_info)
189 infostream<<"EmergeThread: p="
190 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
191 <<"only_from_disk="<<only_from_disk<<std::endl;
193 ServerMap &map = ((ServerMap&)m_server->m_env->getMap());
195 MapBlock *block = NULL;
196 bool got_block = true;
197 core::map<v3s16, MapBlock*> modified_blocks;
200 Try to fetch block from memory or disk.
201 If not found and asked to generate, initialize generator.
204 bool started_generate = false;
205 mapgen::BlockMakeData data;
208 JMutexAutoLock envlock(m_server->m_env_mutex);
210 // Load sector if it isn't loaded
211 if(map.getSectorNoGenerateNoEx(p2d) == NULL)
212 map.loadSectorMeta(p2d);
214 // Attempt to load block
215 block = map.getBlockNoCreateNoEx(p);
216 if(!block || block->isDummy() || !block->isGenerated())
218 if(enable_mapgen_debug_info)
219 infostream<<"EmergeThread: not in memory, "
220 <<"attempting to load from disk"<<std::endl;
222 block = map.loadBlock(p);
225 // If could not load and allowed to generate, start generation
226 // inside this same envlock
227 if(only_from_disk == false &&
228 (block == NULL || block->isGenerated() == false)){
229 if(enable_mapgen_debug_info)
230 infostream<<"EmergeThread: generating"<<std::endl;
231 started_generate = true;
233 map.initBlockMake(&data, p);
238 If generator was initialized, generate now when envlock is free.
243 ScopeProfiler sp(g_profiler, "EmergeThread: mapgen::make_block",
245 TimeTaker t("mapgen::make_block()");
247 mapgen::make_block(&data);
249 if(enable_mapgen_debug_info == false)
250 t.stop(true); // Hide output
254 // Lock environment again to access the map
255 JMutexAutoLock envlock(m_server->m_env_mutex);
257 ScopeProfiler sp(g_profiler, "EmergeThread: after "
258 "mapgen::make_block (envlock)", SPT_AVG);
260 // Blit data back on map, update lighting, add mobs and
261 // whatever this does
262 map.finishBlockMake(&data, modified_blocks);
265 block = map.getBlockNoCreateNoEx(p);
268 Do some post-generate stuff
271 v3s16 minp = block->getPos()*MAP_BLOCKSIZE;
272 v3s16 maxp = minp + v3s16(1,1,1)*(MAP_BLOCKSIZE-1);
273 scriptapi_environment_on_generated(m_server->m_lua,
276 if(enable_mapgen_debug_info)
277 infostream<<"EmergeThread: ended up with: "
278 <<analyze_block(block)<<std::endl;
281 Ignore map edit events, they will not need to be
282 sent to anybody because the block hasn't been sent
285 MapEditEventIgnorer ign(&m_server->m_ignore_map_edit_events);
287 // Activate objects and stuff
288 m_server->m_env->activateBlock(block, 0);
296 Set sent status of modified blocks on clients
299 // NOTE: Server's clients are also behind the connection mutex
300 JMutexAutoLock lock(m_server->m_con_mutex);
303 Add the originally fetched block to the modified list
307 modified_blocks.insert(p, block);
311 Set the modified blocks unsent for all the clients
314 for(core::map<u16, RemoteClient*>::Iterator
315 i = m_server->m_clients.getIterator();
316 i.atEnd() == false; i++)
318 RemoteClient *client = i.getNode()->getValue();
320 if(modified_blocks.size() > 0)
322 // Remove block from sent history
323 client->SetBlocksNotSent(modified_blocks);
329 END_DEBUG_EXCEPTION_HANDLER(errorstream)
331 log_deregister_thread();
336 void RemoteClient::GetNextBlocks(Server *server, float dtime,
337 core::array<PrioritySortedBlockTransfer> &dest)
339 DSTACK(__FUNCTION_NAME);
342 TimeTaker timer("RemoteClient::GetNextBlocks", &timer_result);*/
345 m_nothing_to_send_pause_timer -= dtime;
346 m_nearest_unsent_reset_timer += dtime;
348 if(m_nothing_to_send_pause_timer >= 0)
353 // Won't send anything if already sending
354 if(m_blocks_sending.size() >= g_settings->getU16
355 ("max_simultaneous_block_sends_per_client"))
357 //infostream<<"Not sending any blocks, Queue full."<<std::endl;
361 //TimeTaker timer("RemoteClient::GetNextBlocks");
363 Player *player = server->m_env->getPlayer(peer_id);
365 assert(player != NULL);
367 v3f playerpos = player->getPosition();
368 v3f playerspeed = player->getSpeed();
369 v3f playerspeeddir(0,0,0);
370 if(playerspeed.getLength() > 1.0*BS)
371 playerspeeddir = playerspeed / playerspeed.getLength();
372 // Predict to next block
373 v3f playerpos_predicted = playerpos + playerspeeddir*MAP_BLOCKSIZE*BS;
375 v3s16 center_nodepos = floatToInt(playerpos_predicted, BS);
377 v3s16 center = getNodeBlockPos(center_nodepos);
379 // Camera position and direction
380 v3f camera_pos = player->getEyePosition();
381 v3f camera_dir = v3f(0,0,1);
382 camera_dir.rotateYZBy(player->getPitch());
383 camera_dir.rotateXZBy(player->getYaw());
385 /*infostream<<"camera_dir=("<<camera_dir.X<<","<<camera_dir.Y<<","
386 <<camera_dir.Z<<")"<<std::endl;*/
389 Get the starting value of the block finder radius.
392 if(m_last_center != center)
394 m_nearest_unsent_d = 0;
395 m_last_center = center;
398 /*infostream<<"m_nearest_unsent_reset_timer="
399 <<m_nearest_unsent_reset_timer<<std::endl;*/
401 // Reset periodically to workaround for some bugs or stuff
402 if(m_nearest_unsent_reset_timer > 20.0)
404 m_nearest_unsent_reset_timer = 0;
405 m_nearest_unsent_d = 0;
406 //infostream<<"Resetting m_nearest_unsent_d for "
407 // <<server->getPlayerName(peer_id)<<std::endl;
410 //s16 last_nearest_unsent_d = m_nearest_unsent_d;
411 s16 d_start = m_nearest_unsent_d;
413 //infostream<<"d_start="<<d_start<<std::endl;
415 u16 max_simul_sends_setting = g_settings->getU16
416 ("max_simultaneous_block_sends_per_client");
417 u16 max_simul_sends_usually = max_simul_sends_setting;
420 Check the time from last addNode/removeNode.
422 Decrease send rate if player is building stuff.
424 m_time_from_building += dtime;
425 if(m_time_from_building < g_settings->getFloat(
426 "full_block_send_enable_min_time_from_building"))
428 max_simul_sends_usually
429 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
433 Number of blocks sending + number of blocks selected for sending
435 u32 num_blocks_selected = m_blocks_sending.size();
438 next time d will be continued from the d from which the nearest
439 unsent block was found this time.
441 This is because not necessarily any of the blocks found this
442 time are actually sent.
444 s32 new_nearest_unsent_d = -1;
446 s16 d_max = g_settings->getS16("max_block_send_distance");
447 s16 d_max_gen = g_settings->getS16("max_block_generate_distance");
449 // Don't loop very much at a time
450 s16 max_d_increment_at_time = 2;
451 if(d_max > d_start + max_d_increment_at_time)
452 d_max = d_start + max_d_increment_at_time;
453 /*if(d_max_gen > d_start+2)
454 d_max_gen = d_start+2;*/
456 //infostream<<"Starting from "<<d_start<<std::endl;
458 s32 nearest_emerged_d = -1;
459 s32 nearest_emergefull_d = -1;
460 s32 nearest_sent_d = -1;
461 bool queue_is_full = false;
464 for(d = d_start; d <= d_max; d++)
466 /*errorstream<<"checking d="<<d<<" for "
467 <<server->getPlayerName(peer_id)<<std::endl;*/
468 //infostream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
471 If m_nearest_unsent_d was changed by the EmergeThread
472 (it can change it to 0 through SetBlockNotSent),
474 Else update m_nearest_unsent_d
476 /*if(m_nearest_unsent_d != last_nearest_unsent_d)
478 d = m_nearest_unsent_d;
479 last_nearest_unsent_d = m_nearest_unsent_d;
483 Get the border/face dot coordinates of a "d-radiused"
486 core::list<v3s16> list;
487 getFacePositions(list, d);
489 core::list<v3s16>::Iterator li;
490 for(li=list.begin(); li!=list.end(); li++)
492 v3s16 p = *li + center;
496 - Don't allow too many simultaneous transfers
497 - EXCEPT when the blocks are very close
499 Also, don't send blocks that are already flying.
502 // Start with the usual maximum
503 u16 max_simul_dynamic = max_simul_sends_usually;
505 // If block is very close, allow full maximum
506 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
507 max_simul_dynamic = max_simul_sends_setting;
509 // Don't select too many blocks for sending
510 if(num_blocks_selected >= max_simul_dynamic)
512 queue_is_full = true;
513 goto queue_full_break;
516 // Don't send blocks that are currently being transferred
517 if(m_blocks_sending.find(p) != NULL)
523 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
524 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
525 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
526 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
527 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
528 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
531 // If this is true, inexistent block will be made from scratch
532 bool generate = d <= d_max_gen;
535 /*// Limit the generating area vertically to 2/3
536 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
539 // Limit the send area vertically to 1/2
540 if(abs(p.Y - center.Y) > d_max / 2)
546 If block is far away, don't generate it unless it is
552 // Block center y in nodes
553 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
554 // Don't generate if it's very high or very low
555 if(y < -64 || y > 64)
559 v2s16 p2d_nodes_center(
563 // Get ground height in nodes
564 s16 gh = server->m_env->getServerMap().findGroundLevel(
567 // If differs a lot, don't generate
568 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
570 // Actually, don't even send it
576 //infostream<<"d="<<d<<std::endl;
579 Don't generate or send if not in sight
580 FIXME This only works if the client uses a small enough
581 FOV setting. The default of 72 degrees is fine.
584 float camera_fov = (72.0*PI/180) * 4./3.;
585 if(isBlockInSight(p, camera_pos, camera_dir, camera_fov, 10000*BS) == false)
591 Don't send already sent blocks
594 if(m_blocks_sent.find(p) != NULL)
601 Check if map has this block
603 MapBlock *block = server->m_env->getMap().getBlockNoCreateNoEx(p);
605 bool surely_not_found_on_disk = false;
606 bool block_is_invalid = false;
609 // Reset usage timer, this block will be of use in the future.
610 block->resetUsageTimer();
612 // Block is dummy if data doesn't exist.
613 // It means it has been not found from disk and not generated
616 surely_not_found_on_disk = true;
619 // Block is valid if lighting is up-to-date and data exists
620 if(block->isValid() == false)
622 block_is_invalid = true;
625 /*if(block->isFullyGenerated() == false)
627 block_is_invalid = true;
632 ServerMap *map = (ServerMap*)(&server->m_env->getMap());
633 v2s16 chunkpos = map->sector_to_chunk(p2d);
634 if(map->chunkNonVolatile(chunkpos) == false)
635 block_is_invalid = true;
637 if(block->isGenerated() == false)
638 block_is_invalid = true;
641 If block is not close, don't send it unless it is near
644 Block is near ground level if night-time mesh
645 differs from day-time mesh.
649 if(block->dayNightDiffed() == false)
656 If block has been marked to not exist on disk (dummy)
657 and generating new ones is not wanted, skip block.
659 if(generate == false && surely_not_found_on_disk == true)
666 Add inexistent block to emerge queue.
668 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
670 //TODO: Get value from somewhere
671 // Allow only one block in emerge queue
672 //if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
673 // Allow two blocks in queue per client
674 //if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
676 // Make it more responsive when needing to generate stuff
677 if(surely_not_found_on_disk)
679 if(server->m_emerge_queue.peerItemCount(peer_id) < max_emerge)
681 //infostream<<"Adding block to emerge queue"<<std::endl;
683 // Add it to the emerge queue and trigger the thread
686 if(generate == false)
687 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
689 server->m_emerge_queue.addBlock(peer_id, p, flags);
690 server->m_emergethread.trigger();
692 if(nearest_emerged_d == -1)
693 nearest_emerged_d = d;
695 if(nearest_emergefull_d == -1)
696 nearest_emergefull_d = d;
703 if(nearest_sent_d == -1)
707 Add block to send queue
710 /*errorstream<<"sending from d="<<d<<" to "
711 <<server->getPlayerName(peer_id)<<std::endl;*/
713 PrioritySortedBlockTransfer q((float)d, p, peer_id);
717 num_blocks_selected += 1;
722 //infostream<<"Stopped at "<<d<<std::endl;
724 // If nothing was found for sending and nothing was queued for
725 // emerging, continue next time browsing from here
726 if(nearest_emerged_d != -1){
727 new_nearest_unsent_d = nearest_emerged_d;
728 } else if(nearest_emergefull_d != -1){
729 new_nearest_unsent_d = nearest_emergefull_d;
731 if(d > g_settings->getS16("max_block_send_distance")){
732 new_nearest_unsent_d = 0;
733 m_nothing_to_send_pause_timer = 2.0;
734 /*infostream<<"GetNextBlocks(): d wrapped around for "
735 <<server->getPlayerName(peer_id)
736 <<"; setting to 0 and pausing"<<std::endl;*/
738 if(nearest_sent_d != -1)
739 new_nearest_unsent_d = nearest_sent_d;
741 new_nearest_unsent_d = d;
745 if(new_nearest_unsent_d != -1)
746 m_nearest_unsent_d = new_nearest_unsent_d;
748 /*timer_result = timer.stop(true);
749 if(timer_result != 0)
750 infostream<<"GetNextBlocks duration: "<<timer_result<<" (!=0)"<<std::endl;*/
753 void RemoteClient::GotBlock(v3s16 p)
755 if(m_blocks_sending.find(p) != NULL)
756 m_blocks_sending.remove(p);
759 /*infostream<<"RemoteClient::GotBlock(): Didn't find in"
760 " m_blocks_sending"<<std::endl;*/
761 m_excess_gotblocks++;
763 m_blocks_sent.insert(p, true);
766 void RemoteClient::SentBlock(v3s16 p)
768 if(m_blocks_sending.find(p) == NULL)
769 m_blocks_sending.insert(p, 0.0);
771 infostream<<"RemoteClient::SentBlock(): Sent block"
772 " already in m_blocks_sending"<<std::endl;
775 void RemoteClient::SetBlockNotSent(v3s16 p)
777 m_nearest_unsent_d = 0;
779 if(m_blocks_sending.find(p) != NULL)
780 m_blocks_sending.remove(p);
781 if(m_blocks_sent.find(p) != NULL)
782 m_blocks_sent.remove(p);
785 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
787 m_nearest_unsent_d = 0;
789 for(core::map<v3s16, MapBlock*>::Iterator
790 i = blocks.getIterator();
791 i.atEnd()==false; i++)
793 v3s16 p = i.getNode()->getKey();
795 if(m_blocks_sending.find(p) != NULL)
796 m_blocks_sending.remove(p);
797 if(m_blocks_sent.find(p) != NULL)
798 m_blocks_sent.remove(p);
806 PlayerInfo::PlayerInfo()
812 void PlayerInfo::PrintLine(std::ostream *s)
815 (*s)<<"\""<<name<<"\" ("
816 <<(position.X/10)<<","<<(position.Y/10)
817 <<","<<(position.Z/10)<<") ";
819 (*s)<<" avg_rtt="<<avg_rtt;
823 u32 PIChecksum(core::list<PlayerInfo> &l)
825 core::list<PlayerInfo>::Iterator i;
828 for(i=l.begin(); i!=l.end(); i++)
830 checksum += a * (i->id+1);
831 checksum ^= 0x435aafcd;
845 std::set<std::string> depends;
846 std::set<std::string> unsatisfied_depends;
848 ModSpec(const std::string &name_="", const std::string path_="",
849 const std::set<std::string> &depends_=std::set<std::string>()):
853 unsatisfied_depends(depends_)
857 // Get a dependency-sorted list of ModSpecs
858 static core::list<ModSpec> getMods(core::list<std::string> &modspaths)
860 std::queue<ModSpec> mods_satisfied;
861 core::list<ModSpec> mods_unsorted;
862 core::list<ModSpec> mods_sorted;
863 for(core::list<std::string>::Iterator i = modspaths.begin();
864 i != modspaths.end(); i++){
865 std::string modspath = *i;
866 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(modspath);
867 for(u32 j=0; j<dirlist.size(); j++){
870 std::string modname = dirlist[j].name;
871 std::string modpath = modspath + DIR_DELIM + modname;
872 std::set<std::string> depends;
873 std::ifstream is((modpath+DIR_DELIM+"depends.txt").c_str(),
874 std::ios_base::binary);
877 std::getline(is, dep);
882 ModSpec spec(modname, modpath, depends);
883 mods_unsorted.push_back(spec);
885 mods_satisfied.push(spec);
888 // Sort by depencencies
889 while(!mods_satisfied.empty()){
890 ModSpec mod = mods_satisfied.front();
891 mods_satisfied.pop();
892 mods_sorted.push_back(mod);
893 for(core::list<ModSpec>::Iterator i = mods_unsorted.begin();
894 i != mods_unsorted.end(); i++){
896 if(mod2.unsatisfied_depends.empty())
898 mod2.unsatisfied_depends.erase(mod.name);
899 if(!mod2.unsatisfied_depends.empty())
901 mods_satisfied.push(mod2);
904 // Check unsatisfied dependencies
905 for(core::list<ModSpec>::Iterator i = mods_unsorted.begin();
906 i != mods_unsorted.end(); i++){
908 if(mod.unsatisfied_depends.empty())
910 errorstream<<"mod \""<<mod.name
911 <<"\" has unsatisfied dependencies:";
912 for(std::set<std::string>::iterator
913 i = mod.unsatisfied_depends.begin();
914 i != mod.unsatisfied_depends.end(); i++){
915 errorstream<<" \""<<(*i)<<"\"";
917 errorstream<<". Loading nevertheless."<<std::endl;
918 mods_sorted.push_back(mod);
928 std::string mapsavedir,
929 std::string configpath
932 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
933 m_authmanager(mapsavedir+DIR_DELIM+"auth.txt"),
934 m_banmanager(mapsavedir+DIR_DELIM+"ipban.txt"),
936 m_toolmgr(createToolDefManager()),
937 m_nodedef(createNodeDefManager()),
938 m_craftdef(createCraftDefManager()),
939 m_craftitemdef(createCraftItemDefManager()),
941 m_emergethread(this),
943 m_time_of_day_send_timer(0),
945 m_mapsavedir(mapsavedir),
946 m_configpath(configpath),
947 m_shutdown_requested(false),
948 m_ignore_map_edit_events(false),
949 m_ignore_map_edit_events_peer_id(0)
951 m_liquid_transform_timer = 0.0;
952 m_print_info_timer = 0.0;
953 m_objectdata_timer = 0.0;
954 m_emergethread_trigger_timer = 0.0;
955 m_savemap_timer = 0.0;
959 m_step_dtime_mutex.Init();
962 JMutexAutoLock envlock(m_env_mutex);
963 JMutexAutoLock conlock(m_con_mutex);
965 infostream<<"m_nodedef="<<m_nodedef<<std::endl;
967 // Path to builtin.lua
968 std::string builtinpath = porting::path_data + DIR_DELIM + "builtin.lua";
969 // Add default global mod path
970 m_modspaths.push_back(porting::path_data + DIR_DELIM + "mods");
972 // Initialize scripting
974 infostream<<"Server: Initializing scripting"<<std::endl;
975 m_lua = script_init();
978 scriptapi_export(m_lua, this);
979 // Load and run builtin.lua
980 infostream<<"Server: Loading builtin Lua stuff from \""<<builtinpath
982 bool success = script_load(m_lua, builtinpath.c_str());
984 errorstream<<"Server: Failed to load and run "
985 <<builtinpath<<std::endl;
988 // Load and run "mod" scripts
989 core::list<ModSpec> mods = getMods(m_modspaths);
990 for(core::list<ModSpec>::Iterator i = mods.begin();
991 i != mods.end(); i++){
993 infostream<<"Server: Loading mod \""<<mod.name<<"\""<<std::endl;
994 std::string scriptpath = mod.path + DIR_DELIM + "init.lua";
995 bool success = script_load(m_lua, scriptpath.c_str());
997 errorstream<<"Server: Failed to load and run "
998 <<scriptpath<<std::endl;
1003 // Initialize Environment
1005 m_env = new ServerEnvironment(new ServerMap(mapsavedir, this), m_lua,
1008 // Give environment reference to scripting api
1009 scriptapi_add_environment(m_lua, m_env);
1011 // Register us to receive map edit events
1012 m_env->getMap().addEventReceiver(this);
1014 // If file exists, load environment metadata
1015 if(fs::PathExists(m_mapsavedir+DIR_DELIM+"env_meta.txt"))
1017 infostream<<"Server: Loading environment metadata"<<std::endl;
1018 m_env->loadMeta(m_mapsavedir);
1022 infostream<<"Server: Loading players"<<std::endl;
1023 m_env->deSerializePlayers(m_mapsavedir);
1026 Add some test ActiveBlockModifiers to environment
1028 add_legacy_abms(m_env, m_nodedef);
1033 infostream<<"Server::~Server()"<<std::endl;
1036 Send shutdown message
1039 JMutexAutoLock conlock(m_con_mutex);
1041 std::wstring line = L"*** Server shutting down";
1044 Send the message to clients
1046 for(core::map<u16, RemoteClient*>::Iterator
1047 i = m_clients.getIterator();
1048 i.atEnd() == false; i++)
1050 // Get client and check that it is valid
1051 RemoteClient *client = i.getNode()->getValue();
1052 assert(client->peer_id == i.getNode()->getKey());
1053 if(client->serialization_version == SER_FMT_VER_INVALID)
1057 SendChatMessage(client->peer_id, line);
1059 catch(con::PeerNotFoundException &e)
1065 JMutexAutoLock envlock(m_env_mutex);
1070 infostream<<"Server: Saving players"<<std::endl;
1071 m_env->serializePlayers(m_mapsavedir);
1074 Save environment metadata
1076 infostream<<"Server: Saving environment metadata"<<std::endl;
1077 m_env->saveMeta(m_mapsavedir);
1089 JMutexAutoLock clientslock(m_con_mutex);
1091 for(core::map<u16, RemoteClient*>::Iterator
1092 i = m_clients.getIterator();
1093 i.atEnd() == false; i++)
1096 // NOTE: These are removed by env destructor
1098 u16 peer_id = i.getNode()->getKey();
1099 JMutexAutoLock envlock(m_env_mutex);
1100 m_env->removePlayer(peer_id);
1104 delete i.getNode()->getValue();
1108 // Delete Environment
1114 delete m_craftitemdef;
1116 // Deinitialize scripting
1117 infostream<<"Server: Deinitializing scripting"<<std::endl;
1118 script_deinit(m_lua);
1121 void Server::start(unsigned short port)
1123 DSTACK(__FUNCTION_NAME);
1124 // Stop thread if already running
1127 // Initialize connection
1128 m_con.SetTimeoutMs(30);
1132 m_thread.setRun(true);
1135 infostream<<"Server: Started on port "<<port<<std::endl;
1140 DSTACK(__FUNCTION_NAME);
1142 infostream<<"Server: Stopping and waiting threads"<<std::endl;
1144 // Stop threads (set run=false first so both start stopping)
1145 m_thread.setRun(false);
1146 m_emergethread.setRun(false);
1148 m_emergethread.stop();
1150 infostream<<"Server: Threads stopped"<<std::endl;
1153 void Server::step(float dtime)
1155 DSTACK(__FUNCTION_NAME);
1160 JMutexAutoLock lock(m_step_dtime_mutex);
1161 m_step_dtime += dtime;
1165 void Server::AsyncRunStep()
1167 DSTACK(__FUNCTION_NAME);
1169 g_profiler->add("Server::AsyncRunStep (num)", 1);
1173 JMutexAutoLock lock1(m_step_dtime_mutex);
1174 dtime = m_step_dtime;
1178 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
1179 // Send blocks to clients
1186 g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
1188 //infostream<<"Server steps "<<dtime<<std::endl;
1189 //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1192 JMutexAutoLock lock1(m_step_dtime_mutex);
1193 m_step_dtime -= dtime;
1200 m_uptime.set(m_uptime.get() + dtime);
1204 // Process connection's timeouts
1205 JMutexAutoLock lock2(m_con_mutex);
1206 ScopeProfiler sp(g_profiler, "Server: connection timeout processing");
1207 m_con.RunTimeouts(dtime);
1211 // This has to be called so that the client list gets synced
1212 // with the peer list of the connection
1213 handlePeerChanges();
1217 Update m_time_of_day and overall game time
1220 JMutexAutoLock envlock(m_env_mutex);
1222 m_time_counter += dtime;
1223 f32 speed = g_settings->getFloat("time_speed") * 24000./(24.*3600);
1224 u32 units = (u32)(m_time_counter*speed);
1225 m_time_counter -= (f32)units / speed;
1227 m_env->setTimeOfDay((m_env->getTimeOfDay() + units) % 24000);
1229 //infostream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1232 Send to clients at constant intervals
1235 m_time_of_day_send_timer -= dtime;
1236 if(m_time_of_day_send_timer < 0.0)
1238 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
1240 //JMutexAutoLock envlock(m_env_mutex);
1241 JMutexAutoLock conlock(m_con_mutex);
1243 for(core::map<u16, RemoteClient*>::Iterator
1244 i = m_clients.getIterator();
1245 i.atEnd() == false; i++)
1247 RemoteClient *client = i.getNode()->getValue();
1248 //Player *player = m_env->getPlayer(client->peer_id);
1250 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1251 m_env->getTimeOfDay());
1253 m_con.Send(client->peer_id, 0, data, true);
1259 JMutexAutoLock lock(m_env_mutex);
1261 ScopeProfiler sp(g_profiler, "SEnv step");
1262 ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
1266 const float map_timer_and_unload_dtime = 2.92;
1267 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
1269 JMutexAutoLock lock(m_env_mutex);
1270 // Run Map's timers and unload unused data
1271 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
1272 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
1273 g_settings->getFloat("server_unload_unused_data_timeout"));
1284 JMutexAutoLock lock(m_env_mutex);
1285 JMutexAutoLock lock2(m_con_mutex);
1287 //float player_max_speed = BS * 4.0; // Normal speed
1288 float player_max_speed = BS * 20; // Fast speed
1289 float player_max_speed_up = BS * 20;
1291 player_max_speed *= 2.5; // Tolerance
1292 player_max_speed_up *= 2.5;
1294 for(core::map<u16, RemoteClient*>::Iterator
1295 i = m_clients.getIterator();
1296 i.atEnd() == false; i++)
1298 RemoteClient *client = i.getNode()->getValue();
1299 ServerRemotePlayer *player =
1300 static_cast<ServerRemotePlayer*>
1301 (m_env->getPlayer(client->peer_id));
1306 Check player movements
1308 NOTE: Actually the server should handle player physics like the
1309 client does and compare player's position to what is calculated
1310 on our side. This is required when eg. players fly due to an
1313 player->m_last_good_position_age += dtime;
1314 if(player->m_last_good_position_age >= 2.0){
1315 float age = player->m_last_good_position_age;
1316 v3f diff = (player->getPosition() - player->m_last_good_position);
1317 float d_vert = diff.Y;
1319 float d_horiz = diff.getLength();
1320 /*infostream<<player->getName()<<"'s horizontal speed is "
1321 <<(d_horiz/age)<<std::endl;*/
1322 if(d_horiz <= age * player_max_speed &&
1323 (d_vert < 0 || d_vert < age * player_max_speed_up)){
1324 player->m_last_good_position = player->getPosition();
1326 actionstream<<"Player "<<player->getName()
1327 <<" moved too fast; resetting position"
1329 player->setPosition(player->m_last_good_position);
1330 SendMovePlayer(player);
1332 player->m_last_good_position_age = 0;
1338 HandlePlayerHP(player, 0);
1341 Send player inventories and HPs if necessary
1343 if(player->m_inventory_not_sent){
1344 UpdateCrafting(player->peer_id);
1345 SendInventory(player->peer_id);
1347 if(player->m_hp_not_sent){
1348 SendPlayerHP(player);
1353 /* Transform liquids */
1354 m_liquid_transform_timer += dtime;
1355 if(m_liquid_transform_timer >= 1.00)
1357 m_liquid_transform_timer -= 1.00;
1359 JMutexAutoLock lock(m_env_mutex);
1361 ScopeProfiler sp(g_profiler, "Server: liquid transform");
1363 core::map<v3s16, MapBlock*> modified_blocks;
1364 m_env->getMap().transformLiquids(modified_blocks);
1369 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1370 ServerMap &map = ((ServerMap&)m_env->getMap());
1371 map.updateLighting(modified_blocks, lighting_modified_blocks);
1373 // Add blocks modified by lighting to modified_blocks
1374 for(core::map<v3s16, MapBlock*>::Iterator
1375 i = lighting_modified_blocks.getIterator();
1376 i.atEnd() == false; i++)
1378 MapBlock *block = i.getNode()->getValue();
1379 modified_blocks.insert(block->getPos(), block);
1383 Set the modified blocks unsent for all the clients
1386 JMutexAutoLock lock2(m_con_mutex);
1388 for(core::map<u16, RemoteClient*>::Iterator
1389 i = m_clients.getIterator();
1390 i.atEnd() == false; i++)
1392 RemoteClient *client = i.getNode()->getValue();
1394 if(modified_blocks.size() > 0)
1396 // Remove block from sent history
1397 client->SetBlocksNotSent(modified_blocks);
1402 // Periodically print some info
1404 float &counter = m_print_info_timer;
1410 JMutexAutoLock lock2(m_con_mutex);
1412 if(m_clients.size() != 0)
1413 infostream<<"Players:"<<std::endl;
1414 for(core::map<u16, RemoteClient*>::Iterator
1415 i = m_clients.getIterator();
1416 i.atEnd() == false; i++)
1418 //u16 peer_id = i.getNode()->getKey();
1419 RemoteClient *client = i.getNode()->getValue();
1420 Player *player = m_env->getPlayer(client->peer_id);
1423 infostream<<"* "<<player->getName()<<"\t";
1424 client->PrintInfo(infostream);
1429 //if(g_settings->getBool("enable_experimental"))
1433 Check added and deleted active objects
1436 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
1437 JMutexAutoLock envlock(m_env_mutex);
1438 JMutexAutoLock conlock(m_con_mutex);
1440 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
1442 // Radius inside which objects are active
1443 s16 radius = g_settings->getS16("active_object_send_range_blocks");
1444 radius *= MAP_BLOCKSIZE;
1446 for(core::map<u16, RemoteClient*>::Iterator
1447 i = m_clients.getIterator();
1448 i.atEnd() == false; i++)
1450 RemoteClient *client = i.getNode()->getValue();
1452 // If definitions and textures have not been sent, don't
1453 // send objects either
1454 if(!client->definitions_sent)
1457 Player *player = m_env->getPlayer(client->peer_id);
1460 // This can happen if the client timeouts somehow
1461 /*infostream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
1463 <<" has no associated player"<<std::endl;*/
1466 v3s16 pos = floatToInt(player->getPosition(), BS);
1468 core::map<u16, bool> removed_objects;
1469 core::map<u16, bool> added_objects;
1470 m_env->getRemovedActiveObjects(pos, radius,
1471 client->m_known_objects, removed_objects);
1472 m_env->getAddedActiveObjects(pos, radius,
1473 client->m_known_objects, added_objects);
1475 // Ignore if nothing happened
1476 if(removed_objects.size() == 0 && added_objects.size() == 0)
1478 //infostream<<"active objects: none changed"<<std::endl;
1482 std::string data_buffer;
1486 // Handle removed objects
1487 writeU16((u8*)buf, removed_objects.size());
1488 data_buffer.append(buf, 2);
1489 for(core::map<u16, bool>::Iterator
1490 i = removed_objects.getIterator();
1491 i.atEnd()==false; i++)
1494 u16 id = i.getNode()->getKey();
1495 ServerActiveObject* obj = m_env->getActiveObject(id);
1497 // Add to data buffer for sending
1498 writeU16((u8*)buf, i.getNode()->getKey());
1499 data_buffer.append(buf, 2);
1501 // Remove from known objects
1502 client->m_known_objects.remove(i.getNode()->getKey());
1504 if(obj && obj->m_known_by_count > 0)
1505 obj->m_known_by_count--;
1508 // Handle added objects
1509 writeU16((u8*)buf, added_objects.size());
1510 data_buffer.append(buf, 2);
1511 for(core::map<u16, bool>::Iterator
1512 i = added_objects.getIterator();
1513 i.atEnd()==false; i++)
1516 u16 id = i.getNode()->getKey();
1517 ServerActiveObject* obj = m_env->getActiveObject(id);
1520 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1522 infostream<<"WARNING: "<<__FUNCTION_NAME
1523 <<": NULL object"<<std::endl;
1525 type = obj->getType();
1527 // Add to data buffer for sending
1528 writeU16((u8*)buf, id);
1529 data_buffer.append(buf, 2);
1530 writeU8((u8*)buf, type);
1531 data_buffer.append(buf, 1);
1534 data_buffer.append(serializeLongString(
1535 obj->getClientInitializationData()));
1537 data_buffer.append(serializeLongString(""));
1539 // Add to known objects
1540 client->m_known_objects.insert(i.getNode()->getKey(), false);
1543 obj->m_known_by_count++;
1547 SharedBuffer<u8> reply(2 + data_buffer.size());
1548 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1549 memcpy((char*)&reply[2], data_buffer.c_str(),
1550 data_buffer.size());
1552 m_con.Send(client->peer_id, 0, reply, true);
1554 infostream<<"Server: Sent object remove/add: "
1555 <<removed_objects.size()<<" removed, "
1556 <<added_objects.size()<<" added, "
1557 <<"packet size is "<<reply.getSize()<<std::endl;
1562 Collect a list of all the objects known by the clients
1563 and report it back to the environment.
1566 core::map<u16, bool> all_known_objects;
1568 for(core::map<u16, RemoteClient*>::Iterator
1569 i = m_clients.getIterator();
1570 i.atEnd() == false; i++)
1572 RemoteClient *client = i.getNode()->getValue();
1573 // Go through all known objects of client
1574 for(core::map<u16, bool>::Iterator
1575 i = client->m_known_objects.getIterator();
1576 i.atEnd()==false; i++)
1578 u16 id = i.getNode()->getKey();
1579 all_known_objects[id] = true;
1583 m_env->setKnownActiveObjects(whatever);
1589 Send object messages
1592 JMutexAutoLock envlock(m_env_mutex);
1593 JMutexAutoLock conlock(m_con_mutex);
1595 //ScopeProfiler sp(g_profiler, "Server: sending object messages");
1598 // Value = data sent by object
1599 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1601 // Get active object messages from environment
1604 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
1608 core::list<ActiveObjectMessage>* message_list = NULL;
1609 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1610 n = buffered_messages.find(aom.id);
1613 message_list = new core::list<ActiveObjectMessage>;
1614 buffered_messages.insert(aom.id, message_list);
1618 message_list = n->getValue();
1620 message_list->push_back(aom);
1623 // Route data to every client
1624 for(core::map<u16, RemoteClient*>::Iterator
1625 i = m_clients.getIterator();
1626 i.atEnd()==false; i++)
1628 RemoteClient *client = i.getNode()->getValue();
1629 std::string reliable_data;
1630 std::string unreliable_data;
1631 // Go through all objects in message buffer
1632 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1633 j = buffered_messages.getIterator();
1634 j.atEnd()==false; j++)
1636 // If object is not known by client, skip it
1637 u16 id = j.getNode()->getKey();
1638 if(client->m_known_objects.find(id) == NULL)
1640 // Get message list of object
1641 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1642 // Go through every message
1643 for(core::list<ActiveObjectMessage>::Iterator
1644 k = list->begin(); k != list->end(); k++)
1646 // Compose the full new data with header
1647 ActiveObjectMessage aom = *k;
1648 std::string new_data;
1651 writeU16((u8*)&buf[0], aom.id);
1652 new_data.append(buf, 2);
1654 new_data += serializeString(aom.datastring);
1655 // Add data to buffer
1657 reliable_data += new_data;
1659 unreliable_data += new_data;
1663 reliable_data and unreliable_data are now ready.
1666 if(reliable_data.size() > 0)
1668 SharedBuffer<u8> reply(2 + reliable_data.size());
1669 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1670 memcpy((char*)&reply[2], reliable_data.c_str(),
1671 reliable_data.size());
1673 m_con.Send(client->peer_id, 0, reply, true);
1675 if(unreliable_data.size() > 0)
1677 SharedBuffer<u8> reply(2 + unreliable_data.size());
1678 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1679 memcpy((char*)&reply[2], unreliable_data.c_str(),
1680 unreliable_data.size());
1681 // Send as unreliable
1682 m_con.Send(client->peer_id, 0, reply, false);
1685 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1687 infostream<<"Server: Size of object message data: "
1688 <<"reliable: "<<reliable_data.size()
1689 <<", unreliable: "<<unreliable_data.size()
1694 // Clear buffered_messages
1695 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1696 i = buffered_messages.getIterator();
1697 i.atEnd()==false; i++)
1699 delete i.getNode()->getValue();
1703 } // enable_experimental
1706 Send queued-for-sending map edit events.
1709 // Don't send too many at a time
1712 // Single change sending is disabled if queue size is not small
1713 bool disable_single_change_sending = false;
1714 if(m_unsent_map_edit_queue.size() >= 4)
1715 disable_single_change_sending = true;
1717 bool got_any_events = false;
1719 // We'll log the amount of each
1722 while(m_unsent_map_edit_queue.size() != 0)
1724 got_any_events = true;
1726 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1728 // Players far away from the change are stored here.
1729 // Instead of sending the changes, MapBlocks are set not sent
1731 core::list<u16> far_players;
1733 if(event->type == MEET_ADDNODE)
1735 //infostream<<"Server: MEET_ADDNODE"<<std::endl;
1736 prof.add("MEET_ADDNODE", 1);
1737 if(disable_single_change_sending)
1738 sendAddNode(event->p, event->n, event->already_known_by_peer,
1741 sendAddNode(event->p, event->n, event->already_known_by_peer,
1744 else if(event->type == MEET_REMOVENODE)
1746 //infostream<<"Server: MEET_REMOVENODE"<<std::endl;
1747 prof.add("MEET_REMOVENODE", 1);
1748 if(disable_single_change_sending)
1749 sendRemoveNode(event->p, event->already_known_by_peer,
1752 sendRemoveNode(event->p, event->already_known_by_peer,
1755 else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
1757 infostream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
1758 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
1759 setBlockNotSent(event->p);
1761 else if(event->type == MEET_OTHER)
1763 infostream<<"Server: MEET_OTHER"<<std::endl;
1764 prof.add("MEET_OTHER", 1);
1765 for(core::map<v3s16, bool>::Iterator
1766 i = event->modified_blocks.getIterator();
1767 i.atEnd()==false; i++)
1769 v3s16 p = i.getNode()->getKey();
1775 prof.add("unknown", 1);
1776 infostream<<"WARNING: Server: Unknown MapEditEvent "
1777 <<((u32)event->type)<<std::endl;
1781 Set blocks not sent to far players
1783 if(far_players.size() > 0)
1785 // Convert list format to that wanted by SetBlocksNotSent
1786 core::map<v3s16, MapBlock*> modified_blocks2;
1787 for(core::map<v3s16, bool>::Iterator
1788 i = event->modified_blocks.getIterator();
1789 i.atEnd()==false; i++)
1791 v3s16 p = i.getNode()->getKey();
1792 modified_blocks2.insert(p,
1793 m_env->getMap().getBlockNoCreateNoEx(p));
1795 // Set blocks not sent
1796 for(core::list<u16>::Iterator
1797 i = far_players.begin();
1798 i != far_players.end(); i++)
1801 RemoteClient *client = getClient(peer_id);
1804 client->SetBlocksNotSent(modified_blocks2);
1810 /*// Don't send too many at a time
1812 if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
1818 infostream<<"Server: MapEditEvents:"<<std::endl;
1819 prof.print(infostream);
1825 Trigger emergethread (it somehow gets to a non-triggered but
1826 bysy state sometimes)
1829 float &counter = m_emergethread_trigger_timer;
1835 m_emergethread.trigger();
1839 // Save map, players and auth stuff
1841 float &counter = m_savemap_timer;
1843 if(counter >= g_settings->getFloat("server_map_save_interval"))
1847 ScopeProfiler sp(g_profiler, "Server: saving stuff");
1850 if(m_authmanager.isModified())
1851 m_authmanager.save();
1854 if(m_banmanager.isModified())
1855 m_banmanager.save();
1858 JMutexAutoLock lock(m_env_mutex);
1860 // Save changed parts of map
1861 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
1864 m_env->serializePlayers(m_mapsavedir);
1866 // Save environment metadata
1867 m_env->saveMeta(m_mapsavedir);
1872 void Server::Receive()
1874 DSTACK(__FUNCTION_NAME);
1875 SharedBuffer<u8> data;
1880 JMutexAutoLock conlock(m_con_mutex);
1881 datasize = m_con.Receive(peer_id, data);
1884 // This has to be called so that the client list gets synced
1885 // with the peer list of the connection
1886 handlePeerChanges();
1888 ProcessData(*data, datasize, peer_id);
1890 catch(con::InvalidIncomingDataException &e)
1892 infostream<<"Server::Receive(): "
1893 "InvalidIncomingDataException: what()="
1894 <<e.what()<<std::endl;
1896 catch(con::PeerNotFoundException &e)
1898 //NOTE: This is not needed anymore
1900 // The peer has been disconnected.
1901 // Find the associated player and remove it.
1903 /*JMutexAutoLock envlock(m_env_mutex);
1905 infostream<<"ServerThread: peer_id="<<peer_id
1906 <<" has apparently closed connection. "
1907 <<"Removing player."<<std::endl;
1909 m_env->removePlayer(peer_id);*/
1913 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1915 DSTACK(__FUNCTION_NAME);
1916 // Environment is locked first.
1917 JMutexAutoLock envlock(m_env_mutex);
1918 JMutexAutoLock conlock(m_con_mutex);
1921 Address address = m_con.GetPeerAddress(peer_id);
1923 // drop player if is ip is banned
1924 if(m_banmanager.isIpBanned(address.serializeString())){
1925 SendAccessDenied(m_con, peer_id,
1926 L"Your ip is banned. Banned name was "
1927 +narrow_to_wide(m_banmanager.getBanName(
1928 address.serializeString())));
1929 m_con.DeletePeer(peer_id);
1933 catch(con::PeerNotFoundException &e)
1935 infostream<<"Server::ProcessData(): Cancelling: peer "
1936 <<peer_id<<" not found"<<std::endl;
1940 u8 peer_ser_ver = getClient(peer_id)->serialization_version;
1948 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1950 if(command == TOSERVER_INIT)
1952 // [0] u16 TOSERVER_INIT
1953 // [2] u8 SER_FMT_VER_HIGHEST
1954 // [3] u8[20] player_name
1955 // [23] u8[28] password <--- can be sent without this, from old versions
1957 if(datasize < 2+1+PLAYERNAME_SIZE)
1960 infostream<<"Server: Got TOSERVER_INIT from "
1961 <<peer_id<<std::endl;
1963 // First byte after command is maximum supported
1964 // serialization version
1965 u8 client_max = data[2];
1966 u8 our_max = SER_FMT_VER_HIGHEST;
1967 // Use the highest version supported by both
1968 u8 deployed = core::min_(client_max, our_max);
1969 // If it's lower than the lowest supported, give up.
1970 if(deployed < SER_FMT_VER_LOWEST)
1971 deployed = SER_FMT_VER_INVALID;
1973 //peer->serialization_version = deployed;
1974 getClient(peer_id)->pending_serialization_version = deployed;
1976 if(deployed == SER_FMT_VER_INVALID)
1978 infostream<<"Server: Cannot negotiate "
1979 "serialization version with peer "
1980 <<peer_id<<std::endl;
1981 SendAccessDenied(m_con, peer_id, std::wstring(
1982 L"Your client's version is not supported.\n"
1983 L"Server version is ")
1984 + narrow_to_wide(VERSION_STRING) + L"."
1990 Read and check network protocol version
1993 u16 net_proto_version = 0;
1994 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
1996 net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
1999 getClient(peer_id)->net_proto_version = net_proto_version;
2001 if(net_proto_version == 0)
2003 SendAccessDenied(m_con, peer_id, std::wstring(
2004 L"Your client's version is not supported.\n"
2005 L"Server version is ")
2006 + narrow_to_wide(VERSION_STRING) + L"."
2011 if(g_settings->getBool("strict_protocol_version_checking"))
2013 if(net_proto_version != PROTOCOL_VERSION)
2015 SendAccessDenied(m_con, peer_id, std::wstring(
2016 L"Your client's version is not supported.\n"
2017 L"Server version is ")
2018 + narrow_to_wide(VERSION_STRING) + L",\n"
2019 + L"server's PROTOCOL_VERSION is "
2020 + narrow_to_wide(itos(PROTOCOL_VERSION))
2021 + L", client's PROTOCOL_VERSION is "
2022 + narrow_to_wide(itos(net_proto_version))
2033 char playername[PLAYERNAME_SIZE];
2034 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
2036 playername[i] = data[3+i];
2038 playername[PLAYERNAME_SIZE-1] = 0;
2040 if(playername[0]=='\0')
2042 infostream<<"Server: Player has empty name"<<std::endl;
2043 SendAccessDenied(m_con, peer_id,
2048 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
2050 infostream<<"Server: Player has invalid name"<<std::endl;
2051 SendAccessDenied(m_con, peer_id,
2052 L"Name contains unallowed characters");
2057 char password[PASSWORD_SIZE];
2058 if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
2060 // old version - assume blank password
2065 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2067 password[i] = data[23+i];
2069 password[PASSWORD_SIZE-1] = 0;
2072 // Add player to auth manager
2073 if(m_authmanager.exists(playername) == false)
2075 std::wstring default_password =
2076 narrow_to_wide(g_settings->get("default_password"));
2077 std::string translated_default_password =
2078 translatePassword(playername, default_password);
2080 // If default_password is empty, allow any initial password
2081 if (default_password.length() == 0)
2082 translated_default_password = password;
2084 infostream<<"Server: adding player "<<playername
2085 <<" to auth manager"<<std::endl;
2086 m_authmanager.add(playername);
2087 m_authmanager.setPassword(playername, translated_default_password);
2088 m_authmanager.setPrivs(playername,
2089 stringToPrivs(g_settings->get("default_privs")));
2090 m_authmanager.save();
2093 std::string checkpwd = m_authmanager.getPassword(playername);
2095 /*infostream<<"Server: Client gave password '"<<password
2096 <<"', the correct one is '"<<checkpwd<<"'"<<std::endl;*/
2098 if(password != checkpwd)
2100 infostream<<"Server: peer_id="<<peer_id
2101 <<": supplied invalid password for "
2102 <<playername<<std::endl;
2103 SendAccessDenied(m_con, peer_id, L"Invalid password");
2107 // Enforce user limit.
2108 // Don't enforce for users that have some admin right
2109 if(m_clients.size() >= g_settings->getU16("max_users") &&
2110 (m_authmanager.getPrivs(playername)
2111 & (PRIV_SERVER|PRIV_BAN|PRIV_PRIVS|PRIV_PASSWORD)) == 0 &&
2112 playername != g_settings->get("name"))
2114 SendAccessDenied(m_con, peer_id, L"Too many users.");
2119 ServerRemotePlayer *player = emergePlayer(playername, peer_id);
2121 // If failed, cancel
2124 infostream<<"Server: peer_id="<<peer_id
2125 <<": failed to emerge player"<<std::endl;
2130 player->m_removed = false;
2132 m_env->addActiveObject(player);
2135 Answer with a TOCLIENT_INIT
2138 SharedBuffer<u8> reply(2+1+6+8);
2139 writeU16(&reply[0], TOCLIENT_INIT);
2140 writeU8(&reply[2], deployed);
2141 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
2142 writeU64(&reply[2+1+6], m_env->getServerMap().getSeed());
2145 m_con.Send(peer_id, 0, reply, true);
2149 Send complete position information
2151 SendMovePlayer(player);
2156 if(command == TOSERVER_INIT2)
2158 infostream<<"Server: Got TOSERVER_INIT2 from "
2159 <<peer_id<<std::endl;
2162 getClient(peer_id)->serialization_version
2163 = getClient(peer_id)->pending_serialization_version;
2166 Send some initialization data
2169 // Send tool definitions
2170 SendToolDef(m_con, peer_id, m_toolmgr);
2172 // Send node definitions
2173 SendNodeDef(m_con, peer_id, m_nodedef);
2175 // Send CraftItem definitions
2176 SendCraftItemDef(m_con, peer_id, m_craftitemdef);
2179 SendTextures(peer_id);
2181 // Send player info to all players
2182 //SendPlayerInfos();
2184 // Send inventory to player
2185 UpdateCrafting(peer_id);
2186 SendInventory(peer_id);
2188 // Send player items to all players
2191 Player *player = m_env->getPlayer(peer_id);
2194 SendPlayerHP(player);
2198 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2199 m_env->getTimeOfDay());
2200 m_con.Send(peer_id, 0, data, true);
2203 // Now the client should know about everything
2204 getClient(peer_id)->definitions_sent = true;
2206 // Send information about server to player in chat
2207 SendChatMessage(peer_id, getStatusString());
2209 // Send information about joining in chat
2211 std::wstring name = L"unknown";
2212 Player *player = m_env->getPlayer(peer_id);
2214 name = narrow_to_wide(player->getName());
2216 std::wstring message;
2219 message += L" joined game";
2220 BroadcastChatMessage(message);
2223 // Warnings about protocol version can be issued here
2224 if(getClient(peer_id)->net_proto_version < PROTOCOL_VERSION)
2226 SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT IS OLD AND MAY WORK PROPERLY WITH THIS SERVER");
2230 Check HP, respawn if necessary
2232 HandlePlayerHP(player, 0);
2238 std::ostringstream os(std::ios_base::binary);
2239 for(core::map<u16, RemoteClient*>::Iterator
2240 i = m_clients.getIterator();
2241 i.atEnd() == false; i++)
2243 RemoteClient *client = i.getNode()->getValue();
2244 assert(client->peer_id == i.getNode()->getKey());
2245 if(client->serialization_version == SER_FMT_VER_INVALID)
2248 Player *player = m_env->getPlayer(client->peer_id);
2251 // Get name of player
2252 os<<player->getName()<<" ";
2255 actionstream<<player->getName()<<" joins game. List of players: "
2256 <<os.str()<<std::endl;
2262 if(peer_ser_ver == SER_FMT_VER_INVALID)
2264 infostream<<"Server::ProcessData(): Cancelling: Peer"
2265 " serialization format invalid or not initialized."
2266 " Skipping incoming command="<<command<<std::endl;
2270 Player *player = m_env->getPlayer(peer_id);
2271 ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
2274 infostream<<"Server::ProcessData(): Cancelling: "
2275 "No player for peer_id="<<peer_id
2279 if(command == TOSERVER_PLAYERPOS)
2281 if(datasize < 2+12+12+4+4)
2285 v3s32 ps = readV3S32(&data[start+2]);
2286 v3s32 ss = readV3S32(&data[start+2+12]);
2287 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2288 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2289 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2290 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2291 pitch = wrapDegrees(pitch);
2292 yaw = wrapDegrees(yaw);
2294 player->setPosition(position);
2295 player->setSpeed(speed);
2296 player->setPitch(pitch);
2297 player->setYaw(yaw);
2299 /*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2300 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2301 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2303 else if(command == TOSERVER_GOTBLOCKS)
2316 u16 count = data[2];
2317 for(u16 i=0; i<count; i++)
2319 if((s16)datasize < 2+1+(i+1)*6)
2320 throw con::InvalidIncomingDataException
2321 ("GOTBLOCKS length is too short");
2322 v3s16 p = readV3S16(&data[2+1+i*6]);
2323 /*infostream<<"Server: GOTBLOCKS ("
2324 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2325 RemoteClient *client = getClient(peer_id);
2326 client->GotBlock(p);
2329 else if(command == TOSERVER_DELETEDBLOCKS)
2342 u16 count = data[2];
2343 for(u16 i=0; i<count; i++)
2345 if((s16)datasize < 2+1+(i+1)*6)
2346 throw con::InvalidIncomingDataException
2347 ("DELETEDBLOCKS length is too short");
2348 v3s16 p = readV3S16(&data[2+1+i*6]);
2349 /*infostream<<"Server: DELETEDBLOCKS ("
2350 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2351 RemoteClient *client = getClient(peer_id);
2352 client->SetBlockNotSent(p);
2355 else if(command == TOSERVER_CLICK_OBJECT)
2357 infostream<<"Server: CLICK_OBJECT not supported anymore"<<std::endl;
2360 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2362 infostream<<"Server: CLICK_ACTIVEOBJECT not supported anymore"<<std::endl;
2365 else if(command == TOSERVER_GROUND_ACTION)
2367 infostream<<"Server: GROUND_ACTION not supported anymore"<<std::endl;
2371 else if(command == TOSERVER_RELEASE)
2373 infostream<<"Server: RELEASE not supported anymore"<<std::endl;
2376 else if(command == TOSERVER_SIGNTEXT)
2378 infostream<<"Server: SIGNTEXT not supported anymore"
2382 else if(command == TOSERVER_SIGNNODETEXT)
2384 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2392 std::string datastring((char*)&data[2], datasize-2);
2393 std::istringstream is(datastring, std::ios_base::binary);
2396 is.read((char*)buf, 6);
2397 v3s16 p = readV3S16(buf);
2398 is.read((char*)buf, 2);
2399 u16 textlen = readU16(buf);
2401 for(u16 i=0; i<textlen; i++)
2403 is.read((char*)buf, 1);
2404 text += (char)buf[0];
2407 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
2411 meta->setText(text);
2413 actionstream<<player->getName()<<" writes \""<<text<<"\" to sign"
2414 <<" at "<<PP(p)<<std::endl;
2416 v3s16 blockpos = getNodeBlockPos(p);
2417 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
2420 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2424 setBlockNotSent(blockpos);
2426 else if(command == TOSERVER_INVENTORY_ACTION)
2428 /*// Ignore inventory changes if in creative mode
2429 if(g_settings->getBool("creative_mode") == true)
2431 infostream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2435 // Strip command and create a stream
2436 std::string datastring((char*)&data[2], datasize-2);
2437 infostream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2438 std::istringstream is(datastring, std::ios_base::binary);
2440 InventoryAction *a = InventoryAction::deSerialize(is);
2443 infostream<<"TOSERVER_INVENTORY_ACTION: "
2444 <<"InventoryAction::deSerialize() returned NULL"
2450 c.current_player = player;
2453 Handle restrictions and special cases of the move action
2455 if(a->getType() == IACTION_MOVE
2456 && g_settings->getBool("creative_mode") == false)
2458 InventoryList *rlist = player->inventory.getList("craftresult");
2460 InventoryList *clist = player->inventory.getList("craft");
2462 InventoryList *mlist = player->inventory.getList("main");
2465 IMoveAction *ma = (IMoveAction*)a;
2468 Disable moving items into craftresult from elsewhere
2470 if(ma->to_inv == "current_player"
2471 && ma->to_list == "craftresult"
2472 && (ma->from_inv != "current_player"
2473 || ma->from_list != "craftresult"))
2475 infostream<<"Ignoring IMoveAction from "
2476 <<ma->from_inv<<":"<<ma->from_list
2477 <<" to "<<ma->to_inv<<":"<<ma->to_list
2478 <<" because dst is craftresult"
2479 <<" and src isn't craftresult"<<std::endl;
2485 Handle crafting (source is craftresult, which is preview)
2487 if(ma->from_inv == "current_player"
2488 && ma->from_list == "craftresult"
2489 && player->craftresult_is_preview)
2492 If the craftresult is placed on itself, crafting takes
2493 place and result is moved into main list
2495 if(ma->to_inv == "current_player"
2496 && ma->to_list == "craftresult")
2498 // Except if main list doesn't have free slots
2499 if(mlist->getFreeSlots() == 0){
2500 infostream<<"Cannot craft: Main list doesn't have"
2501 <<" free slots"<<std::endl;
2506 player->craftresult_is_preview = false;
2507 clist->decrementMaterials(1);
2509 InventoryItem *item1 = rlist->changeItem(0, NULL);
2510 mlist->addItem(item1);
2512 srp->m_inventory_not_sent = true;
2518 Disable action if there are no free slots in
2521 If the item is placed on an item that is not of the
2522 same kind, the existing item will be first moved to
2523 craftresult and immediately moved to the free slot.
2526 Inventory *inv_to = getInventory(&c, ma->to_inv);
2528 InventoryList *list_to = inv_to->getList(ma->to_list);
2530 if(list_to->getFreeSlots() == 0){
2531 infostream<<"Cannot craft: Destination doesn't have"
2532 <<" free slots"<<std::endl;
2536 }while(0); // Allow break
2541 player->craftresult_is_preview = false;
2542 clist->decrementMaterials(1);
2544 /* Print out action */
2545 InventoryItem *item = rlist->getItem(0);
2546 std::string itemstring = "NULL";
2548 itemstring = item->getItemString();
2549 actionstream<<player->getName()<<" crafts "
2550 <<itemstring<<std::endl;
2553 a->apply(&c, this, m_env);
2563 // Disallow moving items in elsewhere than player's inventory
2564 // if not allowed to build
2565 if((getPlayerPrivs(player) & PRIV_BUILD) == 0
2566 && (ma->from_inv != "current_player"
2567 || ma->to_inv != "current_player"))
2569 infostream<<"Cannot move outside of player's inventory: "
2570 <<"No build privilege"<<std::endl;
2575 // If player is not an admin, check for ownership of src
2576 if(ma->from_inv != "current_player"
2577 && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
2579 Strfnd fn(ma->from_inv);
2580 std::string id0 = fn.next(":");
2581 if(id0 == "nodemeta")
2584 p.X = stoi(fn.next(","));
2585 p.Y = stoi(fn.next(","));
2586 p.Z = stoi(fn.next(","));
2587 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
2588 if(meta->getOwner() != "" &&
2589 meta->getOwner() != player->getName())
2591 infostream<<"Cannot move item: "
2592 "not owner of metadata"
2599 // If player is not an admin, check for ownership of dst
2600 if(ma->to_inv != "current_player"
2601 && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
2603 Strfnd fn(ma->to_inv);
2604 std::string id0 = fn.next(":");
2605 if(id0 == "nodemeta")
2608 p.X = stoi(fn.next(","));
2609 p.Y = stoi(fn.next(","));
2610 p.Z = stoi(fn.next(","));
2611 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
2612 if(meta->getOwner() != "" &&
2613 meta->getOwner() != player->getName())
2615 infostream<<"Cannot move item: "
2616 "not owner of metadata"
2625 Handle restrictions and special cases of the drop action
2627 else if(a->getType() == IACTION_DROP)
2629 IDropAction *da = (IDropAction*)a;
2630 // Disallow dropping items if not allowed to build
2631 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2636 // If player is not an admin, check for ownership
2637 else if (da->from_inv != "current_player"
2638 && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
2640 Strfnd fn(da->from_inv);
2641 std::string id0 = fn.next(":");
2642 if(id0 == "nodemeta")
2645 p.X = stoi(fn.next(","));
2646 p.Y = stoi(fn.next(","));
2647 p.Z = stoi(fn.next(","));
2648 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
2649 if(meta->getOwner() != "" &&
2650 meta->getOwner() != player->getName())
2652 infostream<<"Cannot move item: "
2653 "not owner of metadata"
2663 a->apply(&c, this, m_env);
2667 else if(command == TOSERVER_CHAT_MESSAGE)
2675 std::string datastring((char*)&data[2], datasize-2);
2676 std::istringstream is(datastring, std::ios_base::binary);
2679 is.read((char*)buf, 2);
2680 u16 len = readU16(buf);
2682 std::wstring message;
2683 for(u16 i=0; i<len; i++)
2685 is.read((char*)buf, 2);
2686 message += (wchar_t)readU16(buf);
2689 // Get player name of this client
2690 std::wstring name = narrow_to_wide(player->getName());
2693 bool ate = scriptapi_on_chat_message(m_lua, player->getName(),
2694 wide_to_narrow(message));
2695 // If script ate the message, don't proceed
2699 // Line to send to players
2701 // Whether to send to the player that sent the line
2702 bool send_to_sender = false;
2703 // Whether to send to other players
2704 bool send_to_others = false;
2706 // Local player gets all privileges regardless of
2707 // what's set on their account.
2708 u64 privs = getPlayerPrivs(player);
2711 if(message[0] == L'/')
2713 size_t strip_size = 1;
2714 if (message[1] == L'#') // support old-style commans
2716 message = message.substr(strip_size);
2718 WStrfnd f1(message);
2719 f1.next(L" "); // Skip over /#whatever
2720 std::wstring paramstring = f1.next(L"");
2722 ServerCommandContext *ctx = new ServerCommandContext(
2723 str_split(message, L' '),
2730 std::wstring reply(processServerCommand(ctx));
2731 send_to_sender = ctx->flags & SEND_TO_SENDER;
2732 send_to_others = ctx->flags & SEND_TO_OTHERS;
2734 if (ctx->flags & SEND_NO_PREFIX)
2737 line += L"Server: " + reply;
2744 if(privs & PRIV_SHOUT)
2750 send_to_others = true;
2754 line += L"Server: You are not allowed to shout";
2755 send_to_sender = true;
2762 actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2765 Send the message to clients
2767 for(core::map<u16, RemoteClient*>::Iterator
2768 i = m_clients.getIterator();
2769 i.atEnd() == false; i++)
2771 // Get client and check that it is valid
2772 RemoteClient *client = i.getNode()->getValue();
2773 assert(client->peer_id == i.getNode()->getKey());
2774 if(client->serialization_version == SER_FMT_VER_INVALID)
2778 bool sender_selected = (peer_id == client->peer_id);
2779 if(sender_selected == true && send_to_sender == false)
2781 if(sender_selected == false && send_to_others == false)
2784 SendChatMessage(client->peer_id, line);
2788 else if(command == TOSERVER_DAMAGE)
2790 std::string datastring((char*)&data[2], datasize-2);
2791 std::istringstream is(datastring, std::ios_base::binary);
2792 u8 damage = readU8(is);
2794 if(g_settings->getBool("enable_damage"))
2796 actionstream<<player->getName()<<" damaged by "
2797 <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
2800 HandlePlayerHP(player, damage);
2804 SendPlayerHP(player);
2807 else if(command == TOSERVER_PASSWORD)
2810 [0] u16 TOSERVER_PASSWORD
2811 [2] u8[28] old password
2812 [30] u8[28] new password
2815 if(datasize != 2+PASSWORD_SIZE*2)
2817 /*char password[PASSWORD_SIZE];
2818 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2819 password[i] = data[2+i];
2820 password[PASSWORD_SIZE-1] = 0;*/
2822 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2830 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2832 char c = data[2+PASSWORD_SIZE+i];
2838 infostream<<"Server: Client requests a password change from "
2839 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
2841 std::string playername = player->getName();
2843 if(m_authmanager.exists(playername) == false)
2845 infostream<<"Server: playername not found in authmanager"<<std::endl;
2846 // Wrong old password supplied!!
2847 SendChatMessage(peer_id, L"playername not found in authmanager");
2851 std::string checkpwd = m_authmanager.getPassword(playername);
2853 if(oldpwd != checkpwd)
2855 infostream<<"Server: invalid old password"<<std::endl;
2856 // Wrong old password supplied!!
2857 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
2861 actionstream<<player->getName()<<" changes password"<<std::endl;
2863 m_authmanager.setPassword(playername, newpwd);
2865 infostream<<"Server: password change successful for "<<playername
2867 SendChatMessage(peer_id, L"Password change successful");
2869 else if(command == TOSERVER_PLAYERITEM)
2874 u16 item = readU16(&data[2]);
2875 player->wieldItem(item);
2876 SendWieldedItem(player);
2878 else if(command == TOSERVER_RESPAWN)
2883 srp->m_respawn_active = false;
2885 RespawnPlayer(player);
2887 actionstream<<player->getName()<<" respawns at "
2888 <<PP(player->getPosition()/BS)<<std::endl;
2890 srp->m_removed = false;
2892 m_env->addActiveObject(srp);
2894 else if(command == TOSERVER_INTERACT)
2896 std::string datastring((char*)&data[2], datasize-2);
2897 std::istringstream is(datastring, std::ios_base::binary);
2903 [5] u32 length of the next item
2904 [9] serialized PointedThing
2906 0: start digging (from undersurface) or use
2907 1: stop digging (all parameters ignored)
2908 2: digging completed
2909 3: place block or item (to abovesurface)
2912 u8 action = readU8(is);
2913 u16 item_i = readU16(is);
2914 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
2915 PointedThing pointed;
2916 pointed.deSerialize(tmp_is);
2918 infostream<<"TOSERVER_INTERACT: action="<<(int)action<<", item="<<item_i<<", pointed="<<pointed.dump()<<std::endl;
2920 v3f player_pos = srp->m_last_good_position;
2922 // Update wielded item
2923 srp->wieldItem(item_i);
2925 // Get pointed to node (undefined if not POINTEDTYPE_NODE)
2926 v3s16 p_under = pointed.node_undersurface;
2927 v3s16 p_above = pointed.node_abovesurface;
2929 // Get pointed to object (NULL if not POINTEDTYPE_OBJECT)
2930 ServerActiveObject *pointed_object = NULL;
2931 if(pointed.type == POINTEDTHING_OBJECT)
2933 pointed_object = m_env->getActiveObject(pointed.object_id);
2934 if(pointed_object == NULL)
2936 infostream<<"TOSERVER_INTERACT: "
2937 "pointed object is NULL"<<std::endl;
2944 Check that target is reasonably close
2945 (only when digging or placing things)
2947 if(action == 0 || action == 2 || action == 3)
2949 v3f pointed_pos = player_pos;
2950 if(pointed.type == POINTEDTHING_NODE)
2952 pointed_pos = intToFloat(p_under, BS);
2954 else if(pointed.type == POINTEDTHING_OBJECT)
2956 pointed_pos = pointed_object->getBasePosition();
2959 float d = player_pos.getDistanceFrom(pointed_pos);
2960 float max_d = BS * 10; // Just some large enough value
2962 actionstream<<"Player "<<player->getName()
2963 <<" tried to access "<<pointed.dump()
2965 <<"d="<<d<<", max_d="<<max_d
2966 <<". ignoring."<<std::endl;
2967 // Re-send block to revert change on client-side
2968 RemoteClient *client = getClient(peer_id);
2969 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos, BS));
2970 client->SetBlockNotSent(blockpos);
2977 Make sure the player is allowed to do it
2979 bool build_priv = (getPlayerPrivs(player) & PRIV_BUILD) != 0;
2982 infostream<<"Ignoring interaction from player "<<player->getName()
2983 <<" because privileges are "<<getPlayerPrivs(player)
2985 // NOTE: no return; here, fall through
2989 0: start digging or punch object
2993 if(pointed.type == POINTEDTHING_NODE)
2996 NOTE: This can be used in the future to check if
2997 somebody is cheating, by checking the timing.
2999 bool cannot_punch_node = !build_priv;
3001 MapNode n(CONTENT_IGNORE);
3005 n = m_env->getMap().getNode(p_under);
3007 catch(InvalidPositionException &e)
3009 infostream<<"Server: Not punching: Node not found."
3010 <<" Adding block to emerge queue."
3012 m_emerge_queue.addBlock(peer_id,
3013 getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
3014 cannot_punch_node = true;
3017 if(cannot_punch_node)
3023 scriptapi_environment_on_punchnode(m_lua, p_under, n, srp);
3025 else if(pointed.type == POINTEDTHING_OBJECT)
3030 // Skip if object has been removed
3031 if(pointed_object->m_removed)
3034 actionstream<<player->getName()<<" punches object "
3035 <<pointed.object_id<<std::endl;
3038 pointed_object->punch(srp);
3046 else if(action == 1)
3051 2: Digging completed
3053 else if(action == 2)
3055 // Only complete digging of nodes
3056 if(pointed.type != POINTEDTHING_NODE)
3059 // Mandatory parameter; actually used for nothing
3060 core::map<v3s16, MapBlock*> modified_blocks;
3062 content_t material = CONTENT_IGNORE;
3063 u8 mineral = MINERAL_NONE;
3065 bool cannot_remove_node = !build_priv;
3067 MapNode n(CONTENT_IGNORE);
3070 n = m_env->getMap().getNode(p_under);
3072 mineral = n.getMineral(m_nodedef);
3073 // Get material at position
3074 material = n.getContent();
3075 // If not yet cancelled
3076 if(cannot_remove_node == false)
3078 // If it's not diggable, do nothing
3079 if(m_nodedef->get(material).diggable == false)
3081 infostream<<"Server: Not finishing digging: "
3082 <<"Node not diggable"
3084 cannot_remove_node = true;
3087 // If not yet cancelled
3088 if(cannot_remove_node == false)
3090 // Get node metadata
3091 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p_under);
3092 if(meta && meta->nodeRemovalDisabled() == true)
3094 infostream<<"Server: Not finishing digging: "
3095 <<"Node metadata disables removal"
3097 cannot_remove_node = true;
3101 catch(InvalidPositionException &e)
3103 infostream<<"Server: Not finishing digging: Node not found."
3104 <<" Adding block to emerge queue."
3106 m_emerge_queue.addBlock(peer_id,
3107 getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
3108 cannot_remove_node = true;
3112 If node can't be removed, set block to be re-sent to
3115 if(cannot_remove_node)
3117 infostream<<"Server: Not finishing digging."<<std::endl;
3119 // Client probably has wrong data.
3120 // Set block not sent, so that client will get
3122 infostream<<"Client "<<peer_id<<" tried to dig "
3123 <<"node; but node cannot be removed."
3124 <<" setting MapBlock not sent."<<std::endl;
3125 RemoteClient *client = getClient(peer_id);
3126 v3s16 blockpos = getNodeBlockPos(p_under);
3127 client->SetBlockNotSent(blockpos);
3132 actionstream<<player->getName()<<" digs "<<PP(p_under)
3133 <<", gets material "<<(int)material<<", mineral "
3134 <<(int)mineral<<std::endl;
3137 Send the removal to all close-by players.
3138 - If other player is close, send REMOVENODE
3139 - Otherwise set blocks not sent
3141 core::list<u16> far_players;
3142 sendRemoveNode(p_under, peer_id, &far_players, 30);
3145 Update and send inventory
3148 if(g_settings->getBool("creative_mode") == false)
3153 InventoryList *mlist = player->inventory.getList("main");
3156 InventoryItem *item = mlist->getItem(item_i);
3157 if(item && (std::string)item->getName() == "ToolItem")
3159 ToolItem *titem = (ToolItem*)item;
3160 std::string toolname = titem->getToolName();
3162 // Get digging properties for material and tool
3163 ToolDiggingProperties tp =
3164 m_toolmgr->getDiggingProperties(toolname);
3165 DiggingProperties prop =
3166 getDiggingProperties(material, &tp, m_nodedef);
3168 if(prop.diggable == false)
3170 infostream<<"Server: WARNING: Player digged"
3171 <<" with impossible material + tool"
3172 <<" combination"<<std::endl;
3175 bool weared_out = titem->addWear(prop.wear);
3179 mlist->deleteItem(item_i);
3182 srp->m_inventory_not_sent = true;
3187 Add dug item to inventory
3190 InventoryItem *item = NULL;
3192 if(mineral != MINERAL_NONE)
3193 item = getDiggedMineralItem(mineral, this);
3198 const std::string &dug_s = m_nodedef->get(material).dug_item;
3201 std::istringstream is(dug_s, std::ios::binary);
3202 item = InventoryItem::deSerialize(is, this);
3208 // Add a item to inventory
3209 player->inventory.addItem("main", item);
3210 srp->m_inventory_not_sent = true;
3215 if(mineral != MINERAL_NONE)
3216 item = getDiggedMineralItem(mineral, this);
3221 const std::string &extra_dug_s = m_nodedef->get(material).extra_dug_item;
3222 s32 extra_rarity = m_nodedef->get(material).extra_dug_item_rarity;
3223 if(extra_dug_s != "" && extra_rarity != 0
3224 && myrand() % extra_rarity == 0)
3226 std::istringstream is(extra_dug_s, std::ios::binary);
3227 item = InventoryItem::deSerialize(is, this);
3233 // Add a item to inventory
3234 player->inventory.addItem("main", item);
3235 srp->m_inventory_not_sent = true;
3241 (this takes some time so it is done after the quick stuff)
3244 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
3246 m_env->getMap().removeNodeAndUpdate(p_under, modified_blocks);
3249 Set blocks not sent to far players
3251 for(core::list<u16>::Iterator
3252 i = far_players.begin();
3253 i != far_players.end(); i++)
3256 RemoteClient *client = getClient(peer_id);
3259 client->SetBlocksNotSent(modified_blocks);
3265 scriptapi_environment_on_dignode(m_lua, p_under, n, srp);
3269 3: place block or right-click object
3271 else if(action == 3)
3273 if(pointed.type == POINTEDTHING_NODE)
3275 InventoryList *ilist = player->inventory.getList("main");
3280 InventoryItem *item = ilist->getItem(item_i);
3282 // If there is no item, it is not possible to add it anywhere
3287 Handle material items
3289 if(std::string("MaterialItem") == item->getName())
3291 bool cannot_place_node = !build_priv;
3294 // Don't add a node if this is not a free space
3295 MapNode n2 = m_env->getMap().getNode(p_above);
3296 if(m_nodedef->get(n2).buildable_to == false)
3298 infostream<<"Client "<<peer_id<<" tried to place"
3299 <<" node in invalid position."<<std::endl;
3300 cannot_place_node = true;
3303 catch(InvalidPositionException &e)
3305 infostream<<"Server: Ignoring ADDNODE: Node not found"
3306 <<" Adding block to emerge queue."
3308 m_emerge_queue.addBlock(peer_id,
3309 getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
3310 cannot_place_node = true;
3313 if(cannot_place_node)
3315 // Client probably has wrong data.
3316 // Set block not sent, so that client will get
3318 RemoteClient *client = getClient(peer_id);
3319 v3s16 blockpos = getNodeBlockPos(p_above);
3320 client->SetBlockNotSent(blockpos);
3324 // Reset build time counter
3325 getClient(peer_id)->m_time_from_building = 0.0;
3328 MaterialItem *mitem = (MaterialItem*)item;
3330 n.setContent(mitem->getMaterial());
3332 actionstream<<player->getName()<<" places material "
3333 <<(int)mitem->getMaterial()
3334 <<" at "<<PP(p_under)<<std::endl;
3336 // Calculate direction for wall mounted stuff
3337 if(m_nodedef->get(n).wall_mounted)
3338 n.param2 = packDir(p_under - p_above);
3340 // Calculate the direction for furnaces and chests and stuff
3341 if(m_nodedef->get(n).param_type == CPT_FACEDIR_SIMPLE)
3343 v3f playerpos = player->getPosition();
3344 v3f blockpos = intToFloat(p_above, BS) - playerpos;
3345 blockpos = blockpos.normalize();
3347 if (fabs(blockpos.X) > fabs(blockpos.Z)) {
3361 Send to all close-by players
3363 core::list<u16> far_players;
3364 sendAddNode(p_above, n, 0, &far_players, 30);
3369 InventoryList *ilist = player->inventory.getList("main");
3370 if(g_settings->getBool("creative_mode") == false && ilist)
3372 // Remove from inventory and send inventory
3373 if(mitem->getCount() <= 1)
3374 ilist->deleteItem(item_i);
3377 srp->m_inventory_not_sent = true;
3383 This takes some time so it is done after the quick stuff
3385 core::map<v3s16, MapBlock*> modified_blocks;
3387 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
3389 std::string p_name = std::string(player->getName());
3390 m_env->getMap().addNodeAndUpdate(p_above, n, modified_blocks, p_name);
3393 Set blocks not sent to far players
3395 for(core::list<u16>::Iterator
3396 i = far_players.begin();
3397 i != far_players.end(); i++)
3400 RemoteClient *client = getClient(peer_id);
3403 client->SetBlocksNotSent(modified_blocks);
3409 scriptapi_environment_on_placenode(m_lua, p_above, n, srp);
3412 Calculate special events
3415 /*if(n.d == LEGN(m_nodedef, "CONTENT_MESE"))
3418 for(s16 z=-1; z<=1; z++)
3419 for(s16 y=-1; y<=1; y++)
3420 for(s16 x=-1; x<=1; x++)
3427 Place other item (not a block)
3433 infostream<<"Not allowing player to place item: "
3434 "no build privileges"<<std::endl;
3438 // Calculate a position for it
3439 v3f pos = player_pos;
3440 if(pointed.type == POINTEDTHING_NOTHING)
3442 infostream<<"Not allowing player to place item: "
3443 "pointing to nothing"<<std::endl;
3446 else if(pointed.type == POINTEDTHING_NODE)
3448 pos = intToFloat(p_above, BS);
3450 else if(pointed.type == POINTEDTHING_OBJECT)
3452 pos = pointed_object->getBasePosition();
3455 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
3456 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
3460 //pos.Y -= BS*0.25; // let it drop a bit
3463 Check that the block is loaded so that the item
3464 can properly be added to the static list too
3466 v3s16 blockpos = getNodeBlockPos(floatToInt(pos, BS));
3467 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3470 infostream<<"Error while placing item: "
3471 "block not found"<<std::endl;
3475 actionstream<<player->getName()<<" places "<<item->getName()
3476 <<" at "<<PP(pos)<<std::endl;
3481 bool remove = item->dropOrPlace(m_env, srp, pos, true, -1);
3482 if(remove && g_settings->getBool("creative_mode") == false)
3484 InventoryList *ilist = player->inventory.getList("main");
3486 // Remove from inventory and send inventory
3487 ilist->deleteItem(item_i);
3488 srp->m_inventory_not_sent = true;
3493 else if(pointed.type == POINTEDTHING_OBJECT)
3495 // Right click object
3500 // Skip if object has been removed
3501 if(pointed_object->m_removed)
3504 actionstream<<player->getName()<<" right-clicks object "
3505 <<pointed.object_id<<std::endl;
3508 pointed_object->rightClick(srp);
3516 else if(action == 4)
3518 InventoryList *ilist = player->inventory.getList("main");
3523 InventoryItem *item = ilist->getItem(item_i);
3525 // If there is no item, it is not possible to add it anywhere
3529 // Requires build privs
3532 infostream<<"Not allowing player to use item: "
3533 "no build privileges"<<std::endl;
3537 actionstream<<player->getName()<<" uses "<<item->getName()
3538 <<", pointing at "<<pointed.dump()<<std::endl;
3540 bool remove = item->use(m_env, srp, pointed);
3542 if(remove && g_settings->getBool("creative_mode") == false)
3544 InventoryList *ilist = player->inventory.getList("main");
3546 // Remove from inventory and send inventory
3547 ilist->deleteItem(item_i);
3548 srp->m_inventory_not_sent = true;
3555 Catch invalid actions
3559 infostream<<"WARNING: Server: Invalid action "
3560 <<action<<std::endl;
3563 // Complete add_to_inventory_later
3564 srp->completeAddToInventoryLater(item_i);
3568 infostream<<"Server::ProcessData(): Ignoring "
3569 "unknown command "<<command<<std::endl;
3573 catch(SendFailedException &e)
3575 errorstream<<"Server::ProcessData(): SendFailedException: "
3581 void Server::onMapEditEvent(MapEditEvent *event)
3583 //infostream<<"Server::onMapEditEvent()"<<std::endl;
3584 if(m_ignore_map_edit_events)
3586 MapEditEvent *e = event->clone();
3587 m_unsent_map_edit_queue.push_back(e);
3590 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3592 if(id == "current_player")
3594 assert(c->current_player);
3595 return &(c->current_player->inventory);
3599 std::string id0 = fn.next(":");
3601 if(id0 == "nodemeta")
3604 p.X = stoi(fn.next(","));
3605 p.Y = stoi(fn.next(","));
3606 p.Z = stoi(fn.next(","));
3607 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3609 return meta->getInventory();
3610 infostream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3611 <<"no metadata found"<<std::endl;
3615 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3618 void Server::inventoryModified(InventoryContext *c, std::string id)
3620 if(id == "current_player")
3622 assert(c->current_player);
3623 ServerRemotePlayer *srp =
3624 static_cast<ServerRemotePlayer*>(c->current_player);
3625 srp->m_inventory_not_sent = true;
3630 std::string id0 = fn.next(":");
3632 if(id0 == "nodemeta")
3635 p.X = stoi(fn.next(","));
3636 p.Y = stoi(fn.next(","));
3637 p.Z = stoi(fn.next(","));
3638 v3s16 blockpos = getNodeBlockPos(p);
3640 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3642 meta->inventoryModified();
3644 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3646 block->raiseModified(MOD_STATE_WRITE_NEEDED);
3648 setBlockNotSent(blockpos);
3653 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3656 core::list<PlayerInfo> Server::getPlayerInfo()
3658 DSTACK(__FUNCTION_NAME);
3659 JMutexAutoLock envlock(m_env_mutex);
3660 JMutexAutoLock conlock(m_con_mutex);
3662 core::list<PlayerInfo> list;
3664 core::list<Player*> players = m_env->getPlayers();
3666 core::list<Player*>::Iterator i;
3667 for(i = players.begin();
3668 i != players.end(); i++)
3672 Player *player = *i;
3675 // Copy info from connection to info struct
3676 info.id = player->peer_id;
3677 info.address = m_con.GetPeerAddress(player->peer_id);
3678 info.avg_rtt = m_con.GetPeerAvgRTT(player->peer_id);
3680 catch(con::PeerNotFoundException &e)
3682 // Set dummy peer info
3684 info.address = Address(0,0,0,0,0);
3688 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3689 info.position = player->getPosition();
3691 list.push_back(info);
3698 void Server::peerAdded(con::Peer *peer)
3700 DSTACK(__FUNCTION_NAME);
3701 infostream<<"Server::peerAdded(): peer->id="
3702 <<peer->id<<std::endl;
3705 c.type = PEER_ADDED;
3706 c.peer_id = peer->id;
3708 m_peer_change_queue.push_back(c);
3711 void Server::deletingPeer(con::Peer *peer, bool timeout)
3713 DSTACK(__FUNCTION_NAME);
3714 infostream<<"Server::deletingPeer(): peer->id="
3715 <<peer->id<<", timeout="<<timeout<<std::endl;
3718 c.type = PEER_REMOVED;
3719 c.peer_id = peer->id;
3720 c.timeout = timeout;
3721 m_peer_change_queue.push_back(c);
3728 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3730 DSTACK(__FUNCTION_NAME);
3731 std::ostringstream os(std::ios_base::binary);
3733 writeU16(os, TOCLIENT_HP);
3737 std::string s = os.str();
3738 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3740 con.Send(peer_id, 0, data, true);
3743 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3744 const std::wstring &reason)
3746 DSTACK(__FUNCTION_NAME);
3747 std::ostringstream os(std::ios_base::binary);
3749 writeU16(os, TOCLIENT_ACCESS_DENIED);
3750 os<<serializeWideString(reason);
3753 std::string s = os.str();
3754 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3756 con.Send(peer_id, 0, data, true);
3759 void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
3760 bool set_camera_point_target, v3f camera_point_target)
3762 DSTACK(__FUNCTION_NAME);
3763 std::ostringstream os(std::ios_base::binary);
3765 writeU16(os, TOCLIENT_DEATHSCREEN);
3766 writeU8(os, set_camera_point_target);
3767 writeV3F1000(os, camera_point_target);
3770 std::string s = os.str();
3771 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3773 con.Send(peer_id, 0, data, true);
3776 void Server::SendToolDef(con::Connection &con, u16 peer_id,
3777 IToolDefManager *tooldef)
3779 DSTACK(__FUNCTION_NAME);
3780 std::ostringstream os(std::ios_base::binary);
3784 u32 length of the next item
3785 serialized ToolDefManager
3787 writeU16(os, TOCLIENT_TOOLDEF);
3788 std::ostringstream tmp_os(std::ios::binary);
3789 tooldef->serialize(tmp_os);
3790 os<<serializeLongString(tmp_os.str());
3793 std::string s = os.str();
3794 infostream<<"Server::SendToolDef(): Sending tool definitions: size="
3795 <<s.size()<<std::endl;
3796 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3798 con.Send(peer_id, 0, data, true);
3801 void Server::SendNodeDef(con::Connection &con, u16 peer_id,
3802 INodeDefManager *nodedef)
3804 DSTACK(__FUNCTION_NAME);
3805 std::ostringstream os(std::ios_base::binary);
3809 u32 length of the next item
3810 serialized NodeDefManager
3812 writeU16(os, TOCLIENT_NODEDEF);
3813 std::ostringstream tmp_os(std::ios::binary);
3814 nodedef->serialize(tmp_os);
3815 os<<serializeLongString(tmp_os.str());
3818 std::string s = os.str();
3819 infostream<<"Server::SendNodeDef(): Sending node definitions: size="
3820 <<s.size()<<std::endl;
3821 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3823 con.Send(peer_id, 0, data, true);
3826 void Server::SendCraftItemDef(con::Connection &con, u16 peer_id,
3827 ICraftItemDefManager *craftitemdef)
3829 DSTACK(__FUNCTION_NAME);
3830 std::ostringstream os(std::ios_base::binary);
3834 u32 length of the next item
3835 serialized CraftItemDefManager
3837 writeU16(os, TOCLIENT_CRAFTITEMDEF);
3838 std::ostringstream tmp_os(std::ios::binary);
3839 craftitemdef->serialize(tmp_os);
3840 os<<serializeLongString(tmp_os.str());
3843 std::string s = os.str();
3844 infostream<<"Server::SendCraftItemDef(): Sending craft item definitions: size="
3845 <<s.size()<<std::endl;
3846 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3848 con.Send(peer_id, 0, data, true);
3852 Non-static send methods
3855 void Server::SendInventory(u16 peer_id)
3857 DSTACK(__FUNCTION_NAME);
3859 ServerRemotePlayer* player =
3860 static_cast<ServerRemotePlayer*>(m_env->getPlayer(peer_id));
3863 player->m_inventory_not_sent = false;
3869 std::ostringstream os;
3870 //os.imbue(std::locale("C"));
3872 player->inventory.serialize(os);
3874 std::string s = os.str();
3876 SharedBuffer<u8> data(s.size()+2);
3877 writeU16(&data[0], TOCLIENT_INVENTORY);
3878 memcpy(&data[2], s.c_str(), s.size());
3881 m_con.Send(peer_id, 0, data, true);
3884 std::string getWieldedItemString(const Player *player)
3886 const InventoryItem *item = player->getWieldItem();
3888 return std::string("");
3889 std::ostringstream os(std::ios_base::binary);
3890 item->serialize(os);
3894 void Server::SendWieldedItem(const Player* player)
3896 DSTACK(__FUNCTION_NAME);
3900 std::ostringstream os(std::ios_base::binary);
3902 writeU16(os, TOCLIENT_PLAYERITEM);
3904 writeU16(os, player->peer_id);
3905 os<<serializeString(getWieldedItemString(player));
3908 std::string s = os.str();
3909 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3911 m_con.SendToAll(0, data, true);
3914 void Server::SendPlayerItems()
3916 DSTACK(__FUNCTION_NAME);
3918 std::ostringstream os(std::ios_base::binary);
3919 core::list<Player *> players = m_env->getPlayers(true);
3921 writeU16(os, TOCLIENT_PLAYERITEM);
3922 writeU16(os, players.size());
3923 core::list<Player *>::Iterator i;
3924 for(i = players.begin(); i != players.end(); ++i)
3927 writeU16(os, p->peer_id);
3928 os<<serializeString(getWieldedItemString(p));
3932 std::string s = os.str();
3933 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3935 m_con.SendToAll(0, data, true);
3938 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3940 DSTACK(__FUNCTION_NAME);
3942 std::ostringstream os(std::ios_base::binary);
3946 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3947 os.write((char*)buf, 2);
3950 writeU16(buf, message.size());
3951 os.write((char*)buf, 2);
3954 for(u32 i=0; i<message.size(); i++)
3958 os.write((char*)buf, 2);
3962 std::string s = os.str();
3963 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3965 m_con.Send(peer_id, 0, data, true);
3968 void Server::BroadcastChatMessage(const std::wstring &message)
3970 for(core::map<u16, RemoteClient*>::Iterator
3971 i = m_clients.getIterator();
3972 i.atEnd() == false; i++)
3974 // Get client and check that it is valid
3975 RemoteClient *client = i.getNode()->getValue();
3976 assert(client->peer_id == i.getNode()->getKey());
3977 if(client->serialization_version == SER_FMT_VER_INVALID)
3980 SendChatMessage(client->peer_id, message);
3984 void Server::SendPlayerHP(Player *player)
3986 SendHP(m_con, player->peer_id, player->hp);
3989 void Server::SendMovePlayer(Player *player)
3991 DSTACK(__FUNCTION_NAME);
3992 std::ostringstream os(std::ios_base::binary);
3994 writeU16(os, TOCLIENT_MOVE_PLAYER);
3995 writeV3F1000(os, player->getPosition());
3996 writeF1000(os, player->getPitch());
3997 writeF1000(os, player->getYaw());
4000 v3f pos = player->getPosition();
4001 f32 pitch = player->getPitch();
4002 f32 yaw = player->getYaw();
4003 infostream<<"Server sending TOCLIENT_MOVE_PLAYER"
4004 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
4011 std::string s = os.str();
4012 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4014 m_con.Send(player->peer_id, 0, data, true);
4017 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
4018 core::list<u16> *far_players, float far_d_nodes)
4020 float maxd = far_d_nodes*BS;
4021 v3f p_f = intToFloat(p, BS);
4025 SharedBuffer<u8> reply(replysize);
4026 writeU16(&reply[0], TOCLIENT_REMOVENODE);
4027 writeS16(&reply[2], p.X);
4028 writeS16(&reply[4], p.Y);
4029 writeS16(&reply[6], p.Z);
4031 for(core::map<u16, RemoteClient*>::Iterator
4032 i = m_clients.getIterator();
4033 i.atEnd() == false; i++)
4035 // Get client and check that it is valid
4036 RemoteClient *client = i.getNode()->getValue();
4037 assert(client->peer_id == i.getNode()->getKey());
4038 if(client->serialization_version == SER_FMT_VER_INVALID)
4041 // Don't send if it's the same one
4042 if(client->peer_id == ignore_id)
4048 Player *player = m_env->getPlayer(client->peer_id);
4051 // If player is far away, only set modified blocks not sent
4052 v3f player_pos = player->getPosition();
4053 if(player_pos.getDistanceFrom(p_f) > maxd)
4055 far_players->push_back(client->peer_id);
4062 m_con.Send(client->peer_id, 0, reply, true);
4066 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
4067 core::list<u16> *far_players, float far_d_nodes)
4069 float maxd = far_d_nodes*BS;
4070 v3f p_f = intToFloat(p, BS);
4072 for(core::map<u16, RemoteClient*>::Iterator
4073 i = m_clients.getIterator();
4074 i.atEnd() == false; i++)
4076 // Get client and check that it is valid
4077 RemoteClient *client = i.getNode()->getValue();
4078 assert(client->peer_id == i.getNode()->getKey());
4079 if(client->serialization_version == SER_FMT_VER_INVALID)
4082 // Don't send if it's the same one
4083 if(client->peer_id == ignore_id)
4089 Player *player = m_env->getPlayer(client->peer_id);
4092 // If player is far away, only set modified blocks not sent
4093 v3f player_pos = player->getPosition();
4094 if(player_pos.getDistanceFrom(p_f) > maxd)
4096 far_players->push_back(client->peer_id);
4103 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
4104 SharedBuffer<u8> reply(replysize);
4105 writeU16(&reply[0], TOCLIENT_ADDNODE);
4106 writeS16(&reply[2], p.X);
4107 writeS16(&reply[4], p.Y);
4108 writeS16(&reply[6], p.Z);
4109 n.serialize(&reply[8], client->serialization_version);
4112 m_con.Send(client->peer_id, 0, reply, true);
4116 void Server::setBlockNotSent(v3s16 p)
4118 for(core::map<u16, RemoteClient*>::Iterator
4119 i = m_clients.getIterator();
4120 i.atEnd()==false; i++)
4122 RemoteClient *client = i.getNode()->getValue();
4123 client->SetBlockNotSent(p);
4127 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
4129 DSTACK(__FUNCTION_NAME);
4131 v3s16 p = block->getPos();
4135 bool completely_air = true;
4136 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4137 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4138 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4140 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
4142 completely_air = false;
4143 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
4148 infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
4150 infostream<<"[completely air] ";
4151 infostream<<std::endl;
4155 Create a packet with the block in the right format
4158 std::ostringstream os(std::ios_base::binary);
4159 block->serialize(os, ver);
4160 std::string s = os.str();
4161 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
4163 u32 replysize = 8 + blockdata.getSize();
4164 SharedBuffer<u8> reply(replysize);
4165 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
4166 writeS16(&reply[2], p.X);
4167 writeS16(&reply[4], p.Y);
4168 writeS16(&reply[6], p.Z);
4169 memcpy(&reply[8], *blockdata, blockdata.getSize());
4171 /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4172 <<": \tpacket size: "<<replysize<<std::endl;*/
4177 m_con.Send(peer_id, 1, reply, true);
4180 void Server::SendBlocks(float dtime)
4182 DSTACK(__FUNCTION_NAME);
4184 JMutexAutoLock envlock(m_env_mutex);
4185 JMutexAutoLock conlock(m_con_mutex);
4187 //TimeTaker timer("Server::SendBlocks");
4189 core::array<PrioritySortedBlockTransfer> queue;
4191 s32 total_sending = 0;
4194 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
4196 for(core::map<u16, RemoteClient*>::Iterator
4197 i = m_clients.getIterator();
4198 i.atEnd() == false; i++)
4200 RemoteClient *client = i.getNode()->getValue();
4201 assert(client->peer_id == i.getNode()->getKey());
4203 // If definitions and textures have not been sent, don't
4204 // send MapBlocks either
4205 if(!client->definitions_sent)
4208 total_sending += client->SendingCount();
4210 if(client->serialization_version == SER_FMT_VER_INVALID)
4213 client->GetNextBlocks(this, dtime, queue);
4218 // Lowest priority number comes first.
4219 // Lowest is most important.
4222 for(u32 i=0; i<queue.size(); i++)
4224 //TODO: Calculate limit dynamically
4225 if(total_sending >= g_settings->getS32
4226 ("max_simultaneous_block_sends_server_total"))
4229 PrioritySortedBlockTransfer q = queue[i];
4231 MapBlock *block = NULL;
4234 block = m_env->getMap().getBlockNoCreate(q.pos);
4236 catch(InvalidPositionException &e)
4241 RemoteClient *client = getClient(q.peer_id);
4243 SendBlockNoLock(q.peer_id, block, client->serialization_version);
4245 client->SentBlock(q.pos);
4251 struct SendableTexture
4257 SendableTexture(const std::string &name_="", const std::string path_="",
4258 const std::string &data_=""):
4265 void Server::SendTextures(u16 peer_id)
4267 DSTACK(__FUNCTION_NAME);
4269 infostream<<"Server::SendTextures(): Sending textures to client"<<std::endl;
4273 // Put 5kB in one bunch (this is not accurate)
4274 u32 bytes_per_bunch = 5000;
4276 core::array< core::list<SendableTexture> > texture_bunches;
4277 texture_bunches.push_back(core::list<SendableTexture>());
4279 u32 texture_size_bunch_total = 0;
4280 core::list<ModSpec> mods = getMods(m_modspaths);
4281 for(core::list<ModSpec>::Iterator i = mods.begin();
4282 i != mods.end(); i++){
4284 std::string texturepath = mod.path + DIR_DELIM + "textures";
4285 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(texturepath);
4286 for(u32 j=0; j<dirlist.size(); j++){
4287 if(dirlist[j].dir) // Ignode dirs
4289 std::string tname = dirlist[j].name;
4290 std::string tpath = texturepath + DIR_DELIM + tname;
4292 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
4293 if(fis.good() == false){
4294 errorstream<<"Server::SendTextures(): Could not open \""
4295 <<tname<<"\" for reading"<<std::endl;
4298 std::ostringstream tmp_os(std::ios_base::binary);
4302 fis.read(buf, 1024);
4303 std::streamsize len = fis.gcount();
4304 tmp_os.write(buf, len);
4305 texture_size_bunch_total += len;
4314 errorstream<<"Server::SendTextures(): Failed to read \""
4315 <<tname<<"\""<<std::endl;
4318 /*infostream<<"Server::SendTextures(): Loaded \""
4319 <<tname<<"\""<<std::endl;*/
4321 texture_bunches[texture_bunches.size()-1].push_back(
4322 SendableTexture(tname, tpath, tmp_os.str()));
4324 // Start next bunch if got enough data
4325 if(texture_size_bunch_total >= bytes_per_bunch){
4326 texture_bunches.push_back(core::list<SendableTexture>());
4327 texture_size_bunch_total = 0;
4332 /* Create and send packets */
4334 u32 num_bunches = texture_bunches.size();
4335 for(u32 i=0; i<num_bunches; i++)
4339 u16 total number of texture bunches
4340 u16 index of this bunch
4341 u32 number of textures in this bunch
4349 std::ostringstream os(std::ios_base::binary);
4351 writeU16(os, TOCLIENT_TEXTURES);
4352 writeU16(os, num_bunches);
4354 writeU32(os, texture_bunches[i].size());
4356 for(core::list<SendableTexture>::Iterator
4357 j = texture_bunches[i].begin();
4358 j != texture_bunches[i].end(); j++){
4359 os<<serializeString(j->name);
4360 os<<serializeLongString(j->data);
4364 std::string s = os.str();
4365 infostream<<"Server::SendTextures(): bunch "<<i<<"/"<<num_bunches
4366 <<" textures="<<texture_bunches[i].size()
4367 <<" size=" <<s.size()<<std::endl;
4368 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4370 m_con.Send(peer_id, 0, data, true);
4378 void Server::HandlePlayerHP(Player *player, s16 damage)
4380 ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
4382 if(srp->m_respawn_active)
4385 if(player->hp > damage)
4387 player->hp -= damage;
4388 SendPlayerHP(player);
4392 infostream<<"Server::HandlePlayerHP(): Player "
4393 <<player->getName()<<" dies"<<std::endl;
4397 //TODO: Throw items around
4399 // Handle players that are not connected
4400 if(player->peer_id == PEER_ID_INEXISTENT){
4401 RespawnPlayer(player);
4405 SendPlayerHP(player);
4407 RemoteClient *client = getClient(player->peer_id);
4408 if(client->net_proto_version >= 3)
4410 SendDeathscreen(m_con, player->peer_id, false, v3f(0,0,0));
4411 srp->m_removed = true;
4412 srp->m_respawn_active = true;
4416 RespawnPlayer(player);
4421 void Server::RespawnPlayer(Player *player)
4424 ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
4425 bool repositioned = scriptapi_on_respawnplayer(m_lua, srp);
4427 v3f pos = findSpawnPos(m_env->getServerMap());
4428 player->setPosition(pos);
4429 srp->m_last_good_position = pos;
4430 srp->m_last_good_position_age = 0;
4432 SendMovePlayer(player);
4433 SendPlayerHP(player);
4436 void Server::UpdateCrafting(u16 peer_id)
4438 DSTACK(__FUNCTION_NAME);
4440 Player* player = m_env->getPlayer(peer_id);
4442 ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
4444 // No crafting in creative mode
4445 if(g_settings->getBool("creative_mode"))
4448 // Get the InventoryLists of the player in which we will operate
4449 InventoryList *clist = player->inventory.getList("craft");
4451 InventoryList *rlist = player->inventory.getList("craftresult");
4453 InventoryList *mlist = player->inventory.getList("main");
4456 // If the result list is not a preview and is not empty, try to
4457 // throw the item into main list
4458 if(!player->craftresult_is_preview && rlist->getUsedSlots() != 0)
4460 // Grab item out of craftresult
4461 InventoryItem *item = rlist->changeItem(0, NULL);
4462 // Try to put in main
4463 InventoryItem *leftover = mlist->addItem(item);
4464 // If there are leftovers, put them back to craftresult and
4466 delete rlist->addItem(leftover);
4467 // Inventory was modified
4468 srp->m_inventory_not_sent = true;
4471 // If result list is empty, we will make it preview what would be
4473 if(rlist->getUsedSlots() == 0)
4474 player->craftresult_is_preview = true;
4476 // If it is a preview, clear the possible old preview in it
4477 if(player->craftresult_is_preview)
4478 rlist->clearItems();
4480 // If it is a preview, find out what is the crafting result
4482 if(player->craftresult_is_preview)
4484 // Mangle crafting grid to an another format
4485 std::vector<InventoryItem*> items;
4486 for(u16 i=0; i<9; i++){
4487 if(clist->getItem(i) == NULL)
4488 items.push_back(NULL);
4490 items.push_back(clist->getItem(i)->clone());
4492 CraftPointerInput cpi(3, items);
4494 // Find out what is crafted and add it to result item slot
4495 InventoryItem *result = m_craftdef->getCraftResult(cpi, this);
4497 rlist->addItem(result);
4501 RemoteClient* Server::getClient(u16 peer_id)
4503 DSTACK(__FUNCTION_NAME);
4504 //JMutexAutoLock lock(m_con_mutex);
4505 core::map<u16, RemoteClient*>::Node *n;
4506 n = m_clients.find(peer_id);
4507 // A client should exist for all peers
4509 return n->getValue();
4512 std::wstring Server::getStatusString()
4514 std::wostringstream os(std::ios_base::binary);
4517 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4519 os<<L", uptime="<<m_uptime.get();
4520 // Information about clients
4522 for(core::map<u16, RemoteClient*>::Iterator
4523 i = m_clients.getIterator();
4524 i.atEnd() == false; i++)
4526 // Get client and check that it is valid
4527 RemoteClient *client = i.getNode()->getValue();
4528 assert(client->peer_id == i.getNode()->getKey());
4529 if(client->serialization_version == SER_FMT_VER_INVALID)
4532 Player *player = m_env->getPlayer(client->peer_id);
4533 // Get name of player
4534 std::wstring name = L"unknown";
4536 name = narrow_to_wide(player->getName());
4537 // Add name to information string
4541 if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
4542 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4543 if(g_settings->get("motd") != "")
4544 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4548 void Server::setPlayerPassword(const std::string &name, const std::wstring &password)
4550 // Add player to auth manager
4551 if(m_authmanager.exists(name) == false)
4553 infostream<<"Server: adding player "<<name
4554 <<" to auth manager"<<std::endl;
4555 m_authmanager.add(name);
4556 m_authmanager.setPrivs(name,
4557 stringToPrivs(g_settings->get("default_privs")));
4559 // Change password and save
4560 m_authmanager.setPassword(name, translatePassword(name, password));
4561 m_authmanager.save();
4564 // Saves g_settings to configpath given at initialization
4565 void Server::saveConfig()
4567 if(m_configpath != "")
4568 g_settings->updateConfigFile(m_configpath.c_str());
4571 void Server::notifyPlayer(const char *name, const std::wstring msg)
4573 Player *player = m_env->getPlayer(name);
4576 SendChatMessage(player->peer_id, std::wstring(L"Server: -!- ")+msg);
4579 void Server::notifyPlayers(const std::wstring msg)
4581 BroadcastChatMessage(msg);
4584 void Server::queueBlockEmerge(v3s16 blockpos, bool allow_generate)
4588 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
4589 m_emerge_queue.addBlock(PEER_ID_INEXISTENT, blockpos, flags);
4592 // IGameDef interface
4594 IToolDefManager* Server::getToolDefManager()
4598 INodeDefManager* Server::getNodeDefManager()
4602 ICraftDefManager* Server::getCraftDefManager()
4606 ICraftItemDefManager* Server::getCraftItemDefManager()
4608 return m_craftitemdef;
4610 ITextureSource* Server::getTextureSource()
4614 u16 Server::allocateUnknownNodeId(const std::string &name)
4616 return m_nodedef->allocateDummy(name);
4619 IWritableToolDefManager* Server::getWritableToolDefManager()
4623 IWritableNodeDefManager* Server::getWritableNodeDefManager()
4627 IWritableCraftDefManager* Server::getWritableCraftDefManager()
4631 IWritableCraftItemDefManager* Server::getWritableCraftItemDefManager()
4633 return m_craftitemdef;
4636 v3f findSpawnPos(ServerMap &map)
4638 //return v3f(50,50,50)*BS;
4643 nodepos = v2s16(0,0);
4648 // Try to find a good place a few times
4649 for(s32 i=0; i<1000; i++)
4652 // We're going to try to throw the player to this position
4653 v2s16 nodepos2d = v2s16(-range + (myrand()%(range*2)),
4654 -range + (myrand()%(range*2)));
4655 //v2s16 sectorpos = getNodeSectorPos(nodepos2d);
4656 // Get ground height at point (fallbacks to heightmap function)
4657 s16 groundheight = map.findGroundLevel(nodepos2d);
4658 // Don't go underwater
4659 if(groundheight < WATER_LEVEL)
4661 //infostream<<"-> Underwater"<<std::endl;
4664 // Don't go to high places
4665 if(groundheight > WATER_LEVEL + 4)
4667 //infostream<<"-> Underwater"<<std::endl;
4671 nodepos = v3s16(nodepos2d.X, groundheight-2, nodepos2d.Y);
4672 bool is_good = false;
4674 for(s32 i=0; i<10; i++){
4675 v3s16 blockpos = getNodeBlockPos(nodepos);
4676 map.emergeBlock(blockpos, true);
4677 MapNode n = map.getNodeNoEx(nodepos);
4678 if(n.getContent() == CONTENT_AIR){
4689 // Found a good place
4690 //infostream<<"Searched through "<<i<<" places."<<std::endl;
4696 return intToFloat(nodepos, BS);
4699 ServerRemotePlayer *Server::emergePlayer(const char *name, u16 peer_id)
4702 Try to get an existing player
4704 ServerRemotePlayer *player =
4705 static_cast<ServerRemotePlayer*>(m_env->getPlayer(name));
4708 // If player is already connected, cancel
4709 if(player->peer_id != 0)
4711 infostream<<"emergePlayer(): Player already connected"<<std::endl;
4716 player->peer_id = peer_id;
4718 // Reset inventory to creative if in creative mode
4719 if(g_settings->getBool("creative_mode"))
4721 // Warning: double code below
4722 // Backup actual inventory
4723 player->inventory_backup = new Inventory();
4724 *(player->inventory_backup) = player->inventory;
4725 // Set creative inventory
4726 craft_set_creative_inventory(player, this);
4733 If player with the wanted peer_id already exists, cancel.
4735 if(m_env->getPlayer(peer_id) != NULL)
4737 infostream<<"emergePlayer(): Player with wrong name but same"
4738 " peer_id already exists"<<std::endl;
4746 /* Set player position */
4748 infostream<<"Server: Finding spawn place for player \""
4749 <<name<<"\""<<std::endl;
4751 v3f pos = findSpawnPos(m_env->getServerMap());
4753 player = new ServerRemotePlayer(m_env, pos, peer_id, name);
4755 /* Add player to environment */
4756 m_env->addPlayer(player);
4759 ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
4760 scriptapi_on_newplayer(m_lua, srp);
4762 /* Add stuff to inventory */
4763 if(g_settings->getBool("creative_mode"))
4765 // Warning: double code above
4766 // Backup actual inventory
4767 player->inventory_backup = new Inventory();
4768 *(player->inventory_backup) = player->inventory;
4769 // Set creative inventory
4770 craft_set_creative_inventory(player, this);
4775 } // create new player
4778 void Server::handlePeerChange(PeerChange &c)
4780 JMutexAutoLock envlock(m_env_mutex);
4781 JMutexAutoLock conlock(m_con_mutex);
4783 if(c.type == PEER_ADDED)
4790 core::map<u16, RemoteClient*>::Node *n;
4791 n = m_clients.find(c.peer_id);
4792 // The client shouldn't already exist
4796 RemoteClient *client = new RemoteClient();
4797 client->peer_id = c.peer_id;
4798 m_clients.insert(client->peer_id, client);
4801 else if(c.type == PEER_REMOVED)
4808 core::map<u16, RemoteClient*>::Node *n;
4809 n = m_clients.find(c.peer_id);
4810 // The client should exist
4814 Mark objects to be not known by the client
4816 RemoteClient *client = n->getValue();
4818 for(core::map<u16, bool>::Iterator
4819 i = client->m_known_objects.getIterator();
4820 i.atEnd()==false; i++)
4823 u16 id = i.getNode()->getKey();
4824 ServerActiveObject* obj = m_env->getActiveObject(id);
4826 if(obj && obj->m_known_by_count > 0)
4827 obj->m_known_by_count--;
4830 ServerRemotePlayer* player =
4831 static_cast<ServerRemotePlayer*>(m_env->getPlayer(c.peer_id));
4833 // Collect information about leaving in chat
4834 std::wstring message;
4838 std::wstring name = narrow_to_wide(player->getName());
4841 message += L" left game";
4843 message += L" (timed out)";
4847 // Remove from environment
4849 player->m_removed = true;
4851 // Set player client disconnected
4853 player->peer_id = 0;
4861 std::ostringstream os(std::ios_base::binary);
4862 for(core::map<u16, RemoteClient*>::Iterator
4863 i = m_clients.getIterator();
4864 i.atEnd() == false; i++)
4866 RemoteClient *client = i.getNode()->getValue();
4867 assert(client->peer_id == i.getNode()->getKey());
4868 if(client->serialization_version == SER_FMT_VER_INVALID)
4871 Player *player = m_env->getPlayer(client->peer_id);
4874 // Get name of player
4875 os<<player->getName()<<" ";
4878 actionstream<<player->getName()<<" "
4879 <<(c.timeout?"times out.":"leaves game.")
4880 <<" List of players: "
4881 <<os.str()<<std::endl;
4886 delete m_clients[c.peer_id];
4887 m_clients.remove(c.peer_id);
4889 // Send player info to all remaining clients
4890 //SendPlayerInfos();
4892 // Send leave chat message to all remaining clients
4893 BroadcastChatMessage(message);
4902 void Server::handlePeerChanges()
4904 while(m_peer_change_queue.size() > 0)
4906 PeerChange c = m_peer_change_queue.pop_front();
4908 infostream<<"Server: Handling peer change: "
4909 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4912 handlePeerChange(c);
4916 u64 Server::getPlayerPrivs(Player *player)
4920 std::string playername = player->getName();
4921 // Local player gets all privileges regardless of
4922 // what's set on their account.
4923 if(g_settings->get("name") == playername)
4929 return getPlayerAuthPrivs(playername);
4933 void dedicated_server_loop(Server &server, bool &kill)
4935 DSTACK(__FUNCTION_NAME);
4937 infostream<<DTIME<<std::endl;
4938 infostream<<"========================"<<std::endl;
4939 infostream<<"Running dedicated server"<<std::endl;
4940 infostream<<"========================"<<std::endl;
4941 infostream<<std::endl;
4943 IntervalLimiter m_profiler_interval;
4947 // This is kind of a hack but can be done like this
4948 // because server.step() is very light
4950 ScopeProfiler sp(g_profiler, "dedicated server sleep");
4955 if(server.getShutdownRequested() || kill)
4957 infostream<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4964 float profiler_print_interval =
4965 g_settings->getFloat("profiler_print_interval");
4966 if(profiler_print_interval != 0)
4968 if(m_profiler_interval.step(0.030, profiler_print_interval))
4970 infostream<<"Profiler:"<<std::endl;
4971 g_profiler->print(infostream);
4972 g_profiler->clear();
4979 static int counter = 0;
4985 core::list<PlayerInfo> list = server.getPlayerInfo();
4986 core::list<PlayerInfo>::Iterator i;
4987 static u32 sum_old = 0;
4988 u32 sum = PIChecksum(list);
4991 infostream<<DTIME<<"Player info:"<<std::endl;
4992 for(i=list.begin(); i!=list.end(); i++)
4994 i->PrintLine(&infostream);