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_nodemeta.h"
38 #include "serverobject.h"
43 #include "scriptapi.h"
47 #include "craftitemdef.h"
49 #include "content_abm.h"
50 #include "content_sao.h" // For PlayerSAO
52 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
54 #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
56 class MapEditEventIgnorer
59 MapEditEventIgnorer(bool *flag):
68 ~MapEditEventIgnorer()
81 void * ServerThread::Thread()
85 log_register_thread("ServerThread");
87 DSTACK(__FUNCTION_NAME);
89 BEGIN_DEBUG_EXCEPTION_HANDLER
94 //TimeTaker timer("AsyncRunStep() + Receive()");
97 //TimeTaker timer("AsyncRunStep()");
98 m_server->AsyncRunStep();
101 //infostream<<"Running m_server->Receive()"<<std::endl;
104 catch(con::NoIncomingDataException &e)
107 catch(con::PeerNotFoundException &e)
109 infostream<<"Server: PeerNotFoundException"<<std::endl;
113 END_DEBUG_EXCEPTION_HANDLER(errorstream)
118 void * EmergeThread::Thread()
122 log_register_thread("EmergeThread");
124 DSTACK(__FUNCTION_NAME);
126 BEGIN_DEBUG_EXCEPTION_HANDLER
128 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
131 Get block info from queue, emerge them and send them
134 After queue is empty, exit.
138 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
142 SharedPtr<QueuedBlockEmerge> q(qptr);
148 Do not generate over-limit
150 if(blockpos_over_limit(p))
153 //infostream<<"EmergeThread::Thread(): running"<<std::endl;
155 //TimeTaker timer("block emerge");
158 Try to emerge it from somewhere.
160 If it is only wanted as optional, only loading from disk
165 Check if any peer wants it as non-optional. In that case it
168 Also decrement the emerge queue count in clients.
171 bool only_from_disk = true;
174 core::map<u16, u8>::Iterator i;
175 for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
177 //u16 peer_id = i.getNode()->getKey();
180 u8 flags = i.getNode()->getValue();
181 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
182 only_from_disk = false;
187 if(enable_mapgen_debug_info)
188 infostream<<"EmergeThread: p="
189 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
190 <<"only_from_disk="<<only_from_disk<<std::endl;
192 ServerMap &map = ((ServerMap&)m_server->m_env->getMap());
194 MapBlock *block = NULL;
195 bool got_block = true;
196 core::map<v3s16, MapBlock*> modified_blocks;
199 Try to fetch block from memory or disk.
200 If not found and asked to generate, initialize generator.
203 bool started_generate = false;
204 mapgen::BlockMakeData data;
207 JMutexAutoLock envlock(m_server->m_env_mutex);
209 // Load sector if it isn't loaded
210 if(map.getSectorNoGenerateNoEx(p2d) == NULL)
211 map.loadSectorMeta(p2d);
213 // Attempt to load block
214 block = map.getBlockNoCreateNoEx(p);
215 if(!block || block->isDummy() || !block->isGenerated())
217 if(enable_mapgen_debug_info)
218 infostream<<"EmergeThread: not in memory, "
219 <<"attempting to load from disk"<<std::endl;
221 block = map.loadBlock(p);
224 // If could not load and allowed to generate, start generation
225 // inside this same envlock
226 if(only_from_disk == false &&
227 (block == NULL || block->isGenerated() == false)){
228 if(enable_mapgen_debug_info)
229 infostream<<"EmergeThread: generating"<<std::endl;
230 started_generate = true;
232 map.initBlockMake(&data, p);
237 If generator was initialized, generate now when envlock is free.
242 ScopeProfiler sp(g_profiler, "EmergeThread: mapgen::make_block",
244 TimeTaker t("mapgen::make_block()");
246 mapgen::make_block(&data);
248 if(enable_mapgen_debug_info == false)
249 t.stop(true); // Hide output
253 // Lock environment again to access the map
254 JMutexAutoLock envlock(m_server->m_env_mutex);
256 ScopeProfiler sp(g_profiler, "EmergeThread: after "
257 "mapgen::make_block (envlock)", SPT_AVG);
259 // Blit data back on map, update lighting, add mobs and
260 // whatever this does
261 map.finishBlockMake(&data, modified_blocks);
264 block = map.getBlockNoCreateNoEx(p);
267 Do some post-generate stuff
270 v3s16 minp = block->getPos()*MAP_BLOCKSIZE;
271 v3s16 maxp = minp + v3s16(1,1,1)*(MAP_BLOCKSIZE-1);
272 scriptapi_environment_on_generated(m_server->m_lua,
275 if(enable_mapgen_debug_info)
276 infostream<<"EmergeThread: ended up with: "
277 <<analyze_block(block)<<std::endl;
280 Ignore map edit events, they will not need to be
281 sent to anybody because the block hasn't been sent
284 MapEditEventIgnorer ign(&m_server->m_ignore_map_edit_events);
286 // Activate objects and stuff
287 m_server->m_env->activateBlock(block, 0);
295 Set sent status of modified blocks on clients
298 // NOTE: Server's clients are also behind the connection mutex
299 JMutexAutoLock lock(m_server->m_con_mutex);
302 Add the originally fetched block to the modified list
306 modified_blocks.insert(p, block);
310 Set the modified blocks unsent for all the clients
313 for(core::map<u16, RemoteClient*>::Iterator
314 i = m_server->m_clients.getIterator();
315 i.atEnd() == false; i++)
317 RemoteClient *client = i.getNode()->getValue();
319 if(modified_blocks.size() > 0)
321 // Remove block from sent history
322 client->SetBlocksNotSent(modified_blocks);
328 END_DEBUG_EXCEPTION_HANDLER(errorstream)
330 log_deregister_thread();
335 void RemoteClient::GetNextBlocks(Server *server, float dtime,
336 core::array<PrioritySortedBlockTransfer> &dest)
338 DSTACK(__FUNCTION_NAME);
341 TimeTaker timer("RemoteClient::GetNextBlocks", &timer_result);*/
344 m_nothing_to_send_pause_timer -= dtime;
345 m_nearest_unsent_reset_timer += dtime;
347 if(m_nothing_to_send_pause_timer >= 0)
352 // Won't send anything if already sending
353 if(m_blocks_sending.size() >= g_settings->getU16
354 ("max_simultaneous_block_sends_per_client"))
356 //infostream<<"Not sending any blocks, Queue full."<<std::endl;
360 //TimeTaker timer("RemoteClient::GetNextBlocks");
362 Player *player = server->m_env->getPlayer(peer_id);
364 assert(player != NULL);
366 v3f playerpos = player->getPosition();
367 v3f playerspeed = player->getSpeed();
368 v3f playerspeeddir(0,0,0);
369 if(playerspeed.getLength() > 1.0*BS)
370 playerspeeddir = playerspeed / playerspeed.getLength();
371 // Predict to next block
372 v3f playerpos_predicted = playerpos + playerspeeddir*MAP_BLOCKSIZE*BS;
374 v3s16 center_nodepos = floatToInt(playerpos_predicted, BS);
376 v3s16 center = getNodeBlockPos(center_nodepos);
378 // Camera position and direction
379 v3f camera_pos = player->getEyePosition();
380 v3f camera_dir = v3f(0,0,1);
381 camera_dir.rotateYZBy(player->getPitch());
382 camera_dir.rotateXZBy(player->getYaw());
384 /*infostream<<"camera_dir=("<<camera_dir.X<<","<<camera_dir.Y<<","
385 <<camera_dir.Z<<")"<<std::endl;*/
388 Get the starting value of the block finder radius.
391 if(m_last_center != center)
393 m_nearest_unsent_d = 0;
394 m_last_center = center;
397 /*infostream<<"m_nearest_unsent_reset_timer="
398 <<m_nearest_unsent_reset_timer<<std::endl;*/
400 // Reset periodically to workaround for some bugs or stuff
401 if(m_nearest_unsent_reset_timer > 20.0)
403 m_nearest_unsent_reset_timer = 0;
404 m_nearest_unsent_d = 0;
405 //infostream<<"Resetting m_nearest_unsent_d for "
406 // <<server->getPlayerName(peer_id)<<std::endl;
409 //s16 last_nearest_unsent_d = m_nearest_unsent_d;
410 s16 d_start = m_nearest_unsent_d;
412 //infostream<<"d_start="<<d_start<<std::endl;
414 u16 max_simul_sends_setting = g_settings->getU16
415 ("max_simultaneous_block_sends_per_client");
416 u16 max_simul_sends_usually = max_simul_sends_setting;
419 Check the time from last addNode/removeNode.
421 Decrease send rate if player is building stuff.
423 m_time_from_building += dtime;
424 if(m_time_from_building < g_settings->getFloat(
425 "full_block_send_enable_min_time_from_building"))
427 max_simul_sends_usually
428 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
432 Number of blocks sending + number of blocks selected for sending
434 u32 num_blocks_selected = m_blocks_sending.size();
437 next time d will be continued from the d from which the nearest
438 unsent block was found this time.
440 This is because not necessarily any of the blocks found this
441 time are actually sent.
443 s32 new_nearest_unsent_d = -1;
445 s16 d_max = g_settings->getS16("max_block_send_distance");
446 s16 d_max_gen = g_settings->getS16("max_block_generate_distance");
448 // Don't loop very much at a time
449 s16 max_d_increment_at_time = 2;
450 if(d_max > d_start + max_d_increment_at_time)
451 d_max = d_start + max_d_increment_at_time;
452 /*if(d_max_gen > d_start+2)
453 d_max_gen = d_start+2;*/
455 //infostream<<"Starting from "<<d_start<<std::endl;
457 s32 nearest_emerged_d = -1;
458 s32 nearest_emergefull_d = -1;
459 s32 nearest_sent_d = -1;
460 bool queue_is_full = false;
463 for(d = d_start; d <= d_max; d++)
465 /*errorstream<<"checking d="<<d<<" for "
466 <<server->getPlayerName(peer_id)<<std::endl;*/
467 //infostream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
470 If m_nearest_unsent_d was changed by the EmergeThread
471 (it can change it to 0 through SetBlockNotSent),
473 Else update m_nearest_unsent_d
475 /*if(m_nearest_unsent_d != last_nearest_unsent_d)
477 d = m_nearest_unsent_d;
478 last_nearest_unsent_d = m_nearest_unsent_d;
482 Get the border/face dot coordinates of a "d-radiused"
485 core::list<v3s16> list;
486 getFacePositions(list, d);
488 core::list<v3s16>::Iterator li;
489 for(li=list.begin(); li!=list.end(); li++)
491 v3s16 p = *li + center;
495 - Don't allow too many simultaneous transfers
496 - EXCEPT when the blocks are very close
498 Also, don't send blocks that are already flying.
501 // Start with the usual maximum
502 u16 max_simul_dynamic = max_simul_sends_usually;
504 // If block is very close, allow full maximum
505 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
506 max_simul_dynamic = max_simul_sends_setting;
508 // Don't select too many blocks for sending
509 if(num_blocks_selected >= max_simul_dynamic)
511 queue_is_full = true;
512 goto queue_full_break;
515 // Don't send blocks that are currently being transferred
516 if(m_blocks_sending.find(p) != NULL)
522 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
523 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
524 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
525 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
526 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
527 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
530 // If this is true, inexistent block will be made from scratch
531 bool generate = d <= d_max_gen;
534 /*// Limit the generating area vertically to 2/3
535 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
538 // Limit the send area vertically to 1/2
539 if(abs(p.Y - center.Y) > d_max / 2)
545 If block is far away, don't generate it unless it is
551 // Block center y in nodes
552 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
553 // Don't generate if it's very high or very low
554 if(y < -64 || y > 64)
558 v2s16 p2d_nodes_center(
562 // Get ground height in nodes
563 s16 gh = server->m_env->getServerMap().findGroundLevel(
566 // If differs a lot, don't generate
567 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
569 // Actually, don't even send it
575 //infostream<<"d="<<d<<std::endl;
578 Don't generate or send if not in sight
579 FIXME This only works if the client uses a small enough
580 FOV setting. The default of 72 degrees is fine.
583 float camera_fov = (72.0*PI/180) * 4./3.;
584 if(isBlockInSight(p, camera_pos, camera_dir, camera_fov, 10000*BS) == false)
590 Don't send already sent blocks
593 if(m_blocks_sent.find(p) != NULL)
600 Check if map has this block
602 MapBlock *block = server->m_env->getMap().getBlockNoCreateNoEx(p);
604 bool surely_not_found_on_disk = false;
605 bool block_is_invalid = false;
608 // Reset usage timer, this block will be of use in the future.
609 block->resetUsageTimer();
611 // Block is dummy if data doesn't exist.
612 // It means it has been not found from disk and not generated
615 surely_not_found_on_disk = true;
618 // Block is valid if lighting is up-to-date and data exists
619 if(block->isValid() == false)
621 block_is_invalid = true;
624 /*if(block->isFullyGenerated() == false)
626 block_is_invalid = true;
631 ServerMap *map = (ServerMap*)(&server->m_env->getMap());
632 v2s16 chunkpos = map->sector_to_chunk(p2d);
633 if(map->chunkNonVolatile(chunkpos) == false)
634 block_is_invalid = true;
636 if(block->isGenerated() == false)
637 block_is_invalid = true;
640 If block is not close, don't send it unless it is near
643 Block is near ground level if night-time mesh
644 differs from day-time mesh.
648 if(block->dayNightDiffed() == false)
655 If block has been marked to not exist on disk (dummy)
656 and generating new ones is not wanted, skip block.
658 if(generate == false && surely_not_found_on_disk == true)
665 Add inexistent block to emerge queue.
667 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
669 //TODO: Get value from somewhere
670 // Allow only one block in emerge queue
671 //if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
672 // Allow two blocks in queue per client
673 //if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
675 // Make it more responsive when needing to generate stuff
676 if(surely_not_found_on_disk)
678 if(server->m_emerge_queue.peerItemCount(peer_id) < max_emerge)
680 //infostream<<"Adding block to emerge queue"<<std::endl;
682 // Add it to the emerge queue and trigger the thread
685 if(generate == false)
686 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
688 server->m_emerge_queue.addBlock(peer_id, p, flags);
689 server->m_emergethread.trigger();
691 if(nearest_emerged_d == -1)
692 nearest_emerged_d = d;
694 if(nearest_emergefull_d == -1)
695 nearest_emergefull_d = d;
702 if(nearest_sent_d == -1)
706 Add block to send queue
709 /*errorstream<<"sending from d="<<d<<" to "
710 <<server->getPlayerName(peer_id)<<std::endl;*/
712 PrioritySortedBlockTransfer q((float)d, p, peer_id);
716 num_blocks_selected += 1;
721 //infostream<<"Stopped at "<<d<<std::endl;
723 // If nothing was found for sending and nothing was queued for
724 // emerging, continue next time browsing from here
725 if(nearest_emerged_d != -1){
726 new_nearest_unsent_d = nearest_emerged_d;
727 } else if(nearest_emergefull_d != -1){
728 new_nearest_unsent_d = nearest_emergefull_d;
730 if(d > g_settings->getS16("max_block_send_distance")){
731 new_nearest_unsent_d = 0;
732 m_nothing_to_send_pause_timer = 2.0;
733 /*infostream<<"GetNextBlocks(): d wrapped around for "
734 <<server->getPlayerName(peer_id)
735 <<"; setting to 0 and pausing"<<std::endl;*/
737 if(nearest_sent_d != -1)
738 new_nearest_unsent_d = nearest_sent_d;
740 new_nearest_unsent_d = d;
744 if(new_nearest_unsent_d != -1)
745 m_nearest_unsent_d = new_nearest_unsent_d;
747 /*timer_result = timer.stop(true);
748 if(timer_result != 0)
749 infostream<<"GetNextBlocks duration: "<<timer_result<<" (!=0)"<<std::endl;*/
752 void RemoteClient::GotBlock(v3s16 p)
754 if(m_blocks_sending.find(p) != NULL)
755 m_blocks_sending.remove(p);
758 /*infostream<<"RemoteClient::GotBlock(): Didn't find in"
759 " m_blocks_sending"<<std::endl;*/
760 m_excess_gotblocks++;
762 m_blocks_sent.insert(p, true);
765 void RemoteClient::SentBlock(v3s16 p)
767 if(m_blocks_sending.find(p) == NULL)
768 m_blocks_sending.insert(p, 0.0);
770 infostream<<"RemoteClient::SentBlock(): Sent block"
771 " already in m_blocks_sending"<<std::endl;
774 void RemoteClient::SetBlockNotSent(v3s16 p)
776 m_nearest_unsent_d = 0;
778 if(m_blocks_sending.find(p) != NULL)
779 m_blocks_sending.remove(p);
780 if(m_blocks_sent.find(p) != NULL)
781 m_blocks_sent.remove(p);
784 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
786 m_nearest_unsent_d = 0;
788 for(core::map<v3s16, MapBlock*>::Iterator
789 i = blocks.getIterator();
790 i.atEnd()==false; i++)
792 v3s16 p = i.getNode()->getKey();
794 if(m_blocks_sending.find(p) != NULL)
795 m_blocks_sending.remove(p);
796 if(m_blocks_sent.find(p) != NULL)
797 m_blocks_sent.remove(p);
805 PlayerInfo::PlayerInfo()
811 void PlayerInfo::PrintLine(std::ostream *s)
814 (*s)<<"\""<<name<<"\" ("
815 <<(position.X/10)<<","<<(position.Y/10)
816 <<","<<(position.Z/10)<<") ";
818 (*s)<<" avg_rtt="<<avg_rtt;
822 u32 PIChecksum(core::list<PlayerInfo> &l)
824 core::list<PlayerInfo>::Iterator i;
827 for(i=l.begin(); i!=l.end(); i++)
829 checksum += a * (i->id+1);
830 checksum ^= 0x435aafcd;
844 std::set<std::string> depends;
845 std::set<std::string> unsatisfied_depends;
847 ModSpec(const std::string &name_="", const std::string path_="",
848 const std::set<std::string> &depends_=std::set<std::string>()):
852 unsatisfied_depends(depends_)
856 // Get a dependency-sorted list of ModSpecs
857 static core::list<ModSpec> getMods(core::list<std::string> &modspaths)
859 std::queue<ModSpec> mods_satisfied;
860 core::list<ModSpec> mods_unsorted;
861 core::list<ModSpec> mods_sorted;
862 for(core::list<std::string>::Iterator i = modspaths.begin();
863 i != modspaths.end(); i++){
864 std::string modspath = *i;
865 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(modspath);
866 for(u32 j=0; j<dirlist.size(); j++){
869 std::string modname = dirlist[j].name;
870 std::string modpath = modspath + DIR_DELIM + modname;
871 std::set<std::string> depends;
872 std::ifstream is((modpath+DIR_DELIM+"depends.txt").c_str(),
873 std::ios_base::binary);
876 std::getline(is, dep);
881 ModSpec spec(modname, modpath, depends);
882 mods_unsorted.push_back(spec);
884 mods_satisfied.push(spec);
887 // Sort by depencencies
888 while(!mods_satisfied.empty()){
889 ModSpec mod = mods_satisfied.front();
890 mods_satisfied.pop();
891 mods_sorted.push_back(mod);
892 for(core::list<ModSpec>::Iterator i = mods_unsorted.begin();
893 i != mods_unsorted.end(); i++){
895 if(mod2.unsatisfied_depends.empty())
897 mod2.unsatisfied_depends.erase(mod.name);
898 if(!mod2.unsatisfied_depends.empty())
900 mods_satisfied.push(mod2);
903 // Check unsatisfied dependencies
904 for(core::list<ModSpec>::Iterator i = mods_unsorted.begin();
905 i != mods_unsorted.end(); i++){
907 if(mod.unsatisfied_depends.empty())
909 errorstream<<"mod \""<<mod.name
910 <<"\" has unsatisfied dependencies:";
911 for(std::set<std::string>::iterator
912 i = mod.unsatisfied_depends.begin();
913 i != mod.unsatisfied_depends.end(); i++){
914 errorstream<<" \""<<(*i)<<"\"";
916 errorstream<<". Loading nevertheless."<<std::endl;
917 mods_sorted.push_back(mod);
927 std::string mapsavedir,
928 std::string configpath
931 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
932 m_authmanager(mapsavedir+DIR_DELIM+"auth.txt"),
933 m_banmanager(mapsavedir+DIR_DELIM+"ipban.txt"),
935 m_toolmgr(createToolDefManager()),
936 m_nodedef(createNodeDefManager()),
937 m_craftdef(createCraftDefManager()),
938 m_craftitemdef(createCraftItemDefManager()),
940 m_emergethread(this),
942 m_time_of_day_send_timer(0),
944 m_mapsavedir(mapsavedir),
945 m_configpath(configpath),
946 m_shutdown_requested(false),
947 m_ignore_map_edit_events(false),
948 m_ignore_map_edit_events_peer_id(0)
950 m_liquid_transform_timer = 0.0;
951 m_print_info_timer = 0.0;
952 m_objectdata_timer = 0.0;
953 m_emergethread_trigger_timer = 0.0;
954 m_savemap_timer = 0.0;
958 m_step_dtime_mutex.Init();
961 JMutexAutoLock envlock(m_env_mutex);
962 JMutexAutoLock conlock(m_con_mutex);
964 infostream<<"m_nodedef="<<m_nodedef<<std::endl;
966 // Path to builtin.lua
967 std::string builtinpath = porting::path_data + DIR_DELIM + "builtin.lua";
968 // Add default global mod path
969 m_modspaths.push_back(porting::path_data + DIR_DELIM + "mods");
971 // Initialize scripting
973 infostream<<"Server: Initializing scripting"<<std::endl;
974 m_lua = script_init();
977 scriptapi_export(m_lua, this);
978 // Load and run builtin.lua
979 infostream<<"Server: Loading builtin Lua stuff from \""<<builtinpath
981 bool success = script_load(m_lua, builtinpath.c_str());
983 errorstream<<"Server: Failed to load and run "
984 <<builtinpath<<std::endl;
987 // Load and run "mod" scripts
988 core::list<ModSpec> mods = getMods(m_modspaths);
989 for(core::list<ModSpec>::Iterator i = mods.begin();
990 i != mods.end(); i++){
992 infostream<<"Server: Loading mod \""<<mod.name<<"\""<<std::endl;
993 std::string scriptpath = mod.path + DIR_DELIM + "init.lua";
994 bool success = script_load(m_lua, scriptpath.c_str());
996 errorstream<<"Server: Failed to load and run "
997 <<scriptpath<<std::endl;
1002 // Initialize Environment
1004 m_env = new ServerEnvironment(new ServerMap(mapsavedir, this), m_lua,
1007 // Give environment reference to scripting api
1008 scriptapi_add_environment(m_lua, m_env);
1010 // Register us to receive map edit events
1011 m_env->getMap().addEventReceiver(this);
1013 // If file exists, load environment metadata
1014 if(fs::PathExists(m_mapsavedir+DIR_DELIM+"env_meta.txt"))
1016 infostream<<"Server: Loading environment metadata"<<std::endl;
1017 m_env->loadMeta(m_mapsavedir);
1021 infostream<<"Server: Loading players"<<std::endl;
1022 m_env->deSerializePlayers(m_mapsavedir);
1025 Add some test ActiveBlockModifiers to environment
1027 add_legacy_abms(m_env, m_nodedef);
1032 infostream<<"Server::~Server()"<<std::endl;
1035 Send shutdown message
1038 JMutexAutoLock conlock(m_con_mutex);
1040 std::wstring line = L"*** Server shutting down";
1043 Send the message to clients
1045 for(core::map<u16, RemoteClient*>::Iterator
1046 i = m_clients.getIterator();
1047 i.atEnd() == false; i++)
1049 // Get client and check that it is valid
1050 RemoteClient *client = i.getNode()->getValue();
1051 assert(client->peer_id == i.getNode()->getKey());
1052 if(client->serialization_version == SER_FMT_VER_INVALID)
1056 SendChatMessage(client->peer_id, line);
1058 catch(con::PeerNotFoundException &e)
1064 JMutexAutoLock envlock(m_env_mutex);
1069 infostream<<"Server: Saving players"<<std::endl;
1070 m_env->serializePlayers(m_mapsavedir);
1073 Save environment metadata
1075 infostream<<"Server: Saving environment metadata"<<std::endl;
1076 m_env->saveMeta(m_mapsavedir);
1088 JMutexAutoLock clientslock(m_con_mutex);
1090 for(core::map<u16, RemoteClient*>::Iterator
1091 i = m_clients.getIterator();
1092 i.atEnd() == false; i++)
1095 // NOTE: These are removed by env destructor
1097 u16 peer_id = i.getNode()->getKey();
1098 JMutexAutoLock envlock(m_env_mutex);
1099 m_env->removePlayer(peer_id);
1103 delete i.getNode()->getValue();
1107 // Delete Environment
1113 delete m_craftitemdef;
1115 // Deinitialize scripting
1116 infostream<<"Server: Deinitializing scripting"<<std::endl;
1117 script_deinit(m_lua);
1120 void Server::start(unsigned short port)
1122 DSTACK(__FUNCTION_NAME);
1123 // Stop thread if already running
1126 // Initialize connection
1127 m_con.SetTimeoutMs(30);
1131 m_thread.setRun(true);
1134 infostream<<"Server: Started on port "<<port<<std::endl;
1139 DSTACK(__FUNCTION_NAME);
1141 infostream<<"Server: Stopping and waiting threads"<<std::endl;
1143 // Stop threads (set run=false first so both start stopping)
1144 m_thread.setRun(false);
1145 m_emergethread.setRun(false);
1147 m_emergethread.stop();
1149 infostream<<"Server: Threads stopped"<<std::endl;
1152 void Server::step(float dtime)
1154 DSTACK(__FUNCTION_NAME);
1159 JMutexAutoLock lock(m_step_dtime_mutex);
1160 m_step_dtime += dtime;
1164 void Server::AsyncRunStep()
1166 DSTACK(__FUNCTION_NAME);
1168 g_profiler->add("Server::AsyncRunStep (num)", 1);
1172 JMutexAutoLock lock1(m_step_dtime_mutex);
1173 dtime = m_step_dtime;
1177 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
1178 // Send blocks to clients
1185 g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
1187 //infostream<<"Server steps "<<dtime<<std::endl;
1188 //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1191 JMutexAutoLock lock1(m_step_dtime_mutex);
1192 m_step_dtime -= dtime;
1199 m_uptime.set(m_uptime.get() + dtime);
1203 // Process connection's timeouts
1204 JMutexAutoLock lock2(m_con_mutex);
1205 ScopeProfiler sp(g_profiler, "Server: connection timeout processing");
1206 m_con.RunTimeouts(dtime);
1210 // This has to be called so that the client list gets synced
1211 // with the peer list of the connection
1212 handlePeerChanges();
1216 Update m_time_of_day and overall game time
1219 JMutexAutoLock envlock(m_env_mutex);
1221 m_time_counter += dtime;
1222 f32 speed = g_settings->getFloat("time_speed") * 24000./(24.*3600);
1223 u32 units = (u32)(m_time_counter*speed);
1224 m_time_counter -= (f32)units / speed;
1226 m_env->setTimeOfDay((m_env->getTimeOfDay() + units) % 24000);
1228 //infostream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1231 Send to clients at constant intervals
1234 m_time_of_day_send_timer -= dtime;
1235 if(m_time_of_day_send_timer < 0.0)
1237 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
1239 //JMutexAutoLock envlock(m_env_mutex);
1240 JMutexAutoLock conlock(m_con_mutex);
1242 for(core::map<u16, RemoteClient*>::Iterator
1243 i = m_clients.getIterator();
1244 i.atEnd() == false; i++)
1246 RemoteClient *client = i.getNode()->getValue();
1247 //Player *player = m_env->getPlayer(client->peer_id);
1249 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1250 m_env->getTimeOfDay());
1252 m_con.Send(client->peer_id, 0, data, true);
1258 JMutexAutoLock lock(m_env_mutex);
1260 ScopeProfiler sp(g_profiler, "SEnv step");
1261 ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
1265 const float map_timer_and_unload_dtime = 2.92;
1266 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
1268 JMutexAutoLock lock(m_env_mutex);
1269 // Run Map's timers and unload unused data
1270 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
1271 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
1272 g_settings->getFloat("server_unload_unused_data_timeout"));
1283 JMutexAutoLock lock(m_env_mutex);
1284 JMutexAutoLock lock2(m_con_mutex);
1286 ScopeProfiler sp(g_profiler, "Server: handle players");
1288 //float player_max_speed = BS * 4.0; // Normal speed
1289 float player_max_speed = BS * 20; // Fast speed
1290 float player_max_speed_up = BS * 20;
1292 player_max_speed *= 2.5; // Tolerance
1293 player_max_speed_up *= 2.5;
1295 for(core::map<u16, RemoteClient*>::Iterator
1296 i = m_clients.getIterator();
1297 i.atEnd() == false; i++)
1299 RemoteClient *client = i.getNode()->getValue();
1300 ServerRemotePlayer *player =
1301 static_cast<ServerRemotePlayer*>
1302 (m_env->getPlayer(client->peer_id));
1307 Check player movements
1309 NOTE: Actually the server should handle player physics like the
1310 client does and compare player's position to what is calculated
1311 on our side. This is required when eg. players fly due to an
1314 player->m_last_good_position_age += dtime;
1315 if(player->m_last_good_position_age >= 2.0){
1316 float age = player->m_last_good_position_age;
1317 v3f diff = (player->getPosition() - player->m_last_good_position);
1318 float d_vert = diff.Y;
1320 float d_horiz = diff.getLength();
1321 /*infostream<<player->getName()<<"'s horizontal speed is "
1322 <<(d_horiz/age)<<std::endl;*/
1323 if(d_horiz <= age * player_max_speed &&
1324 (d_vert < 0 || d_vert < age * player_max_speed_up)){
1325 player->m_last_good_position = player->getPosition();
1327 actionstream<<"Player "<<player->getName()
1328 <<" moved too fast; resetting position"
1330 player->setPosition(player->m_last_good_position);
1331 SendMovePlayer(player);
1333 player->m_last_good_position_age = 0;
1337 Handle player HPs (die if hp=0)
1339 HandlePlayerHP(player, 0);
1342 Send player inventories and HPs if necessary
1344 if(player->m_inventory_not_sent){
1345 UpdateCrafting(player->peer_id);
1346 SendInventory(player->peer_id);
1348 if(player->m_hp_not_sent){
1349 SendPlayerHP(player);
1353 Add to environment if is not in respawn screen
1355 if(!player->m_is_in_environment && !player->m_respawn_active){
1356 player->m_removed = false;
1358 m_env->addActiveObject(player);
1363 /* Transform liquids */
1364 m_liquid_transform_timer += dtime;
1365 if(m_liquid_transform_timer >= 1.00)
1367 m_liquid_transform_timer -= 1.00;
1369 JMutexAutoLock lock(m_env_mutex);
1371 ScopeProfiler sp(g_profiler, "Server: liquid transform");
1373 core::map<v3s16, MapBlock*> modified_blocks;
1374 m_env->getMap().transformLiquids(modified_blocks);
1379 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1380 ServerMap &map = ((ServerMap&)m_env->getMap());
1381 map.updateLighting(modified_blocks, lighting_modified_blocks);
1383 // Add blocks modified by lighting to modified_blocks
1384 for(core::map<v3s16, MapBlock*>::Iterator
1385 i = lighting_modified_blocks.getIterator();
1386 i.atEnd() == false; i++)
1388 MapBlock *block = i.getNode()->getValue();
1389 modified_blocks.insert(block->getPos(), block);
1393 Set the modified blocks unsent for all the clients
1396 JMutexAutoLock lock2(m_con_mutex);
1398 for(core::map<u16, RemoteClient*>::Iterator
1399 i = m_clients.getIterator();
1400 i.atEnd() == false; i++)
1402 RemoteClient *client = i.getNode()->getValue();
1404 if(modified_blocks.size() > 0)
1406 // Remove block from sent history
1407 client->SetBlocksNotSent(modified_blocks);
1412 // Periodically print some info
1414 float &counter = m_print_info_timer;
1420 JMutexAutoLock lock2(m_con_mutex);
1422 if(m_clients.size() != 0)
1423 infostream<<"Players:"<<std::endl;
1424 for(core::map<u16, RemoteClient*>::Iterator
1425 i = m_clients.getIterator();
1426 i.atEnd() == false; i++)
1428 //u16 peer_id = i.getNode()->getKey();
1429 RemoteClient *client = i.getNode()->getValue();
1430 Player *player = m_env->getPlayer(client->peer_id);
1433 infostream<<"* "<<player->getName()<<"\t";
1434 client->PrintInfo(infostream);
1439 //if(g_settings->getBool("enable_experimental"))
1443 Check added and deleted active objects
1446 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
1447 JMutexAutoLock envlock(m_env_mutex);
1448 JMutexAutoLock conlock(m_con_mutex);
1450 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
1452 // Radius inside which objects are active
1453 s16 radius = g_settings->getS16("active_object_send_range_blocks");
1454 radius *= MAP_BLOCKSIZE;
1456 for(core::map<u16, RemoteClient*>::Iterator
1457 i = m_clients.getIterator();
1458 i.atEnd() == false; i++)
1460 RemoteClient *client = i.getNode()->getValue();
1462 // If definitions and textures have not been sent, don't
1463 // send objects either
1464 if(!client->definitions_sent)
1467 Player *player = m_env->getPlayer(client->peer_id);
1470 // This can happen if the client timeouts somehow
1471 /*infostream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
1473 <<" has no associated player"<<std::endl;*/
1476 v3s16 pos = floatToInt(player->getPosition(), BS);
1478 core::map<u16, bool> removed_objects;
1479 core::map<u16, bool> added_objects;
1480 m_env->getRemovedActiveObjects(pos, radius,
1481 client->m_known_objects, removed_objects);
1482 m_env->getAddedActiveObjects(pos, radius,
1483 client->m_known_objects, added_objects);
1485 // Ignore if nothing happened
1486 if(removed_objects.size() == 0 && added_objects.size() == 0)
1488 //infostream<<"active objects: none changed"<<std::endl;
1492 std::string data_buffer;
1496 // Handle removed objects
1497 writeU16((u8*)buf, removed_objects.size());
1498 data_buffer.append(buf, 2);
1499 for(core::map<u16, bool>::Iterator
1500 i = removed_objects.getIterator();
1501 i.atEnd()==false; i++)
1504 u16 id = i.getNode()->getKey();
1505 ServerActiveObject* obj = m_env->getActiveObject(id);
1507 // Add to data buffer for sending
1508 writeU16((u8*)buf, i.getNode()->getKey());
1509 data_buffer.append(buf, 2);
1511 // Remove from known objects
1512 client->m_known_objects.remove(i.getNode()->getKey());
1514 if(obj && obj->m_known_by_count > 0)
1515 obj->m_known_by_count--;
1518 // Handle added objects
1519 writeU16((u8*)buf, added_objects.size());
1520 data_buffer.append(buf, 2);
1521 for(core::map<u16, bool>::Iterator
1522 i = added_objects.getIterator();
1523 i.atEnd()==false; i++)
1526 u16 id = i.getNode()->getKey();
1527 ServerActiveObject* obj = m_env->getActiveObject(id);
1530 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1532 infostream<<"WARNING: "<<__FUNCTION_NAME
1533 <<": NULL object"<<std::endl;
1535 type = obj->getType();
1537 // Add to data buffer for sending
1538 writeU16((u8*)buf, id);
1539 data_buffer.append(buf, 2);
1540 writeU8((u8*)buf, type);
1541 data_buffer.append(buf, 1);
1544 data_buffer.append(serializeLongString(
1545 obj->getClientInitializationData()));
1547 data_buffer.append(serializeLongString(""));
1549 // Add to known objects
1550 client->m_known_objects.insert(i.getNode()->getKey(), false);
1553 obj->m_known_by_count++;
1557 SharedBuffer<u8> reply(2 + data_buffer.size());
1558 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1559 memcpy((char*)&reply[2], data_buffer.c_str(),
1560 data_buffer.size());
1562 m_con.Send(client->peer_id, 0, reply, true);
1564 infostream<<"Server: Sent object remove/add: "
1565 <<removed_objects.size()<<" removed, "
1566 <<added_objects.size()<<" added, "
1567 <<"packet size is "<<reply.getSize()<<std::endl;
1572 Collect a list of all the objects known by the clients
1573 and report it back to the environment.
1576 core::map<u16, bool> all_known_objects;
1578 for(core::map<u16, RemoteClient*>::Iterator
1579 i = m_clients.getIterator();
1580 i.atEnd() == false; i++)
1582 RemoteClient *client = i.getNode()->getValue();
1583 // Go through all known objects of client
1584 for(core::map<u16, bool>::Iterator
1585 i = client->m_known_objects.getIterator();
1586 i.atEnd()==false; i++)
1588 u16 id = i.getNode()->getKey();
1589 all_known_objects[id] = true;
1593 m_env->setKnownActiveObjects(whatever);
1599 Send object messages
1602 JMutexAutoLock envlock(m_env_mutex);
1603 JMutexAutoLock conlock(m_con_mutex);
1605 ScopeProfiler sp(g_profiler, "Server: sending object messages");
1608 // Value = data sent by object
1609 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1611 // Get active object messages from environment
1614 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
1618 core::list<ActiveObjectMessage>* message_list = NULL;
1619 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1620 n = buffered_messages.find(aom.id);
1623 message_list = new core::list<ActiveObjectMessage>;
1624 buffered_messages.insert(aom.id, message_list);
1628 message_list = n->getValue();
1630 message_list->push_back(aom);
1633 // Route data to every client
1634 for(core::map<u16, RemoteClient*>::Iterator
1635 i = m_clients.getIterator();
1636 i.atEnd()==false; i++)
1638 RemoteClient *client = i.getNode()->getValue();
1639 std::string reliable_data;
1640 std::string unreliable_data;
1641 // Go through all objects in message buffer
1642 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1643 j = buffered_messages.getIterator();
1644 j.atEnd()==false; j++)
1646 // If object is not known by client, skip it
1647 u16 id = j.getNode()->getKey();
1648 if(client->m_known_objects.find(id) == NULL)
1650 // Get message list of object
1651 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1652 // Go through every message
1653 for(core::list<ActiveObjectMessage>::Iterator
1654 k = list->begin(); k != list->end(); k++)
1656 // Compose the full new data with header
1657 ActiveObjectMessage aom = *k;
1658 std::string new_data;
1661 writeU16((u8*)&buf[0], aom.id);
1662 new_data.append(buf, 2);
1664 new_data += serializeString(aom.datastring);
1665 // Add data to buffer
1667 reliable_data += new_data;
1669 unreliable_data += new_data;
1673 reliable_data and unreliable_data are now ready.
1676 if(reliable_data.size() > 0)
1678 SharedBuffer<u8> reply(2 + reliable_data.size());
1679 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1680 memcpy((char*)&reply[2], reliable_data.c_str(),
1681 reliable_data.size());
1683 m_con.Send(client->peer_id, 0, reply, true);
1685 if(unreliable_data.size() > 0)
1687 SharedBuffer<u8> reply(2 + unreliable_data.size());
1688 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1689 memcpy((char*)&reply[2], unreliable_data.c_str(),
1690 unreliable_data.size());
1691 // Send as unreliable
1692 m_con.Send(client->peer_id, 0, reply, false);
1695 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1697 infostream<<"Server: Size of object message data: "
1698 <<"reliable: "<<reliable_data.size()
1699 <<", unreliable: "<<unreliable_data.size()
1704 // Clear buffered_messages
1705 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1706 i = buffered_messages.getIterator();
1707 i.atEnd()==false; i++)
1709 delete i.getNode()->getValue();
1713 } // enable_experimental
1716 Send queued-for-sending map edit events.
1719 // Don't send too many at a time
1722 // Single change sending is disabled if queue size is not small
1723 bool disable_single_change_sending = false;
1724 if(m_unsent_map_edit_queue.size() >= 4)
1725 disable_single_change_sending = true;
1727 bool got_any_events = false;
1729 // We'll log the amount of each
1732 while(m_unsent_map_edit_queue.size() != 0)
1734 got_any_events = true;
1736 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1738 // Players far away from the change are stored here.
1739 // Instead of sending the changes, MapBlocks are set not sent
1741 core::list<u16> far_players;
1743 if(event->type == MEET_ADDNODE)
1745 //infostream<<"Server: MEET_ADDNODE"<<std::endl;
1746 prof.add("MEET_ADDNODE", 1);
1747 if(disable_single_change_sending)
1748 sendAddNode(event->p, event->n, event->already_known_by_peer,
1751 sendAddNode(event->p, event->n, event->already_known_by_peer,
1754 else if(event->type == MEET_REMOVENODE)
1756 //infostream<<"Server: MEET_REMOVENODE"<<std::endl;
1757 prof.add("MEET_REMOVENODE", 1);
1758 if(disable_single_change_sending)
1759 sendRemoveNode(event->p, event->already_known_by_peer,
1762 sendRemoveNode(event->p, event->already_known_by_peer,
1765 else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
1767 infostream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
1768 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
1769 setBlockNotSent(event->p);
1771 else if(event->type == MEET_OTHER)
1773 infostream<<"Server: MEET_OTHER"<<std::endl;
1774 prof.add("MEET_OTHER", 1);
1775 for(core::map<v3s16, bool>::Iterator
1776 i = event->modified_blocks.getIterator();
1777 i.atEnd()==false; i++)
1779 v3s16 p = i.getNode()->getKey();
1785 prof.add("unknown", 1);
1786 infostream<<"WARNING: Server: Unknown MapEditEvent "
1787 <<((u32)event->type)<<std::endl;
1791 Set blocks not sent to far players
1793 if(far_players.size() > 0)
1795 // Convert list format to that wanted by SetBlocksNotSent
1796 core::map<v3s16, MapBlock*> modified_blocks2;
1797 for(core::map<v3s16, bool>::Iterator
1798 i = event->modified_blocks.getIterator();
1799 i.atEnd()==false; i++)
1801 v3s16 p = i.getNode()->getKey();
1802 modified_blocks2.insert(p,
1803 m_env->getMap().getBlockNoCreateNoEx(p));
1805 // Set blocks not sent
1806 for(core::list<u16>::Iterator
1807 i = far_players.begin();
1808 i != far_players.end(); i++)
1811 RemoteClient *client = getClient(peer_id);
1814 client->SetBlocksNotSent(modified_blocks2);
1820 /*// Don't send too many at a time
1822 if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
1828 infostream<<"Server: MapEditEvents:"<<std::endl;
1829 prof.print(infostream);
1835 Trigger emergethread (it somehow gets to a non-triggered but
1836 bysy state sometimes)
1839 float &counter = m_emergethread_trigger_timer;
1845 m_emergethread.trigger();
1849 // Save map, players and auth stuff
1851 float &counter = m_savemap_timer;
1853 if(counter >= g_settings->getFloat("server_map_save_interval"))
1857 ScopeProfiler sp(g_profiler, "Server: saving stuff");
1860 if(m_authmanager.isModified())
1861 m_authmanager.save();
1864 if(m_banmanager.isModified())
1865 m_banmanager.save();
1868 JMutexAutoLock lock(m_env_mutex);
1870 // Save changed parts of map
1871 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
1874 m_env->serializePlayers(m_mapsavedir);
1876 // Save environment metadata
1877 m_env->saveMeta(m_mapsavedir);
1882 void Server::Receive()
1884 DSTACK(__FUNCTION_NAME);
1885 SharedBuffer<u8> data;
1890 JMutexAutoLock conlock(m_con_mutex);
1891 datasize = m_con.Receive(peer_id, data);
1894 // This has to be called so that the client list gets synced
1895 // with the peer list of the connection
1896 handlePeerChanges();
1898 ProcessData(*data, datasize, peer_id);
1900 catch(con::InvalidIncomingDataException &e)
1902 infostream<<"Server::Receive(): "
1903 "InvalidIncomingDataException: what()="
1904 <<e.what()<<std::endl;
1906 catch(con::PeerNotFoundException &e)
1908 //NOTE: This is not needed anymore
1910 // The peer has been disconnected.
1911 // Find the associated player and remove it.
1913 /*JMutexAutoLock envlock(m_env_mutex);
1915 infostream<<"ServerThread: peer_id="<<peer_id
1916 <<" has apparently closed connection. "
1917 <<"Removing player."<<std::endl;
1919 m_env->removePlayer(peer_id);*/
1923 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1925 DSTACK(__FUNCTION_NAME);
1926 // Environment is locked first.
1927 JMutexAutoLock envlock(m_env_mutex);
1928 JMutexAutoLock conlock(m_con_mutex);
1931 Address address = m_con.GetPeerAddress(peer_id);
1933 // drop player if is ip is banned
1934 if(m_banmanager.isIpBanned(address.serializeString())){
1935 SendAccessDenied(m_con, peer_id,
1936 L"Your ip is banned. Banned name was "
1937 +narrow_to_wide(m_banmanager.getBanName(
1938 address.serializeString())));
1939 m_con.DeletePeer(peer_id);
1943 catch(con::PeerNotFoundException &e)
1945 infostream<<"Server::ProcessData(): Cancelling: peer "
1946 <<peer_id<<" not found"<<std::endl;
1950 u8 peer_ser_ver = getClient(peer_id)->serialization_version;
1958 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1960 if(command == TOSERVER_INIT)
1962 // [0] u16 TOSERVER_INIT
1963 // [2] u8 SER_FMT_VER_HIGHEST
1964 // [3] u8[20] player_name
1965 // [23] u8[28] password <--- can be sent without this, from old versions
1967 if(datasize < 2+1+PLAYERNAME_SIZE)
1970 infostream<<"Server: Got TOSERVER_INIT from "
1971 <<peer_id<<std::endl;
1973 // First byte after command is maximum supported
1974 // serialization version
1975 u8 client_max = data[2];
1976 u8 our_max = SER_FMT_VER_HIGHEST;
1977 // Use the highest version supported by both
1978 u8 deployed = core::min_(client_max, our_max);
1979 // If it's lower than the lowest supported, give up.
1980 if(deployed < SER_FMT_VER_LOWEST)
1981 deployed = SER_FMT_VER_INVALID;
1983 //peer->serialization_version = deployed;
1984 getClient(peer_id)->pending_serialization_version = deployed;
1986 if(deployed == SER_FMT_VER_INVALID)
1988 infostream<<"Server: Cannot negotiate "
1989 "serialization version with peer "
1990 <<peer_id<<std::endl;
1991 SendAccessDenied(m_con, peer_id, std::wstring(
1992 L"Your client's version is not supported.\n"
1993 L"Server version is ")
1994 + narrow_to_wide(VERSION_STRING) + L"."
2000 Read and check network protocol version
2003 u16 net_proto_version = 0;
2004 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
2006 net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
2009 getClient(peer_id)->net_proto_version = net_proto_version;
2011 if(net_proto_version == 0)
2013 SendAccessDenied(m_con, peer_id, std::wstring(
2014 L"Your client's version is not supported.\n"
2015 L"Server version is ")
2016 + narrow_to_wide(VERSION_STRING) + L"."
2021 if(g_settings->getBool("strict_protocol_version_checking"))
2023 if(net_proto_version != PROTOCOL_VERSION)
2025 SendAccessDenied(m_con, peer_id, std::wstring(
2026 L"Your client's version is not supported.\n"
2027 L"Server version is ")
2028 + narrow_to_wide(VERSION_STRING) + L",\n"
2029 + L"server's PROTOCOL_VERSION is "
2030 + narrow_to_wide(itos(PROTOCOL_VERSION))
2031 + L", client's PROTOCOL_VERSION is "
2032 + narrow_to_wide(itos(net_proto_version))
2043 char playername[PLAYERNAME_SIZE];
2044 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
2046 playername[i] = data[3+i];
2048 playername[PLAYERNAME_SIZE-1] = 0;
2050 if(playername[0]=='\0')
2052 infostream<<"Server: Player has empty name"<<std::endl;
2053 SendAccessDenied(m_con, peer_id,
2058 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
2060 infostream<<"Server: Player has invalid name"<<std::endl;
2061 SendAccessDenied(m_con, peer_id,
2062 L"Name contains unallowed characters");
2067 char password[PASSWORD_SIZE];
2068 if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
2070 // old version - assume blank password
2075 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2077 password[i] = data[23+i];
2079 password[PASSWORD_SIZE-1] = 0;
2082 // Add player to auth manager
2083 if(m_authmanager.exists(playername) == false)
2085 std::wstring default_password =
2086 narrow_to_wide(g_settings->get("default_password"));
2087 std::string translated_default_password =
2088 translatePassword(playername, default_password);
2090 // If default_password is empty, allow any initial password
2091 if (default_password.length() == 0)
2092 translated_default_password = password;
2094 infostream<<"Server: adding player "<<playername
2095 <<" to auth manager"<<std::endl;
2096 m_authmanager.add(playername);
2097 m_authmanager.setPassword(playername, translated_default_password);
2098 m_authmanager.setPrivs(playername,
2099 stringToPrivs(g_settings->get("default_privs")));
2100 m_authmanager.save();
2103 std::string checkpwd = m_authmanager.getPassword(playername);
2105 /*infostream<<"Server: Client gave password '"<<password
2106 <<"', the correct one is '"<<checkpwd<<"'"<<std::endl;*/
2108 if(password != checkpwd)
2110 infostream<<"Server: peer_id="<<peer_id
2111 <<": supplied invalid password for "
2112 <<playername<<std::endl;
2113 SendAccessDenied(m_con, peer_id, L"Invalid password");
2117 // Enforce user limit.
2118 // Don't enforce for users that have some admin right
2119 if(m_clients.size() >= g_settings->getU16("max_users") &&
2120 (m_authmanager.getPrivs(playername)
2121 & (PRIV_SERVER|PRIV_BAN|PRIV_PRIVS|PRIV_PASSWORD)) == 0 &&
2122 playername != g_settings->get("name"))
2124 SendAccessDenied(m_con, peer_id, L"Too many users.");
2129 ServerRemotePlayer *player = emergePlayer(playername, peer_id);
2131 // If failed, cancel
2134 infostream<<"Server: peer_id="<<peer_id
2135 <<": failed to emerge player"<<std::endl;
2140 Answer with a TOCLIENT_INIT
2143 SharedBuffer<u8> reply(2+1+6+8);
2144 writeU16(&reply[0], TOCLIENT_INIT);
2145 writeU8(&reply[2], deployed);
2146 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
2147 writeU64(&reply[2+1+6], m_env->getServerMap().getSeed());
2150 m_con.Send(peer_id, 0, reply, true);
2154 Send complete position information
2156 SendMovePlayer(player);
2161 if(command == TOSERVER_INIT2)
2163 infostream<<"Server: Got TOSERVER_INIT2 from "
2164 <<peer_id<<std::endl;
2167 getClient(peer_id)->serialization_version
2168 = getClient(peer_id)->pending_serialization_version;
2171 Send some initialization data
2174 // Send tool definitions
2175 SendToolDef(m_con, peer_id, m_toolmgr);
2177 // Send node definitions
2178 SendNodeDef(m_con, peer_id, m_nodedef);
2180 // Send CraftItem definitions
2181 SendCraftItemDef(m_con, peer_id, m_craftitemdef);
2184 SendTextures(peer_id);
2186 // Send player info to all players
2187 //SendPlayerInfos();
2189 // Send inventory to player
2190 UpdateCrafting(peer_id);
2191 SendInventory(peer_id);
2193 // Send player items to all players
2196 Player *player = m_env->getPlayer(peer_id);
2199 SendPlayerHP(player);
2203 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2204 m_env->getTimeOfDay());
2205 m_con.Send(peer_id, 0, data, true);
2208 // Now the client should know about everything
2209 getClient(peer_id)->definitions_sent = true;
2211 // Send information about server to player in chat
2212 SendChatMessage(peer_id, getStatusString());
2214 // Send information about joining in chat
2216 std::wstring name = L"unknown";
2217 Player *player = m_env->getPlayer(peer_id);
2219 name = narrow_to_wide(player->getName());
2221 std::wstring message;
2224 message += L" joined game";
2225 BroadcastChatMessage(message);
2228 // Warnings about protocol version can be issued here
2229 if(getClient(peer_id)->net_proto_version < PROTOCOL_VERSION)
2231 SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT IS OLD AND MAY WORK PROPERLY WITH THIS SERVER");
2235 Check HP, respawn if necessary
2237 HandlePlayerHP(player, 0);
2243 std::ostringstream os(std::ios_base::binary);
2244 for(core::map<u16, RemoteClient*>::Iterator
2245 i = m_clients.getIterator();
2246 i.atEnd() == false; i++)
2248 RemoteClient *client = i.getNode()->getValue();
2249 assert(client->peer_id == i.getNode()->getKey());
2250 if(client->serialization_version == SER_FMT_VER_INVALID)
2253 Player *player = m_env->getPlayer(client->peer_id);
2256 // Get name of player
2257 os<<player->getName()<<" ";
2260 actionstream<<player->getName()<<" joins game. List of players: "
2261 <<os.str()<<std::endl;
2267 if(peer_ser_ver == SER_FMT_VER_INVALID)
2269 infostream<<"Server::ProcessData(): Cancelling: Peer"
2270 " serialization format invalid or not initialized."
2271 " Skipping incoming command="<<command<<std::endl;
2275 Player *player = m_env->getPlayer(peer_id);
2276 ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
2279 infostream<<"Server::ProcessData(): Cancelling: "
2280 "No player for peer_id="<<peer_id
2284 if(command == TOSERVER_PLAYERPOS)
2286 if(datasize < 2+12+12+4+4)
2290 v3s32 ps = readV3S32(&data[start+2]);
2291 v3s32 ss = readV3S32(&data[start+2+12]);
2292 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2293 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2294 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2295 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2296 pitch = wrapDegrees(pitch);
2297 yaw = wrapDegrees(yaw);
2299 player->setPosition(position);
2300 player->setSpeed(speed);
2301 player->setPitch(pitch);
2302 player->setYaw(yaw);
2304 /*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2305 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2306 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2308 else if(command == TOSERVER_GOTBLOCKS)
2321 u16 count = data[2];
2322 for(u16 i=0; i<count; i++)
2324 if((s16)datasize < 2+1+(i+1)*6)
2325 throw con::InvalidIncomingDataException
2326 ("GOTBLOCKS length is too short");
2327 v3s16 p = readV3S16(&data[2+1+i*6]);
2328 /*infostream<<"Server: GOTBLOCKS ("
2329 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2330 RemoteClient *client = getClient(peer_id);
2331 client->GotBlock(p);
2334 else if(command == TOSERVER_DELETEDBLOCKS)
2347 u16 count = data[2];
2348 for(u16 i=0; i<count; i++)
2350 if((s16)datasize < 2+1+(i+1)*6)
2351 throw con::InvalidIncomingDataException
2352 ("DELETEDBLOCKS length is too short");
2353 v3s16 p = readV3S16(&data[2+1+i*6]);
2354 /*infostream<<"Server: DELETEDBLOCKS ("
2355 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2356 RemoteClient *client = getClient(peer_id);
2357 client->SetBlockNotSent(p);
2360 else if(command == TOSERVER_CLICK_OBJECT)
2362 infostream<<"Server: CLICK_OBJECT not supported anymore"<<std::endl;
2365 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2367 infostream<<"Server: CLICK_ACTIVEOBJECT not supported anymore"<<std::endl;
2370 else if(command == TOSERVER_GROUND_ACTION)
2372 infostream<<"Server: GROUND_ACTION not supported anymore"<<std::endl;
2376 else if(command == TOSERVER_RELEASE)
2378 infostream<<"Server: RELEASE not supported anymore"<<std::endl;
2381 else if(command == TOSERVER_SIGNTEXT)
2383 infostream<<"Server: SIGNTEXT not supported anymore"
2387 else if(command == TOSERVER_SIGNNODETEXT)
2389 if((getPlayerPrivs(player) & PRIV_INTERACT) == 0)
2397 std::string datastring((char*)&data[2], datasize-2);
2398 std::istringstream is(datastring, std::ios_base::binary);
2401 is.read((char*)buf, 6);
2402 v3s16 p = readV3S16(buf);
2403 is.read((char*)buf, 2);
2404 u16 textlen = readU16(buf);
2406 for(u16 i=0; i<textlen; i++)
2408 is.read((char*)buf, 1);
2409 text += (char)buf[0];
2412 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
2416 meta->setText(text);
2418 actionstream<<player->getName()<<" writes \""<<text<<"\" to sign"
2419 <<" at "<<PP(p)<<std::endl;
2421 v3s16 blockpos = getNodeBlockPos(p);
2422 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
2425 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2429 setBlockNotSent(blockpos);
2431 else if(command == TOSERVER_INVENTORY_ACTION)
2433 /*// Ignore inventory changes if in creative mode
2434 if(g_settings->getBool("creative_mode") == true)
2436 infostream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2440 // Strip command and create a stream
2441 std::string datastring((char*)&data[2], datasize-2);
2442 infostream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2443 std::istringstream is(datastring, std::ios_base::binary);
2445 InventoryAction *a = InventoryAction::deSerialize(is);
2448 infostream<<"TOSERVER_INVENTORY_ACTION: "
2449 <<"InventoryAction::deSerialize() returned NULL"
2455 c.current_player = player;
2458 Handle restrictions and special cases of the move action
2460 if(a->getType() == IACTION_MOVE
2461 && g_settings->getBool("creative_mode") == false)
2463 InventoryList *rlist = player->inventory.getList("craftresult");
2465 InventoryList *clist = player->inventory.getList("craft");
2467 InventoryList *mlist = player->inventory.getList("main");
2470 IMoveAction *ma = (IMoveAction*)a;
2473 Disable moving items into craftresult from elsewhere
2475 if(ma->to_inv == "current_player"
2476 && ma->to_list == "craftresult"
2477 && (ma->from_inv != "current_player"
2478 || ma->from_list != "craftresult"))
2480 infostream<<"Ignoring IMoveAction from "
2481 <<ma->from_inv<<":"<<ma->from_list
2482 <<" to "<<ma->to_inv<<":"<<ma->to_list
2483 <<" because dst is craftresult"
2484 <<" and src isn't craftresult"<<std::endl;
2490 Handle crafting (source is craftresult, which is preview)
2492 if(ma->from_inv == "current_player"
2493 && ma->from_list == "craftresult"
2494 && player->craftresult_is_preview)
2497 If the craftresult is placed on itself, crafting takes
2498 place and result is moved into main list
2500 if(ma->to_inv == "current_player"
2501 && ma->to_list == "craftresult")
2503 // Except if main list doesn't have free slots
2504 if(mlist->getFreeSlots() == 0){
2505 infostream<<"Cannot craft: Main list doesn't have"
2506 <<" free slots"<<std::endl;
2511 player->craftresult_is_preview = false;
2512 clist->decrementMaterials(1);
2514 InventoryItem *item1 = rlist->changeItem(0, NULL);
2515 mlist->addItem(item1);
2517 srp->m_inventory_not_sent = true;
2523 Disable action if there are no free slots in
2526 If the item is placed on an item that is not of the
2527 same kind, the existing item will be first moved to
2528 craftresult and immediately moved to the free slot.
2531 Inventory *inv_to = getInventory(&c, ma->to_inv);
2533 InventoryList *list_to = inv_to->getList(ma->to_list);
2535 if(list_to->getFreeSlots() == 0){
2536 infostream<<"Cannot craft: Destination doesn't have"
2537 <<" free slots"<<std::endl;
2541 }while(0); // Allow break
2546 player->craftresult_is_preview = false;
2547 clist->decrementMaterials(1);
2549 /* Print out action */
2550 InventoryItem *item = rlist->getItem(0);
2551 std::string itemstring = "NULL";
2553 itemstring = item->getItemString();
2554 actionstream<<player->getName()<<" crafts "
2555 <<itemstring<<std::endl;
2558 a->apply(&c, this, m_env);
2568 // Disallow moving items in elsewhere than player's inventory
2569 // if not allowed to build
2570 if((getPlayerPrivs(player) & PRIV_INTERACT) == 0
2571 && (ma->from_inv != "current_player"
2572 || ma->to_inv != "current_player"))
2574 infostream<<"Cannot move outside of player's inventory: "
2575 <<"No build privilege"<<std::endl;
2580 // If player is not an admin, check for ownership of src
2581 if(ma->from_inv != "current_player"
2582 && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
2584 Strfnd fn(ma->from_inv);
2585 std::string id0 = fn.next(":");
2586 if(id0 == "nodemeta")
2589 p.X = stoi(fn.next(","));
2590 p.Y = stoi(fn.next(","));
2591 p.Z = stoi(fn.next(","));
2592 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
2593 if(meta->getOwner() != "" &&
2594 meta->getOwner() != player->getName())
2596 infostream<<"Cannot move item: "
2597 "not owner of metadata"
2604 // If player is not an admin, check for ownership of dst
2605 if(ma->to_inv != "current_player"
2606 && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
2608 Strfnd fn(ma->to_inv);
2609 std::string id0 = fn.next(":");
2610 if(id0 == "nodemeta")
2613 p.X = stoi(fn.next(","));
2614 p.Y = stoi(fn.next(","));
2615 p.Z = stoi(fn.next(","));
2616 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
2617 if(meta->getOwner() != "" &&
2618 meta->getOwner() != player->getName())
2620 infostream<<"Cannot move item: "
2621 "not owner of metadata"
2630 Handle restrictions and special cases of the drop action
2632 else if(a->getType() == IACTION_DROP)
2634 IDropAction *da = (IDropAction*)a;
2635 // Disallow dropping items if not allowed to build
2636 if((getPlayerPrivs(player) & PRIV_INTERACT) == 0)
2641 // If player is not an admin, check for ownership
2642 else if (da->from_inv != "current_player"
2643 && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
2645 Strfnd fn(da->from_inv);
2646 std::string id0 = fn.next(":");
2647 if(id0 == "nodemeta")
2650 p.X = stoi(fn.next(","));
2651 p.Y = stoi(fn.next(","));
2652 p.Z = stoi(fn.next(","));
2653 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
2654 if(meta->getOwner() != "" &&
2655 meta->getOwner() != player->getName())
2657 infostream<<"Cannot move item: "
2658 "not owner of metadata"
2668 a->apply(&c, this, m_env);
2672 else if(command == TOSERVER_CHAT_MESSAGE)
2680 std::string datastring((char*)&data[2], datasize-2);
2681 std::istringstream is(datastring, std::ios_base::binary);
2684 is.read((char*)buf, 2);
2685 u16 len = readU16(buf);
2687 std::wstring message;
2688 for(u16 i=0; i<len; i++)
2690 is.read((char*)buf, 2);
2691 message += (wchar_t)readU16(buf);
2694 // Get player name of this client
2695 std::wstring name = narrow_to_wide(player->getName());
2698 bool ate = scriptapi_on_chat_message(m_lua, player->getName(),
2699 wide_to_narrow(message));
2700 // If script ate the message, don't proceed
2704 // Line to send to players
2706 // Whether to send to the player that sent the line
2707 bool send_to_sender = false;
2708 // Whether to send to other players
2709 bool send_to_others = false;
2711 // Local player gets all privileges regardless of
2712 // what's set on their account.
2713 u64 privs = getPlayerPrivs(player);
2716 if(message[0] == L'/')
2718 size_t strip_size = 1;
2719 if (message[1] == L'#') // support old-style commans
2721 message = message.substr(strip_size);
2723 WStrfnd f1(message);
2724 f1.next(L" "); // Skip over /#whatever
2725 std::wstring paramstring = f1.next(L"");
2727 ServerCommandContext *ctx = new ServerCommandContext(
2728 str_split(message, L' '),
2735 std::wstring reply(processServerCommand(ctx));
2736 send_to_sender = ctx->flags & SEND_TO_SENDER;
2737 send_to_others = ctx->flags & SEND_TO_OTHERS;
2739 if (ctx->flags & SEND_NO_PREFIX)
2742 line += L"Server: " + reply;
2749 if(privs & PRIV_SHOUT)
2755 send_to_others = true;
2759 line += L"Server: You are not allowed to shout";
2760 send_to_sender = true;
2767 actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2770 Send the message to clients
2772 for(core::map<u16, RemoteClient*>::Iterator
2773 i = m_clients.getIterator();
2774 i.atEnd() == false; i++)
2776 // Get client and check that it is valid
2777 RemoteClient *client = i.getNode()->getValue();
2778 assert(client->peer_id == i.getNode()->getKey());
2779 if(client->serialization_version == SER_FMT_VER_INVALID)
2783 bool sender_selected = (peer_id == client->peer_id);
2784 if(sender_selected == true && send_to_sender == false)
2786 if(sender_selected == false && send_to_others == false)
2789 SendChatMessage(client->peer_id, line);
2793 else if(command == TOSERVER_DAMAGE)
2795 std::string datastring((char*)&data[2], datasize-2);
2796 std::istringstream is(datastring, std::ios_base::binary);
2797 u8 damage = readU8(is);
2799 if(g_settings->getBool("enable_damage"))
2801 actionstream<<player->getName()<<" damaged by "
2802 <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
2805 HandlePlayerHP(player, damage);
2809 SendPlayerHP(player);
2812 else if(command == TOSERVER_PASSWORD)
2815 [0] u16 TOSERVER_PASSWORD
2816 [2] u8[28] old password
2817 [30] u8[28] new password
2820 if(datasize != 2+PASSWORD_SIZE*2)
2822 /*char password[PASSWORD_SIZE];
2823 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2824 password[i] = data[2+i];
2825 password[PASSWORD_SIZE-1] = 0;*/
2827 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2835 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2837 char c = data[2+PASSWORD_SIZE+i];
2843 infostream<<"Server: Client requests a password change from "
2844 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
2846 std::string playername = player->getName();
2848 if(m_authmanager.exists(playername) == false)
2850 infostream<<"Server: playername not found in authmanager"<<std::endl;
2851 // Wrong old password supplied!!
2852 SendChatMessage(peer_id, L"playername not found in authmanager");
2856 std::string checkpwd = m_authmanager.getPassword(playername);
2858 if(oldpwd != checkpwd)
2860 infostream<<"Server: invalid old password"<<std::endl;
2861 // Wrong old password supplied!!
2862 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
2866 actionstream<<player->getName()<<" changes password"<<std::endl;
2868 m_authmanager.setPassword(playername, newpwd);
2870 infostream<<"Server: password change successful for "<<playername
2872 SendChatMessage(peer_id, L"Password change successful");
2874 else if(command == TOSERVER_PLAYERITEM)
2879 u16 item = readU16(&data[2]);
2880 player->wieldItem(item);
2881 SendWieldedItem(player);
2883 else if(command == TOSERVER_RESPAWN)
2888 srp->m_respawn_active = false;
2890 RespawnPlayer(player);
2892 actionstream<<player->getName()<<" respawns at "
2893 <<PP(player->getPosition()/BS)<<std::endl;
2895 // ActiveObject is added to environment in AsyncRunStep after
2896 // the previous addition has been succesfully removed
2898 else if(command == TOSERVER_INTERACT)
2900 std::string datastring((char*)&data[2], datasize-2);
2901 std::istringstream is(datastring, std::ios_base::binary);
2907 [5] u32 length of the next item
2908 [9] serialized PointedThing
2910 0: start digging (from undersurface) or use
2911 1: stop digging (all parameters ignored)
2912 2: digging completed
2913 3: place block or item (to abovesurface)
2916 u8 action = readU8(is);
2917 u16 item_i = readU16(is);
2918 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
2919 PointedThing pointed;
2920 pointed.deSerialize(tmp_is);
2922 infostream<<"TOSERVER_INTERACT: action="<<(int)action<<", item="<<item_i<<", pointed="<<pointed.dump()<<std::endl;
2924 v3f player_pos = srp->m_last_good_position;
2926 // Update wielded item
2927 srp->wieldItem(item_i);
2929 // Get pointed to node (undefined if not POINTEDTYPE_NODE)
2930 v3s16 p_under = pointed.node_undersurface;
2931 v3s16 p_above = pointed.node_abovesurface;
2933 // Get pointed to object (NULL if not POINTEDTYPE_OBJECT)
2934 ServerActiveObject *pointed_object = NULL;
2935 if(pointed.type == POINTEDTHING_OBJECT)
2937 pointed_object = m_env->getActiveObject(pointed.object_id);
2938 if(pointed_object == NULL)
2940 infostream<<"TOSERVER_INTERACT: "
2941 "pointed object is NULL"<<std::endl;
2948 Check that target is reasonably close
2949 (only when digging or placing things)
2951 if(action == 0 || action == 2 || action == 3)
2953 v3f pointed_pos = player_pos;
2954 if(pointed.type == POINTEDTHING_NODE)
2956 pointed_pos = intToFloat(p_under, BS);
2958 else if(pointed.type == POINTEDTHING_OBJECT)
2960 pointed_pos = pointed_object->getBasePosition();
2963 float d = player_pos.getDistanceFrom(pointed_pos);
2964 float max_d = BS * 10; // Just some large enough value
2966 actionstream<<"Player "<<player->getName()
2967 <<" tried to access "<<pointed.dump()
2969 <<"d="<<d<<", max_d="<<max_d
2970 <<". ignoring."<<std::endl;
2971 // Re-send block to revert change on client-side
2972 RemoteClient *client = getClient(peer_id);
2973 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos, BS));
2974 client->SetBlockNotSent(blockpos);
2981 Make sure the player is allowed to do it
2983 bool build_priv = (getPlayerPrivs(player) & PRIV_INTERACT) != 0;
2986 infostream<<"Ignoring interaction from player "<<player->getName()
2987 <<" because privileges are "<<getPlayerPrivs(player)
2989 // NOTE: no return; here, fall through
2993 0: start digging or punch object
2997 if(pointed.type == POINTEDTHING_NODE)
3000 NOTE: This can be used in the future to check if
3001 somebody is cheating, by checking the timing.
3003 bool cannot_punch_node = !build_priv;
3005 MapNode n(CONTENT_IGNORE);
3009 n = m_env->getMap().getNode(p_under);
3011 catch(InvalidPositionException &e)
3013 infostream<<"Server: Not punching: Node not found."
3014 <<" Adding block to emerge queue."
3016 m_emerge_queue.addBlock(peer_id,
3017 getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
3018 cannot_punch_node = true;
3021 if(cannot_punch_node)
3027 scriptapi_environment_on_punchnode(m_lua, p_under, n, srp);
3029 else if(pointed.type == POINTEDTHING_OBJECT)
3034 // Skip if object has been removed
3035 if(pointed_object->m_removed)
3038 actionstream<<player->getName()<<" punches object "
3039 <<pointed.object_id<<std::endl;
3042 pointed_object->punch(srp);
3050 else if(action == 1)
3055 2: Digging completed
3057 else if(action == 2)
3059 // Only complete digging of nodes
3060 if(pointed.type != POINTEDTHING_NODE)
3063 // Mandatory parameter; actually used for nothing
3064 core::map<v3s16, MapBlock*> modified_blocks;
3066 content_t material = CONTENT_IGNORE;
3067 u8 mineral = MINERAL_NONE;
3069 bool cannot_remove_node = !build_priv;
3071 MapNode n(CONTENT_IGNORE);
3074 n = m_env->getMap().getNode(p_under);
3076 mineral = n.getMineral(m_nodedef);
3077 // Get material at position
3078 material = n.getContent();
3079 // If not yet cancelled
3080 if(cannot_remove_node == false)
3082 // If it's not diggable, do nothing
3083 if(m_nodedef->get(material).diggable == false)
3085 infostream<<"Server: Not finishing digging: "
3086 <<"Node not diggable"
3088 cannot_remove_node = true;
3091 // If not yet cancelled
3092 if(cannot_remove_node == false)
3094 // Get node metadata
3095 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p_under);
3096 if(meta && meta->nodeRemovalDisabled() == true)
3098 infostream<<"Server: Not finishing digging: "
3099 <<"Node metadata disables removal"
3101 cannot_remove_node = true;
3105 catch(InvalidPositionException &e)
3107 infostream<<"Server: Not finishing digging: Node not found."
3108 <<" Adding block to emerge queue."
3110 m_emerge_queue.addBlock(peer_id,
3111 getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
3112 cannot_remove_node = true;
3116 If node can't be removed, set block to be re-sent to
3119 if(cannot_remove_node)
3121 infostream<<"Server: Not finishing digging."<<std::endl;
3123 // Client probably has wrong data.
3124 // Set block not sent, so that client will get
3126 infostream<<"Client "<<peer_id<<" tried to dig "
3127 <<"node; but node cannot be removed."
3128 <<" setting MapBlock not sent."<<std::endl;
3129 RemoteClient *client = getClient(peer_id);
3130 v3s16 blockpos = getNodeBlockPos(p_under);
3131 client->SetBlockNotSent(blockpos);
3136 actionstream<<player->getName()<<" digs "<<PP(p_under)
3137 <<", gets material "<<(int)material<<", mineral "
3138 <<(int)mineral<<std::endl;
3141 Send the removal to all close-by players.
3142 - If other player is close, send REMOVENODE
3143 - Otherwise set blocks not sent
3145 core::list<u16> far_players;
3146 sendRemoveNode(p_under, peer_id, &far_players, 30);
3149 Update and send inventory
3152 if(g_settings->getBool("creative_mode") == false)
3157 InventoryList *mlist = player->inventory.getList("main");
3160 InventoryItem *item = mlist->getItem(item_i);
3161 if(item && (std::string)item->getName() == "ToolItem")
3163 ToolItem *titem = (ToolItem*)item;
3164 std::string toolname = titem->getToolName();
3166 // Get digging properties for material and tool
3167 ToolDiggingProperties tp =
3168 m_toolmgr->getDiggingProperties(toolname);
3169 DiggingProperties prop =
3170 getDiggingProperties(material, &tp, m_nodedef);
3172 if(prop.diggable == false)
3174 infostream<<"Server: WARNING: Player digged"
3175 <<" with impossible material + tool"
3176 <<" combination"<<std::endl;
3179 bool weared_out = titem->addWear(prop.wear);
3183 mlist->deleteItem(item_i);
3186 srp->m_inventory_not_sent = true;
3191 Add dug item to inventory
3194 InventoryItem *item = NULL;
3196 if(mineral != MINERAL_NONE)
3197 item = getDiggedMineralItem(mineral, this);
3202 const std::string &dug_s = m_nodedef->get(material).dug_item;
3205 std::istringstream is(dug_s, std::ios::binary);
3206 item = InventoryItem::deSerialize(is, this);
3212 // Add a item to inventory
3213 player->inventory.addItem("main", item);
3214 srp->m_inventory_not_sent = true;
3219 if(mineral != MINERAL_NONE)
3220 item = getDiggedMineralItem(mineral, this);
3225 const std::string &extra_dug_s = m_nodedef->get(material).extra_dug_item;
3226 s32 extra_rarity = m_nodedef->get(material).extra_dug_item_rarity;
3227 if(extra_dug_s != "" && extra_rarity != 0
3228 && myrand() % extra_rarity == 0)
3230 std::istringstream is(extra_dug_s, std::ios::binary);
3231 item = InventoryItem::deSerialize(is, this);
3237 // Add a item to inventory
3238 player->inventory.addItem("main", item);
3239 srp->m_inventory_not_sent = true;
3245 (this takes some time so it is done after the quick stuff)
3248 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
3250 m_env->getMap().removeNodeAndUpdate(p_under, modified_blocks);
3253 Set blocks not sent to far players
3255 for(core::list<u16>::Iterator
3256 i = far_players.begin();
3257 i != far_players.end(); i++)
3260 RemoteClient *client = getClient(peer_id);
3263 client->SetBlocksNotSent(modified_blocks);
3269 scriptapi_environment_on_dignode(m_lua, p_under, n, srp);
3273 3: place block or right-click object
3275 else if(action == 3)
3277 if(pointed.type == POINTEDTHING_NODE)
3279 InventoryList *ilist = player->inventory.getList("main");
3284 InventoryItem *item = ilist->getItem(item_i);
3286 // If there is no item, it is not possible to add it anywhere
3291 Handle material items
3293 if(std::string("MaterialItem") == item->getName())
3295 bool cannot_place_node = !build_priv;
3298 // Don't add a node if this is not a free space
3299 MapNode n2 = m_env->getMap().getNode(p_above);
3300 if(m_nodedef->get(n2).buildable_to == false)
3302 infostream<<"Client "<<peer_id<<" tried to place"
3303 <<" node in invalid position."<<std::endl;
3304 cannot_place_node = true;
3307 catch(InvalidPositionException &e)
3309 infostream<<"Server: Ignoring ADDNODE: Node not found"
3310 <<" Adding block to emerge queue."
3312 m_emerge_queue.addBlock(peer_id,
3313 getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
3314 cannot_place_node = true;
3317 if(cannot_place_node)
3319 // Client probably has wrong data.
3320 // Set block not sent, so that client will get
3322 RemoteClient *client = getClient(peer_id);
3323 v3s16 blockpos = getNodeBlockPos(p_above);
3324 client->SetBlockNotSent(blockpos);
3328 // Reset build time counter
3329 getClient(peer_id)->m_time_from_building = 0.0;
3332 MaterialItem *mitem = (MaterialItem*)item;
3334 n.setContent(mitem->getMaterial());
3336 actionstream<<player->getName()<<" places material "
3337 <<(int)mitem->getMaterial()
3338 <<" at "<<PP(p_under)<<std::endl;
3340 // Calculate direction for wall mounted stuff
3341 if(m_nodedef->get(n).wall_mounted)
3342 n.param2 = packDir(p_under - p_above);
3344 // Calculate the direction for furnaces and chests and stuff
3345 if(m_nodedef->get(n).param_type == CPT_FACEDIR_SIMPLE)
3347 v3f playerpos = player->getPosition();
3348 v3f blockpos = intToFloat(p_above, BS) - playerpos;
3349 blockpos = blockpos.normalize();
3351 if (fabs(blockpos.X) > fabs(blockpos.Z)) {
3365 Send to all close-by players
3367 core::list<u16> far_players;
3368 sendAddNode(p_above, n, 0, &far_players, 30);
3373 InventoryList *ilist = player->inventory.getList("main");
3374 if(g_settings->getBool("creative_mode") == false && ilist)
3376 // Remove from inventory and send inventory
3377 if(mitem->getCount() <= 1)
3378 ilist->deleteItem(item_i);
3381 srp->m_inventory_not_sent = true;
3387 This takes some time so it is done after the quick stuff
3389 core::map<v3s16, MapBlock*> modified_blocks;
3391 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
3393 std::string p_name = std::string(player->getName());
3394 m_env->getMap().addNodeAndUpdate(p_above, n, modified_blocks, p_name);
3397 Set blocks not sent to far players
3399 for(core::list<u16>::Iterator
3400 i = far_players.begin();
3401 i != far_players.end(); i++)
3404 RemoteClient *client = getClient(peer_id);
3407 client->SetBlocksNotSent(modified_blocks);
3413 scriptapi_environment_on_placenode(m_lua, p_above, n, srp);
3416 Calculate special events
3419 /*if(n.d == LEGN(m_nodedef, "CONTENT_MESE"))
3422 for(s16 z=-1; z<=1; z++)
3423 for(s16 y=-1; y<=1; y++)
3424 for(s16 x=-1; x<=1; x++)
3431 Place other item (not a block)
3437 infostream<<"Not allowing player to place item: "
3438 "no build privileges"<<std::endl;
3442 // Calculate a position for it
3443 v3f pos = player_pos;
3444 if(pointed.type == POINTEDTHING_NOTHING)
3446 infostream<<"Not allowing player to place item: "
3447 "pointing to nothing"<<std::endl;
3450 else if(pointed.type == POINTEDTHING_NODE)
3452 pos = intToFloat(p_above, BS);
3454 else if(pointed.type == POINTEDTHING_OBJECT)
3456 pos = pointed_object->getBasePosition();
3459 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
3460 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
3464 //pos.Y -= BS*0.25; // let it drop a bit
3467 Check that the block is loaded so that the item
3468 can properly be added to the static list too
3470 v3s16 blockpos = getNodeBlockPos(floatToInt(pos, BS));
3471 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3474 infostream<<"Error while placing item: "
3475 "block not found"<<std::endl;
3479 actionstream<<player->getName()<<" places "<<item->getName()
3480 <<" at "<<PP(pos)<<std::endl;
3485 bool remove = item->dropOrPlace(m_env, srp, pos, true, -1);
3486 if(remove && g_settings->getBool("creative_mode") == false)
3488 InventoryList *ilist = player->inventory.getList("main");
3490 // Remove from inventory and send inventory
3491 ilist->deleteItem(item_i);
3492 srp->m_inventory_not_sent = true;
3497 else if(pointed.type == POINTEDTHING_OBJECT)
3499 // Right click object
3504 // Skip if object has been removed
3505 if(pointed_object->m_removed)
3508 actionstream<<player->getName()<<" right-clicks object "
3509 <<pointed.object_id<<std::endl;
3512 pointed_object->rightClick(srp);
3520 else if(action == 4)
3522 InventoryList *ilist = player->inventory.getList("main");
3527 InventoryItem *item = ilist->getItem(item_i);
3529 // If there is no item, it is not possible to add it anywhere
3533 // Requires build privs
3536 infostream<<"Not allowing player to use item: "
3537 "no build privileges"<<std::endl;
3541 actionstream<<player->getName()<<" uses "<<item->getName()
3542 <<", pointing at "<<pointed.dump()<<std::endl;
3544 bool remove = item->use(m_env, srp, pointed);
3546 if(remove && g_settings->getBool("creative_mode") == false)
3548 InventoryList *ilist = player->inventory.getList("main");
3550 // Remove from inventory and send inventory
3551 ilist->deleteItem(item_i);
3552 srp->m_inventory_not_sent = true;
3559 Catch invalid actions
3563 infostream<<"WARNING: Server: Invalid action "
3564 <<action<<std::endl;
3567 // Complete add_to_inventory_later
3568 srp->completeAddToInventoryLater(item_i);
3572 infostream<<"Server::ProcessData(): Ignoring "
3573 "unknown command "<<command<<std::endl;
3577 catch(SendFailedException &e)
3579 errorstream<<"Server::ProcessData(): SendFailedException: "
3585 void Server::onMapEditEvent(MapEditEvent *event)
3587 //infostream<<"Server::onMapEditEvent()"<<std::endl;
3588 if(m_ignore_map_edit_events)
3590 MapEditEvent *e = event->clone();
3591 m_unsent_map_edit_queue.push_back(e);
3594 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3596 if(id == "current_player")
3598 assert(c->current_player);
3599 return &(c->current_player->inventory);
3603 std::string id0 = fn.next(":");
3605 if(id0 == "nodemeta")
3608 p.X = stoi(fn.next(","));
3609 p.Y = stoi(fn.next(","));
3610 p.Z = stoi(fn.next(","));
3611 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3613 return meta->getInventory();
3614 infostream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3615 <<"no metadata found"<<std::endl;
3619 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3622 void Server::inventoryModified(InventoryContext *c, std::string id)
3624 if(id == "current_player")
3626 assert(c->current_player);
3627 ServerRemotePlayer *srp =
3628 static_cast<ServerRemotePlayer*>(c->current_player);
3629 srp->m_inventory_not_sent = true;
3634 std::string id0 = fn.next(":");
3636 if(id0 == "nodemeta")
3639 p.X = stoi(fn.next(","));
3640 p.Y = stoi(fn.next(","));
3641 p.Z = stoi(fn.next(","));
3642 v3s16 blockpos = getNodeBlockPos(p);
3644 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3646 meta->inventoryModified();
3648 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3650 block->raiseModified(MOD_STATE_WRITE_NEEDED);
3652 setBlockNotSent(blockpos);
3657 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3660 core::list<PlayerInfo> Server::getPlayerInfo()
3662 DSTACK(__FUNCTION_NAME);
3663 JMutexAutoLock envlock(m_env_mutex);
3664 JMutexAutoLock conlock(m_con_mutex);
3666 core::list<PlayerInfo> list;
3668 core::list<Player*> players = m_env->getPlayers();
3670 core::list<Player*>::Iterator i;
3671 for(i = players.begin();
3672 i != players.end(); i++)
3676 Player *player = *i;
3679 // Copy info from connection to info struct
3680 info.id = player->peer_id;
3681 info.address = m_con.GetPeerAddress(player->peer_id);
3682 info.avg_rtt = m_con.GetPeerAvgRTT(player->peer_id);
3684 catch(con::PeerNotFoundException &e)
3686 // Set dummy peer info
3688 info.address = Address(0,0,0,0,0);
3692 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3693 info.position = player->getPosition();
3695 list.push_back(info);
3702 void Server::peerAdded(con::Peer *peer)
3704 DSTACK(__FUNCTION_NAME);
3705 infostream<<"Server::peerAdded(): peer->id="
3706 <<peer->id<<std::endl;
3709 c.type = PEER_ADDED;
3710 c.peer_id = peer->id;
3712 m_peer_change_queue.push_back(c);
3715 void Server::deletingPeer(con::Peer *peer, bool timeout)
3717 DSTACK(__FUNCTION_NAME);
3718 infostream<<"Server::deletingPeer(): peer->id="
3719 <<peer->id<<", timeout="<<timeout<<std::endl;
3722 c.type = PEER_REMOVED;
3723 c.peer_id = peer->id;
3724 c.timeout = timeout;
3725 m_peer_change_queue.push_back(c);
3732 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3734 DSTACK(__FUNCTION_NAME);
3735 std::ostringstream os(std::ios_base::binary);
3737 writeU16(os, TOCLIENT_HP);
3741 std::string s = os.str();
3742 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3744 con.Send(peer_id, 0, data, true);
3747 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3748 const std::wstring &reason)
3750 DSTACK(__FUNCTION_NAME);
3751 std::ostringstream os(std::ios_base::binary);
3753 writeU16(os, TOCLIENT_ACCESS_DENIED);
3754 os<<serializeWideString(reason);
3757 std::string s = os.str();
3758 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3760 con.Send(peer_id, 0, data, true);
3763 void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
3764 bool set_camera_point_target, v3f camera_point_target)
3766 DSTACK(__FUNCTION_NAME);
3767 std::ostringstream os(std::ios_base::binary);
3769 writeU16(os, TOCLIENT_DEATHSCREEN);
3770 writeU8(os, set_camera_point_target);
3771 writeV3F1000(os, camera_point_target);
3774 std::string s = os.str();
3775 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3777 con.Send(peer_id, 0, data, true);
3780 void Server::SendToolDef(con::Connection &con, u16 peer_id,
3781 IToolDefManager *tooldef)
3783 DSTACK(__FUNCTION_NAME);
3784 std::ostringstream os(std::ios_base::binary);
3788 u32 length of the next item
3789 serialized ToolDefManager
3791 writeU16(os, TOCLIENT_TOOLDEF);
3792 std::ostringstream tmp_os(std::ios::binary);
3793 tooldef->serialize(tmp_os);
3794 os<<serializeLongString(tmp_os.str());
3797 std::string s = os.str();
3798 infostream<<"Server::SendToolDef(): Sending tool definitions: size="
3799 <<s.size()<<std::endl;
3800 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3802 con.Send(peer_id, 0, data, true);
3805 void Server::SendNodeDef(con::Connection &con, u16 peer_id,
3806 INodeDefManager *nodedef)
3808 DSTACK(__FUNCTION_NAME);
3809 std::ostringstream os(std::ios_base::binary);
3813 u32 length of the next item
3814 serialized NodeDefManager
3816 writeU16(os, TOCLIENT_NODEDEF);
3817 std::ostringstream tmp_os(std::ios::binary);
3818 nodedef->serialize(tmp_os);
3819 os<<serializeLongString(tmp_os.str());
3822 std::string s = os.str();
3823 infostream<<"Server::SendNodeDef(): Sending node definitions: size="
3824 <<s.size()<<std::endl;
3825 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3827 con.Send(peer_id, 0, data, true);
3830 void Server::SendCraftItemDef(con::Connection &con, u16 peer_id,
3831 ICraftItemDefManager *craftitemdef)
3833 DSTACK(__FUNCTION_NAME);
3834 std::ostringstream os(std::ios_base::binary);
3838 u32 length of the next item
3839 serialized CraftItemDefManager
3841 writeU16(os, TOCLIENT_CRAFTITEMDEF);
3842 std::ostringstream tmp_os(std::ios::binary);
3843 craftitemdef->serialize(tmp_os);
3844 os<<serializeLongString(tmp_os.str());
3847 std::string s = os.str();
3848 infostream<<"Server::SendCraftItemDef(): Sending craft item definitions: size="
3849 <<s.size()<<std::endl;
3850 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3852 con.Send(peer_id, 0, data, true);
3856 Non-static send methods
3859 void Server::SendInventory(u16 peer_id)
3861 DSTACK(__FUNCTION_NAME);
3863 ServerRemotePlayer* player =
3864 static_cast<ServerRemotePlayer*>(m_env->getPlayer(peer_id));
3867 player->m_inventory_not_sent = false;
3873 std::ostringstream os;
3874 //os.imbue(std::locale("C"));
3876 player->inventory.serialize(os);
3878 std::string s = os.str();
3880 SharedBuffer<u8> data(s.size()+2);
3881 writeU16(&data[0], TOCLIENT_INVENTORY);
3882 memcpy(&data[2], s.c_str(), s.size());
3885 m_con.Send(peer_id, 0, data, true);
3888 std::string getWieldedItemString(const Player *player)
3890 const InventoryItem *item = player->getWieldItem();
3892 return std::string("");
3893 std::ostringstream os(std::ios_base::binary);
3894 item->serialize(os);
3898 void Server::SendWieldedItem(const Player* player)
3900 DSTACK(__FUNCTION_NAME);
3904 std::ostringstream os(std::ios_base::binary);
3906 writeU16(os, TOCLIENT_PLAYERITEM);
3908 writeU16(os, player->peer_id);
3909 os<<serializeString(getWieldedItemString(player));
3912 std::string s = os.str();
3913 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3915 m_con.SendToAll(0, data, true);
3918 void Server::SendPlayerItems()
3920 DSTACK(__FUNCTION_NAME);
3922 std::ostringstream os(std::ios_base::binary);
3923 core::list<Player *> players = m_env->getPlayers(true);
3925 writeU16(os, TOCLIENT_PLAYERITEM);
3926 writeU16(os, players.size());
3927 core::list<Player *>::Iterator i;
3928 for(i = players.begin(); i != players.end(); ++i)
3931 writeU16(os, p->peer_id);
3932 os<<serializeString(getWieldedItemString(p));
3936 std::string s = os.str();
3937 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3939 m_con.SendToAll(0, data, true);
3942 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3944 DSTACK(__FUNCTION_NAME);
3946 std::ostringstream os(std::ios_base::binary);
3950 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3951 os.write((char*)buf, 2);
3954 writeU16(buf, message.size());
3955 os.write((char*)buf, 2);
3958 for(u32 i=0; i<message.size(); i++)
3962 os.write((char*)buf, 2);
3966 std::string s = os.str();
3967 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3969 m_con.Send(peer_id, 0, data, true);
3972 void Server::BroadcastChatMessage(const std::wstring &message)
3974 for(core::map<u16, RemoteClient*>::Iterator
3975 i = m_clients.getIterator();
3976 i.atEnd() == false; i++)
3978 // Get client and check that it is valid
3979 RemoteClient *client = i.getNode()->getValue();
3980 assert(client->peer_id == i.getNode()->getKey());
3981 if(client->serialization_version == SER_FMT_VER_INVALID)
3984 SendChatMessage(client->peer_id, message);
3988 void Server::SendPlayerHP(Player *player)
3990 SendHP(m_con, player->peer_id, player->hp);
3991 static_cast<ServerRemotePlayer*>(player)->m_hp_not_sent = false;
3994 void Server::SendMovePlayer(Player *player)
3996 DSTACK(__FUNCTION_NAME);
3997 std::ostringstream os(std::ios_base::binary);
3999 writeU16(os, TOCLIENT_MOVE_PLAYER);
4000 writeV3F1000(os, player->getPosition());
4001 writeF1000(os, player->getPitch());
4002 writeF1000(os, player->getYaw());
4005 v3f pos = player->getPosition();
4006 f32 pitch = player->getPitch();
4007 f32 yaw = player->getYaw();
4008 infostream<<"Server sending TOCLIENT_MOVE_PLAYER"
4009 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
4016 std::string s = os.str();
4017 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4019 m_con.Send(player->peer_id, 0, data, true);
4022 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
4023 core::list<u16> *far_players, float far_d_nodes)
4025 float maxd = far_d_nodes*BS;
4026 v3f p_f = intToFloat(p, BS);
4030 SharedBuffer<u8> reply(replysize);
4031 writeU16(&reply[0], TOCLIENT_REMOVENODE);
4032 writeS16(&reply[2], p.X);
4033 writeS16(&reply[4], p.Y);
4034 writeS16(&reply[6], p.Z);
4036 for(core::map<u16, RemoteClient*>::Iterator
4037 i = m_clients.getIterator();
4038 i.atEnd() == false; i++)
4040 // Get client and check that it is valid
4041 RemoteClient *client = i.getNode()->getValue();
4042 assert(client->peer_id == i.getNode()->getKey());
4043 if(client->serialization_version == SER_FMT_VER_INVALID)
4046 // Don't send if it's the same one
4047 if(client->peer_id == ignore_id)
4053 Player *player = m_env->getPlayer(client->peer_id);
4056 // If player is far away, only set modified blocks not sent
4057 v3f player_pos = player->getPosition();
4058 if(player_pos.getDistanceFrom(p_f) > maxd)
4060 far_players->push_back(client->peer_id);
4067 m_con.Send(client->peer_id, 0, reply, true);
4071 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
4072 core::list<u16> *far_players, float far_d_nodes)
4074 float maxd = far_d_nodes*BS;
4075 v3f p_f = intToFloat(p, BS);
4077 for(core::map<u16, RemoteClient*>::Iterator
4078 i = m_clients.getIterator();
4079 i.atEnd() == false; i++)
4081 // Get client and check that it is valid
4082 RemoteClient *client = i.getNode()->getValue();
4083 assert(client->peer_id == i.getNode()->getKey());
4084 if(client->serialization_version == SER_FMT_VER_INVALID)
4087 // Don't send if it's the same one
4088 if(client->peer_id == ignore_id)
4094 Player *player = m_env->getPlayer(client->peer_id);
4097 // If player is far away, only set modified blocks not sent
4098 v3f player_pos = player->getPosition();
4099 if(player_pos.getDistanceFrom(p_f) > maxd)
4101 far_players->push_back(client->peer_id);
4108 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
4109 SharedBuffer<u8> reply(replysize);
4110 writeU16(&reply[0], TOCLIENT_ADDNODE);
4111 writeS16(&reply[2], p.X);
4112 writeS16(&reply[4], p.Y);
4113 writeS16(&reply[6], p.Z);
4114 n.serialize(&reply[8], client->serialization_version);
4117 m_con.Send(client->peer_id, 0, reply, true);
4121 void Server::setBlockNotSent(v3s16 p)
4123 for(core::map<u16, RemoteClient*>::Iterator
4124 i = m_clients.getIterator();
4125 i.atEnd()==false; i++)
4127 RemoteClient *client = i.getNode()->getValue();
4128 client->SetBlockNotSent(p);
4132 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
4134 DSTACK(__FUNCTION_NAME);
4136 v3s16 p = block->getPos();
4140 bool completely_air = true;
4141 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4142 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4143 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4145 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
4147 completely_air = false;
4148 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
4153 infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
4155 infostream<<"[completely air] ";
4156 infostream<<std::endl;
4160 Create a packet with the block in the right format
4163 std::ostringstream os(std::ios_base::binary);
4164 block->serialize(os, ver);
4165 std::string s = os.str();
4166 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
4168 u32 replysize = 8 + blockdata.getSize();
4169 SharedBuffer<u8> reply(replysize);
4170 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
4171 writeS16(&reply[2], p.X);
4172 writeS16(&reply[4], p.Y);
4173 writeS16(&reply[6], p.Z);
4174 memcpy(&reply[8], *blockdata, blockdata.getSize());
4176 /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4177 <<": \tpacket size: "<<replysize<<std::endl;*/
4182 m_con.Send(peer_id, 1, reply, true);
4185 void Server::SendBlocks(float dtime)
4187 DSTACK(__FUNCTION_NAME);
4189 JMutexAutoLock envlock(m_env_mutex);
4190 JMutexAutoLock conlock(m_con_mutex);
4192 //TimeTaker timer("Server::SendBlocks");
4194 core::array<PrioritySortedBlockTransfer> queue;
4196 s32 total_sending = 0;
4199 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
4201 for(core::map<u16, RemoteClient*>::Iterator
4202 i = m_clients.getIterator();
4203 i.atEnd() == false; i++)
4205 RemoteClient *client = i.getNode()->getValue();
4206 assert(client->peer_id == i.getNode()->getKey());
4208 // If definitions and textures have not been sent, don't
4209 // send MapBlocks either
4210 if(!client->definitions_sent)
4213 total_sending += client->SendingCount();
4215 if(client->serialization_version == SER_FMT_VER_INVALID)
4218 client->GetNextBlocks(this, dtime, queue);
4223 // Lowest priority number comes first.
4224 // Lowest is most important.
4227 for(u32 i=0; i<queue.size(); i++)
4229 //TODO: Calculate limit dynamically
4230 if(total_sending >= g_settings->getS32
4231 ("max_simultaneous_block_sends_server_total"))
4234 PrioritySortedBlockTransfer q = queue[i];
4236 MapBlock *block = NULL;
4239 block = m_env->getMap().getBlockNoCreate(q.pos);
4241 catch(InvalidPositionException &e)
4246 RemoteClient *client = getClient(q.peer_id);
4248 SendBlockNoLock(q.peer_id, block, client->serialization_version);
4250 client->SentBlock(q.pos);
4256 struct SendableTexture
4262 SendableTexture(const std::string &name_="", const std::string path_="",
4263 const std::string &data_=""):
4270 void Server::SendTextures(u16 peer_id)
4272 DSTACK(__FUNCTION_NAME);
4274 infostream<<"Server::SendTextures(): Sending textures to client"<<std::endl;
4278 // Put 5kB in one bunch (this is not accurate)
4279 u32 bytes_per_bunch = 5000;
4281 core::array< core::list<SendableTexture> > texture_bunches;
4282 texture_bunches.push_back(core::list<SendableTexture>());
4284 u32 texture_size_bunch_total = 0;
4285 core::list<ModSpec> mods = getMods(m_modspaths);
4286 for(core::list<ModSpec>::Iterator i = mods.begin();
4287 i != mods.end(); i++){
4289 std::string texturepath = mod.path + DIR_DELIM + "textures";
4290 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(texturepath);
4291 for(u32 j=0; j<dirlist.size(); j++){
4292 if(dirlist[j].dir) // Ignode dirs
4294 std::string tname = dirlist[j].name;
4295 std::string tpath = texturepath + DIR_DELIM + tname;
4297 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
4298 if(fis.good() == false){
4299 errorstream<<"Server::SendTextures(): Could not open \""
4300 <<tname<<"\" for reading"<<std::endl;
4303 std::ostringstream tmp_os(std::ios_base::binary);
4307 fis.read(buf, 1024);
4308 std::streamsize len = fis.gcount();
4309 tmp_os.write(buf, len);
4310 texture_size_bunch_total += len;
4319 errorstream<<"Server::SendTextures(): Failed to read \""
4320 <<tname<<"\""<<std::endl;
4323 /*infostream<<"Server::SendTextures(): Loaded \""
4324 <<tname<<"\""<<std::endl;*/
4326 texture_bunches[texture_bunches.size()-1].push_back(
4327 SendableTexture(tname, tpath, tmp_os.str()));
4329 // Start next bunch if got enough data
4330 if(texture_size_bunch_total >= bytes_per_bunch){
4331 texture_bunches.push_back(core::list<SendableTexture>());
4332 texture_size_bunch_total = 0;
4337 /* Create and send packets */
4339 u32 num_bunches = texture_bunches.size();
4340 for(u32 i=0; i<num_bunches; i++)
4344 u16 total number of texture bunches
4345 u16 index of this bunch
4346 u32 number of textures in this bunch
4354 std::ostringstream os(std::ios_base::binary);
4356 writeU16(os, TOCLIENT_TEXTURES);
4357 writeU16(os, num_bunches);
4359 writeU32(os, texture_bunches[i].size());
4361 for(core::list<SendableTexture>::Iterator
4362 j = texture_bunches[i].begin();
4363 j != texture_bunches[i].end(); j++){
4364 os<<serializeString(j->name);
4365 os<<serializeLongString(j->data);
4369 std::string s = os.str();
4370 infostream<<"Server::SendTextures(): bunch "<<i<<"/"<<num_bunches
4371 <<" textures="<<texture_bunches[i].size()
4372 <<" size=" <<s.size()<<std::endl;
4373 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4375 m_con.Send(peer_id, 0, data, true);
4383 void Server::HandlePlayerHP(Player *player, s16 damage)
4385 ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
4387 if(srp->m_respawn_active)
4393 if(player->hp > damage)
4395 player->hp -= damage;
4396 SendPlayerHP(player);
4400 infostream<<"Server::HandlePlayerHP(): Player "
4401 <<player->getName()<<" dies"<<std::endl;
4405 //TODO: Throw items around
4407 // Handle players that are not connected
4408 if(player->peer_id == PEER_ID_INEXISTENT){
4409 RespawnPlayer(player);
4413 SendPlayerHP(player);
4415 RemoteClient *client = getClient(player->peer_id);
4416 if(client->net_proto_version >= 3)
4418 SendDeathscreen(m_con, player->peer_id, false, v3f(0,0,0));
4419 srp->m_removed = true;
4420 srp->m_respawn_active = true;
4424 RespawnPlayer(player);
4428 void Server::RespawnPlayer(Player *player)
4431 ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
4432 bool repositioned = scriptapi_on_respawnplayer(m_lua, srp);
4434 v3f pos = findSpawnPos(m_env->getServerMap());
4435 player->setPosition(pos);
4436 srp->m_last_good_position = pos;
4437 srp->m_last_good_position_age = 0;
4439 SendMovePlayer(player);
4440 SendPlayerHP(player);
4443 void Server::UpdateCrafting(u16 peer_id)
4445 DSTACK(__FUNCTION_NAME);
4447 Player* player = m_env->getPlayer(peer_id);
4449 ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
4451 // No crafting in creative mode
4452 if(g_settings->getBool("creative_mode"))
4455 // Get the InventoryLists of the player in which we will operate
4456 InventoryList *clist = player->inventory.getList("craft");
4458 InventoryList *rlist = player->inventory.getList("craftresult");
4460 InventoryList *mlist = player->inventory.getList("main");
4463 // If the result list is not a preview and is not empty, try to
4464 // throw the item into main list
4465 if(!player->craftresult_is_preview && rlist->getUsedSlots() != 0)
4467 // Grab item out of craftresult
4468 InventoryItem *item = rlist->changeItem(0, NULL);
4469 // Try to put in main
4470 InventoryItem *leftover = mlist->addItem(item);
4471 // If there are leftovers, put them back to craftresult and
4473 delete rlist->addItem(leftover);
4474 // Inventory was modified
4475 srp->m_inventory_not_sent = true;
4478 // If result list is empty, we will make it preview what would be
4480 if(rlist->getUsedSlots() == 0)
4481 player->craftresult_is_preview = true;
4483 // If it is a preview, clear the possible old preview in it
4484 if(player->craftresult_is_preview)
4485 rlist->clearItems();
4487 // If it is a preview, find out what is the crafting result
4489 if(player->craftresult_is_preview)
4491 // Mangle crafting grid to an another format
4492 std::vector<InventoryItem*> items;
4493 for(u16 i=0; i<9; i++){
4494 if(clist->getItem(i) == NULL)
4495 items.push_back(NULL);
4497 items.push_back(clist->getItem(i)->clone());
4499 CraftPointerInput cpi(3, items);
4501 // Find out what is crafted and add it to result item slot
4502 InventoryItem *result = m_craftdef->getCraftResult(cpi, this);
4504 rlist->addItem(result);
4508 RemoteClient* Server::getClient(u16 peer_id)
4510 DSTACK(__FUNCTION_NAME);
4511 //JMutexAutoLock lock(m_con_mutex);
4512 core::map<u16, RemoteClient*>::Node *n;
4513 n = m_clients.find(peer_id);
4514 // A client should exist for all peers
4516 return n->getValue();
4519 std::wstring Server::getStatusString()
4521 std::wostringstream os(std::ios_base::binary);
4524 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4526 os<<L", uptime="<<m_uptime.get();
4527 // Information about clients
4529 for(core::map<u16, RemoteClient*>::Iterator
4530 i = m_clients.getIterator();
4531 i.atEnd() == false; i++)
4533 // Get client and check that it is valid
4534 RemoteClient *client = i.getNode()->getValue();
4535 assert(client->peer_id == i.getNode()->getKey());
4536 if(client->serialization_version == SER_FMT_VER_INVALID)
4539 Player *player = m_env->getPlayer(client->peer_id);
4540 // Get name of player
4541 std::wstring name = L"unknown";
4543 name = narrow_to_wide(player->getName());
4544 // Add name to information string
4548 if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
4549 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4550 if(g_settings->get("motd") != "")
4551 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4555 void Server::setPlayerPassword(const std::string &name, const std::wstring &password)
4557 // Add player to auth manager
4558 if(m_authmanager.exists(name) == false)
4560 infostream<<"Server: adding player "<<name
4561 <<" to auth manager"<<std::endl;
4562 m_authmanager.add(name);
4563 m_authmanager.setPrivs(name,
4564 stringToPrivs(g_settings->get("default_privs")));
4566 // Change password and save
4567 m_authmanager.setPassword(name, translatePassword(name, password));
4568 m_authmanager.save();
4571 // Saves g_settings to configpath given at initialization
4572 void Server::saveConfig()
4574 if(m_configpath != "")
4575 g_settings->updateConfigFile(m_configpath.c_str());
4578 void Server::notifyPlayer(const char *name, const std::wstring msg)
4580 Player *player = m_env->getPlayer(name);
4583 SendChatMessage(player->peer_id, std::wstring(L"Server: -!- ")+msg);
4586 void Server::notifyPlayers(const std::wstring msg)
4588 BroadcastChatMessage(msg);
4591 void Server::queueBlockEmerge(v3s16 blockpos, bool allow_generate)
4595 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
4596 m_emerge_queue.addBlock(PEER_ID_INEXISTENT, blockpos, flags);
4599 // IGameDef interface
4601 IToolDefManager* Server::getToolDefManager()
4605 INodeDefManager* Server::getNodeDefManager()
4609 ICraftDefManager* Server::getCraftDefManager()
4613 ICraftItemDefManager* Server::getCraftItemDefManager()
4615 return m_craftitemdef;
4617 ITextureSource* Server::getTextureSource()
4621 u16 Server::allocateUnknownNodeId(const std::string &name)
4623 return m_nodedef->allocateDummy(name);
4626 IWritableToolDefManager* Server::getWritableToolDefManager()
4630 IWritableNodeDefManager* Server::getWritableNodeDefManager()
4634 IWritableCraftDefManager* Server::getWritableCraftDefManager()
4638 IWritableCraftItemDefManager* Server::getWritableCraftItemDefManager()
4640 return m_craftitemdef;
4643 v3f findSpawnPos(ServerMap &map)
4645 //return v3f(50,50,50)*BS;
4650 nodepos = v2s16(0,0);
4655 // Try to find a good place a few times
4656 for(s32 i=0; i<1000; i++)
4659 // We're going to try to throw the player to this position
4660 v2s16 nodepos2d = v2s16(-range + (myrand()%(range*2)),
4661 -range + (myrand()%(range*2)));
4662 //v2s16 sectorpos = getNodeSectorPos(nodepos2d);
4663 // Get ground height at point (fallbacks to heightmap function)
4664 s16 groundheight = map.findGroundLevel(nodepos2d);
4665 // Don't go underwater
4666 if(groundheight < WATER_LEVEL)
4668 //infostream<<"-> Underwater"<<std::endl;
4671 // Don't go to high places
4672 if(groundheight > WATER_LEVEL + 4)
4674 //infostream<<"-> Underwater"<<std::endl;
4678 nodepos = v3s16(nodepos2d.X, groundheight-2, nodepos2d.Y);
4679 bool is_good = false;
4681 for(s32 i=0; i<10; i++){
4682 v3s16 blockpos = getNodeBlockPos(nodepos);
4683 map.emergeBlock(blockpos, true);
4684 MapNode n = map.getNodeNoEx(nodepos);
4685 if(n.getContent() == CONTENT_AIR){
4696 // Found a good place
4697 //infostream<<"Searched through "<<i<<" places."<<std::endl;
4703 return intToFloat(nodepos, BS);
4706 ServerRemotePlayer *Server::emergePlayer(const char *name, u16 peer_id)
4709 Try to get an existing player
4711 ServerRemotePlayer *player =
4712 static_cast<ServerRemotePlayer*>(m_env->getPlayer(name));
4715 // If player is already connected, cancel
4716 if(player->peer_id != 0)
4718 infostream<<"emergePlayer(): Player already connected"<<std::endl;
4723 player->peer_id = peer_id;
4725 // Reset inventory to creative if in creative mode
4726 if(g_settings->getBool("creative_mode"))
4728 // Warning: double code below
4729 // Backup actual inventory
4730 player->inventory_backup = new Inventory();
4731 *(player->inventory_backup) = player->inventory;
4732 // Set creative inventory
4733 player->resetInventory();
4734 scriptapi_get_creative_inventory(m_lua, player);
4741 If player with the wanted peer_id already exists, cancel.
4743 if(m_env->getPlayer(peer_id) != NULL)
4745 infostream<<"emergePlayer(): Player with wrong name but same"
4746 " peer_id already exists"<<std::endl;
4754 /* Set player position */
4756 infostream<<"Server: Finding spawn place for player \""
4757 <<name<<"\""<<std::endl;
4759 v3f pos = findSpawnPos(m_env->getServerMap());
4761 player = new ServerRemotePlayer(m_env, pos, peer_id, name);
4763 /* Add player to environment */
4764 m_env->addPlayer(player);
4767 ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
4768 scriptapi_on_newplayer(m_lua, srp);
4770 /* Add stuff to inventory */
4771 if(g_settings->getBool("creative_mode"))
4773 // Warning: double code above
4774 // Backup actual inventory
4775 player->inventory_backup = new Inventory();
4776 *(player->inventory_backup) = player->inventory;
4777 // Set creative inventory
4778 player->resetInventory();
4779 scriptapi_get_creative_inventory(m_lua, player);
4784 } // create new player
4787 void Server::handlePeerChange(PeerChange &c)
4789 JMutexAutoLock envlock(m_env_mutex);
4790 JMutexAutoLock conlock(m_con_mutex);
4792 if(c.type == PEER_ADDED)
4799 core::map<u16, RemoteClient*>::Node *n;
4800 n = m_clients.find(c.peer_id);
4801 // The client shouldn't already exist
4805 RemoteClient *client = new RemoteClient();
4806 client->peer_id = c.peer_id;
4807 m_clients.insert(client->peer_id, client);
4810 else if(c.type == PEER_REMOVED)
4817 core::map<u16, RemoteClient*>::Node *n;
4818 n = m_clients.find(c.peer_id);
4819 // The client should exist
4823 Mark objects to be not known by the client
4825 RemoteClient *client = n->getValue();
4827 for(core::map<u16, bool>::Iterator
4828 i = client->m_known_objects.getIterator();
4829 i.atEnd()==false; i++)
4832 u16 id = i.getNode()->getKey();
4833 ServerActiveObject* obj = m_env->getActiveObject(id);
4835 if(obj && obj->m_known_by_count > 0)
4836 obj->m_known_by_count--;
4839 ServerRemotePlayer* player =
4840 static_cast<ServerRemotePlayer*>(m_env->getPlayer(c.peer_id));
4842 // Collect information about leaving in chat
4843 std::wstring message;
4847 std::wstring name = narrow_to_wide(player->getName());
4850 message += L" left game";
4852 message += L" (timed out)";
4856 // Remove from environment
4858 player->m_removed = true;
4860 // Set player client disconnected
4862 player->peer_id = 0;
4870 std::ostringstream os(std::ios_base::binary);
4871 for(core::map<u16, RemoteClient*>::Iterator
4872 i = m_clients.getIterator();
4873 i.atEnd() == false; i++)
4875 RemoteClient *client = i.getNode()->getValue();
4876 assert(client->peer_id == i.getNode()->getKey());
4877 if(client->serialization_version == SER_FMT_VER_INVALID)
4880 Player *player = m_env->getPlayer(client->peer_id);
4883 // Get name of player
4884 os<<player->getName()<<" ";
4887 actionstream<<player->getName()<<" "
4888 <<(c.timeout?"times out.":"leaves game.")
4889 <<" List of players: "
4890 <<os.str()<<std::endl;
4895 delete m_clients[c.peer_id];
4896 m_clients.remove(c.peer_id);
4898 // Send player info to all remaining clients
4899 //SendPlayerInfos();
4901 // Send leave chat message to all remaining clients
4902 BroadcastChatMessage(message);
4911 void Server::handlePeerChanges()
4913 while(m_peer_change_queue.size() > 0)
4915 PeerChange c = m_peer_change_queue.pop_front();
4917 infostream<<"Server: Handling peer change: "
4918 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4921 handlePeerChange(c);
4925 u64 Server::getPlayerPrivs(Player *player)
4929 std::string playername = player->getName();
4930 // Local player gets all privileges regardless of
4931 // what's set on their account.
4932 if(g_settings->get("name") == playername)
4938 return getPlayerAuthPrivs(playername);
4942 void dedicated_server_loop(Server &server, bool &kill)
4944 DSTACK(__FUNCTION_NAME);
4946 infostream<<DTIME<<std::endl;
4947 infostream<<"========================"<<std::endl;
4948 infostream<<"Running dedicated server"<<std::endl;
4949 infostream<<"========================"<<std::endl;
4950 infostream<<std::endl;
4952 IntervalLimiter m_profiler_interval;
4956 // This is kind of a hack but can be done like this
4957 // because server.step() is very light
4959 ScopeProfiler sp(g_profiler, "dedicated server sleep");
4964 if(server.getShutdownRequested() || kill)
4966 infostream<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4973 float profiler_print_interval =
4974 g_settings->getFloat("profiler_print_interval");
4975 if(profiler_print_interval != 0)
4977 if(m_profiler_interval.step(0.030, profiler_print_interval))
4979 infostream<<"Profiler:"<<std::endl;
4980 g_profiler->print(infostream);
4981 g_profiler->clear();
4988 static int counter = 0;
4994 core::list<PlayerInfo> list = server.getPlayerInfo();
4995 core::list<PlayerInfo>::Iterator i;
4996 static u32 sum_old = 0;
4997 u32 sum = PIChecksum(list);
5000 infostream<<DTIME<<"Player info:"<<std::endl;
5001 for(i=list.begin(); i!=list.end(); i++)
5003 i->PrintLine(&infostream);