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();
1451 Player *player = m_env->getPlayer(client->peer_id);
1454 // This can happen if the client timeouts somehow
1455 /*infostream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
1457 <<" has no associated player"<<std::endl;*/
1460 v3s16 pos = floatToInt(player->getPosition(), BS);
1462 core::map<u16, bool> removed_objects;
1463 core::map<u16, bool> added_objects;
1464 m_env->getRemovedActiveObjects(pos, radius,
1465 client->m_known_objects, removed_objects);
1466 m_env->getAddedActiveObjects(pos, radius,
1467 client->m_known_objects, added_objects);
1469 // Ignore if nothing happened
1470 if(removed_objects.size() == 0 && added_objects.size() == 0)
1472 //infostream<<"active objects: none changed"<<std::endl;
1476 std::string data_buffer;
1480 // Handle removed objects
1481 writeU16((u8*)buf, removed_objects.size());
1482 data_buffer.append(buf, 2);
1483 for(core::map<u16, bool>::Iterator
1484 i = removed_objects.getIterator();
1485 i.atEnd()==false; i++)
1488 u16 id = i.getNode()->getKey();
1489 ServerActiveObject* obj = m_env->getActiveObject(id);
1491 // Add to data buffer for sending
1492 writeU16((u8*)buf, i.getNode()->getKey());
1493 data_buffer.append(buf, 2);
1495 // Remove from known objects
1496 client->m_known_objects.remove(i.getNode()->getKey());
1498 if(obj && obj->m_known_by_count > 0)
1499 obj->m_known_by_count--;
1502 // Handle added objects
1503 writeU16((u8*)buf, added_objects.size());
1504 data_buffer.append(buf, 2);
1505 for(core::map<u16, bool>::Iterator
1506 i = added_objects.getIterator();
1507 i.atEnd()==false; i++)
1510 u16 id = i.getNode()->getKey();
1511 ServerActiveObject* obj = m_env->getActiveObject(id);
1514 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1516 infostream<<"WARNING: "<<__FUNCTION_NAME
1517 <<": NULL object"<<std::endl;
1519 type = obj->getType();
1521 // Add to data buffer for sending
1522 writeU16((u8*)buf, id);
1523 data_buffer.append(buf, 2);
1524 writeU8((u8*)buf, type);
1525 data_buffer.append(buf, 1);
1528 data_buffer.append(serializeLongString(
1529 obj->getClientInitializationData()));
1531 data_buffer.append(serializeLongString(""));
1533 // Add to known objects
1534 client->m_known_objects.insert(i.getNode()->getKey(), false);
1537 obj->m_known_by_count++;
1541 SharedBuffer<u8> reply(2 + data_buffer.size());
1542 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1543 memcpy((char*)&reply[2], data_buffer.c_str(),
1544 data_buffer.size());
1546 m_con.Send(client->peer_id, 0, reply, true);
1548 infostream<<"Server: Sent object remove/add: "
1549 <<removed_objects.size()<<" removed, "
1550 <<added_objects.size()<<" added, "
1551 <<"packet size is "<<reply.getSize()<<std::endl;
1556 Collect a list of all the objects known by the clients
1557 and report it back to the environment.
1560 core::map<u16, bool> all_known_objects;
1562 for(core::map<u16, RemoteClient*>::Iterator
1563 i = m_clients.getIterator();
1564 i.atEnd() == false; i++)
1566 RemoteClient *client = i.getNode()->getValue();
1567 // Go through all known objects of client
1568 for(core::map<u16, bool>::Iterator
1569 i = client->m_known_objects.getIterator();
1570 i.atEnd()==false; i++)
1572 u16 id = i.getNode()->getKey();
1573 all_known_objects[id] = true;
1577 m_env->setKnownActiveObjects(whatever);
1583 Send object messages
1586 JMutexAutoLock envlock(m_env_mutex);
1587 JMutexAutoLock conlock(m_con_mutex);
1589 //ScopeProfiler sp(g_profiler, "Server: sending object messages");
1592 // Value = data sent by object
1593 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1595 // Get active object messages from environment
1598 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
1602 core::list<ActiveObjectMessage>* message_list = NULL;
1603 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1604 n = buffered_messages.find(aom.id);
1607 message_list = new core::list<ActiveObjectMessage>;
1608 buffered_messages.insert(aom.id, message_list);
1612 message_list = n->getValue();
1614 message_list->push_back(aom);
1617 // Route data to every client
1618 for(core::map<u16, RemoteClient*>::Iterator
1619 i = m_clients.getIterator();
1620 i.atEnd()==false; i++)
1622 RemoteClient *client = i.getNode()->getValue();
1623 std::string reliable_data;
1624 std::string unreliable_data;
1625 // Go through all objects in message buffer
1626 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1627 j = buffered_messages.getIterator();
1628 j.atEnd()==false; j++)
1630 // If object is not known by client, skip it
1631 u16 id = j.getNode()->getKey();
1632 if(client->m_known_objects.find(id) == NULL)
1634 // Get message list of object
1635 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1636 // Go through every message
1637 for(core::list<ActiveObjectMessage>::Iterator
1638 k = list->begin(); k != list->end(); k++)
1640 // Compose the full new data with header
1641 ActiveObjectMessage aom = *k;
1642 std::string new_data;
1645 writeU16((u8*)&buf[0], aom.id);
1646 new_data.append(buf, 2);
1648 new_data += serializeString(aom.datastring);
1649 // Add data to buffer
1651 reliable_data += new_data;
1653 unreliable_data += new_data;
1657 reliable_data and unreliable_data are now ready.
1660 if(reliable_data.size() > 0)
1662 SharedBuffer<u8> reply(2 + reliable_data.size());
1663 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1664 memcpy((char*)&reply[2], reliable_data.c_str(),
1665 reliable_data.size());
1667 m_con.Send(client->peer_id, 0, reply, true);
1669 if(unreliable_data.size() > 0)
1671 SharedBuffer<u8> reply(2 + unreliable_data.size());
1672 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1673 memcpy((char*)&reply[2], unreliable_data.c_str(),
1674 unreliable_data.size());
1675 // Send as unreliable
1676 m_con.Send(client->peer_id, 0, reply, false);
1679 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1681 infostream<<"Server: Size of object message data: "
1682 <<"reliable: "<<reliable_data.size()
1683 <<", unreliable: "<<unreliable_data.size()
1688 // Clear buffered_messages
1689 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1690 i = buffered_messages.getIterator();
1691 i.atEnd()==false; i++)
1693 delete i.getNode()->getValue();
1697 } // enable_experimental
1700 Send queued-for-sending map edit events.
1703 // Don't send too many at a time
1706 // Single change sending is disabled if queue size is not small
1707 bool disable_single_change_sending = false;
1708 if(m_unsent_map_edit_queue.size() >= 4)
1709 disable_single_change_sending = true;
1711 bool got_any_events = false;
1713 // We'll log the amount of each
1716 while(m_unsent_map_edit_queue.size() != 0)
1718 got_any_events = true;
1720 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1722 // Players far away from the change are stored here.
1723 // Instead of sending the changes, MapBlocks are set not sent
1725 core::list<u16> far_players;
1727 if(event->type == MEET_ADDNODE)
1729 //infostream<<"Server: MEET_ADDNODE"<<std::endl;
1730 prof.add("MEET_ADDNODE", 1);
1731 if(disable_single_change_sending)
1732 sendAddNode(event->p, event->n, event->already_known_by_peer,
1735 sendAddNode(event->p, event->n, event->already_known_by_peer,
1738 else if(event->type == MEET_REMOVENODE)
1740 //infostream<<"Server: MEET_REMOVENODE"<<std::endl;
1741 prof.add("MEET_REMOVENODE", 1);
1742 if(disable_single_change_sending)
1743 sendRemoveNode(event->p, event->already_known_by_peer,
1746 sendRemoveNode(event->p, event->already_known_by_peer,
1749 else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
1751 infostream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
1752 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
1753 setBlockNotSent(event->p);
1755 else if(event->type == MEET_OTHER)
1757 infostream<<"Server: MEET_OTHER"<<std::endl;
1758 prof.add("MEET_OTHER", 1);
1759 for(core::map<v3s16, bool>::Iterator
1760 i = event->modified_blocks.getIterator();
1761 i.atEnd()==false; i++)
1763 v3s16 p = i.getNode()->getKey();
1769 prof.add("unknown", 1);
1770 infostream<<"WARNING: Server: Unknown MapEditEvent "
1771 <<((u32)event->type)<<std::endl;
1775 Set blocks not sent to far players
1777 if(far_players.size() > 0)
1779 // Convert list format to that wanted by SetBlocksNotSent
1780 core::map<v3s16, MapBlock*> modified_blocks2;
1781 for(core::map<v3s16, bool>::Iterator
1782 i = event->modified_blocks.getIterator();
1783 i.atEnd()==false; i++)
1785 v3s16 p = i.getNode()->getKey();
1786 modified_blocks2.insert(p,
1787 m_env->getMap().getBlockNoCreateNoEx(p));
1789 // Set blocks not sent
1790 for(core::list<u16>::Iterator
1791 i = far_players.begin();
1792 i != far_players.end(); i++)
1795 RemoteClient *client = getClient(peer_id);
1798 client->SetBlocksNotSent(modified_blocks2);
1804 /*// Don't send too many at a time
1806 if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
1812 infostream<<"Server: MapEditEvents:"<<std::endl;
1813 prof.print(infostream);
1819 Trigger emergethread (it somehow gets to a non-triggered but
1820 bysy state sometimes)
1823 float &counter = m_emergethread_trigger_timer;
1829 m_emergethread.trigger();
1833 // Save map, players and auth stuff
1835 float &counter = m_savemap_timer;
1837 if(counter >= g_settings->getFloat("server_map_save_interval"))
1841 ScopeProfiler sp(g_profiler, "Server: saving stuff");
1844 if(m_authmanager.isModified())
1845 m_authmanager.save();
1848 if(m_banmanager.isModified())
1849 m_banmanager.save();
1852 JMutexAutoLock lock(m_env_mutex);
1854 // Save changed parts of map
1855 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
1858 m_env->serializePlayers(m_mapsavedir);
1860 // Save environment metadata
1861 m_env->saveMeta(m_mapsavedir);
1866 void Server::Receive()
1868 DSTACK(__FUNCTION_NAME);
1869 SharedBuffer<u8> data;
1874 JMutexAutoLock conlock(m_con_mutex);
1875 datasize = m_con.Receive(peer_id, data);
1878 // This has to be called so that the client list gets synced
1879 // with the peer list of the connection
1880 handlePeerChanges();
1882 ProcessData(*data, datasize, peer_id);
1884 catch(con::InvalidIncomingDataException &e)
1886 infostream<<"Server::Receive(): "
1887 "InvalidIncomingDataException: what()="
1888 <<e.what()<<std::endl;
1890 catch(con::PeerNotFoundException &e)
1892 //NOTE: This is not needed anymore
1894 // The peer has been disconnected.
1895 // Find the associated player and remove it.
1897 /*JMutexAutoLock envlock(m_env_mutex);
1899 infostream<<"ServerThread: peer_id="<<peer_id
1900 <<" has apparently closed connection. "
1901 <<"Removing player."<<std::endl;
1903 m_env->removePlayer(peer_id);*/
1907 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1909 DSTACK(__FUNCTION_NAME);
1910 // Environment is locked first.
1911 JMutexAutoLock envlock(m_env_mutex);
1912 JMutexAutoLock conlock(m_con_mutex);
1915 Address address = m_con.GetPeerAddress(peer_id);
1917 // drop player if is ip is banned
1918 if(m_banmanager.isIpBanned(address.serializeString())){
1919 SendAccessDenied(m_con, peer_id,
1920 L"Your ip is banned. Banned name was "
1921 +narrow_to_wide(m_banmanager.getBanName(
1922 address.serializeString())));
1923 m_con.DeletePeer(peer_id);
1927 catch(con::PeerNotFoundException &e)
1929 infostream<<"Server::ProcessData(): Cancelling: peer "
1930 <<peer_id<<" not found"<<std::endl;
1934 u8 peer_ser_ver = getClient(peer_id)->serialization_version;
1942 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1944 if(command == TOSERVER_INIT)
1946 // [0] u16 TOSERVER_INIT
1947 // [2] u8 SER_FMT_VER_HIGHEST
1948 // [3] u8[20] player_name
1949 // [23] u8[28] password <--- can be sent without this, from old versions
1951 if(datasize < 2+1+PLAYERNAME_SIZE)
1954 infostream<<"Server: Got TOSERVER_INIT from "
1955 <<peer_id<<std::endl;
1957 // First byte after command is maximum supported
1958 // serialization version
1959 u8 client_max = data[2];
1960 u8 our_max = SER_FMT_VER_HIGHEST;
1961 // Use the highest version supported by both
1962 u8 deployed = core::min_(client_max, our_max);
1963 // If it's lower than the lowest supported, give up.
1964 if(deployed < SER_FMT_VER_LOWEST)
1965 deployed = SER_FMT_VER_INVALID;
1967 //peer->serialization_version = deployed;
1968 getClient(peer_id)->pending_serialization_version = deployed;
1970 if(deployed == SER_FMT_VER_INVALID)
1972 infostream<<"Server: Cannot negotiate "
1973 "serialization version with peer "
1974 <<peer_id<<std::endl;
1975 SendAccessDenied(m_con, peer_id, std::wstring(
1976 L"Your client's version is not supported.\n"
1977 L"Server version is ")
1978 + narrow_to_wide(VERSION_STRING) + L"."
1984 Read and check network protocol version
1987 u16 net_proto_version = 0;
1988 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
1990 net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
1993 getClient(peer_id)->net_proto_version = net_proto_version;
1995 if(net_proto_version == 0)
1997 SendAccessDenied(m_con, peer_id, std::wstring(
1998 L"Your client's version is not supported.\n"
1999 L"Server version is ")
2000 + narrow_to_wide(VERSION_STRING) + L"."
2005 if(g_settings->getBool("strict_protocol_version_checking"))
2007 if(net_proto_version != PROTOCOL_VERSION)
2009 SendAccessDenied(m_con, peer_id, std::wstring(
2010 L"Your client's version is not supported.\n"
2011 L"Server version is ")
2012 + narrow_to_wide(VERSION_STRING) + L"."
2023 char playername[PLAYERNAME_SIZE];
2024 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
2026 playername[i] = data[3+i];
2028 playername[PLAYERNAME_SIZE-1] = 0;
2030 if(playername[0]=='\0')
2032 infostream<<"Server: Player has empty name"<<std::endl;
2033 SendAccessDenied(m_con, peer_id,
2038 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
2040 infostream<<"Server: Player has invalid name"<<std::endl;
2041 SendAccessDenied(m_con, peer_id,
2042 L"Name contains unallowed characters");
2047 char password[PASSWORD_SIZE];
2048 if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
2050 // old version - assume blank password
2055 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2057 password[i] = data[23+i];
2059 password[PASSWORD_SIZE-1] = 0;
2062 // Add player to auth manager
2063 if(m_authmanager.exists(playername) == false)
2065 std::wstring default_password =
2066 narrow_to_wide(g_settings->get("default_password"));
2067 std::string translated_default_password =
2068 translatePassword(playername, default_password);
2070 // If default_password is empty, allow any initial password
2071 if (default_password.length() == 0)
2072 translated_default_password = password;
2074 infostream<<"Server: adding player "<<playername
2075 <<" to auth manager"<<std::endl;
2076 m_authmanager.add(playername);
2077 m_authmanager.setPassword(playername, translated_default_password);
2078 m_authmanager.setPrivs(playername,
2079 stringToPrivs(g_settings->get("default_privs")));
2080 m_authmanager.save();
2083 std::string checkpwd = m_authmanager.getPassword(playername);
2085 /*infostream<<"Server: Client gave password '"<<password
2086 <<"', the correct one is '"<<checkpwd<<"'"<<std::endl;*/
2088 if(password != checkpwd)
2090 infostream<<"Server: peer_id="<<peer_id
2091 <<": supplied invalid password for "
2092 <<playername<<std::endl;
2093 SendAccessDenied(m_con, peer_id, L"Invalid password");
2097 // Enforce user limit.
2098 // Don't enforce for users that have some admin right
2099 if(m_clients.size() >= g_settings->getU16("max_users") &&
2100 (m_authmanager.getPrivs(playername)
2101 & (PRIV_SERVER|PRIV_BAN|PRIV_PRIVS|PRIV_PASSWORD)) == 0 &&
2102 playername != g_settings->get("name"))
2104 SendAccessDenied(m_con, peer_id, L"Too many users.");
2109 ServerRemotePlayer *player = emergePlayer(playername, peer_id);
2111 // If failed, cancel
2114 infostream<<"Server: peer_id="<<peer_id
2115 <<": failed to emerge player"<<std::endl;
2120 player->m_removed = false;
2121 m_env->addActiveObject(player);
2124 Answer with a TOCLIENT_INIT
2127 SharedBuffer<u8> reply(2+1+6+8);
2128 writeU16(&reply[0], TOCLIENT_INIT);
2129 writeU8(&reply[2], deployed);
2130 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
2131 writeU64(&reply[2+1+6], m_env->getServerMap().getSeed());
2134 m_con.Send(peer_id, 0, reply, true);
2138 Send complete position information
2140 SendMovePlayer(player);
2145 if(command == TOSERVER_INIT2)
2147 infostream<<"Server: Got TOSERVER_INIT2 from "
2148 <<peer_id<<std::endl;
2151 getClient(peer_id)->serialization_version
2152 = getClient(peer_id)->pending_serialization_version;
2155 Send some initialization data
2158 // Send tool definitions
2159 SendToolDef(m_con, peer_id, m_toolmgr);
2161 // Send node definitions
2162 SendNodeDef(m_con, peer_id, m_nodedef);
2164 // Send CraftItem definitions
2165 SendCraftItemDef(m_con, peer_id, m_craftitemdef);
2168 SendTextures(peer_id);
2170 // Send player info to all players
2171 //SendPlayerInfos();
2173 // Send inventory to player
2174 UpdateCrafting(peer_id);
2175 SendInventory(peer_id);
2177 // Send player items to all players
2180 Player *player = m_env->getPlayer(peer_id);
2183 SendPlayerHP(player);
2187 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2188 m_env->getTimeOfDay());
2189 m_con.Send(peer_id, 0, data, true);
2192 // Send information about server to player in chat
2193 SendChatMessage(peer_id, getStatusString());
2195 // Send information about joining in chat
2197 std::wstring name = L"unknown";
2198 Player *player = m_env->getPlayer(peer_id);
2200 name = narrow_to_wide(player->getName());
2202 std::wstring message;
2205 message += L" joined game";
2206 BroadcastChatMessage(message);
2209 // Warnings about protocol version can be issued here
2210 if(getClient(peer_id)->net_proto_version < PROTOCOL_VERSION)
2212 SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT IS OLD AND MAY WORK PROPERLY WITH THIS SERVER");
2216 Check HP, respawn if necessary
2218 HandlePlayerHP(player, 0);
2224 std::ostringstream os(std::ios_base::binary);
2225 for(core::map<u16, RemoteClient*>::Iterator
2226 i = m_clients.getIterator();
2227 i.atEnd() == false; i++)
2229 RemoteClient *client = i.getNode()->getValue();
2230 assert(client->peer_id == i.getNode()->getKey());
2231 if(client->serialization_version == SER_FMT_VER_INVALID)
2234 Player *player = m_env->getPlayer(client->peer_id);
2237 // Get name of player
2238 os<<player->getName()<<" ";
2241 actionstream<<player->getName()<<" joins game. List of players: "
2242 <<os.str()<<std::endl;
2248 if(peer_ser_ver == SER_FMT_VER_INVALID)
2250 infostream<<"Server::ProcessData(): Cancelling: Peer"
2251 " serialization format invalid or not initialized."
2252 " Skipping incoming command="<<command<<std::endl;
2256 Player *player = m_env->getPlayer(peer_id);
2257 ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
2260 infostream<<"Server::ProcessData(): Cancelling: "
2261 "No player for peer_id="<<peer_id
2265 if(command == TOSERVER_PLAYERPOS)
2267 if(datasize < 2+12+12+4+4)
2271 v3s32 ps = readV3S32(&data[start+2]);
2272 v3s32 ss = readV3S32(&data[start+2+12]);
2273 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2274 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2275 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2276 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2277 pitch = wrapDegrees(pitch);
2278 yaw = wrapDegrees(yaw);
2280 player->setPosition(position);
2281 player->setSpeed(speed);
2282 player->setPitch(pitch);
2283 player->setYaw(yaw);
2285 /*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2286 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2287 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2289 else if(command == TOSERVER_GOTBLOCKS)
2302 u16 count = data[2];
2303 for(u16 i=0; i<count; i++)
2305 if((s16)datasize < 2+1+(i+1)*6)
2306 throw con::InvalidIncomingDataException
2307 ("GOTBLOCKS length is too short");
2308 v3s16 p = readV3S16(&data[2+1+i*6]);
2309 /*infostream<<"Server: GOTBLOCKS ("
2310 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2311 RemoteClient *client = getClient(peer_id);
2312 client->GotBlock(p);
2315 else if(command == TOSERVER_DELETEDBLOCKS)
2328 u16 count = data[2];
2329 for(u16 i=0; i<count; i++)
2331 if((s16)datasize < 2+1+(i+1)*6)
2332 throw con::InvalidIncomingDataException
2333 ("DELETEDBLOCKS length is too short");
2334 v3s16 p = readV3S16(&data[2+1+i*6]);
2335 /*infostream<<"Server: DELETEDBLOCKS ("
2336 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2337 RemoteClient *client = getClient(peer_id);
2338 client->SetBlockNotSent(p);
2341 else if(command == TOSERVER_CLICK_OBJECT)
2343 infostream<<"Server: CLICK_OBJECT not supported anymore"<<std::endl;
2346 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2348 infostream<<"Server: CLICK_ACTIVEOBJECT not supported anymore"<<std::endl;
2351 else if(command == TOSERVER_GROUND_ACTION)
2353 infostream<<"Server: GROUND_ACTION not supported anymore"<<std::endl;
2357 else if(command == TOSERVER_RELEASE)
2359 infostream<<"Server: RELEASE not supported anymore"<<std::endl;
2362 else if(command == TOSERVER_SIGNTEXT)
2364 infostream<<"Server: SIGNTEXT not supported anymore"
2368 else if(command == TOSERVER_SIGNNODETEXT)
2370 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2378 std::string datastring((char*)&data[2], datasize-2);
2379 std::istringstream is(datastring, std::ios_base::binary);
2382 is.read((char*)buf, 6);
2383 v3s16 p = readV3S16(buf);
2384 is.read((char*)buf, 2);
2385 u16 textlen = readU16(buf);
2387 for(u16 i=0; i<textlen; i++)
2389 is.read((char*)buf, 1);
2390 text += (char)buf[0];
2393 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
2397 meta->setText(text);
2399 actionstream<<player->getName()<<" writes \""<<text<<"\" to sign"
2400 <<" at "<<PP(p)<<std::endl;
2402 v3s16 blockpos = getNodeBlockPos(p);
2403 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
2406 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2410 setBlockNotSent(blockpos);
2412 else if(command == TOSERVER_INVENTORY_ACTION)
2414 /*// Ignore inventory changes if in creative mode
2415 if(g_settings->getBool("creative_mode") == true)
2417 infostream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2421 // Strip command and create a stream
2422 std::string datastring((char*)&data[2], datasize-2);
2423 infostream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2424 std::istringstream is(datastring, std::ios_base::binary);
2426 InventoryAction *a = InventoryAction::deSerialize(is);
2429 infostream<<"TOSERVER_INVENTORY_ACTION: "
2430 <<"InventoryAction::deSerialize() returned NULL"
2436 c.current_player = player;
2439 Handle restrictions and special cases of the move action
2441 if(a->getType() == IACTION_MOVE
2442 && g_settings->getBool("creative_mode") == false)
2444 InventoryList *rlist = player->inventory.getList("craftresult");
2446 InventoryList *clist = player->inventory.getList("craft");
2448 InventoryList *mlist = player->inventory.getList("main");
2451 IMoveAction *ma = (IMoveAction*)a;
2454 Disable moving items into craftresult from elsewhere
2456 if(ma->to_inv == "current_player"
2457 && ma->to_list == "craftresult"
2458 && (ma->from_inv != "current_player"
2459 || ma->from_list != "craftresult"))
2461 infostream<<"Ignoring IMoveAction from "
2462 <<ma->from_inv<<":"<<ma->from_list
2463 <<" to "<<ma->to_inv<<":"<<ma->to_list
2464 <<" because dst is craftresult"
2465 <<" and src isn't craftresult"<<std::endl;
2471 Handle crafting (source is craftresult, which is preview)
2473 if(ma->from_inv == "current_player"
2474 && ma->from_list == "craftresult"
2475 && player->craftresult_is_preview)
2478 If the craftresult is placed on itself, crafting takes
2479 place and result is moved into main list
2481 if(ma->to_inv == "current_player"
2482 && ma->to_list == "craftresult")
2484 // Except if main list doesn't have free slots
2485 if(mlist->getFreeSlots() == 0){
2486 infostream<<"Cannot craft: Main list doesn't have"
2487 <<" free slots"<<std::endl;
2492 player->craftresult_is_preview = false;
2493 clist->decrementMaterials(1);
2495 InventoryItem *item1 = rlist->changeItem(0, NULL);
2496 mlist->addItem(item1);
2498 srp->m_inventory_not_sent = true;
2504 Disable action if there are no free slots in
2507 If the item is placed on an item that is not of the
2508 same kind, the existing item will be first moved to
2509 craftresult and immediately moved to the free slot.
2512 Inventory *inv_to = getInventory(&c, ma->to_inv);
2514 InventoryList *list_to = inv_to->getList(ma->to_list);
2516 if(list_to->getFreeSlots() == 0){
2517 infostream<<"Cannot craft: Destination doesn't have"
2518 <<" free slots"<<std::endl;
2522 }while(0); // Allow break
2527 player->craftresult_is_preview = false;
2528 clist->decrementMaterials(1);
2530 /* Print out action */
2531 InventoryItem *item = rlist->getItem(0);
2532 std::string itemstring = "NULL";
2534 itemstring = item->getItemString();
2535 actionstream<<player->getName()<<" crafts "
2536 <<itemstring<<std::endl;
2539 a->apply(&c, this, m_env);
2549 // Disallow moving items in elsewhere than player's inventory
2550 // if not allowed to build
2551 if((getPlayerPrivs(player) & PRIV_BUILD) == 0
2552 && (ma->from_inv != "current_player"
2553 || ma->to_inv != "current_player"))
2555 infostream<<"Cannot move outside of player's inventory: "
2556 <<"No build privilege"<<std::endl;
2561 // If player is not an admin, check for ownership of src
2562 if(ma->from_inv != "current_player"
2563 && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
2565 Strfnd fn(ma->from_inv);
2566 std::string id0 = fn.next(":");
2567 if(id0 == "nodemeta")
2570 p.X = stoi(fn.next(","));
2571 p.Y = stoi(fn.next(","));
2572 p.Z = stoi(fn.next(","));
2573 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
2574 if(meta->getOwner() != "" &&
2575 meta->getOwner() != player->getName())
2577 infostream<<"Cannot move item: "
2578 "not owner of metadata"
2585 // If player is not an admin, check for ownership of dst
2586 if(ma->to_inv != "current_player"
2587 && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
2589 Strfnd fn(ma->to_inv);
2590 std::string id0 = fn.next(":");
2591 if(id0 == "nodemeta")
2594 p.X = stoi(fn.next(","));
2595 p.Y = stoi(fn.next(","));
2596 p.Z = stoi(fn.next(","));
2597 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
2598 if(meta->getOwner() != "" &&
2599 meta->getOwner() != player->getName())
2601 infostream<<"Cannot move item: "
2602 "not owner of metadata"
2611 Handle restrictions and special cases of the drop action
2613 else if(a->getType() == IACTION_DROP)
2615 IDropAction *da = (IDropAction*)a;
2616 // Disallow dropping items if not allowed to build
2617 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2622 // If player is not an admin, check for ownership
2623 else if (da->from_inv != "current_player"
2624 && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
2626 Strfnd fn(da->from_inv);
2627 std::string id0 = fn.next(":");
2628 if(id0 == "nodemeta")
2631 p.X = stoi(fn.next(","));
2632 p.Y = stoi(fn.next(","));
2633 p.Z = stoi(fn.next(","));
2634 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
2635 if(meta->getOwner() != "" &&
2636 meta->getOwner() != player->getName())
2638 infostream<<"Cannot move item: "
2639 "not owner of metadata"
2649 a->apply(&c, this, m_env);
2653 else if(command == TOSERVER_CHAT_MESSAGE)
2661 std::string datastring((char*)&data[2], datasize-2);
2662 std::istringstream is(datastring, std::ios_base::binary);
2665 is.read((char*)buf, 2);
2666 u16 len = readU16(buf);
2668 std::wstring message;
2669 for(u16 i=0; i<len; i++)
2671 is.read((char*)buf, 2);
2672 message += (wchar_t)readU16(buf);
2675 // Get player name of this client
2676 std::wstring name = narrow_to_wide(player->getName());
2679 bool ate = scriptapi_on_chat_message(m_lua, player->getName(),
2680 wide_to_narrow(message));
2681 // If script ate the message, don't proceed
2685 // Line to send to players
2687 // Whether to send to the player that sent the line
2688 bool send_to_sender = false;
2689 // Whether to send to other players
2690 bool send_to_others = false;
2692 // Local player gets all privileges regardless of
2693 // what's set on their account.
2694 u64 privs = getPlayerPrivs(player);
2697 if(message[0] == L'/')
2699 size_t strip_size = 1;
2700 if (message[1] == L'#') // support old-style commans
2702 message = message.substr(strip_size);
2704 WStrfnd f1(message);
2705 f1.next(L" "); // Skip over /#whatever
2706 std::wstring paramstring = f1.next(L"");
2708 ServerCommandContext *ctx = new ServerCommandContext(
2709 str_split(message, L' '),
2716 std::wstring reply(processServerCommand(ctx));
2717 send_to_sender = ctx->flags & SEND_TO_SENDER;
2718 send_to_others = ctx->flags & SEND_TO_OTHERS;
2720 if (ctx->flags & SEND_NO_PREFIX)
2723 line += L"Server: " + reply;
2730 if(privs & PRIV_SHOUT)
2736 send_to_others = true;
2740 line += L"Server: You are not allowed to shout";
2741 send_to_sender = true;
2748 actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2751 Send the message to clients
2753 for(core::map<u16, RemoteClient*>::Iterator
2754 i = m_clients.getIterator();
2755 i.atEnd() == false; i++)
2757 // Get client and check that it is valid
2758 RemoteClient *client = i.getNode()->getValue();
2759 assert(client->peer_id == i.getNode()->getKey());
2760 if(client->serialization_version == SER_FMT_VER_INVALID)
2764 bool sender_selected = (peer_id == client->peer_id);
2765 if(sender_selected == true && send_to_sender == false)
2767 if(sender_selected == false && send_to_others == false)
2770 SendChatMessage(client->peer_id, line);
2774 else if(command == TOSERVER_DAMAGE)
2776 std::string datastring((char*)&data[2], datasize-2);
2777 std::istringstream is(datastring, std::ios_base::binary);
2778 u8 damage = readU8(is);
2780 if(g_settings->getBool("enable_damage"))
2782 actionstream<<player->getName()<<" damaged by "
2783 <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
2786 HandlePlayerHP(player, damage);
2790 SendPlayerHP(player);
2793 else if(command == TOSERVER_PASSWORD)
2796 [0] u16 TOSERVER_PASSWORD
2797 [2] u8[28] old password
2798 [30] u8[28] new password
2801 if(datasize != 2+PASSWORD_SIZE*2)
2803 /*char password[PASSWORD_SIZE];
2804 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2805 password[i] = data[2+i];
2806 password[PASSWORD_SIZE-1] = 0;*/
2808 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2816 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2818 char c = data[2+PASSWORD_SIZE+i];
2824 infostream<<"Server: Client requests a password change from "
2825 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
2827 std::string playername = player->getName();
2829 if(m_authmanager.exists(playername) == false)
2831 infostream<<"Server: playername not found in authmanager"<<std::endl;
2832 // Wrong old password supplied!!
2833 SendChatMessage(peer_id, L"playername not found in authmanager");
2837 std::string checkpwd = m_authmanager.getPassword(playername);
2839 if(oldpwd != checkpwd)
2841 infostream<<"Server: invalid old password"<<std::endl;
2842 // Wrong old password supplied!!
2843 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
2847 actionstream<<player->getName()<<" changes password"<<std::endl;
2849 m_authmanager.setPassword(playername, newpwd);
2851 infostream<<"Server: password change successful for "<<playername
2853 SendChatMessage(peer_id, L"Password change successful");
2855 else if(command == TOSERVER_PLAYERITEM)
2860 u16 item = readU16(&data[2]);
2861 player->wieldItem(item);
2862 SendWieldedItem(player);
2864 else if(command == TOSERVER_RESPAWN)
2869 RespawnPlayer(player);
2871 actionstream<<player->getName()<<" respawns at "
2872 <<PP(player->getPosition()/BS)<<std::endl;
2874 srp->m_removed = false;
2875 m_env->addActiveObject(srp);
2877 else if(command == TOSERVER_INTERACT)
2879 std::string datastring((char*)&data[2], datasize-2);
2880 std::istringstream is(datastring, std::ios_base::binary);
2886 [5] u32 length of the next item
2887 [9] serialized PointedThing
2889 0: start digging (from undersurface) or use
2890 1: stop digging (all parameters ignored)
2891 2: digging completed
2892 3: place block or item (to abovesurface)
2895 u8 action = readU8(is);
2896 u16 item_i = readU16(is);
2897 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
2898 PointedThing pointed;
2899 pointed.deSerialize(tmp_is);
2901 infostream<<"TOSERVER_INTERACT: action="<<(int)action<<", item="<<item_i<<", pointed="<<pointed.dump()<<std::endl;
2903 v3f player_pos = srp->m_last_good_position;
2905 // Update wielded item
2906 srp->wieldItem(item_i);
2908 // Get pointed to node (undefined if not POINTEDTYPE_NODE)
2909 v3s16 p_under = pointed.node_undersurface;
2910 v3s16 p_above = pointed.node_abovesurface;
2912 // Get pointed to object (NULL if not POINTEDTYPE_OBJECT)
2913 ServerActiveObject *pointed_object = NULL;
2914 if(pointed.type == POINTEDTHING_OBJECT)
2916 pointed_object = m_env->getActiveObject(pointed.object_id);
2917 if(pointed_object == NULL)
2919 infostream<<"TOSERVER_INTERACT: "
2920 "pointed object is NULL"<<std::endl;
2927 Check that target is reasonably close
2928 (only when digging or placing things)
2930 if(action == 0 || action == 2 || action == 3)
2932 v3f pointed_pos = player_pos;
2933 if(pointed.type == POINTEDTHING_NODE)
2935 pointed_pos = intToFloat(p_under, BS);
2937 else if(pointed.type == POINTEDTHING_OBJECT)
2939 pointed_pos = pointed_object->getBasePosition();
2942 float d = player_pos.getDistanceFrom(pointed_pos);
2943 float max_d = BS * 10; // Just some large enough value
2945 actionstream<<"Player "<<player->getName()
2946 <<" tried to access "<<pointed.dump()
2948 <<"d="<<d<<", max_d="<<max_d
2949 <<". ignoring."<<std::endl;
2950 // Re-send block to revert change on client-side
2951 RemoteClient *client = getClient(peer_id);
2952 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos, BS));
2953 client->SetBlockNotSent(blockpos);
2960 Make sure the player is allowed to do it
2962 bool build_priv = (getPlayerPrivs(player) & PRIV_BUILD) != 0;
2965 infostream<<"Ignoring interaction from player "<<player->getName()
2966 <<" because privileges are "<<getPlayerPrivs(player)
2968 // NOTE: no return; here, fall through
2972 0: start digging or punch object
2976 if(pointed.type == POINTEDTHING_NODE)
2979 NOTE: This can be used in the future to check if
2980 somebody is cheating, by checking the timing.
2982 bool cannot_punch_node = !build_priv;
2984 MapNode n(CONTENT_IGNORE);
2988 n = m_env->getMap().getNode(p_under);
2990 catch(InvalidPositionException &e)
2992 infostream<<"Server: Not punching: Node not found."
2993 <<" Adding block to emerge queue."
2995 m_emerge_queue.addBlock(peer_id,
2996 getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
2997 cannot_punch_node = true;
3000 if(cannot_punch_node)
3006 scriptapi_environment_on_punchnode(m_lua, p_under, n, srp);
3008 else if(pointed.type == POINTEDTHING_OBJECT)
3013 // Skip if object has been removed
3014 if(pointed_object->m_removed)
3017 actionstream<<player->getName()<<" punches object "
3018 <<pointed.object_id<<std::endl;
3021 pointed_object->punch(srp);
3029 else if(action == 1)
3034 2: Digging completed
3036 else if(action == 2)
3038 // Only complete digging of nodes
3039 if(pointed.type != POINTEDTHING_NODE)
3042 // Mandatory parameter; actually used for nothing
3043 core::map<v3s16, MapBlock*> modified_blocks;
3045 content_t material = CONTENT_IGNORE;
3046 u8 mineral = MINERAL_NONE;
3048 bool cannot_remove_node = !build_priv;
3050 MapNode n(CONTENT_IGNORE);
3053 n = m_env->getMap().getNode(p_under);
3055 mineral = n.getMineral(m_nodedef);
3056 // Get material at position
3057 material = n.getContent();
3058 // If not yet cancelled
3059 if(cannot_remove_node == false)
3061 // If it's not diggable, do nothing
3062 if(m_nodedef->get(material).diggable == false)
3064 infostream<<"Server: Not finishing digging: "
3065 <<"Node not diggable"
3067 cannot_remove_node = true;
3070 // If not yet cancelled
3071 if(cannot_remove_node == false)
3073 // Get node metadata
3074 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p_under);
3075 if(meta && meta->nodeRemovalDisabled() == true)
3077 infostream<<"Server: Not finishing digging: "
3078 <<"Node metadata disables removal"
3080 cannot_remove_node = true;
3084 catch(InvalidPositionException &e)
3086 infostream<<"Server: Not finishing digging: Node not found."
3087 <<" Adding block to emerge queue."
3089 m_emerge_queue.addBlock(peer_id,
3090 getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
3091 cannot_remove_node = true;
3095 If node can't be removed, set block to be re-sent to
3098 if(cannot_remove_node)
3100 infostream<<"Server: Not finishing digging."<<std::endl;
3102 // Client probably has wrong data.
3103 // Set block not sent, so that client will get
3105 infostream<<"Client "<<peer_id<<" tried to dig "
3106 <<"node; but node cannot be removed."
3107 <<" setting MapBlock not sent."<<std::endl;
3108 RemoteClient *client = getClient(peer_id);
3109 v3s16 blockpos = getNodeBlockPos(p_under);
3110 client->SetBlockNotSent(blockpos);
3115 actionstream<<player->getName()<<" digs "<<PP(p_under)
3116 <<", gets material "<<(int)material<<", mineral "
3117 <<(int)mineral<<std::endl;
3120 Send the removal to all close-by players.
3121 - If other player is close, send REMOVENODE
3122 - Otherwise set blocks not sent
3124 core::list<u16> far_players;
3125 sendRemoveNode(p_under, peer_id, &far_players, 30);
3128 Update and send inventory
3131 if(g_settings->getBool("creative_mode") == false)
3136 InventoryList *mlist = player->inventory.getList("main");
3139 InventoryItem *item = mlist->getItem(item_i);
3140 if(item && (std::string)item->getName() == "ToolItem")
3142 ToolItem *titem = (ToolItem*)item;
3143 std::string toolname = titem->getToolName();
3145 // Get digging properties for material and tool
3146 ToolDiggingProperties tp =
3147 m_toolmgr->getDiggingProperties(toolname);
3148 DiggingProperties prop =
3149 getDiggingProperties(material, &tp, m_nodedef);
3151 if(prop.diggable == false)
3153 infostream<<"Server: WARNING: Player digged"
3154 <<" with impossible material + tool"
3155 <<" combination"<<std::endl;
3158 bool weared_out = titem->addWear(prop.wear);
3162 mlist->deleteItem(item_i);
3165 srp->m_inventory_not_sent = true;
3170 Add dug item to inventory
3173 InventoryItem *item = NULL;
3175 if(mineral != MINERAL_NONE)
3176 item = getDiggedMineralItem(mineral, this);
3181 const std::string &dug_s = m_nodedef->get(material).dug_item;
3184 std::istringstream is(dug_s, std::ios::binary);
3185 item = InventoryItem::deSerialize(is, this);
3191 // Add a item to inventory
3192 player->inventory.addItem("main", item);
3193 srp->m_inventory_not_sent = true;
3198 if(mineral != MINERAL_NONE)
3199 item = getDiggedMineralItem(mineral, this);
3204 const std::string &extra_dug_s = m_nodedef->get(material).extra_dug_item;
3205 s32 extra_rarity = m_nodedef->get(material).extra_dug_item_rarity;
3206 if(extra_dug_s != "" && extra_rarity != 0
3207 && myrand() % extra_rarity == 0)
3209 std::istringstream is(extra_dug_s, std::ios::binary);
3210 item = InventoryItem::deSerialize(is, this);
3216 // Add a item to inventory
3217 player->inventory.addItem("main", item);
3218 srp->m_inventory_not_sent = true;
3224 (this takes some time so it is done after the quick stuff)
3227 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
3229 m_env->getMap().removeNodeAndUpdate(p_under, modified_blocks);
3232 Set blocks not sent to far players
3234 for(core::list<u16>::Iterator
3235 i = far_players.begin();
3236 i != far_players.end(); i++)
3239 RemoteClient *client = getClient(peer_id);
3242 client->SetBlocksNotSent(modified_blocks);
3248 scriptapi_environment_on_dignode(m_lua, p_under, n, srp);
3252 3: place block or right-click object
3254 else if(action == 3)
3256 if(pointed.type == POINTEDTHING_NODE)
3258 InventoryList *ilist = player->inventory.getList("main");
3263 InventoryItem *item = ilist->getItem(item_i);
3265 // If there is no item, it is not possible to add it anywhere
3270 Handle material items
3272 if(std::string("MaterialItem") == item->getName())
3274 bool cannot_place_node = !build_priv;
3277 // Don't add a node if this is not a free space
3278 MapNode n2 = m_env->getMap().getNode(p_above);
3279 if(m_nodedef->get(n2).buildable_to == false)
3281 infostream<<"Client "<<peer_id<<" tried to place"
3282 <<" node in invalid position."<<std::endl;
3283 cannot_place_node = true;
3286 catch(InvalidPositionException &e)
3288 infostream<<"Server: Ignoring ADDNODE: Node not found"
3289 <<" Adding block to emerge queue."
3291 m_emerge_queue.addBlock(peer_id,
3292 getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
3293 cannot_place_node = true;
3296 if(cannot_place_node)
3298 // Client probably has wrong data.
3299 // Set block not sent, so that client will get
3301 RemoteClient *client = getClient(peer_id);
3302 v3s16 blockpos = getNodeBlockPos(p_above);
3303 client->SetBlockNotSent(blockpos);
3307 // Reset build time counter
3308 getClient(peer_id)->m_time_from_building = 0.0;
3311 MaterialItem *mitem = (MaterialItem*)item;
3313 n.setContent(mitem->getMaterial());
3315 actionstream<<player->getName()<<" places material "
3316 <<(int)mitem->getMaterial()
3317 <<" at "<<PP(p_under)<<std::endl;
3319 // Calculate direction for wall mounted stuff
3320 if(m_nodedef->get(n).wall_mounted)
3321 n.param2 = packDir(p_under - p_above);
3323 // Calculate the direction for furnaces and chests and stuff
3324 if(m_nodedef->get(n).param_type == CPT_FACEDIR_SIMPLE)
3326 v3f playerpos = player->getPosition();
3327 v3f blockpos = intToFloat(p_above, BS) - playerpos;
3328 blockpos = blockpos.normalize();
3330 if (fabs(blockpos.X) > fabs(blockpos.Z)) {
3344 Send to all close-by players
3346 core::list<u16> far_players;
3347 sendAddNode(p_above, n, 0, &far_players, 30);
3352 InventoryList *ilist = player->inventory.getList("main");
3353 if(g_settings->getBool("creative_mode") == false && ilist)
3355 // Remove from inventory and send inventory
3356 if(mitem->getCount() <= 1)
3357 ilist->deleteItem(item_i);
3360 srp->m_inventory_not_sent = true;
3366 This takes some time so it is done after the quick stuff
3368 core::map<v3s16, MapBlock*> modified_blocks;
3370 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
3372 std::string p_name = std::string(player->getName());
3373 m_env->getMap().addNodeAndUpdate(p_above, n, modified_blocks, p_name);
3376 Set blocks not sent to far players
3378 for(core::list<u16>::Iterator
3379 i = far_players.begin();
3380 i != far_players.end(); i++)
3383 RemoteClient *client = getClient(peer_id);
3386 client->SetBlocksNotSent(modified_blocks);
3392 scriptapi_environment_on_placenode(m_lua, p_above, n, srp);
3395 Calculate special events
3398 /*if(n.d == LEGN(m_nodedef, "CONTENT_MESE"))
3401 for(s16 z=-1; z<=1; z++)
3402 for(s16 y=-1; y<=1; y++)
3403 for(s16 x=-1; x<=1; x++)
3410 Place other item (not a block)
3416 infostream<<"Not allowing player to place item: "
3417 "no build privileges"<<std::endl;
3421 // Calculate a position for it
3422 v3f pos = player_pos;
3423 if(pointed.type == POINTEDTHING_NOTHING)
3425 infostream<<"Not allowing player to place item: "
3426 "pointing to nothing"<<std::endl;
3429 else if(pointed.type == POINTEDTHING_NODE)
3431 pos = intToFloat(p_above, BS);
3433 else if(pointed.type == POINTEDTHING_OBJECT)
3435 pos = pointed_object->getBasePosition();
3438 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
3439 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
3443 //pos.Y -= BS*0.25; // let it drop a bit
3446 Check that the block is loaded so that the item
3447 can properly be added to the static list too
3449 v3s16 blockpos = getNodeBlockPos(floatToInt(pos, BS));
3450 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3453 infostream<<"Error while placing item: "
3454 "block not found"<<std::endl;
3458 actionstream<<player->getName()<<" places "<<item->getName()
3459 <<" at "<<PP(pos)<<std::endl;
3464 bool remove = item->dropOrPlace(m_env, srp, pos, true, -1);
3465 if(remove && g_settings->getBool("creative_mode") == false)
3467 InventoryList *ilist = player->inventory.getList("main");
3469 // Remove from inventory and send inventory
3470 ilist->deleteItem(item_i);
3471 srp->m_inventory_not_sent = true;
3476 else if(pointed.type == POINTEDTHING_OBJECT)
3478 // Right click object
3483 // Skip if object has been removed
3484 if(pointed_object->m_removed)
3487 actionstream<<player->getName()<<" right-clicks object "
3488 <<pointed.object_id<<std::endl;
3491 pointed_object->rightClick(srp);
3499 else if(action == 4)
3501 InventoryList *ilist = player->inventory.getList("main");
3506 InventoryItem *item = ilist->getItem(item_i);
3508 // If there is no item, it is not possible to add it anywhere
3512 // Requires build privs
3515 infostream<<"Not allowing player to use item: "
3516 "no build privileges"<<std::endl;
3520 actionstream<<player->getName()<<" uses "<<item->getName()
3521 <<", pointing at "<<pointed.dump()<<std::endl;
3523 bool remove = item->use(m_env, srp, pointed);
3525 if(remove && g_settings->getBool("creative_mode") == false)
3527 InventoryList *ilist = player->inventory.getList("main");
3529 // Remove from inventory and send inventory
3530 ilist->deleteItem(item_i);
3531 srp->m_inventory_not_sent = true;
3538 Catch invalid actions
3542 infostream<<"WARNING: Server: Invalid action "
3543 <<action<<std::endl;
3546 // Complete add_to_inventory_later
3547 srp->completeAddToInventoryLater(item_i);
3551 infostream<<"Server::ProcessData(): Ignoring "
3552 "unknown command "<<command<<std::endl;
3556 catch(SendFailedException &e)
3558 errorstream<<"Server::ProcessData(): SendFailedException: "
3564 void Server::onMapEditEvent(MapEditEvent *event)
3566 //infostream<<"Server::onMapEditEvent()"<<std::endl;
3567 if(m_ignore_map_edit_events)
3569 MapEditEvent *e = event->clone();
3570 m_unsent_map_edit_queue.push_back(e);
3573 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3575 if(id == "current_player")
3577 assert(c->current_player);
3578 return &(c->current_player->inventory);
3582 std::string id0 = fn.next(":");
3584 if(id0 == "nodemeta")
3587 p.X = stoi(fn.next(","));
3588 p.Y = stoi(fn.next(","));
3589 p.Z = stoi(fn.next(","));
3590 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3592 return meta->getInventory();
3593 infostream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3594 <<"no metadata found"<<std::endl;
3598 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3601 void Server::inventoryModified(InventoryContext *c, std::string id)
3603 if(id == "current_player")
3605 assert(c->current_player);
3606 ServerRemotePlayer *srp =
3607 static_cast<ServerRemotePlayer*>(c->current_player);
3608 srp->m_inventory_not_sent = true;
3613 std::string id0 = fn.next(":");
3615 if(id0 == "nodemeta")
3618 p.X = stoi(fn.next(","));
3619 p.Y = stoi(fn.next(","));
3620 p.Z = stoi(fn.next(","));
3621 v3s16 blockpos = getNodeBlockPos(p);
3623 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3625 meta->inventoryModified();
3627 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3629 block->raiseModified(MOD_STATE_WRITE_NEEDED);
3631 setBlockNotSent(blockpos);
3636 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3639 core::list<PlayerInfo> Server::getPlayerInfo()
3641 DSTACK(__FUNCTION_NAME);
3642 JMutexAutoLock envlock(m_env_mutex);
3643 JMutexAutoLock conlock(m_con_mutex);
3645 core::list<PlayerInfo> list;
3647 core::list<Player*> players = m_env->getPlayers();
3649 core::list<Player*>::Iterator i;
3650 for(i = players.begin();
3651 i != players.end(); i++)
3655 Player *player = *i;
3658 // Copy info from connection to info struct
3659 info.id = player->peer_id;
3660 info.address = m_con.GetPeerAddress(player->peer_id);
3661 info.avg_rtt = m_con.GetPeerAvgRTT(player->peer_id);
3663 catch(con::PeerNotFoundException &e)
3665 // Set dummy peer info
3667 info.address = Address(0,0,0,0,0);
3671 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3672 info.position = player->getPosition();
3674 list.push_back(info);
3681 void Server::peerAdded(con::Peer *peer)
3683 DSTACK(__FUNCTION_NAME);
3684 infostream<<"Server::peerAdded(): peer->id="
3685 <<peer->id<<std::endl;
3688 c.type = PEER_ADDED;
3689 c.peer_id = peer->id;
3691 m_peer_change_queue.push_back(c);
3694 void Server::deletingPeer(con::Peer *peer, bool timeout)
3696 DSTACK(__FUNCTION_NAME);
3697 infostream<<"Server::deletingPeer(): peer->id="
3698 <<peer->id<<", timeout="<<timeout<<std::endl;
3701 c.type = PEER_REMOVED;
3702 c.peer_id = peer->id;
3703 c.timeout = timeout;
3704 m_peer_change_queue.push_back(c);
3711 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3713 DSTACK(__FUNCTION_NAME);
3714 std::ostringstream os(std::ios_base::binary);
3716 writeU16(os, TOCLIENT_HP);
3720 std::string s = os.str();
3721 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3723 con.Send(peer_id, 0, data, true);
3726 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3727 const std::wstring &reason)
3729 DSTACK(__FUNCTION_NAME);
3730 std::ostringstream os(std::ios_base::binary);
3732 writeU16(os, TOCLIENT_ACCESS_DENIED);
3733 os<<serializeWideString(reason);
3736 std::string s = os.str();
3737 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3739 con.Send(peer_id, 0, data, true);
3742 void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
3743 bool set_camera_point_target, v3f camera_point_target)
3745 DSTACK(__FUNCTION_NAME);
3746 std::ostringstream os(std::ios_base::binary);
3748 writeU16(os, TOCLIENT_DEATHSCREEN);
3749 writeU8(os, set_camera_point_target);
3750 writeV3F1000(os, camera_point_target);
3753 std::string s = os.str();
3754 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3756 con.Send(peer_id, 0, data, true);
3759 void Server::SendToolDef(con::Connection &con, u16 peer_id,
3760 IToolDefManager *tooldef)
3762 DSTACK(__FUNCTION_NAME);
3763 std::ostringstream os(std::ios_base::binary);
3767 u32 length of the next item
3768 serialized ToolDefManager
3770 writeU16(os, TOCLIENT_TOOLDEF);
3771 std::ostringstream tmp_os(std::ios::binary);
3772 tooldef->serialize(tmp_os);
3773 os<<serializeLongString(tmp_os.str());
3776 std::string s = os.str();
3777 infostream<<"Server::SendToolDef(): Sending tool definitions: size="
3778 <<s.size()<<std::endl;
3779 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3781 con.Send(peer_id, 0, data, true);
3784 void Server::SendNodeDef(con::Connection &con, u16 peer_id,
3785 INodeDefManager *nodedef)
3787 DSTACK(__FUNCTION_NAME);
3788 std::ostringstream os(std::ios_base::binary);
3792 u32 length of the next item
3793 serialized NodeDefManager
3795 writeU16(os, TOCLIENT_NODEDEF);
3796 std::ostringstream tmp_os(std::ios::binary);
3797 nodedef->serialize(tmp_os);
3798 os<<serializeLongString(tmp_os.str());
3801 std::string s = os.str();
3802 infostream<<"Server::SendNodeDef(): Sending node definitions: size="
3803 <<s.size()<<std::endl;
3804 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3806 con.Send(peer_id, 0, data, true);
3809 void Server::SendCraftItemDef(con::Connection &con, u16 peer_id,
3810 ICraftItemDefManager *craftitemdef)
3812 DSTACK(__FUNCTION_NAME);
3813 std::ostringstream os(std::ios_base::binary);
3817 u32 length of the next item
3818 serialized CraftItemDefManager
3820 writeU16(os, TOCLIENT_CRAFTITEMDEF);
3821 std::ostringstream tmp_os(std::ios::binary);
3822 craftitemdef->serialize(tmp_os);
3823 os<<serializeLongString(tmp_os.str());
3826 std::string s = os.str();
3827 infostream<<"Server::SendCraftItemDef(): Sending craft item definitions: size="
3828 <<s.size()<<std::endl;
3829 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3831 con.Send(peer_id, 0, data, true);
3835 Non-static send methods
3838 void Server::SendInventory(u16 peer_id)
3840 DSTACK(__FUNCTION_NAME);
3842 ServerRemotePlayer* player =
3843 static_cast<ServerRemotePlayer*>(m_env->getPlayer(peer_id));
3846 player->m_inventory_not_sent = false;
3852 std::ostringstream os;
3853 //os.imbue(std::locale("C"));
3855 player->inventory.serialize(os);
3857 std::string s = os.str();
3859 SharedBuffer<u8> data(s.size()+2);
3860 writeU16(&data[0], TOCLIENT_INVENTORY);
3861 memcpy(&data[2], s.c_str(), s.size());
3864 m_con.Send(peer_id, 0, data, true);
3867 std::string getWieldedItemString(const Player *player)
3869 const InventoryItem *item = player->getWieldItem();
3871 return std::string("");
3872 std::ostringstream os(std::ios_base::binary);
3873 item->serialize(os);
3877 void Server::SendWieldedItem(const Player* player)
3879 DSTACK(__FUNCTION_NAME);
3883 std::ostringstream os(std::ios_base::binary);
3885 writeU16(os, TOCLIENT_PLAYERITEM);
3887 writeU16(os, player->peer_id);
3888 os<<serializeString(getWieldedItemString(player));
3891 std::string s = os.str();
3892 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3894 m_con.SendToAll(0, data, true);
3897 void Server::SendPlayerItems()
3899 DSTACK(__FUNCTION_NAME);
3901 std::ostringstream os(std::ios_base::binary);
3902 core::list<Player *> players = m_env->getPlayers(true);
3904 writeU16(os, TOCLIENT_PLAYERITEM);
3905 writeU16(os, players.size());
3906 core::list<Player *>::Iterator i;
3907 for(i = players.begin(); i != players.end(); ++i)
3910 writeU16(os, p->peer_id);
3911 os<<serializeString(getWieldedItemString(p));
3915 std::string s = os.str();
3916 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3918 m_con.SendToAll(0, data, true);
3921 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3923 DSTACK(__FUNCTION_NAME);
3925 std::ostringstream os(std::ios_base::binary);
3929 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3930 os.write((char*)buf, 2);
3933 writeU16(buf, message.size());
3934 os.write((char*)buf, 2);
3937 for(u32 i=0; i<message.size(); i++)
3941 os.write((char*)buf, 2);
3945 std::string s = os.str();
3946 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3948 m_con.Send(peer_id, 0, data, true);
3951 void Server::BroadcastChatMessage(const std::wstring &message)
3953 for(core::map<u16, RemoteClient*>::Iterator
3954 i = m_clients.getIterator();
3955 i.atEnd() == false; i++)
3957 // Get client and check that it is valid
3958 RemoteClient *client = i.getNode()->getValue();
3959 assert(client->peer_id == i.getNode()->getKey());
3960 if(client->serialization_version == SER_FMT_VER_INVALID)
3963 SendChatMessage(client->peer_id, message);
3967 void Server::SendPlayerHP(Player *player)
3969 SendHP(m_con, player->peer_id, player->hp);
3972 void Server::SendMovePlayer(Player *player)
3974 DSTACK(__FUNCTION_NAME);
3975 std::ostringstream os(std::ios_base::binary);
3977 writeU16(os, TOCLIENT_MOVE_PLAYER);
3978 writeV3F1000(os, player->getPosition());
3979 writeF1000(os, player->getPitch());
3980 writeF1000(os, player->getYaw());
3983 v3f pos = player->getPosition();
3984 f32 pitch = player->getPitch();
3985 f32 yaw = player->getYaw();
3986 infostream<<"Server sending TOCLIENT_MOVE_PLAYER"
3987 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3994 std::string s = os.str();
3995 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3997 m_con.Send(player->peer_id, 0, data, true);
4000 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
4001 core::list<u16> *far_players, float far_d_nodes)
4003 float maxd = far_d_nodes*BS;
4004 v3f p_f = intToFloat(p, BS);
4008 SharedBuffer<u8> reply(replysize);
4009 writeU16(&reply[0], TOCLIENT_REMOVENODE);
4010 writeS16(&reply[2], p.X);
4011 writeS16(&reply[4], p.Y);
4012 writeS16(&reply[6], p.Z);
4014 for(core::map<u16, RemoteClient*>::Iterator
4015 i = m_clients.getIterator();
4016 i.atEnd() == false; i++)
4018 // Get client and check that it is valid
4019 RemoteClient *client = i.getNode()->getValue();
4020 assert(client->peer_id == i.getNode()->getKey());
4021 if(client->serialization_version == SER_FMT_VER_INVALID)
4024 // Don't send if it's the same one
4025 if(client->peer_id == ignore_id)
4031 Player *player = m_env->getPlayer(client->peer_id);
4034 // If player is far away, only set modified blocks not sent
4035 v3f player_pos = player->getPosition();
4036 if(player_pos.getDistanceFrom(p_f) > maxd)
4038 far_players->push_back(client->peer_id);
4045 m_con.Send(client->peer_id, 0, reply, true);
4049 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
4050 core::list<u16> *far_players, float far_d_nodes)
4052 float maxd = far_d_nodes*BS;
4053 v3f p_f = intToFloat(p, BS);
4055 for(core::map<u16, RemoteClient*>::Iterator
4056 i = m_clients.getIterator();
4057 i.atEnd() == false; i++)
4059 // Get client and check that it is valid
4060 RemoteClient *client = i.getNode()->getValue();
4061 assert(client->peer_id == i.getNode()->getKey());
4062 if(client->serialization_version == SER_FMT_VER_INVALID)
4065 // Don't send if it's the same one
4066 if(client->peer_id == ignore_id)
4072 Player *player = m_env->getPlayer(client->peer_id);
4075 // If player is far away, only set modified blocks not sent
4076 v3f player_pos = player->getPosition();
4077 if(player_pos.getDistanceFrom(p_f) > maxd)
4079 far_players->push_back(client->peer_id);
4086 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
4087 SharedBuffer<u8> reply(replysize);
4088 writeU16(&reply[0], TOCLIENT_ADDNODE);
4089 writeS16(&reply[2], p.X);
4090 writeS16(&reply[4], p.Y);
4091 writeS16(&reply[6], p.Z);
4092 n.serialize(&reply[8], client->serialization_version);
4095 m_con.Send(client->peer_id, 0, reply, true);
4099 void Server::setBlockNotSent(v3s16 p)
4101 for(core::map<u16, RemoteClient*>::Iterator
4102 i = m_clients.getIterator();
4103 i.atEnd()==false; i++)
4105 RemoteClient *client = i.getNode()->getValue();
4106 client->SetBlockNotSent(p);
4110 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
4112 DSTACK(__FUNCTION_NAME);
4114 v3s16 p = block->getPos();
4118 bool completely_air = true;
4119 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4120 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4121 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4123 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
4125 completely_air = false;
4126 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
4131 infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
4133 infostream<<"[completely air] ";
4134 infostream<<std::endl;
4138 Create a packet with the block in the right format
4141 std::ostringstream os(std::ios_base::binary);
4142 block->serialize(os, ver);
4143 std::string s = os.str();
4144 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
4146 u32 replysize = 8 + blockdata.getSize();
4147 SharedBuffer<u8> reply(replysize);
4148 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
4149 writeS16(&reply[2], p.X);
4150 writeS16(&reply[4], p.Y);
4151 writeS16(&reply[6], p.Z);
4152 memcpy(&reply[8], *blockdata, blockdata.getSize());
4154 /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4155 <<": \tpacket size: "<<replysize<<std::endl;*/
4160 m_con.Send(peer_id, 1, reply, true);
4163 void Server::SendBlocks(float dtime)
4165 DSTACK(__FUNCTION_NAME);
4167 JMutexAutoLock envlock(m_env_mutex);
4168 JMutexAutoLock conlock(m_con_mutex);
4170 //TimeTaker timer("Server::SendBlocks");
4172 core::array<PrioritySortedBlockTransfer> queue;
4174 s32 total_sending = 0;
4177 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
4179 for(core::map<u16, RemoteClient*>::Iterator
4180 i = m_clients.getIterator();
4181 i.atEnd() == false; i++)
4183 RemoteClient *client = i.getNode()->getValue();
4184 assert(client->peer_id == i.getNode()->getKey());
4186 total_sending += client->SendingCount();
4188 if(client->serialization_version == SER_FMT_VER_INVALID)
4191 client->GetNextBlocks(this, dtime, queue);
4196 // Lowest priority number comes first.
4197 // Lowest is most important.
4200 for(u32 i=0; i<queue.size(); i++)
4202 //TODO: Calculate limit dynamically
4203 if(total_sending >= g_settings->getS32
4204 ("max_simultaneous_block_sends_server_total"))
4207 PrioritySortedBlockTransfer q = queue[i];
4209 MapBlock *block = NULL;
4212 block = m_env->getMap().getBlockNoCreate(q.pos);
4214 catch(InvalidPositionException &e)
4219 RemoteClient *client = getClient(q.peer_id);
4221 SendBlockNoLock(q.peer_id, block, client->serialization_version);
4223 client->SentBlock(q.pos);
4229 struct SendableTexture
4235 SendableTexture(const std::string &name_="", const std::string path_="",
4236 const std::string &data_=""):
4243 void Server::SendTextures(u16 peer_id)
4245 DSTACK(__FUNCTION_NAME);
4247 infostream<<"Server::SendTextures(): Sending textures to client"<<std::endl;
4251 // Put 5kB in one bunch (this is not accurate)
4252 u32 bytes_per_bunch = 5000;
4254 core::array< core::list<SendableTexture> > texture_bunches;
4255 texture_bunches.push_back(core::list<SendableTexture>());
4257 u32 texture_size_bunch_total = 0;
4258 core::list<ModSpec> mods = getMods(m_modspaths);
4259 for(core::list<ModSpec>::Iterator i = mods.begin();
4260 i != mods.end(); i++){
4262 std::string texturepath = mod.path + DIR_DELIM + "textures";
4263 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(texturepath);
4264 for(u32 j=0; j<dirlist.size(); j++){
4265 if(dirlist[j].dir) // Ignode dirs
4267 std::string tname = dirlist[j].name;
4268 std::string tpath = texturepath + DIR_DELIM + tname;
4270 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
4271 if(fis.good() == false){
4272 errorstream<<"Server::SendTextures(): Could not open \""
4273 <<tname<<"\" for reading"<<std::endl;
4276 std::ostringstream tmp_os(std::ios_base::binary);
4280 fis.read(buf, 1024);
4281 std::streamsize len = fis.gcount();
4282 tmp_os.write(buf, len);
4283 texture_size_bunch_total += len;
4292 errorstream<<"Server::SendTextures(): Failed to read \""
4293 <<tname<<"\""<<std::endl;
4296 /*infostream<<"Server::SendTextures(): Loaded \""
4297 <<tname<<"\""<<std::endl;*/
4299 texture_bunches[texture_bunches.size()-1].push_back(
4300 SendableTexture(tname, tpath, tmp_os.str()));
4302 // Start next bunch if got enough data
4303 if(texture_size_bunch_total >= bytes_per_bunch){
4304 texture_bunches.push_back(core::list<SendableTexture>());
4305 texture_size_bunch_total = 0;
4310 /* Create and send packets */
4312 u32 num_bunches = texture_bunches.size();
4313 for(u32 i=0; i<num_bunches; i++)
4317 u16 total number of texture bunches
4318 u16 index of this bunch
4319 u32 number of textures in this bunch
4327 std::ostringstream os(std::ios_base::binary);
4329 writeU16(os, TOCLIENT_TEXTURES);
4330 writeU16(os, num_bunches);
4332 writeU32(os, texture_bunches[i].size());
4334 for(core::list<SendableTexture>::Iterator
4335 j = texture_bunches[i].begin();
4336 j != texture_bunches[i].end(); j++){
4337 os<<serializeString(j->name);
4338 os<<serializeLongString(j->data);
4342 std::string s = os.str();
4343 infostream<<"Server::SendTextures(): bunch "<<i<<"/"<<num_bunches
4344 <<" textures="<<texture_bunches[i].size()
4345 <<" size=" <<s.size()<<std::endl;
4346 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4348 m_con.Send(peer_id, 0, data, true);
4356 void Server::HandlePlayerHP(Player *player, s16 damage)
4358 ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
4360 if(srp->m_respawn_active)
4363 if(player->hp > damage)
4365 player->hp -= damage;
4366 SendPlayerHP(player);
4370 infostream<<"Server::HandlePlayerHP(): Player "
4371 <<player->getName()<<" dies"<<std::endl;
4375 //TODO: Throw items around
4377 // Handle players that are not connected
4378 if(player->peer_id == PEER_ID_INEXISTENT){
4379 RespawnPlayer(player);
4383 SendPlayerHP(player);
4385 RemoteClient *client = getClient(player->peer_id);
4386 if(client->net_proto_version >= 3)
4388 SendDeathscreen(m_con, player->peer_id, false, v3f(0,0,0));
4389 srp->m_removed = true;
4390 srp->m_respawn_active = true;
4394 RespawnPlayer(player);
4399 void Server::RespawnPlayer(Player *player)
4402 ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
4403 srp->m_respawn_active = false;
4404 bool repositioned = scriptapi_on_respawnplayer(m_lua, srp);
4406 v3f pos = findSpawnPos(m_env->getServerMap());
4407 player->setPosition(pos);
4408 srp->m_last_good_position = pos;
4409 srp->m_last_good_position_age = 0;
4411 SendMovePlayer(player);
4412 SendPlayerHP(player);
4415 void Server::UpdateCrafting(u16 peer_id)
4417 DSTACK(__FUNCTION_NAME);
4419 Player* player = m_env->getPlayer(peer_id);
4421 ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
4423 // No crafting in creative mode
4424 if(g_settings->getBool("creative_mode"))
4427 // Get the InventoryLists of the player in which we will operate
4428 InventoryList *clist = player->inventory.getList("craft");
4430 InventoryList *rlist = player->inventory.getList("craftresult");
4432 InventoryList *mlist = player->inventory.getList("main");
4435 // If the result list is not a preview and is not empty, try to
4436 // throw the item into main list
4437 if(!player->craftresult_is_preview && rlist->getUsedSlots() != 0)
4439 // Grab item out of craftresult
4440 InventoryItem *item = rlist->changeItem(0, NULL);
4441 // Try to put in main
4442 InventoryItem *leftover = mlist->addItem(item);
4443 // If there are leftovers, put them back to craftresult and
4445 delete rlist->addItem(leftover);
4446 // Inventory was modified
4447 srp->m_inventory_not_sent = true;
4450 // If result list is empty, we will make it preview what would be
4452 if(rlist->getUsedSlots() == 0)
4453 player->craftresult_is_preview = true;
4455 // If it is a preview, clear the possible old preview in it
4456 if(player->craftresult_is_preview)
4457 rlist->clearItems();
4459 // If it is a preview, find out what is the crafting result
4461 if(player->craftresult_is_preview)
4463 // Mangle crafting grid to an another format
4464 std::vector<InventoryItem*> items;
4465 for(u16 i=0; i<9; i++){
4466 if(clist->getItem(i) == NULL)
4467 items.push_back(NULL);
4469 items.push_back(clist->getItem(i)->clone());
4471 CraftPointerInput cpi(3, items);
4473 // Find out what is crafted and add it to result item slot
4474 InventoryItem *result = m_craftdef->getCraftResult(cpi, this);
4476 rlist->addItem(result);
4480 RemoteClient* Server::getClient(u16 peer_id)
4482 DSTACK(__FUNCTION_NAME);
4483 //JMutexAutoLock lock(m_con_mutex);
4484 core::map<u16, RemoteClient*>::Node *n;
4485 n = m_clients.find(peer_id);
4486 // A client should exist for all peers
4488 return n->getValue();
4491 std::wstring Server::getStatusString()
4493 std::wostringstream os(std::ios_base::binary);
4496 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4498 os<<L", uptime="<<m_uptime.get();
4499 // Information about clients
4501 for(core::map<u16, RemoteClient*>::Iterator
4502 i = m_clients.getIterator();
4503 i.atEnd() == false; i++)
4505 // Get client and check that it is valid
4506 RemoteClient *client = i.getNode()->getValue();
4507 assert(client->peer_id == i.getNode()->getKey());
4508 if(client->serialization_version == SER_FMT_VER_INVALID)
4511 Player *player = m_env->getPlayer(client->peer_id);
4512 // Get name of player
4513 std::wstring name = L"unknown";
4515 name = narrow_to_wide(player->getName());
4516 // Add name to information string
4520 if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
4521 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4522 if(g_settings->get("motd") != "")
4523 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4527 void Server::setPlayerPassword(const std::string &name, const std::wstring &password)
4529 // Add player to auth manager
4530 if(m_authmanager.exists(name) == false)
4532 infostream<<"Server: adding player "<<name
4533 <<" to auth manager"<<std::endl;
4534 m_authmanager.add(name);
4535 m_authmanager.setPrivs(name,
4536 stringToPrivs(g_settings->get("default_privs")));
4538 // Change password and save
4539 m_authmanager.setPassword(name, translatePassword(name, password));
4540 m_authmanager.save();
4543 // Saves g_settings to configpath given at initialization
4544 void Server::saveConfig()
4546 if(m_configpath != "")
4547 g_settings->updateConfigFile(m_configpath.c_str());
4550 void Server::notifyPlayer(const char *name, const std::wstring msg)
4552 Player *player = m_env->getPlayer(name);
4555 SendChatMessage(player->peer_id, std::wstring(L"Server: -!- ")+msg);
4558 void Server::notifyPlayers(const std::wstring msg)
4560 BroadcastChatMessage(msg);
4563 void Server::queueBlockEmerge(v3s16 blockpos, bool allow_generate)
4567 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
4568 m_emerge_queue.addBlock(PEER_ID_INEXISTENT, blockpos, flags);
4571 // IGameDef interface
4573 IToolDefManager* Server::getToolDefManager()
4577 INodeDefManager* Server::getNodeDefManager()
4581 ICraftDefManager* Server::getCraftDefManager()
4585 ICraftItemDefManager* Server::getCraftItemDefManager()
4587 return m_craftitemdef;
4589 ITextureSource* Server::getTextureSource()
4593 u16 Server::allocateUnknownNodeId(const std::string &name)
4595 return m_nodedef->allocateDummy(name);
4598 IWritableToolDefManager* Server::getWritableToolDefManager()
4602 IWritableNodeDefManager* Server::getWritableNodeDefManager()
4606 IWritableCraftDefManager* Server::getWritableCraftDefManager()
4610 IWritableCraftItemDefManager* Server::getWritableCraftItemDefManager()
4612 return m_craftitemdef;
4615 v3f findSpawnPos(ServerMap &map)
4617 //return v3f(50,50,50)*BS;
4622 nodepos = v2s16(0,0);
4627 // Try to find a good place a few times
4628 for(s32 i=0; i<1000; i++)
4631 // We're going to try to throw the player to this position
4632 v2s16 nodepos2d = v2s16(-range + (myrand()%(range*2)),
4633 -range + (myrand()%(range*2)));
4634 //v2s16 sectorpos = getNodeSectorPos(nodepos2d);
4635 // Get ground height at point (fallbacks to heightmap function)
4636 s16 groundheight = map.findGroundLevel(nodepos2d);
4637 // Don't go underwater
4638 if(groundheight < WATER_LEVEL)
4640 //infostream<<"-> Underwater"<<std::endl;
4643 // Don't go to high places
4644 if(groundheight > WATER_LEVEL + 4)
4646 //infostream<<"-> Underwater"<<std::endl;
4650 nodepos = v3s16(nodepos2d.X, groundheight-2, nodepos2d.Y);
4651 bool is_good = false;
4653 for(s32 i=0; i<10; i++){
4654 v3s16 blockpos = getNodeBlockPos(nodepos);
4655 map.emergeBlock(blockpos, true);
4656 MapNode n = map.getNodeNoEx(nodepos);
4657 if(n.getContent() == CONTENT_AIR){
4668 // Found a good place
4669 //infostream<<"Searched through "<<i<<" places."<<std::endl;
4675 return intToFloat(nodepos, BS);
4678 ServerRemotePlayer *Server::emergePlayer(const char *name, u16 peer_id)
4681 Try to get an existing player
4683 ServerRemotePlayer *player =
4684 static_cast<ServerRemotePlayer*>(m_env->getPlayer(name));
4687 // If player is already connected, cancel
4688 if(player->peer_id != 0)
4690 infostream<<"emergePlayer(): Player already connected"<<std::endl;
4695 player->peer_id = peer_id;
4697 // Reset inventory to creative if in creative mode
4698 if(g_settings->getBool("creative_mode"))
4700 // Warning: double code below
4701 // Backup actual inventory
4702 player->inventory_backup = new Inventory();
4703 *(player->inventory_backup) = player->inventory;
4704 // Set creative inventory
4705 craft_set_creative_inventory(player, this);
4712 If player with the wanted peer_id already exists, cancel.
4714 if(m_env->getPlayer(peer_id) != NULL)
4716 infostream<<"emergePlayer(): Player with wrong name but same"
4717 " peer_id already exists"<<std::endl;
4725 /* Set player position */
4727 infostream<<"Server: Finding spawn place for player \""
4728 <<name<<"\""<<std::endl;
4730 v3f pos = findSpawnPos(m_env->getServerMap());
4732 player = new ServerRemotePlayer(m_env, pos, peer_id, name);
4734 /* Add player to environment */
4735 m_env->addPlayer(player);
4738 ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
4739 scriptapi_on_newplayer(m_lua, srp);
4741 /* Add stuff to inventory */
4742 if(g_settings->getBool("creative_mode"))
4744 // Warning: double code above
4745 // Backup actual inventory
4746 player->inventory_backup = new Inventory();
4747 *(player->inventory_backup) = player->inventory;
4748 // Set creative inventory
4749 craft_set_creative_inventory(player, this);
4754 } // create new player
4757 void Server::handlePeerChange(PeerChange &c)
4759 JMutexAutoLock envlock(m_env_mutex);
4760 JMutexAutoLock conlock(m_con_mutex);
4762 if(c.type == PEER_ADDED)
4769 core::map<u16, RemoteClient*>::Node *n;
4770 n = m_clients.find(c.peer_id);
4771 // The client shouldn't already exist
4775 RemoteClient *client = new RemoteClient();
4776 client->peer_id = c.peer_id;
4777 m_clients.insert(client->peer_id, client);
4780 else if(c.type == PEER_REMOVED)
4787 core::map<u16, RemoteClient*>::Node *n;
4788 n = m_clients.find(c.peer_id);
4789 // The client should exist
4793 Mark objects to be not known by the client
4795 RemoteClient *client = n->getValue();
4797 for(core::map<u16, bool>::Iterator
4798 i = client->m_known_objects.getIterator();
4799 i.atEnd()==false; i++)
4802 u16 id = i.getNode()->getKey();
4803 ServerActiveObject* obj = m_env->getActiveObject(id);
4805 if(obj && obj->m_known_by_count > 0)
4806 obj->m_known_by_count--;
4809 ServerRemotePlayer* player =
4810 static_cast<ServerRemotePlayer*>(m_env->getPlayer(c.peer_id));
4812 // Collect information about leaving in chat
4813 std::wstring message;
4817 std::wstring name = narrow_to_wide(player->getName());
4820 message += L" left game";
4822 message += L" (timed out)";
4826 // Remove from environment
4828 player->m_removed = true;
4830 // Set player client disconnected
4832 player->peer_id = 0;
4840 std::ostringstream os(std::ios_base::binary);
4841 for(core::map<u16, RemoteClient*>::Iterator
4842 i = m_clients.getIterator();
4843 i.atEnd() == false; i++)
4845 RemoteClient *client = i.getNode()->getValue();
4846 assert(client->peer_id == i.getNode()->getKey());
4847 if(client->serialization_version == SER_FMT_VER_INVALID)
4850 Player *player = m_env->getPlayer(client->peer_id);
4853 // Get name of player
4854 os<<player->getName()<<" ";
4857 actionstream<<player->getName()<<" "
4858 <<(c.timeout?"times out.":"leaves game.")
4859 <<" List of players: "
4860 <<os.str()<<std::endl;
4865 delete m_clients[c.peer_id];
4866 m_clients.remove(c.peer_id);
4868 // Send player info to all remaining clients
4869 //SendPlayerInfos();
4871 // Send leave chat message to all remaining clients
4872 BroadcastChatMessage(message);
4881 void Server::handlePeerChanges()
4883 while(m_peer_change_queue.size() > 0)
4885 PeerChange c = m_peer_change_queue.pop_front();
4887 infostream<<"Server: Handling peer change: "
4888 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4891 handlePeerChange(c);
4895 u64 Server::getPlayerPrivs(Player *player)
4899 std::string playername = player->getName();
4900 // Local player gets all privileges regardless of
4901 // what's set on their account.
4902 if(g_settings->get("name") == playername)
4908 return getPlayerAuthPrivs(playername);
4912 void dedicated_server_loop(Server &server, bool &kill)
4914 DSTACK(__FUNCTION_NAME);
4916 infostream<<DTIME<<std::endl;
4917 infostream<<"========================"<<std::endl;
4918 infostream<<"Running dedicated server"<<std::endl;
4919 infostream<<"========================"<<std::endl;
4920 infostream<<std::endl;
4922 IntervalLimiter m_profiler_interval;
4926 // This is kind of a hack but can be done like this
4927 // because server.step() is very light
4929 ScopeProfiler sp(g_profiler, "dedicated server sleep");
4934 if(server.getShutdownRequested() || kill)
4936 infostream<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4943 float profiler_print_interval =
4944 g_settings->getFloat("profiler_print_interval");
4945 if(profiler_print_interval != 0)
4947 if(m_profiler_interval.step(0.030, profiler_print_interval))
4949 infostream<<"Profiler:"<<std::endl;
4950 g_profiler->print(infostream);
4951 g_profiler->clear();
4958 static int counter = 0;
4964 core::list<PlayerInfo> list = server.getPlayerInfo();
4965 core::list<PlayerInfo>::Iterator i;
4966 static u32 sum_old = 0;
4967 u32 sum = PIChecksum(list);
4970 infostream<<DTIME<<"Player info:"<<std::endl;
4971 for(i=list.begin(); i!=list.end(); i++)
4973 i->PrintLine(&infostream);