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"."
2029 char playername[PLAYERNAME_SIZE];
2030 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
2032 playername[i] = data[3+i];
2034 playername[PLAYERNAME_SIZE-1] = 0;
2036 if(playername[0]=='\0')
2038 infostream<<"Server: Player has empty name"<<std::endl;
2039 SendAccessDenied(m_con, peer_id,
2044 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
2046 infostream<<"Server: Player has invalid name"<<std::endl;
2047 SendAccessDenied(m_con, peer_id,
2048 L"Name contains unallowed characters");
2053 char password[PASSWORD_SIZE];
2054 if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
2056 // old version - assume blank password
2061 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2063 password[i] = data[23+i];
2065 password[PASSWORD_SIZE-1] = 0;
2068 // Add player to auth manager
2069 if(m_authmanager.exists(playername) == false)
2071 std::wstring default_password =
2072 narrow_to_wide(g_settings->get("default_password"));
2073 std::string translated_default_password =
2074 translatePassword(playername, default_password);
2076 // If default_password is empty, allow any initial password
2077 if (default_password.length() == 0)
2078 translated_default_password = password;
2080 infostream<<"Server: adding player "<<playername
2081 <<" to auth manager"<<std::endl;
2082 m_authmanager.add(playername);
2083 m_authmanager.setPassword(playername, translated_default_password);
2084 m_authmanager.setPrivs(playername,
2085 stringToPrivs(g_settings->get("default_privs")));
2086 m_authmanager.save();
2089 std::string checkpwd = m_authmanager.getPassword(playername);
2091 /*infostream<<"Server: Client gave password '"<<password
2092 <<"', the correct one is '"<<checkpwd<<"'"<<std::endl;*/
2094 if(password != checkpwd)
2096 infostream<<"Server: peer_id="<<peer_id
2097 <<": supplied invalid password for "
2098 <<playername<<std::endl;
2099 SendAccessDenied(m_con, peer_id, L"Invalid password");
2103 // Enforce user limit.
2104 // Don't enforce for users that have some admin right
2105 if(m_clients.size() >= g_settings->getU16("max_users") &&
2106 (m_authmanager.getPrivs(playername)
2107 & (PRIV_SERVER|PRIV_BAN|PRIV_PRIVS|PRIV_PASSWORD)) == 0 &&
2108 playername != g_settings->get("name"))
2110 SendAccessDenied(m_con, peer_id, L"Too many users.");
2115 ServerRemotePlayer *player = emergePlayer(playername, peer_id);
2117 // If failed, cancel
2120 infostream<<"Server: peer_id="<<peer_id
2121 <<": failed to emerge player"<<std::endl;
2126 player->m_removed = false;
2127 m_env->addActiveObject(player);
2130 Answer with a TOCLIENT_INIT
2133 SharedBuffer<u8> reply(2+1+6+8);
2134 writeU16(&reply[0], TOCLIENT_INIT);
2135 writeU8(&reply[2], deployed);
2136 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
2137 writeU64(&reply[2+1+6], m_env->getServerMap().getSeed());
2140 m_con.Send(peer_id, 0, reply, true);
2144 Send complete position information
2146 SendMovePlayer(player);
2151 if(command == TOSERVER_INIT2)
2153 infostream<<"Server: Got TOSERVER_INIT2 from "
2154 <<peer_id<<std::endl;
2157 getClient(peer_id)->serialization_version
2158 = getClient(peer_id)->pending_serialization_version;
2161 Send some initialization data
2164 // Send tool definitions
2165 SendToolDef(m_con, peer_id, m_toolmgr);
2167 // Send node definitions
2168 SendNodeDef(m_con, peer_id, m_nodedef);
2170 // Send CraftItem definitions
2171 SendCraftItemDef(m_con, peer_id, m_craftitemdef);
2174 SendTextures(peer_id);
2176 // Send player info to all players
2177 //SendPlayerInfos();
2179 // Send inventory to player
2180 UpdateCrafting(peer_id);
2181 SendInventory(peer_id);
2183 // Send player items to all players
2186 Player *player = m_env->getPlayer(peer_id);
2189 SendPlayerHP(player);
2193 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2194 m_env->getTimeOfDay());
2195 m_con.Send(peer_id, 0, data, true);
2198 // Now the client should know about everything
2199 getClient(peer_id)->definitions_sent = true;
2201 // Send information about server to player in chat
2202 SendChatMessage(peer_id, getStatusString());
2204 // Send information about joining in chat
2206 std::wstring name = L"unknown";
2207 Player *player = m_env->getPlayer(peer_id);
2209 name = narrow_to_wide(player->getName());
2211 std::wstring message;
2214 message += L" joined game";
2215 BroadcastChatMessage(message);
2218 // Warnings about protocol version can be issued here
2219 if(getClient(peer_id)->net_proto_version < PROTOCOL_VERSION)
2221 SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT IS OLD AND MAY WORK PROPERLY WITH THIS SERVER");
2225 Check HP, respawn if necessary
2227 HandlePlayerHP(player, 0);
2233 std::ostringstream os(std::ios_base::binary);
2234 for(core::map<u16, RemoteClient*>::Iterator
2235 i = m_clients.getIterator();
2236 i.atEnd() == false; i++)
2238 RemoteClient *client = i.getNode()->getValue();
2239 assert(client->peer_id == i.getNode()->getKey());
2240 if(client->serialization_version == SER_FMT_VER_INVALID)
2243 Player *player = m_env->getPlayer(client->peer_id);
2246 // Get name of player
2247 os<<player->getName()<<" ";
2250 actionstream<<player->getName()<<" joins game. List of players: "
2251 <<os.str()<<std::endl;
2257 if(peer_ser_ver == SER_FMT_VER_INVALID)
2259 infostream<<"Server::ProcessData(): Cancelling: Peer"
2260 " serialization format invalid or not initialized."
2261 " Skipping incoming command="<<command<<std::endl;
2265 Player *player = m_env->getPlayer(peer_id);
2266 ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
2269 infostream<<"Server::ProcessData(): Cancelling: "
2270 "No player for peer_id="<<peer_id
2274 if(command == TOSERVER_PLAYERPOS)
2276 if(datasize < 2+12+12+4+4)
2280 v3s32 ps = readV3S32(&data[start+2]);
2281 v3s32 ss = readV3S32(&data[start+2+12]);
2282 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2283 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2284 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2285 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2286 pitch = wrapDegrees(pitch);
2287 yaw = wrapDegrees(yaw);
2289 player->setPosition(position);
2290 player->setSpeed(speed);
2291 player->setPitch(pitch);
2292 player->setYaw(yaw);
2294 /*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2295 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2296 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2298 else if(command == TOSERVER_GOTBLOCKS)
2311 u16 count = data[2];
2312 for(u16 i=0; i<count; i++)
2314 if((s16)datasize < 2+1+(i+1)*6)
2315 throw con::InvalidIncomingDataException
2316 ("GOTBLOCKS length is too short");
2317 v3s16 p = readV3S16(&data[2+1+i*6]);
2318 /*infostream<<"Server: GOTBLOCKS ("
2319 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2320 RemoteClient *client = getClient(peer_id);
2321 client->GotBlock(p);
2324 else if(command == TOSERVER_DELETEDBLOCKS)
2337 u16 count = data[2];
2338 for(u16 i=0; i<count; i++)
2340 if((s16)datasize < 2+1+(i+1)*6)
2341 throw con::InvalidIncomingDataException
2342 ("DELETEDBLOCKS length is too short");
2343 v3s16 p = readV3S16(&data[2+1+i*6]);
2344 /*infostream<<"Server: DELETEDBLOCKS ("
2345 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2346 RemoteClient *client = getClient(peer_id);
2347 client->SetBlockNotSent(p);
2350 else if(command == TOSERVER_CLICK_OBJECT)
2352 infostream<<"Server: CLICK_OBJECT not supported anymore"<<std::endl;
2355 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2357 infostream<<"Server: CLICK_ACTIVEOBJECT not supported anymore"<<std::endl;
2360 else if(command == TOSERVER_GROUND_ACTION)
2362 infostream<<"Server: GROUND_ACTION not supported anymore"<<std::endl;
2366 else if(command == TOSERVER_RELEASE)
2368 infostream<<"Server: RELEASE not supported anymore"<<std::endl;
2371 else if(command == TOSERVER_SIGNTEXT)
2373 infostream<<"Server: SIGNTEXT not supported anymore"
2377 else if(command == TOSERVER_SIGNNODETEXT)
2379 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2387 std::string datastring((char*)&data[2], datasize-2);
2388 std::istringstream is(datastring, std::ios_base::binary);
2391 is.read((char*)buf, 6);
2392 v3s16 p = readV3S16(buf);
2393 is.read((char*)buf, 2);
2394 u16 textlen = readU16(buf);
2396 for(u16 i=0; i<textlen; i++)
2398 is.read((char*)buf, 1);
2399 text += (char)buf[0];
2402 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
2406 meta->setText(text);
2408 actionstream<<player->getName()<<" writes \""<<text<<"\" to sign"
2409 <<" at "<<PP(p)<<std::endl;
2411 v3s16 blockpos = getNodeBlockPos(p);
2412 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
2415 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2419 setBlockNotSent(blockpos);
2421 else if(command == TOSERVER_INVENTORY_ACTION)
2423 /*// Ignore inventory changes if in creative mode
2424 if(g_settings->getBool("creative_mode") == true)
2426 infostream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2430 // Strip command and create a stream
2431 std::string datastring((char*)&data[2], datasize-2);
2432 infostream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2433 std::istringstream is(datastring, std::ios_base::binary);
2435 InventoryAction *a = InventoryAction::deSerialize(is);
2438 infostream<<"TOSERVER_INVENTORY_ACTION: "
2439 <<"InventoryAction::deSerialize() returned NULL"
2445 c.current_player = player;
2448 Handle restrictions and special cases of the move action
2450 if(a->getType() == IACTION_MOVE
2451 && g_settings->getBool("creative_mode") == false)
2453 InventoryList *rlist = player->inventory.getList("craftresult");
2455 InventoryList *clist = player->inventory.getList("craft");
2457 InventoryList *mlist = player->inventory.getList("main");
2460 IMoveAction *ma = (IMoveAction*)a;
2463 Disable moving items into craftresult from elsewhere
2465 if(ma->to_inv == "current_player"
2466 && ma->to_list == "craftresult"
2467 && (ma->from_inv != "current_player"
2468 || ma->from_list != "craftresult"))
2470 infostream<<"Ignoring IMoveAction from "
2471 <<ma->from_inv<<":"<<ma->from_list
2472 <<" to "<<ma->to_inv<<":"<<ma->to_list
2473 <<" because dst is craftresult"
2474 <<" and src isn't craftresult"<<std::endl;
2480 Handle crafting (source is craftresult, which is preview)
2482 if(ma->from_inv == "current_player"
2483 && ma->from_list == "craftresult"
2484 && player->craftresult_is_preview)
2487 If the craftresult is placed on itself, crafting takes
2488 place and result is moved into main list
2490 if(ma->to_inv == "current_player"
2491 && ma->to_list == "craftresult")
2493 // Except if main list doesn't have free slots
2494 if(mlist->getFreeSlots() == 0){
2495 infostream<<"Cannot craft: Main list doesn't have"
2496 <<" free slots"<<std::endl;
2501 player->craftresult_is_preview = false;
2502 clist->decrementMaterials(1);
2504 InventoryItem *item1 = rlist->changeItem(0, NULL);
2505 mlist->addItem(item1);
2507 srp->m_inventory_not_sent = true;
2513 Disable action if there are no free slots in
2516 If the item is placed on an item that is not of the
2517 same kind, the existing item will be first moved to
2518 craftresult and immediately moved to the free slot.
2521 Inventory *inv_to = getInventory(&c, ma->to_inv);
2523 InventoryList *list_to = inv_to->getList(ma->to_list);
2525 if(list_to->getFreeSlots() == 0){
2526 infostream<<"Cannot craft: Destination doesn't have"
2527 <<" free slots"<<std::endl;
2531 }while(0); // Allow break
2536 player->craftresult_is_preview = false;
2537 clist->decrementMaterials(1);
2539 /* Print out action */
2540 InventoryItem *item = rlist->getItem(0);
2541 std::string itemstring = "NULL";
2543 itemstring = item->getItemString();
2544 actionstream<<player->getName()<<" crafts "
2545 <<itemstring<<std::endl;
2548 a->apply(&c, this, m_env);
2558 // Disallow moving items in elsewhere than player's inventory
2559 // if not allowed to build
2560 if((getPlayerPrivs(player) & PRIV_BUILD) == 0
2561 && (ma->from_inv != "current_player"
2562 || ma->to_inv != "current_player"))
2564 infostream<<"Cannot move outside of player's inventory: "
2565 <<"No build privilege"<<std::endl;
2570 // If player is not an admin, check for ownership of src
2571 if(ma->from_inv != "current_player"
2572 && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
2574 Strfnd fn(ma->from_inv);
2575 std::string id0 = fn.next(":");
2576 if(id0 == "nodemeta")
2579 p.X = stoi(fn.next(","));
2580 p.Y = stoi(fn.next(","));
2581 p.Z = stoi(fn.next(","));
2582 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
2583 if(meta->getOwner() != "" &&
2584 meta->getOwner() != player->getName())
2586 infostream<<"Cannot move item: "
2587 "not owner of metadata"
2594 // If player is not an admin, check for ownership of dst
2595 if(ma->to_inv != "current_player"
2596 && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
2598 Strfnd fn(ma->to_inv);
2599 std::string id0 = fn.next(":");
2600 if(id0 == "nodemeta")
2603 p.X = stoi(fn.next(","));
2604 p.Y = stoi(fn.next(","));
2605 p.Z = stoi(fn.next(","));
2606 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
2607 if(meta->getOwner() != "" &&
2608 meta->getOwner() != player->getName())
2610 infostream<<"Cannot move item: "
2611 "not owner of metadata"
2620 Handle restrictions and special cases of the drop action
2622 else if(a->getType() == IACTION_DROP)
2624 IDropAction *da = (IDropAction*)a;
2625 // Disallow dropping items if not allowed to build
2626 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2631 // If player is not an admin, check for ownership
2632 else if (da->from_inv != "current_player"
2633 && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
2635 Strfnd fn(da->from_inv);
2636 std::string id0 = fn.next(":");
2637 if(id0 == "nodemeta")
2640 p.X = stoi(fn.next(","));
2641 p.Y = stoi(fn.next(","));
2642 p.Z = stoi(fn.next(","));
2643 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
2644 if(meta->getOwner() != "" &&
2645 meta->getOwner() != player->getName())
2647 infostream<<"Cannot move item: "
2648 "not owner of metadata"
2658 a->apply(&c, this, m_env);
2662 else if(command == TOSERVER_CHAT_MESSAGE)
2670 std::string datastring((char*)&data[2], datasize-2);
2671 std::istringstream is(datastring, std::ios_base::binary);
2674 is.read((char*)buf, 2);
2675 u16 len = readU16(buf);
2677 std::wstring message;
2678 for(u16 i=0; i<len; i++)
2680 is.read((char*)buf, 2);
2681 message += (wchar_t)readU16(buf);
2684 // Get player name of this client
2685 std::wstring name = narrow_to_wide(player->getName());
2688 bool ate = scriptapi_on_chat_message(m_lua, player->getName(),
2689 wide_to_narrow(message));
2690 // If script ate the message, don't proceed
2694 // Line to send to players
2696 // Whether to send to the player that sent the line
2697 bool send_to_sender = false;
2698 // Whether to send to other players
2699 bool send_to_others = false;
2701 // Local player gets all privileges regardless of
2702 // what's set on their account.
2703 u64 privs = getPlayerPrivs(player);
2706 if(message[0] == L'/')
2708 size_t strip_size = 1;
2709 if (message[1] == L'#') // support old-style commans
2711 message = message.substr(strip_size);
2713 WStrfnd f1(message);
2714 f1.next(L" "); // Skip over /#whatever
2715 std::wstring paramstring = f1.next(L"");
2717 ServerCommandContext *ctx = new ServerCommandContext(
2718 str_split(message, L' '),
2725 std::wstring reply(processServerCommand(ctx));
2726 send_to_sender = ctx->flags & SEND_TO_SENDER;
2727 send_to_others = ctx->flags & SEND_TO_OTHERS;
2729 if (ctx->flags & SEND_NO_PREFIX)
2732 line += L"Server: " + reply;
2739 if(privs & PRIV_SHOUT)
2745 send_to_others = true;
2749 line += L"Server: You are not allowed to shout";
2750 send_to_sender = true;
2757 actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2760 Send the message to clients
2762 for(core::map<u16, RemoteClient*>::Iterator
2763 i = m_clients.getIterator();
2764 i.atEnd() == false; i++)
2766 // Get client and check that it is valid
2767 RemoteClient *client = i.getNode()->getValue();
2768 assert(client->peer_id == i.getNode()->getKey());
2769 if(client->serialization_version == SER_FMT_VER_INVALID)
2773 bool sender_selected = (peer_id == client->peer_id);
2774 if(sender_selected == true && send_to_sender == false)
2776 if(sender_selected == false && send_to_others == false)
2779 SendChatMessage(client->peer_id, line);
2783 else if(command == TOSERVER_DAMAGE)
2785 std::string datastring((char*)&data[2], datasize-2);
2786 std::istringstream is(datastring, std::ios_base::binary);
2787 u8 damage = readU8(is);
2789 if(g_settings->getBool("enable_damage"))
2791 actionstream<<player->getName()<<" damaged by "
2792 <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
2795 HandlePlayerHP(player, damage);
2799 SendPlayerHP(player);
2802 else if(command == TOSERVER_PASSWORD)
2805 [0] u16 TOSERVER_PASSWORD
2806 [2] u8[28] old password
2807 [30] u8[28] new password
2810 if(datasize != 2+PASSWORD_SIZE*2)
2812 /*char password[PASSWORD_SIZE];
2813 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2814 password[i] = data[2+i];
2815 password[PASSWORD_SIZE-1] = 0;*/
2817 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2825 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2827 char c = data[2+PASSWORD_SIZE+i];
2833 infostream<<"Server: Client requests a password change from "
2834 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
2836 std::string playername = player->getName();
2838 if(m_authmanager.exists(playername) == false)
2840 infostream<<"Server: playername not found in authmanager"<<std::endl;
2841 // Wrong old password supplied!!
2842 SendChatMessage(peer_id, L"playername not found in authmanager");
2846 std::string checkpwd = m_authmanager.getPassword(playername);
2848 if(oldpwd != checkpwd)
2850 infostream<<"Server: invalid old password"<<std::endl;
2851 // Wrong old password supplied!!
2852 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
2856 actionstream<<player->getName()<<" changes password"<<std::endl;
2858 m_authmanager.setPassword(playername, newpwd);
2860 infostream<<"Server: password change successful for "<<playername
2862 SendChatMessage(peer_id, L"Password change successful");
2864 else if(command == TOSERVER_PLAYERITEM)
2869 u16 item = readU16(&data[2]);
2870 player->wieldItem(item);
2871 SendWieldedItem(player);
2873 else if(command == TOSERVER_RESPAWN)
2878 srp->m_respawn_active = false;
2880 RespawnPlayer(player);
2882 actionstream<<player->getName()<<" respawns at "
2883 <<PP(player->getPosition()/BS)<<std::endl;
2885 srp->m_removed = false;
2886 m_env->addActiveObject(srp);
2888 else if(command == TOSERVER_INTERACT)
2890 std::string datastring((char*)&data[2], datasize-2);
2891 std::istringstream is(datastring, std::ios_base::binary);
2897 [5] u32 length of the next item
2898 [9] serialized PointedThing
2900 0: start digging (from undersurface) or use
2901 1: stop digging (all parameters ignored)
2902 2: digging completed
2903 3: place block or item (to abovesurface)
2906 u8 action = readU8(is);
2907 u16 item_i = readU16(is);
2908 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
2909 PointedThing pointed;
2910 pointed.deSerialize(tmp_is);
2912 infostream<<"TOSERVER_INTERACT: action="<<(int)action<<", item="<<item_i<<", pointed="<<pointed.dump()<<std::endl;
2914 v3f player_pos = srp->m_last_good_position;
2916 // Update wielded item
2917 srp->wieldItem(item_i);
2919 // Get pointed to node (undefined if not POINTEDTYPE_NODE)
2920 v3s16 p_under = pointed.node_undersurface;
2921 v3s16 p_above = pointed.node_abovesurface;
2923 // Get pointed to object (NULL if not POINTEDTYPE_OBJECT)
2924 ServerActiveObject *pointed_object = NULL;
2925 if(pointed.type == POINTEDTHING_OBJECT)
2927 pointed_object = m_env->getActiveObject(pointed.object_id);
2928 if(pointed_object == NULL)
2930 infostream<<"TOSERVER_INTERACT: "
2931 "pointed object is NULL"<<std::endl;
2938 Check that target is reasonably close
2939 (only when digging or placing things)
2941 if(action == 0 || action == 2 || action == 3)
2943 v3f pointed_pos = player_pos;
2944 if(pointed.type == POINTEDTHING_NODE)
2946 pointed_pos = intToFloat(p_under, BS);
2948 else if(pointed.type == POINTEDTHING_OBJECT)
2950 pointed_pos = pointed_object->getBasePosition();
2953 float d = player_pos.getDistanceFrom(pointed_pos);
2954 float max_d = BS * 10; // Just some large enough value
2956 actionstream<<"Player "<<player->getName()
2957 <<" tried to access "<<pointed.dump()
2959 <<"d="<<d<<", max_d="<<max_d
2960 <<". ignoring."<<std::endl;
2961 // Re-send block to revert change on client-side
2962 RemoteClient *client = getClient(peer_id);
2963 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos, BS));
2964 client->SetBlockNotSent(blockpos);
2971 Make sure the player is allowed to do it
2973 bool build_priv = (getPlayerPrivs(player) & PRIV_BUILD) != 0;
2976 infostream<<"Ignoring interaction from player "<<player->getName()
2977 <<" because privileges are "<<getPlayerPrivs(player)
2979 // NOTE: no return; here, fall through
2983 0: start digging or punch object
2987 if(pointed.type == POINTEDTHING_NODE)
2990 NOTE: This can be used in the future to check if
2991 somebody is cheating, by checking the timing.
2993 bool cannot_punch_node = !build_priv;
2995 MapNode n(CONTENT_IGNORE);
2999 n = m_env->getMap().getNode(p_under);
3001 catch(InvalidPositionException &e)
3003 infostream<<"Server: Not punching: Node not found."
3004 <<" Adding block to emerge queue."
3006 m_emerge_queue.addBlock(peer_id,
3007 getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
3008 cannot_punch_node = true;
3011 if(cannot_punch_node)
3017 scriptapi_environment_on_punchnode(m_lua, p_under, n, srp);
3019 else if(pointed.type == POINTEDTHING_OBJECT)
3024 // Skip if object has been removed
3025 if(pointed_object->m_removed)
3028 actionstream<<player->getName()<<" punches object "
3029 <<pointed.object_id<<std::endl;
3032 pointed_object->punch(srp);
3040 else if(action == 1)
3045 2: Digging completed
3047 else if(action == 2)
3049 // Only complete digging of nodes
3050 if(pointed.type != POINTEDTHING_NODE)
3053 // Mandatory parameter; actually used for nothing
3054 core::map<v3s16, MapBlock*> modified_blocks;
3056 content_t material = CONTENT_IGNORE;
3057 u8 mineral = MINERAL_NONE;
3059 bool cannot_remove_node = !build_priv;
3061 MapNode n(CONTENT_IGNORE);
3064 n = m_env->getMap().getNode(p_under);
3066 mineral = n.getMineral(m_nodedef);
3067 // Get material at position
3068 material = n.getContent();
3069 // If not yet cancelled
3070 if(cannot_remove_node == false)
3072 // If it's not diggable, do nothing
3073 if(m_nodedef->get(material).diggable == false)
3075 infostream<<"Server: Not finishing digging: "
3076 <<"Node not diggable"
3078 cannot_remove_node = true;
3081 // If not yet cancelled
3082 if(cannot_remove_node == false)
3084 // Get node metadata
3085 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p_under);
3086 if(meta && meta->nodeRemovalDisabled() == true)
3088 infostream<<"Server: Not finishing digging: "
3089 <<"Node metadata disables removal"
3091 cannot_remove_node = true;
3095 catch(InvalidPositionException &e)
3097 infostream<<"Server: Not finishing digging: Node not found."
3098 <<" Adding block to emerge queue."
3100 m_emerge_queue.addBlock(peer_id,
3101 getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
3102 cannot_remove_node = true;
3106 If node can't be removed, set block to be re-sent to
3109 if(cannot_remove_node)
3111 infostream<<"Server: Not finishing digging."<<std::endl;
3113 // Client probably has wrong data.
3114 // Set block not sent, so that client will get
3116 infostream<<"Client "<<peer_id<<" tried to dig "
3117 <<"node; but node cannot be removed."
3118 <<" setting MapBlock not sent."<<std::endl;
3119 RemoteClient *client = getClient(peer_id);
3120 v3s16 blockpos = getNodeBlockPos(p_under);
3121 client->SetBlockNotSent(blockpos);
3126 actionstream<<player->getName()<<" digs "<<PP(p_under)
3127 <<", gets material "<<(int)material<<", mineral "
3128 <<(int)mineral<<std::endl;
3131 Send the removal to all close-by players.
3132 - If other player is close, send REMOVENODE
3133 - Otherwise set blocks not sent
3135 core::list<u16> far_players;
3136 sendRemoveNode(p_under, peer_id, &far_players, 30);
3139 Update and send inventory
3142 if(g_settings->getBool("creative_mode") == false)
3147 InventoryList *mlist = player->inventory.getList("main");
3150 InventoryItem *item = mlist->getItem(item_i);
3151 if(item && (std::string)item->getName() == "ToolItem")
3153 ToolItem *titem = (ToolItem*)item;
3154 std::string toolname = titem->getToolName();
3156 // Get digging properties for material and tool
3157 ToolDiggingProperties tp =
3158 m_toolmgr->getDiggingProperties(toolname);
3159 DiggingProperties prop =
3160 getDiggingProperties(material, &tp, m_nodedef);
3162 if(prop.diggable == false)
3164 infostream<<"Server: WARNING: Player digged"
3165 <<" with impossible material + tool"
3166 <<" combination"<<std::endl;
3169 bool weared_out = titem->addWear(prop.wear);
3173 mlist->deleteItem(item_i);
3176 srp->m_inventory_not_sent = true;
3181 Add dug item to inventory
3184 InventoryItem *item = NULL;
3186 if(mineral != MINERAL_NONE)
3187 item = getDiggedMineralItem(mineral, this);
3192 const std::string &dug_s = m_nodedef->get(material).dug_item;
3195 std::istringstream is(dug_s, std::ios::binary);
3196 item = InventoryItem::deSerialize(is, this);
3202 // Add a item to inventory
3203 player->inventory.addItem("main", item);
3204 srp->m_inventory_not_sent = true;
3209 if(mineral != MINERAL_NONE)
3210 item = getDiggedMineralItem(mineral, this);
3215 const std::string &extra_dug_s = m_nodedef->get(material).extra_dug_item;
3216 s32 extra_rarity = m_nodedef->get(material).extra_dug_item_rarity;
3217 if(extra_dug_s != "" && extra_rarity != 0
3218 && myrand() % extra_rarity == 0)
3220 std::istringstream is(extra_dug_s, std::ios::binary);
3221 item = InventoryItem::deSerialize(is, this);
3227 // Add a item to inventory
3228 player->inventory.addItem("main", item);
3229 srp->m_inventory_not_sent = true;
3235 (this takes some time so it is done after the quick stuff)
3238 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
3240 m_env->getMap().removeNodeAndUpdate(p_under, modified_blocks);
3243 Set blocks not sent to far players
3245 for(core::list<u16>::Iterator
3246 i = far_players.begin();
3247 i != far_players.end(); i++)
3250 RemoteClient *client = getClient(peer_id);
3253 client->SetBlocksNotSent(modified_blocks);
3259 scriptapi_environment_on_dignode(m_lua, p_under, n, srp);
3263 3: place block or right-click object
3265 else if(action == 3)
3267 if(pointed.type == POINTEDTHING_NODE)
3269 InventoryList *ilist = player->inventory.getList("main");
3274 InventoryItem *item = ilist->getItem(item_i);
3276 // If there is no item, it is not possible to add it anywhere
3281 Handle material items
3283 if(std::string("MaterialItem") == item->getName())
3285 bool cannot_place_node = !build_priv;
3288 // Don't add a node if this is not a free space
3289 MapNode n2 = m_env->getMap().getNode(p_above);
3290 if(m_nodedef->get(n2).buildable_to == false)
3292 infostream<<"Client "<<peer_id<<" tried to place"
3293 <<" node in invalid position."<<std::endl;
3294 cannot_place_node = true;
3297 catch(InvalidPositionException &e)
3299 infostream<<"Server: Ignoring ADDNODE: Node not found"
3300 <<" Adding block to emerge queue."
3302 m_emerge_queue.addBlock(peer_id,
3303 getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
3304 cannot_place_node = true;
3307 if(cannot_place_node)
3309 // Client probably has wrong data.
3310 // Set block not sent, so that client will get
3312 RemoteClient *client = getClient(peer_id);
3313 v3s16 blockpos = getNodeBlockPos(p_above);
3314 client->SetBlockNotSent(blockpos);
3318 // Reset build time counter
3319 getClient(peer_id)->m_time_from_building = 0.0;
3322 MaterialItem *mitem = (MaterialItem*)item;
3324 n.setContent(mitem->getMaterial());
3326 actionstream<<player->getName()<<" places material "
3327 <<(int)mitem->getMaterial()
3328 <<" at "<<PP(p_under)<<std::endl;
3330 // Calculate direction for wall mounted stuff
3331 if(m_nodedef->get(n).wall_mounted)
3332 n.param2 = packDir(p_under - p_above);
3334 // Calculate the direction for furnaces and chests and stuff
3335 if(m_nodedef->get(n).param_type == CPT_FACEDIR_SIMPLE)
3337 v3f playerpos = player->getPosition();
3338 v3f blockpos = intToFloat(p_above, BS) - playerpos;
3339 blockpos = blockpos.normalize();
3341 if (fabs(blockpos.X) > fabs(blockpos.Z)) {
3355 Send to all close-by players
3357 core::list<u16> far_players;
3358 sendAddNode(p_above, n, 0, &far_players, 30);
3363 InventoryList *ilist = player->inventory.getList("main");
3364 if(g_settings->getBool("creative_mode") == false && ilist)
3366 // Remove from inventory and send inventory
3367 if(mitem->getCount() <= 1)
3368 ilist->deleteItem(item_i);
3371 srp->m_inventory_not_sent = true;
3377 This takes some time so it is done after the quick stuff
3379 core::map<v3s16, MapBlock*> modified_blocks;
3381 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
3383 std::string p_name = std::string(player->getName());
3384 m_env->getMap().addNodeAndUpdate(p_above, n, modified_blocks, p_name);
3387 Set blocks not sent to far players
3389 for(core::list<u16>::Iterator
3390 i = far_players.begin();
3391 i != far_players.end(); i++)
3394 RemoteClient *client = getClient(peer_id);
3397 client->SetBlocksNotSent(modified_blocks);
3403 scriptapi_environment_on_placenode(m_lua, p_above, n, srp);
3406 Calculate special events
3409 /*if(n.d == LEGN(m_nodedef, "CONTENT_MESE"))
3412 for(s16 z=-1; z<=1; z++)
3413 for(s16 y=-1; y<=1; y++)
3414 for(s16 x=-1; x<=1; x++)
3421 Place other item (not a block)
3427 infostream<<"Not allowing player to place item: "
3428 "no build privileges"<<std::endl;
3432 // Calculate a position for it
3433 v3f pos = player_pos;
3434 if(pointed.type == POINTEDTHING_NOTHING)
3436 infostream<<"Not allowing player to place item: "
3437 "pointing to nothing"<<std::endl;
3440 else if(pointed.type == POINTEDTHING_NODE)
3442 pos = intToFloat(p_above, BS);
3444 else if(pointed.type == POINTEDTHING_OBJECT)
3446 pos = pointed_object->getBasePosition();
3449 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
3450 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
3454 //pos.Y -= BS*0.25; // let it drop a bit
3457 Check that the block is loaded so that the item
3458 can properly be added to the static list too
3460 v3s16 blockpos = getNodeBlockPos(floatToInt(pos, BS));
3461 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3464 infostream<<"Error while placing item: "
3465 "block not found"<<std::endl;
3469 actionstream<<player->getName()<<" places "<<item->getName()
3470 <<" at "<<PP(pos)<<std::endl;
3475 bool remove = item->dropOrPlace(m_env, srp, pos, true, -1);
3476 if(remove && g_settings->getBool("creative_mode") == false)
3478 InventoryList *ilist = player->inventory.getList("main");
3480 // Remove from inventory and send inventory
3481 ilist->deleteItem(item_i);
3482 srp->m_inventory_not_sent = true;
3487 else if(pointed.type == POINTEDTHING_OBJECT)
3489 // Right click object
3494 // Skip if object has been removed
3495 if(pointed_object->m_removed)
3498 actionstream<<player->getName()<<" right-clicks object "
3499 <<pointed.object_id<<std::endl;
3502 pointed_object->rightClick(srp);
3510 else if(action == 4)
3512 InventoryList *ilist = player->inventory.getList("main");
3517 InventoryItem *item = ilist->getItem(item_i);
3519 // If there is no item, it is not possible to add it anywhere
3523 // Requires build privs
3526 infostream<<"Not allowing player to use item: "
3527 "no build privileges"<<std::endl;
3531 actionstream<<player->getName()<<" uses "<<item->getName()
3532 <<", pointing at "<<pointed.dump()<<std::endl;
3534 bool remove = item->use(m_env, srp, pointed);
3536 if(remove && g_settings->getBool("creative_mode") == false)
3538 InventoryList *ilist = player->inventory.getList("main");
3540 // Remove from inventory and send inventory
3541 ilist->deleteItem(item_i);
3542 srp->m_inventory_not_sent = true;
3549 Catch invalid actions
3553 infostream<<"WARNING: Server: Invalid action "
3554 <<action<<std::endl;
3557 // Complete add_to_inventory_later
3558 srp->completeAddToInventoryLater(item_i);
3562 infostream<<"Server::ProcessData(): Ignoring "
3563 "unknown command "<<command<<std::endl;
3567 catch(SendFailedException &e)
3569 errorstream<<"Server::ProcessData(): SendFailedException: "
3575 void Server::onMapEditEvent(MapEditEvent *event)
3577 //infostream<<"Server::onMapEditEvent()"<<std::endl;
3578 if(m_ignore_map_edit_events)
3580 MapEditEvent *e = event->clone();
3581 m_unsent_map_edit_queue.push_back(e);
3584 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3586 if(id == "current_player")
3588 assert(c->current_player);
3589 return &(c->current_player->inventory);
3593 std::string id0 = fn.next(":");
3595 if(id0 == "nodemeta")
3598 p.X = stoi(fn.next(","));
3599 p.Y = stoi(fn.next(","));
3600 p.Z = stoi(fn.next(","));
3601 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3603 return meta->getInventory();
3604 infostream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3605 <<"no metadata found"<<std::endl;
3609 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3612 void Server::inventoryModified(InventoryContext *c, std::string id)
3614 if(id == "current_player")
3616 assert(c->current_player);
3617 ServerRemotePlayer *srp =
3618 static_cast<ServerRemotePlayer*>(c->current_player);
3619 srp->m_inventory_not_sent = true;
3624 std::string id0 = fn.next(":");
3626 if(id0 == "nodemeta")
3629 p.X = stoi(fn.next(","));
3630 p.Y = stoi(fn.next(","));
3631 p.Z = stoi(fn.next(","));
3632 v3s16 blockpos = getNodeBlockPos(p);
3634 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3636 meta->inventoryModified();
3638 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3640 block->raiseModified(MOD_STATE_WRITE_NEEDED);
3642 setBlockNotSent(blockpos);
3647 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3650 core::list<PlayerInfo> Server::getPlayerInfo()
3652 DSTACK(__FUNCTION_NAME);
3653 JMutexAutoLock envlock(m_env_mutex);
3654 JMutexAutoLock conlock(m_con_mutex);
3656 core::list<PlayerInfo> list;
3658 core::list<Player*> players = m_env->getPlayers();
3660 core::list<Player*>::Iterator i;
3661 for(i = players.begin();
3662 i != players.end(); i++)
3666 Player *player = *i;
3669 // Copy info from connection to info struct
3670 info.id = player->peer_id;
3671 info.address = m_con.GetPeerAddress(player->peer_id);
3672 info.avg_rtt = m_con.GetPeerAvgRTT(player->peer_id);
3674 catch(con::PeerNotFoundException &e)
3676 // Set dummy peer info
3678 info.address = Address(0,0,0,0,0);
3682 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3683 info.position = player->getPosition();
3685 list.push_back(info);
3692 void Server::peerAdded(con::Peer *peer)
3694 DSTACK(__FUNCTION_NAME);
3695 infostream<<"Server::peerAdded(): peer->id="
3696 <<peer->id<<std::endl;
3699 c.type = PEER_ADDED;
3700 c.peer_id = peer->id;
3702 m_peer_change_queue.push_back(c);
3705 void Server::deletingPeer(con::Peer *peer, bool timeout)
3707 DSTACK(__FUNCTION_NAME);
3708 infostream<<"Server::deletingPeer(): peer->id="
3709 <<peer->id<<", timeout="<<timeout<<std::endl;
3712 c.type = PEER_REMOVED;
3713 c.peer_id = peer->id;
3714 c.timeout = timeout;
3715 m_peer_change_queue.push_back(c);
3722 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3724 DSTACK(__FUNCTION_NAME);
3725 std::ostringstream os(std::ios_base::binary);
3727 writeU16(os, TOCLIENT_HP);
3731 std::string s = os.str();
3732 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3734 con.Send(peer_id, 0, data, true);
3737 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3738 const std::wstring &reason)
3740 DSTACK(__FUNCTION_NAME);
3741 std::ostringstream os(std::ios_base::binary);
3743 writeU16(os, TOCLIENT_ACCESS_DENIED);
3744 os<<serializeWideString(reason);
3747 std::string s = os.str();
3748 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3750 con.Send(peer_id, 0, data, true);
3753 void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
3754 bool set_camera_point_target, v3f camera_point_target)
3756 DSTACK(__FUNCTION_NAME);
3757 std::ostringstream os(std::ios_base::binary);
3759 writeU16(os, TOCLIENT_DEATHSCREEN);
3760 writeU8(os, set_camera_point_target);
3761 writeV3F1000(os, camera_point_target);
3764 std::string s = os.str();
3765 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3767 con.Send(peer_id, 0, data, true);
3770 void Server::SendToolDef(con::Connection &con, u16 peer_id,
3771 IToolDefManager *tooldef)
3773 DSTACK(__FUNCTION_NAME);
3774 std::ostringstream os(std::ios_base::binary);
3778 u32 length of the next item
3779 serialized ToolDefManager
3781 writeU16(os, TOCLIENT_TOOLDEF);
3782 std::ostringstream tmp_os(std::ios::binary);
3783 tooldef->serialize(tmp_os);
3784 os<<serializeLongString(tmp_os.str());
3787 std::string s = os.str();
3788 infostream<<"Server::SendToolDef(): Sending tool definitions: size="
3789 <<s.size()<<std::endl;
3790 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3792 con.Send(peer_id, 0, data, true);
3795 void Server::SendNodeDef(con::Connection &con, u16 peer_id,
3796 INodeDefManager *nodedef)
3798 DSTACK(__FUNCTION_NAME);
3799 std::ostringstream os(std::ios_base::binary);
3803 u32 length of the next item
3804 serialized NodeDefManager
3806 writeU16(os, TOCLIENT_NODEDEF);
3807 std::ostringstream tmp_os(std::ios::binary);
3808 nodedef->serialize(tmp_os);
3809 os<<serializeLongString(tmp_os.str());
3812 std::string s = os.str();
3813 infostream<<"Server::SendNodeDef(): Sending node definitions: size="
3814 <<s.size()<<std::endl;
3815 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3817 con.Send(peer_id, 0, data, true);
3820 void Server::SendCraftItemDef(con::Connection &con, u16 peer_id,
3821 ICraftItemDefManager *craftitemdef)
3823 DSTACK(__FUNCTION_NAME);
3824 std::ostringstream os(std::ios_base::binary);
3828 u32 length of the next item
3829 serialized CraftItemDefManager
3831 writeU16(os, TOCLIENT_CRAFTITEMDEF);
3832 std::ostringstream tmp_os(std::ios::binary);
3833 craftitemdef->serialize(tmp_os);
3834 os<<serializeLongString(tmp_os.str());
3837 std::string s = os.str();
3838 infostream<<"Server::SendCraftItemDef(): Sending craft item definitions: size="
3839 <<s.size()<<std::endl;
3840 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3842 con.Send(peer_id, 0, data, true);
3846 Non-static send methods
3849 void Server::SendInventory(u16 peer_id)
3851 DSTACK(__FUNCTION_NAME);
3853 ServerRemotePlayer* player =
3854 static_cast<ServerRemotePlayer*>(m_env->getPlayer(peer_id));
3857 player->m_inventory_not_sent = false;
3863 std::ostringstream os;
3864 //os.imbue(std::locale("C"));
3866 player->inventory.serialize(os);
3868 std::string s = os.str();
3870 SharedBuffer<u8> data(s.size()+2);
3871 writeU16(&data[0], TOCLIENT_INVENTORY);
3872 memcpy(&data[2], s.c_str(), s.size());
3875 m_con.Send(peer_id, 0, data, true);
3878 std::string getWieldedItemString(const Player *player)
3880 const InventoryItem *item = player->getWieldItem();
3882 return std::string("");
3883 std::ostringstream os(std::ios_base::binary);
3884 item->serialize(os);
3888 void Server::SendWieldedItem(const Player* player)
3890 DSTACK(__FUNCTION_NAME);
3894 std::ostringstream os(std::ios_base::binary);
3896 writeU16(os, TOCLIENT_PLAYERITEM);
3898 writeU16(os, player->peer_id);
3899 os<<serializeString(getWieldedItemString(player));
3902 std::string s = os.str();
3903 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3905 m_con.SendToAll(0, data, true);
3908 void Server::SendPlayerItems()
3910 DSTACK(__FUNCTION_NAME);
3912 std::ostringstream os(std::ios_base::binary);
3913 core::list<Player *> players = m_env->getPlayers(true);
3915 writeU16(os, TOCLIENT_PLAYERITEM);
3916 writeU16(os, players.size());
3917 core::list<Player *>::Iterator i;
3918 for(i = players.begin(); i != players.end(); ++i)
3921 writeU16(os, p->peer_id);
3922 os<<serializeString(getWieldedItemString(p));
3926 std::string s = os.str();
3927 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3929 m_con.SendToAll(0, data, true);
3932 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3934 DSTACK(__FUNCTION_NAME);
3936 std::ostringstream os(std::ios_base::binary);
3940 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3941 os.write((char*)buf, 2);
3944 writeU16(buf, message.size());
3945 os.write((char*)buf, 2);
3948 for(u32 i=0; i<message.size(); i++)
3952 os.write((char*)buf, 2);
3956 std::string s = os.str();
3957 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3959 m_con.Send(peer_id, 0, data, true);
3962 void Server::BroadcastChatMessage(const std::wstring &message)
3964 for(core::map<u16, RemoteClient*>::Iterator
3965 i = m_clients.getIterator();
3966 i.atEnd() == false; i++)
3968 // Get client and check that it is valid
3969 RemoteClient *client = i.getNode()->getValue();
3970 assert(client->peer_id == i.getNode()->getKey());
3971 if(client->serialization_version == SER_FMT_VER_INVALID)
3974 SendChatMessage(client->peer_id, message);
3978 void Server::SendPlayerHP(Player *player)
3980 SendHP(m_con, player->peer_id, player->hp);
3983 void Server::SendMovePlayer(Player *player)
3985 DSTACK(__FUNCTION_NAME);
3986 std::ostringstream os(std::ios_base::binary);
3988 writeU16(os, TOCLIENT_MOVE_PLAYER);
3989 writeV3F1000(os, player->getPosition());
3990 writeF1000(os, player->getPitch());
3991 writeF1000(os, player->getYaw());
3994 v3f pos = player->getPosition();
3995 f32 pitch = player->getPitch();
3996 f32 yaw = player->getYaw();
3997 infostream<<"Server sending TOCLIENT_MOVE_PLAYER"
3998 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
4005 std::string s = os.str();
4006 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4008 m_con.Send(player->peer_id, 0, data, true);
4011 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
4012 core::list<u16> *far_players, float far_d_nodes)
4014 float maxd = far_d_nodes*BS;
4015 v3f p_f = intToFloat(p, BS);
4019 SharedBuffer<u8> reply(replysize);
4020 writeU16(&reply[0], TOCLIENT_REMOVENODE);
4021 writeS16(&reply[2], p.X);
4022 writeS16(&reply[4], p.Y);
4023 writeS16(&reply[6], p.Z);
4025 for(core::map<u16, RemoteClient*>::Iterator
4026 i = m_clients.getIterator();
4027 i.atEnd() == false; i++)
4029 // Get client and check that it is valid
4030 RemoteClient *client = i.getNode()->getValue();
4031 assert(client->peer_id == i.getNode()->getKey());
4032 if(client->serialization_version == SER_FMT_VER_INVALID)
4035 // Don't send if it's the same one
4036 if(client->peer_id == ignore_id)
4042 Player *player = m_env->getPlayer(client->peer_id);
4045 // If player is far away, only set modified blocks not sent
4046 v3f player_pos = player->getPosition();
4047 if(player_pos.getDistanceFrom(p_f) > maxd)
4049 far_players->push_back(client->peer_id);
4056 m_con.Send(client->peer_id, 0, reply, true);
4060 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
4061 core::list<u16> *far_players, float far_d_nodes)
4063 float maxd = far_d_nodes*BS;
4064 v3f p_f = intToFloat(p, BS);
4066 for(core::map<u16, RemoteClient*>::Iterator
4067 i = m_clients.getIterator();
4068 i.atEnd() == false; i++)
4070 // Get client and check that it is valid
4071 RemoteClient *client = i.getNode()->getValue();
4072 assert(client->peer_id == i.getNode()->getKey());
4073 if(client->serialization_version == SER_FMT_VER_INVALID)
4076 // Don't send if it's the same one
4077 if(client->peer_id == ignore_id)
4083 Player *player = m_env->getPlayer(client->peer_id);
4086 // If player is far away, only set modified blocks not sent
4087 v3f player_pos = player->getPosition();
4088 if(player_pos.getDistanceFrom(p_f) > maxd)
4090 far_players->push_back(client->peer_id);
4097 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
4098 SharedBuffer<u8> reply(replysize);
4099 writeU16(&reply[0], TOCLIENT_ADDNODE);
4100 writeS16(&reply[2], p.X);
4101 writeS16(&reply[4], p.Y);
4102 writeS16(&reply[6], p.Z);
4103 n.serialize(&reply[8], client->serialization_version);
4106 m_con.Send(client->peer_id, 0, reply, true);
4110 void Server::setBlockNotSent(v3s16 p)
4112 for(core::map<u16, RemoteClient*>::Iterator
4113 i = m_clients.getIterator();
4114 i.atEnd()==false; i++)
4116 RemoteClient *client = i.getNode()->getValue();
4117 client->SetBlockNotSent(p);
4121 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
4123 DSTACK(__FUNCTION_NAME);
4125 v3s16 p = block->getPos();
4129 bool completely_air = true;
4130 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4131 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4132 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4134 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
4136 completely_air = false;
4137 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
4142 infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
4144 infostream<<"[completely air] ";
4145 infostream<<std::endl;
4149 Create a packet with the block in the right format
4152 std::ostringstream os(std::ios_base::binary);
4153 block->serialize(os, ver);
4154 std::string s = os.str();
4155 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
4157 u32 replysize = 8 + blockdata.getSize();
4158 SharedBuffer<u8> reply(replysize);
4159 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
4160 writeS16(&reply[2], p.X);
4161 writeS16(&reply[4], p.Y);
4162 writeS16(&reply[6], p.Z);
4163 memcpy(&reply[8], *blockdata, blockdata.getSize());
4165 /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4166 <<": \tpacket size: "<<replysize<<std::endl;*/
4171 m_con.Send(peer_id, 1, reply, true);
4174 void Server::SendBlocks(float dtime)
4176 DSTACK(__FUNCTION_NAME);
4178 JMutexAutoLock envlock(m_env_mutex);
4179 JMutexAutoLock conlock(m_con_mutex);
4181 //TimeTaker timer("Server::SendBlocks");
4183 core::array<PrioritySortedBlockTransfer> queue;
4185 s32 total_sending = 0;
4188 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
4190 for(core::map<u16, RemoteClient*>::Iterator
4191 i = m_clients.getIterator();
4192 i.atEnd() == false; i++)
4194 RemoteClient *client = i.getNode()->getValue();
4195 assert(client->peer_id == i.getNode()->getKey());
4197 // If definitions and textures have not been sent, don't
4198 // send MapBlocks either
4199 if(!client->definitions_sent)
4202 total_sending += client->SendingCount();
4204 if(client->serialization_version == SER_FMT_VER_INVALID)
4207 client->GetNextBlocks(this, dtime, queue);
4212 // Lowest priority number comes first.
4213 // Lowest is most important.
4216 for(u32 i=0; i<queue.size(); i++)
4218 //TODO: Calculate limit dynamically
4219 if(total_sending >= g_settings->getS32
4220 ("max_simultaneous_block_sends_server_total"))
4223 PrioritySortedBlockTransfer q = queue[i];
4225 MapBlock *block = NULL;
4228 block = m_env->getMap().getBlockNoCreate(q.pos);
4230 catch(InvalidPositionException &e)
4235 RemoteClient *client = getClient(q.peer_id);
4237 SendBlockNoLock(q.peer_id, block, client->serialization_version);
4239 client->SentBlock(q.pos);
4245 struct SendableTexture
4251 SendableTexture(const std::string &name_="", const std::string path_="",
4252 const std::string &data_=""):
4259 void Server::SendTextures(u16 peer_id)
4261 DSTACK(__FUNCTION_NAME);
4263 infostream<<"Server::SendTextures(): Sending textures to client"<<std::endl;
4267 // Put 5kB in one bunch (this is not accurate)
4268 u32 bytes_per_bunch = 5000;
4270 core::array< core::list<SendableTexture> > texture_bunches;
4271 texture_bunches.push_back(core::list<SendableTexture>());
4273 u32 texture_size_bunch_total = 0;
4274 core::list<ModSpec> mods = getMods(m_modspaths);
4275 for(core::list<ModSpec>::Iterator i = mods.begin();
4276 i != mods.end(); i++){
4278 std::string texturepath = mod.path + DIR_DELIM + "textures";
4279 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(texturepath);
4280 for(u32 j=0; j<dirlist.size(); j++){
4281 if(dirlist[j].dir) // Ignode dirs
4283 std::string tname = dirlist[j].name;
4284 std::string tpath = texturepath + DIR_DELIM + tname;
4286 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
4287 if(fis.good() == false){
4288 errorstream<<"Server::SendTextures(): Could not open \""
4289 <<tname<<"\" for reading"<<std::endl;
4292 std::ostringstream tmp_os(std::ios_base::binary);
4296 fis.read(buf, 1024);
4297 std::streamsize len = fis.gcount();
4298 tmp_os.write(buf, len);
4299 texture_size_bunch_total += len;
4308 errorstream<<"Server::SendTextures(): Failed to read \""
4309 <<tname<<"\""<<std::endl;
4312 /*infostream<<"Server::SendTextures(): Loaded \""
4313 <<tname<<"\""<<std::endl;*/
4315 texture_bunches[texture_bunches.size()-1].push_back(
4316 SendableTexture(tname, tpath, tmp_os.str()));
4318 // Start next bunch if got enough data
4319 if(texture_size_bunch_total >= bytes_per_bunch){
4320 texture_bunches.push_back(core::list<SendableTexture>());
4321 texture_size_bunch_total = 0;
4326 /* Create and send packets */
4328 u32 num_bunches = texture_bunches.size();
4329 for(u32 i=0; i<num_bunches; i++)
4333 u16 total number of texture bunches
4334 u16 index of this bunch
4335 u32 number of textures in this bunch
4343 std::ostringstream os(std::ios_base::binary);
4345 writeU16(os, TOCLIENT_TEXTURES);
4346 writeU16(os, num_bunches);
4348 writeU32(os, texture_bunches[i].size());
4350 for(core::list<SendableTexture>::Iterator
4351 j = texture_bunches[i].begin();
4352 j != texture_bunches[i].end(); j++){
4353 os<<serializeString(j->name);
4354 os<<serializeLongString(j->data);
4358 std::string s = os.str();
4359 infostream<<"Server::SendTextures(): bunch "<<i<<"/"<<num_bunches
4360 <<" textures="<<texture_bunches[i].size()
4361 <<" size=" <<s.size()<<std::endl;
4362 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4364 m_con.Send(peer_id, 0, data, true);
4372 void Server::HandlePlayerHP(Player *player, s16 damage)
4374 ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
4376 if(srp->m_respawn_active)
4379 if(player->hp > damage)
4381 player->hp -= damage;
4382 SendPlayerHP(player);
4386 infostream<<"Server::HandlePlayerHP(): Player "
4387 <<player->getName()<<" dies"<<std::endl;
4391 //TODO: Throw items around
4393 // Handle players that are not connected
4394 if(player->peer_id == PEER_ID_INEXISTENT){
4395 RespawnPlayer(player);
4399 SendPlayerHP(player);
4401 RemoteClient *client = getClient(player->peer_id);
4402 if(client->net_proto_version >= 3)
4404 SendDeathscreen(m_con, player->peer_id, false, v3f(0,0,0));
4405 srp->m_removed = true;
4406 srp->m_respawn_active = true;
4410 RespawnPlayer(player);
4415 void Server::RespawnPlayer(Player *player)
4418 ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
4419 bool repositioned = scriptapi_on_respawnplayer(m_lua, srp);
4421 v3f pos = findSpawnPos(m_env->getServerMap());
4422 player->setPosition(pos);
4423 srp->m_last_good_position = pos;
4424 srp->m_last_good_position_age = 0;
4426 SendMovePlayer(player);
4427 SendPlayerHP(player);
4430 void Server::UpdateCrafting(u16 peer_id)
4432 DSTACK(__FUNCTION_NAME);
4434 Player* player = m_env->getPlayer(peer_id);
4436 ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
4438 // No crafting in creative mode
4439 if(g_settings->getBool("creative_mode"))
4442 // Get the InventoryLists of the player in which we will operate
4443 InventoryList *clist = player->inventory.getList("craft");
4445 InventoryList *rlist = player->inventory.getList("craftresult");
4447 InventoryList *mlist = player->inventory.getList("main");
4450 // If the result list is not a preview and is not empty, try to
4451 // throw the item into main list
4452 if(!player->craftresult_is_preview && rlist->getUsedSlots() != 0)
4454 // Grab item out of craftresult
4455 InventoryItem *item = rlist->changeItem(0, NULL);
4456 // Try to put in main
4457 InventoryItem *leftover = mlist->addItem(item);
4458 // If there are leftovers, put them back to craftresult and
4460 delete rlist->addItem(leftover);
4461 // Inventory was modified
4462 srp->m_inventory_not_sent = true;
4465 // If result list is empty, we will make it preview what would be
4467 if(rlist->getUsedSlots() == 0)
4468 player->craftresult_is_preview = true;
4470 // If it is a preview, clear the possible old preview in it
4471 if(player->craftresult_is_preview)
4472 rlist->clearItems();
4474 // If it is a preview, find out what is the crafting result
4476 if(player->craftresult_is_preview)
4478 // Mangle crafting grid to an another format
4479 std::vector<InventoryItem*> items;
4480 for(u16 i=0; i<9; i++){
4481 if(clist->getItem(i) == NULL)
4482 items.push_back(NULL);
4484 items.push_back(clist->getItem(i)->clone());
4486 CraftPointerInput cpi(3, items);
4488 // Find out what is crafted and add it to result item slot
4489 InventoryItem *result = m_craftdef->getCraftResult(cpi, this);
4491 rlist->addItem(result);
4495 RemoteClient* Server::getClient(u16 peer_id)
4497 DSTACK(__FUNCTION_NAME);
4498 //JMutexAutoLock lock(m_con_mutex);
4499 core::map<u16, RemoteClient*>::Node *n;
4500 n = m_clients.find(peer_id);
4501 // A client should exist for all peers
4503 return n->getValue();
4506 std::wstring Server::getStatusString()
4508 std::wostringstream os(std::ios_base::binary);
4511 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4513 os<<L", uptime="<<m_uptime.get();
4514 // Information about clients
4516 for(core::map<u16, RemoteClient*>::Iterator
4517 i = m_clients.getIterator();
4518 i.atEnd() == false; i++)
4520 // Get client and check that it is valid
4521 RemoteClient *client = i.getNode()->getValue();
4522 assert(client->peer_id == i.getNode()->getKey());
4523 if(client->serialization_version == SER_FMT_VER_INVALID)
4526 Player *player = m_env->getPlayer(client->peer_id);
4527 // Get name of player
4528 std::wstring name = L"unknown";
4530 name = narrow_to_wide(player->getName());
4531 // Add name to information string
4535 if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
4536 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4537 if(g_settings->get("motd") != "")
4538 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4542 void Server::setPlayerPassword(const std::string &name, const std::wstring &password)
4544 // Add player to auth manager
4545 if(m_authmanager.exists(name) == false)
4547 infostream<<"Server: adding player "<<name
4548 <<" to auth manager"<<std::endl;
4549 m_authmanager.add(name);
4550 m_authmanager.setPrivs(name,
4551 stringToPrivs(g_settings->get("default_privs")));
4553 // Change password and save
4554 m_authmanager.setPassword(name, translatePassword(name, password));
4555 m_authmanager.save();
4558 // Saves g_settings to configpath given at initialization
4559 void Server::saveConfig()
4561 if(m_configpath != "")
4562 g_settings->updateConfigFile(m_configpath.c_str());
4565 void Server::notifyPlayer(const char *name, const std::wstring msg)
4567 Player *player = m_env->getPlayer(name);
4570 SendChatMessage(player->peer_id, std::wstring(L"Server: -!- ")+msg);
4573 void Server::notifyPlayers(const std::wstring msg)
4575 BroadcastChatMessage(msg);
4578 void Server::queueBlockEmerge(v3s16 blockpos, bool allow_generate)
4582 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
4583 m_emerge_queue.addBlock(PEER_ID_INEXISTENT, blockpos, flags);
4586 // IGameDef interface
4588 IToolDefManager* Server::getToolDefManager()
4592 INodeDefManager* Server::getNodeDefManager()
4596 ICraftDefManager* Server::getCraftDefManager()
4600 ICraftItemDefManager* Server::getCraftItemDefManager()
4602 return m_craftitemdef;
4604 ITextureSource* Server::getTextureSource()
4608 u16 Server::allocateUnknownNodeId(const std::string &name)
4610 return m_nodedef->allocateDummy(name);
4613 IWritableToolDefManager* Server::getWritableToolDefManager()
4617 IWritableNodeDefManager* Server::getWritableNodeDefManager()
4621 IWritableCraftDefManager* Server::getWritableCraftDefManager()
4625 IWritableCraftItemDefManager* Server::getWritableCraftItemDefManager()
4627 return m_craftitemdef;
4630 v3f findSpawnPos(ServerMap &map)
4632 //return v3f(50,50,50)*BS;
4637 nodepos = v2s16(0,0);
4642 // Try to find a good place a few times
4643 for(s32 i=0; i<1000; i++)
4646 // We're going to try to throw the player to this position
4647 v2s16 nodepos2d = v2s16(-range + (myrand()%(range*2)),
4648 -range + (myrand()%(range*2)));
4649 //v2s16 sectorpos = getNodeSectorPos(nodepos2d);
4650 // Get ground height at point (fallbacks to heightmap function)
4651 s16 groundheight = map.findGroundLevel(nodepos2d);
4652 // Don't go underwater
4653 if(groundheight < WATER_LEVEL)
4655 //infostream<<"-> Underwater"<<std::endl;
4658 // Don't go to high places
4659 if(groundheight > WATER_LEVEL + 4)
4661 //infostream<<"-> Underwater"<<std::endl;
4665 nodepos = v3s16(nodepos2d.X, groundheight-2, nodepos2d.Y);
4666 bool is_good = false;
4668 for(s32 i=0; i<10; i++){
4669 v3s16 blockpos = getNodeBlockPos(nodepos);
4670 map.emergeBlock(blockpos, true);
4671 MapNode n = map.getNodeNoEx(nodepos);
4672 if(n.getContent() == CONTENT_AIR){
4683 // Found a good place
4684 //infostream<<"Searched through "<<i<<" places."<<std::endl;
4690 return intToFloat(nodepos, BS);
4693 ServerRemotePlayer *Server::emergePlayer(const char *name, u16 peer_id)
4696 Try to get an existing player
4698 ServerRemotePlayer *player =
4699 static_cast<ServerRemotePlayer*>(m_env->getPlayer(name));
4702 // If player is already connected, cancel
4703 if(player->peer_id != 0)
4705 infostream<<"emergePlayer(): Player already connected"<<std::endl;
4710 player->peer_id = peer_id;
4712 // Reset inventory to creative if in creative mode
4713 if(g_settings->getBool("creative_mode"))
4715 // Warning: double code below
4716 // Backup actual inventory
4717 player->inventory_backup = new Inventory();
4718 *(player->inventory_backup) = player->inventory;
4719 // Set creative inventory
4720 craft_set_creative_inventory(player, this);
4727 If player with the wanted peer_id already exists, cancel.
4729 if(m_env->getPlayer(peer_id) != NULL)
4731 infostream<<"emergePlayer(): Player with wrong name but same"
4732 " peer_id already exists"<<std::endl;
4740 /* Set player position */
4742 infostream<<"Server: Finding spawn place for player \""
4743 <<name<<"\""<<std::endl;
4745 v3f pos = findSpawnPos(m_env->getServerMap());
4747 player = new ServerRemotePlayer(m_env, pos, peer_id, name);
4749 /* Add player to environment */
4750 m_env->addPlayer(player);
4753 ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
4754 scriptapi_on_newplayer(m_lua, srp);
4756 /* Add stuff to inventory */
4757 if(g_settings->getBool("creative_mode"))
4759 // Warning: double code above
4760 // Backup actual inventory
4761 player->inventory_backup = new Inventory();
4762 *(player->inventory_backup) = player->inventory;
4763 // Set creative inventory
4764 craft_set_creative_inventory(player, this);
4769 } // create new player
4772 void Server::handlePeerChange(PeerChange &c)
4774 JMutexAutoLock envlock(m_env_mutex);
4775 JMutexAutoLock conlock(m_con_mutex);
4777 if(c.type == PEER_ADDED)
4784 core::map<u16, RemoteClient*>::Node *n;
4785 n = m_clients.find(c.peer_id);
4786 // The client shouldn't already exist
4790 RemoteClient *client = new RemoteClient();
4791 client->peer_id = c.peer_id;
4792 m_clients.insert(client->peer_id, client);
4795 else if(c.type == PEER_REMOVED)
4802 core::map<u16, RemoteClient*>::Node *n;
4803 n = m_clients.find(c.peer_id);
4804 // The client should exist
4808 Mark objects to be not known by the client
4810 RemoteClient *client = n->getValue();
4812 for(core::map<u16, bool>::Iterator
4813 i = client->m_known_objects.getIterator();
4814 i.atEnd()==false; i++)
4817 u16 id = i.getNode()->getKey();
4818 ServerActiveObject* obj = m_env->getActiveObject(id);
4820 if(obj && obj->m_known_by_count > 0)
4821 obj->m_known_by_count--;
4824 ServerRemotePlayer* player =
4825 static_cast<ServerRemotePlayer*>(m_env->getPlayer(c.peer_id));
4827 // Collect information about leaving in chat
4828 std::wstring message;
4832 std::wstring name = narrow_to_wide(player->getName());
4835 message += L" left game";
4837 message += L" (timed out)";
4841 // Remove from environment
4843 player->m_removed = true;
4845 // Set player client disconnected
4847 player->peer_id = 0;
4855 std::ostringstream os(std::ios_base::binary);
4856 for(core::map<u16, RemoteClient*>::Iterator
4857 i = m_clients.getIterator();
4858 i.atEnd() == false; i++)
4860 RemoteClient *client = i.getNode()->getValue();
4861 assert(client->peer_id == i.getNode()->getKey());
4862 if(client->serialization_version == SER_FMT_VER_INVALID)
4865 Player *player = m_env->getPlayer(client->peer_id);
4868 // Get name of player
4869 os<<player->getName()<<" ";
4872 actionstream<<player->getName()<<" "
4873 <<(c.timeout?"times out.":"leaves game.")
4874 <<" List of players: "
4875 <<os.str()<<std::endl;
4880 delete m_clients[c.peer_id];
4881 m_clients.remove(c.peer_id);
4883 // Send player info to all remaining clients
4884 //SendPlayerInfos();
4886 // Send leave chat message to all remaining clients
4887 BroadcastChatMessage(message);
4896 void Server::handlePeerChanges()
4898 while(m_peer_change_queue.size() > 0)
4900 PeerChange c = m_peer_change_queue.pop_front();
4902 infostream<<"Server: Handling peer change: "
4903 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4906 handlePeerChange(c);
4910 u64 Server::getPlayerPrivs(Player *player)
4914 std::string playername = player->getName();
4915 // Local player gets all privileges regardless of
4916 // what's set on their account.
4917 if(g_settings->get("name") == playername)
4923 return getPlayerAuthPrivs(playername);
4927 void dedicated_server_loop(Server &server, bool &kill)
4929 DSTACK(__FUNCTION_NAME);
4931 infostream<<DTIME<<std::endl;
4932 infostream<<"========================"<<std::endl;
4933 infostream<<"Running dedicated server"<<std::endl;
4934 infostream<<"========================"<<std::endl;
4935 infostream<<std::endl;
4937 IntervalLimiter m_profiler_interval;
4941 // This is kind of a hack but can be done like this
4942 // because server.step() is very light
4944 ScopeProfiler sp(g_profiler, "dedicated server sleep");
4949 if(server.getShutdownRequested() || kill)
4951 infostream<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4958 float profiler_print_interval =
4959 g_settings->getFloat("profiler_print_interval");
4960 if(profiler_print_interval != 0)
4962 if(m_profiler_interval.step(0.030, profiler_print_interval))
4964 infostream<<"Profiler:"<<std::endl;
4965 g_profiler->print(infostream);
4966 g_profiler->clear();
4973 static int counter = 0;
4979 core::list<PlayerInfo> list = server.getPlayerInfo();
4980 core::list<PlayerInfo>::Iterator i;
4981 static u32 sum_old = 0;
4982 u32 sum = PIChecksum(list);
4985 infostream<<DTIME<<"Player info:"<<std::endl;
4986 for(i=list.begin(); i!=list.end(); i++)
4988 i->PrintLine(&infostream);