3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 #include "clientserver.h"
26 #include "jmutexautolock.h"
28 #include "constants.h"
30 #include "materials.h"
33 #include "servercommand.h"
35 #include "content_mapnode.h"
36 #include "content_craft.h"
37 #include "content_nodemeta.h"
39 #include "serverobject.h"
44 #include "scriptapi.h"
48 #include "craftitemdef.h"
50 #include "content_abm.h"
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::SendObjectData(
755 core::map<v3s16, bool> &stepped_blocks
758 DSTACK(__FUNCTION_NAME);
760 // Can't send anything without knowing version
761 if(serialization_version == SER_FMT_VER_INVALID)
763 infostream<<"RemoteClient::SendObjectData(): Not sending, no version."
769 Send a TOCLIENT_OBJECTDATA packet.
773 u16 number of player positions
785 std::ostringstream os(std::ios_base::binary);
789 writeU16(buf, TOCLIENT_OBJECTDATA);
790 os.write((char*)buf, 2);
793 Get and write player data
796 // Get connected players
797 core::list<Player*> players = server->m_env->getPlayers(true);
799 // Write player count
800 u16 playercount = players.size();
801 writeU16(buf, playercount);
802 os.write((char*)buf, 2);
804 core::list<Player*>::Iterator i;
805 for(i = players.begin();
806 i != players.end(); i++)
810 v3f pf = player->getPosition();
811 v3f sf = player->getSpeed();
813 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
814 v3s32 speed_i (sf.X*100, sf.Y*100, sf.Z*100);
815 s32 pitch_i (player->getPitch() * 100);
816 s32 yaw_i (player->getYaw() * 100);
818 writeU16(buf, player->peer_id);
819 os.write((char*)buf, 2);
820 writeV3S32(buf, position_i);
821 os.write((char*)buf, 12);
822 writeV3S32(buf, speed_i);
823 os.write((char*)buf, 12);
824 writeS32(buf, pitch_i);
825 os.write((char*)buf, 4);
826 writeS32(buf, yaw_i);
827 os.write((char*)buf, 4);
831 Get and write object data (dummy, for compatibility)
836 os.write((char*)buf, 2);
842 //infostream<<"Server: Sending object data to "<<peer_id<<std::endl;
845 std::string s = os.str();
846 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
847 // Send as unreliable
848 server->m_con.Send(peer_id, 0, data, false);
851 void RemoteClient::GotBlock(v3s16 p)
853 if(m_blocks_sending.find(p) != NULL)
854 m_blocks_sending.remove(p);
857 /*infostream<<"RemoteClient::GotBlock(): Didn't find in"
858 " m_blocks_sending"<<std::endl;*/
859 m_excess_gotblocks++;
861 m_blocks_sent.insert(p, true);
864 void RemoteClient::SentBlock(v3s16 p)
866 if(m_blocks_sending.find(p) == NULL)
867 m_blocks_sending.insert(p, 0.0);
869 infostream<<"RemoteClient::SentBlock(): Sent block"
870 " already in m_blocks_sending"<<std::endl;
873 void RemoteClient::SetBlockNotSent(v3s16 p)
875 m_nearest_unsent_d = 0;
877 if(m_blocks_sending.find(p) != NULL)
878 m_blocks_sending.remove(p);
879 if(m_blocks_sent.find(p) != NULL)
880 m_blocks_sent.remove(p);
883 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
885 m_nearest_unsent_d = 0;
887 for(core::map<v3s16, MapBlock*>::Iterator
888 i = blocks.getIterator();
889 i.atEnd()==false; i++)
891 v3s16 p = i.getNode()->getKey();
893 if(m_blocks_sending.find(p) != NULL)
894 m_blocks_sending.remove(p);
895 if(m_blocks_sent.find(p) != NULL)
896 m_blocks_sent.remove(p);
904 PlayerInfo::PlayerInfo()
910 void PlayerInfo::PrintLine(std::ostream *s)
913 (*s)<<"\""<<name<<"\" ("
914 <<(position.X/10)<<","<<(position.Y/10)
915 <<","<<(position.Z/10)<<") ";
917 (*s)<<" avg_rtt="<<avg_rtt;
921 u32 PIChecksum(core::list<PlayerInfo> &l)
923 core::list<PlayerInfo>::Iterator i;
926 for(i=l.begin(); i!=l.end(); i++)
928 checksum += a * (i->id+1);
929 checksum ^= 0x435aafcd;
943 std::set<std::string> depends;
944 std::set<std::string> unsatisfied_depends;
946 ModSpec(const std::string &name_="", const std::string path_="",
947 const std::set<std::string> &depends_=std::set<std::string>()):
951 unsatisfied_depends(depends_)
955 // Get a dependency-sorted list of ModSpecs
956 static core::list<ModSpec> getMods(core::list<std::string> &modspaths)
958 std::queue<ModSpec> mods_satisfied;
959 core::list<ModSpec> mods_unsorted;
960 core::list<ModSpec> mods_sorted;
961 for(core::list<std::string>::Iterator i = modspaths.begin();
962 i != modspaths.end(); i++){
963 std::string modspath = *i;
964 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(modspath);
965 for(u32 j=0; j<dirlist.size(); j++){
968 std::string modname = dirlist[j].name;
969 std::string modpath = modspath + DIR_DELIM + modname;
970 std::set<std::string> depends;
971 std::ifstream is((modpath+DIR_DELIM+"depends.txt").c_str(),
972 std::ios_base::binary);
975 std::getline(is, dep);
980 ModSpec spec(modname, modpath, depends);
981 mods_unsorted.push_back(spec);
983 mods_satisfied.push(spec);
986 // Sort by depencencies
987 while(!mods_satisfied.empty()){
988 ModSpec mod = mods_satisfied.front();
989 mods_satisfied.pop();
990 mods_sorted.push_back(mod);
991 for(core::list<ModSpec>::Iterator i = mods_unsorted.begin();
992 i != mods_unsorted.end(); i++){
994 if(mod2.unsatisfied_depends.empty())
996 mod2.unsatisfied_depends.erase(mod.name);
997 if(!mod2.unsatisfied_depends.empty())
999 mods_satisfied.push(mod2);
1002 // Check unsatisfied dependencies
1003 for(core::list<ModSpec>::Iterator i = mods_unsorted.begin();
1004 i != mods_unsorted.end(); i++){
1006 if(mod.unsatisfied_depends.empty())
1008 errorstream<<"mod \""<<mod.name
1009 <<"\" has unsatisfied dependencies:";
1010 for(std::set<std::string>::iterator
1011 i = mod.unsatisfied_depends.begin();
1012 i != mod.unsatisfied_depends.end(); i++){
1013 errorstream<<" \""<<(*i)<<"\"";
1015 errorstream<<". Loading nevertheless."<<std::endl;
1016 mods_sorted.push_back(mod);
1026 std::string mapsavedir,
1027 std::string configpath
1030 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
1031 m_authmanager(mapsavedir+DIR_DELIM+"auth.txt"),
1032 m_banmanager(mapsavedir+DIR_DELIM+"ipban.txt"),
1034 m_toolmgr(createToolDefManager()),
1035 m_nodedef(createNodeDefManager()),
1036 m_craftdef(createCraftDefManager()),
1037 m_craftitemdef(createCraftItemDefManager()),
1039 m_emergethread(this),
1041 m_time_of_day_send_timer(0),
1043 m_mapsavedir(mapsavedir),
1044 m_configpath(configpath),
1045 m_shutdown_requested(false),
1046 m_ignore_map_edit_events(false),
1047 m_ignore_map_edit_events_peer_id(0)
1049 m_liquid_transform_timer = 0.0;
1050 m_print_info_timer = 0.0;
1051 m_objectdata_timer = 0.0;
1052 m_emergethread_trigger_timer = 0.0;
1053 m_savemap_timer = 0.0;
1057 m_step_dtime_mutex.Init();
1060 JMutexAutoLock envlock(m_env_mutex);
1061 JMutexAutoLock conlock(m_con_mutex);
1063 infostream<<"m_nodedef="<<m_nodedef<<std::endl;
1065 // Path to builtin.lua
1066 std::string builtinpath = porting::path_data + DIR_DELIM + "builtin.lua";
1067 // Add default global mod path
1068 m_modspaths.push_back(porting::path_data + DIR_DELIM + "mods");
1070 // Initialize scripting
1072 infostream<<"Server: Initializing scripting"<<std::endl;
1073 m_lua = script_init();
1076 scriptapi_export(m_lua, this);
1077 // Load and run builtin.lua
1078 infostream<<"Server: Loading builtin Lua stuff from \""<<builtinpath
1080 bool success = script_load(m_lua, builtinpath.c_str());
1082 errorstream<<"Server: Failed to load and run "
1083 <<builtinpath<<std::endl;
1086 // Load and run "mod" scripts
1087 core::list<ModSpec> mods = getMods(m_modspaths);
1088 for(core::list<ModSpec>::Iterator i = mods.begin();
1089 i != mods.end(); i++){
1091 infostream<<"Server: Loading mod \""<<mod.name<<"\""<<std::endl;
1092 std::string scriptpath = mod.path + DIR_DELIM + "init.lua";
1093 bool success = script_load(m_lua, scriptpath.c_str());
1095 errorstream<<"Server: Failed to load and run "
1096 <<scriptpath<<std::endl;
1101 // Initialize Environment
1103 m_env = new ServerEnvironment(new ServerMap(mapsavedir, this), m_lua,
1106 // Give environment reference to scripting api
1107 scriptapi_add_environment(m_lua, m_env);
1109 // Register us to receive map edit events
1110 m_env->getMap().addEventReceiver(this);
1112 // If file exists, load environment metadata
1113 if(fs::PathExists(m_mapsavedir+DIR_DELIM+"env_meta.txt"))
1115 infostream<<"Server: Loading environment metadata"<<std::endl;
1116 m_env->loadMeta(m_mapsavedir);
1120 infostream<<"Server: Loading players"<<std::endl;
1121 m_env->deSerializePlayers(m_mapsavedir);
1124 Add some test ActiveBlockModifiers to environment
1126 add_legacy_abms(m_env, m_nodedef);
1131 infostream<<"Server::~Server()"<<std::endl;
1134 Send shutdown message
1137 JMutexAutoLock conlock(m_con_mutex);
1139 std::wstring line = L"*** Server shutting down";
1142 Send the message to clients
1144 for(core::map<u16, RemoteClient*>::Iterator
1145 i = m_clients.getIterator();
1146 i.atEnd() == false; i++)
1148 // Get client and check that it is valid
1149 RemoteClient *client = i.getNode()->getValue();
1150 assert(client->peer_id == i.getNode()->getKey());
1151 if(client->serialization_version == SER_FMT_VER_INVALID)
1155 SendChatMessage(client->peer_id, line);
1157 catch(con::PeerNotFoundException &e)
1163 JMutexAutoLock envlock(m_env_mutex);
1168 infostream<<"Server: Saving players"<<std::endl;
1169 m_env->serializePlayers(m_mapsavedir);
1172 Save environment metadata
1174 infostream<<"Server: Saving environment metadata"<<std::endl;
1175 m_env->saveMeta(m_mapsavedir);
1187 JMutexAutoLock clientslock(m_con_mutex);
1189 for(core::map<u16, RemoteClient*>::Iterator
1190 i = m_clients.getIterator();
1191 i.atEnd() == false; i++)
1194 // NOTE: These are removed by env destructor
1196 u16 peer_id = i.getNode()->getKey();
1197 JMutexAutoLock envlock(m_env_mutex);
1198 m_env->removePlayer(peer_id);
1202 delete i.getNode()->getValue();
1206 // Delete Environment
1212 delete m_craftitemdef;
1214 // Deinitialize scripting
1215 infostream<<"Server: Deinitializing scripting"<<std::endl;
1216 script_deinit(m_lua);
1219 void Server::start(unsigned short port)
1221 DSTACK(__FUNCTION_NAME);
1222 // Stop thread if already running
1225 // Initialize connection
1226 m_con.SetTimeoutMs(30);
1230 m_thread.setRun(true);
1233 infostream<<"Server: Started on port "<<port<<std::endl;
1238 DSTACK(__FUNCTION_NAME);
1240 infostream<<"Server: Stopping and waiting threads"<<std::endl;
1242 // Stop threads (set run=false first so both start stopping)
1243 m_thread.setRun(false);
1244 m_emergethread.setRun(false);
1246 m_emergethread.stop();
1248 infostream<<"Server: Threads stopped"<<std::endl;
1251 void Server::step(float dtime)
1253 DSTACK(__FUNCTION_NAME);
1258 JMutexAutoLock lock(m_step_dtime_mutex);
1259 m_step_dtime += dtime;
1263 void Server::AsyncRunStep()
1265 DSTACK(__FUNCTION_NAME);
1267 g_profiler->add("Server::AsyncRunStep (num)", 1);
1271 JMutexAutoLock lock1(m_step_dtime_mutex);
1272 dtime = m_step_dtime;
1276 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
1277 // Send blocks to clients
1284 g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
1286 //infostream<<"Server steps "<<dtime<<std::endl;
1287 //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1290 JMutexAutoLock lock1(m_step_dtime_mutex);
1291 m_step_dtime -= dtime;
1298 m_uptime.set(m_uptime.get() + dtime);
1302 // Process connection's timeouts
1303 JMutexAutoLock lock2(m_con_mutex);
1304 ScopeProfiler sp(g_profiler, "Server: connection timeout processing");
1305 m_con.RunTimeouts(dtime);
1309 // This has to be called so that the client list gets synced
1310 // with the peer list of the connection
1311 handlePeerChanges();
1315 Update m_time_of_day and overall game time
1318 JMutexAutoLock envlock(m_env_mutex);
1320 m_time_counter += dtime;
1321 f32 speed = g_settings->getFloat("time_speed") * 24000./(24.*3600);
1322 u32 units = (u32)(m_time_counter*speed);
1323 m_time_counter -= (f32)units / speed;
1325 m_env->setTimeOfDay((m_env->getTimeOfDay() + units) % 24000);
1327 //infostream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1330 Send to clients at constant intervals
1333 m_time_of_day_send_timer -= dtime;
1334 if(m_time_of_day_send_timer < 0.0)
1336 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
1338 //JMutexAutoLock envlock(m_env_mutex);
1339 JMutexAutoLock conlock(m_con_mutex);
1341 for(core::map<u16, RemoteClient*>::Iterator
1342 i = m_clients.getIterator();
1343 i.atEnd() == false; i++)
1345 RemoteClient *client = i.getNode()->getValue();
1346 //Player *player = m_env->getPlayer(client->peer_id);
1348 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1349 m_env->getTimeOfDay());
1351 m_con.Send(client->peer_id, 0, data, true);
1357 JMutexAutoLock lock(m_env_mutex);
1359 ScopeProfiler sp(g_profiler, "SEnv step");
1360 ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
1364 const float map_timer_and_unload_dtime = 2.92;
1365 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
1367 JMutexAutoLock lock(m_env_mutex);
1368 // Run Map's timers and unload unused data
1369 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
1370 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
1371 g_settings->getFloat("server_unload_unused_data_timeout"));
1382 JMutexAutoLock lock(m_env_mutex);
1383 JMutexAutoLock lock2(m_con_mutex);
1385 //float player_max_speed = BS * 4.0; // Normal speed
1386 float player_max_speed = BS * 20; // Fast speed
1387 float player_max_speed_up = BS * 20;
1389 player_max_speed *= 2.5; // Tolerance
1390 player_max_speed_up *= 2.5;
1392 for(core::map<u16, RemoteClient*>::Iterator
1393 i = m_clients.getIterator();
1394 i.atEnd() == false; i++)
1396 RemoteClient *client = i.getNode()->getValue();
1397 ServerRemotePlayer *player =
1398 static_cast<ServerRemotePlayer*>
1399 (m_env->getPlayer(client->peer_id));
1404 Check player movements
1406 NOTE: Actually the server should handle player physics like the
1407 client does and compare player's position to what is calculated
1408 on our side. This is required when eg. players fly due to an
1411 player->m_last_good_position_age += dtime;
1412 if(player->m_last_good_position_age >= 2.0){
1413 float age = player->m_last_good_position_age;
1414 v3f diff = (player->getPosition() - player->m_last_good_position);
1415 float d_vert = diff.Y;
1417 float d_horiz = diff.getLength();
1418 /*infostream<<player->getName()<<"'s horizontal speed is "
1419 <<(d_horiz/age)<<std::endl;*/
1420 if(d_horiz <= age * player_max_speed &&
1421 (d_vert < 0 || d_vert < age * player_max_speed_up)){
1422 player->m_last_good_position = player->getPosition();
1424 actionstream<<"Player "<<player->getName()
1425 <<" moved too fast; resetting position"
1427 player->setPosition(player->m_last_good_position);
1428 SendMovePlayer(player);
1430 player->m_last_good_position_age = 0;
1434 Send player inventories and HPs if necessary
1436 if(player->m_inventory_not_sent){
1437 UpdateCrafting(player->peer_id);
1438 SendInventory(player->peer_id);
1440 if(player->m_hp_not_sent){
1441 SendPlayerHP(player);
1446 /* Transform liquids */
1447 m_liquid_transform_timer += dtime;
1448 if(m_liquid_transform_timer >= 1.00)
1450 m_liquid_transform_timer -= 1.00;
1452 JMutexAutoLock lock(m_env_mutex);
1454 ScopeProfiler sp(g_profiler, "Server: liquid transform");
1456 core::map<v3s16, MapBlock*> modified_blocks;
1457 m_env->getMap().transformLiquids(modified_blocks);
1462 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1463 ServerMap &map = ((ServerMap&)m_env->getMap());
1464 map.updateLighting(modified_blocks, lighting_modified_blocks);
1466 // Add blocks modified by lighting to modified_blocks
1467 for(core::map<v3s16, MapBlock*>::Iterator
1468 i = lighting_modified_blocks.getIterator();
1469 i.atEnd() == false; i++)
1471 MapBlock *block = i.getNode()->getValue();
1472 modified_blocks.insert(block->getPos(), block);
1476 Set the modified blocks unsent for all the clients
1479 JMutexAutoLock lock2(m_con_mutex);
1481 for(core::map<u16, RemoteClient*>::Iterator
1482 i = m_clients.getIterator();
1483 i.atEnd() == false; i++)
1485 RemoteClient *client = i.getNode()->getValue();
1487 if(modified_blocks.size() > 0)
1489 // Remove block from sent history
1490 client->SetBlocksNotSent(modified_blocks);
1495 // Periodically print some info
1497 float &counter = m_print_info_timer;
1503 JMutexAutoLock lock2(m_con_mutex);
1505 if(m_clients.size() != 0)
1506 infostream<<"Players:"<<std::endl;
1507 for(core::map<u16, RemoteClient*>::Iterator
1508 i = m_clients.getIterator();
1509 i.atEnd() == false; i++)
1511 //u16 peer_id = i.getNode()->getKey();
1512 RemoteClient *client = i.getNode()->getValue();
1513 Player *player = m_env->getPlayer(client->peer_id);
1516 infostream<<"* "<<player->getName()<<"\t";
1517 client->PrintInfo(infostream);
1522 //if(g_settings->getBool("enable_experimental"))
1526 Check added and deleted active objects
1529 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
1530 JMutexAutoLock envlock(m_env_mutex);
1531 JMutexAutoLock conlock(m_con_mutex);
1533 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
1535 // Radius inside which objects are active
1536 s16 radius = g_settings->getS16("active_object_send_range_blocks");
1537 radius *= MAP_BLOCKSIZE;
1539 for(core::map<u16, RemoteClient*>::Iterator
1540 i = m_clients.getIterator();
1541 i.atEnd() == false; i++)
1543 RemoteClient *client = i.getNode()->getValue();
1544 Player *player = m_env->getPlayer(client->peer_id);
1547 // This can happen if the client timeouts somehow
1548 /*infostream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
1550 <<" has no associated player"<<std::endl;*/
1553 v3s16 pos = floatToInt(player->getPosition(), BS);
1555 core::map<u16, bool> removed_objects;
1556 core::map<u16, bool> added_objects;
1557 m_env->getRemovedActiveObjects(pos, radius,
1558 client->m_known_objects, removed_objects);
1559 m_env->getAddedActiveObjects(pos, radius,
1560 client->m_known_objects, added_objects);
1562 // Ignore if nothing happened
1563 if(removed_objects.size() == 0 && added_objects.size() == 0)
1565 //infostream<<"active objects: none changed"<<std::endl;
1569 std::string data_buffer;
1573 // Handle removed objects
1574 writeU16((u8*)buf, removed_objects.size());
1575 data_buffer.append(buf, 2);
1576 for(core::map<u16, bool>::Iterator
1577 i = removed_objects.getIterator();
1578 i.atEnd()==false; i++)
1581 u16 id = i.getNode()->getKey();
1582 ServerActiveObject* obj = m_env->getActiveObject(id);
1584 // Add to data buffer for sending
1585 writeU16((u8*)buf, i.getNode()->getKey());
1586 data_buffer.append(buf, 2);
1588 // Remove from known objects
1589 client->m_known_objects.remove(i.getNode()->getKey());
1591 if(obj && obj->m_known_by_count > 0)
1592 obj->m_known_by_count--;
1595 // Handle added objects
1596 writeU16((u8*)buf, added_objects.size());
1597 data_buffer.append(buf, 2);
1598 for(core::map<u16, bool>::Iterator
1599 i = added_objects.getIterator();
1600 i.atEnd()==false; i++)
1603 u16 id = i.getNode()->getKey();
1604 ServerActiveObject* obj = m_env->getActiveObject(id);
1607 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1609 infostream<<"WARNING: "<<__FUNCTION_NAME
1610 <<": NULL object"<<std::endl;
1612 type = obj->getType();
1614 // Add to data buffer for sending
1615 writeU16((u8*)buf, id);
1616 data_buffer.append(buf, 2);
1617 writeU8((u8*)buf, type);
1618 data_buffer.append(buf, 1);
1621 data_buffer.append(serializeLongString(
1622 obj->getClientInitializationData()));
1624 data_buffer.append(serializeLongString(""));
1626 // Add to known objects
1627 client->m_known_objects.insert(i.getNode()->getKey(), false);
1630 obj->m_known_by_count++;
1634 SharedBuffer<u8> reply(2 + data_buffer.size());
1635 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1636 memcpy((char*)&reply[2], data_buffer.c_str(),
1637 data_buffer.size());
1639 m_con.Send(client->peer_id, 0, reply, true);
1641 infostream<<"Server: Sent object remove/add: "
1642 <<removed_objects.size()<<" removed, "
1643 <<added_objects.size()<<" added, "
1644 <<"packet size is "<<reply.getSize()<<std::endl;
1649 Collect a list of all the objects known by the clients
1650 and report it back to the environment.
1653 core::map<u16, bool> all_known_objects;
1655 for(core::map<u16, RemoteClient*>::Iterator
1656 i = m_clients.getIterator();
1657 i.atEnd() == false; i++)
1659 RemoteClient *client = i.getNode()->getValue();
1660 // Go through all known objects of client
1661 for(core::map<u16, bool>::Iterator
1662 i = client->m_known_objects.getIterator();
1663 i.atEnd()==false; i++)
1665 u16 id = i.getNode()->getKey();
1666 all_known_objects[id] = true;
1670 m_env->setKnownActiveObjects(whatever);
1676 Send object messages
1679 JMutexAutoLock envlock(m_env_mutex);
1680 JMutexAutoLock conlock(m_con_mutex);
1682 //ScopeProfiler sp(g_profiler, "Server: sending object messages");
1685 // Value = data sent by object
1686 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1688 // Get active object messages from environment
1691 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
1695 core::list<ActiveObjectMessage>* message_list = NULL;
1696 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1697 n = buffered_messages.find(aom.id);
1700 message_list = new core::list<ActiveObjectMessage>;
1701 buffered_messages.insert(aom.id, message_list);
1705 message_list = n->getValue();
1707 message_list->push_back(aom);
1710 // Route data to every client
1711 for(core::map<u16, RemoteClient*>::Iterator
1712 i = m_clients.getIterator();
1713 i.atEnd()==false; i++)
1715 RemoteClient *client = i.getNode()->getValue();
1716 std::string reliable_data;
1717 std::string unreliable_data;
1718 // Go through all objects in message buffer
1719 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1720 j = buffered_messages.getIterator();
1721 j.atEnd()==false; j++)
1723 // If object is not known by client, skip it
1724 u16 id = j.getNode()->getKey();
1725 if(client->m_known_objects.find(id) == NULL)
1727 // Get message list of object
1728 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1729 // Go through every message
1730 for(core::list<ActiveObjectMessage>::Iterator
1731 k = list->begin(); k != list->end(); k++)
1733 // Compose the full new data with header
1734 ActiveObjectMessage aom = *k;
1735 std::string new_data;
1738 writeU16((u8*)&buf[0], aom.id);
1739 new_data.append(buf, 2);
1741 new_data += serializeString(aom.datastring);
1742 // Add data to buffer
1744 reliable_data += new_data;
1746 unreliable_data += new_data;
1750 reliable_data and unreliable_data are now ready.
1753 if(reliable_data.size() > 0)
1755 SharedBuffer<u8> reply(2 + reliable_data.size());
1756 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1757 memcpy((char*)&reply[2], reliable_data.c_str(),
1758 reliable_data.size());
1760 m_con.Send(client->peer_id, 0, reply, true);
1762 if(unreliable_data.size() > 0)
1764 SharedBuffer<u8> reply(2 + unreliable_data.size());
1765 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1766 memcpy((char*)&reply[2], unreliable_data.c_str(),
1767 unreliable_data.size());
1768 // Send as unreliable
1769 m_con.Send(client->peer_id, 0, reply, false);
1772 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1774 infostream<<"Server: Size of object message data: "
1775 <<"reliable: "<<reliable_data.size()
1776 <<", unreliable: "<<unreliable_data.size()
1781 // Clear buffered_messages
1782 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1783 i = buffered_messages.getIterator();
1784 i.atEnd()==false; i++)
1786 delete i.getNode()->getValue();
1790 } // enable_experimental
1793 Send queued-for-sending map edit events.
1796 // Don't send too many at a time
1799 // Single change sending is disabled if queue size is not small
1800 bool disable_single_change_sending = false;
1801 if(m_unsent_map_edit_queue.size() >= 4)
1802 disable_single_change_sending = true;
1804 bool got_any_events = false;
1806 // We'll log the amount of each
1809 while(m_unsent_map_edit_queue.size() != 0)
1811 got_any_events = true;
1813 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1815 // Players far away from the change are stored here.
1816 // Instead of sending the changes, MapBlocks are set not sent
1818 core::list<u16> far_players;
1820 if(event->type == MEET_ADDNODE)
1822 //infostream<<"Server: MEET_ADDNODE"<<std::endl;
1823 prof.add("MEET_ADDNODE", 1);
1824 if(disable_single_change_sending)
1825 sendAddNode(event->p, event->n, event->already_known_by_peer,
1828 sendAddNode(event->p, event->n, event->already_known_by_peer,
1831 else if(event->type == MEET_REMOVENODE)
1833 //infostream<<"Server: MEET_REMOVENODE"<<std::endl;
1834 prof.add("MEET_REMOVENODE", 1);
1835 if(disable_single_change_sending)
1836 sendRemoveNode(event->p, event->already_known_by_peer,
1839 sendRemoveNode(event->p, event->already_known_by_peer,
1842 else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
1844 infostream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
1845 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
1846 setBlockNotSent(event->p);
1848 else if(event->type == MEET_OTHER)
1850 infostream<<"Server: MEET_OTHER"<<std::endl;
1851 prof.add("MEET_OTHER", 1);
1852 for(core::map<v3s16, bool>::Iterator
1853 i = event->modified_blocks.getIterator();
1854 i.atEnd()==false; i++)
1856 v3s16 p = i.getNode()->getKey();
1862 prof.add("unknown", 1);
1863 infostream<<"WARNING: Server: Unknown MapEditEvent "
1864 <<((u32)event->type)<<std::endl;
1868 Set blocks not sent to far players
1870 if(far_players.size() > 0)
1872 // Convert list format to that wanted by SetBlocksNotSent
1873 core::map<v3s16, MapBlock*> modified_blocks2;
1874 for(core::map<v3s16, bool>::Iterator
1875 i = event->modified_blocks.getIterator();
1876 i.atEnd()==false; i++)
1878 v3s16 p = i.getNode()->getKey();
1879 modified_blocks2.insert(p,
1880 m_env->getMap().getBlockNoCreateNoEx(p));
1882 // Set blocks not sent
1883 for(core::list<u16>::Iterator
1884 i = far_players.begin();
1885 i != far_players.end(); i++)
1888 RemoteClient *client = getClient(peer_id);
1891 client->SetBlocksNotSent(modified_blocks2);
1897 /*// Don't send too many at a time
1899 if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
1905 infostream<<"Server: MapEditEvents:"<<std::endl;
1906 prof.print(infostream);
1912 Send object positions
1915 float &counter = m_objectdata_timer;
1917 if(counter >= g_settings->getFloat("objectdata_interval"))
1919 JMutexAutoLock lock1(m_env_mutex);
1920 JMutexAutoLock lock2(m_con_mutex);
1922 //ScopeProfiler sp(g_profiler, "Server: sending player positions");
1924 SendObjectData(counter);
1931 Trigger emergethread (it somehow gets to a non-triggered but
1932 bysy state sometimes)
1935 float &counter = m_emergethread_trigger_timer;
1941 m_emergethread.trigger();
1945 // Save map, players and auth stuff
1947 float &counter = m_savemap_timer;
1949 if(counter >= g_settings->getFloat("server_map_save_interval"))
1953 ScopeProfiler sp(g_profiler, "Server: saving stuff");
1956 if(m_authmanager.isModified())
1957 m_authmanager.save();
1960 if(m_banmanager.isModified())
1961 m_banmanager.save();
1964 JMutexAutoLock lock(m_env_mutex);
1966 // Save changed parts of map
1967 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
1970 m_env->serializePlayers(m_mapsavedir);
1972 // Save environment metadata
1973 m_env->saveMeta(m_mapsavedir);
1978 void Server::Receive()
1980 DSTACK(__FUNCTION_NAME);
1981 SharedBuffer<u8> data;
1986 JMutexAutoLock conlock(m_con_mutex);
1987 datasize = m_con.Receive(peer_id, data);
1990 // This has to be called so that the client list gets synced
1991 // with the peer list of the connection
1992 handlePeerChanges();
1994 ProcessData(*data, datasize, peer_id);
1996 catch(con::InvalidIncomingDataException &e)
1998 infostream<<"Server::Receive(): "
1999 "InvalidIncomingDataException: what()="
2000 <<e.what()<<std::endl;
2002 catch(con::PeerNotFoundException &e)
2004 //NOTE: This is not needed anymore
2006 // The peer has been disconnected.
2007 // Find the associated player and remove it.
2009 /*JMutexAutoLock envlock(m_env_mutex);
2011 infostream<<"ServerThread: peer_id="<<peer_id
2012 <<" has apparently closed connection. "
2013 <<"Removing player."<<std::endl;
2015 m_env->removePlayer(peer_id);*/
2019 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
2021 DSTACK(__FUNCTION_NAME);
2022 // Environment is locked first.
2023 JMutexAutoLock envlock(m_env_mutex);
2024 JMutexAutoLock conlock(m_con_mutex);
2027 Address address = m_con.GetPeerAddress(peer_id);
2029 // drop player if is ip is banned
2030 if(m_banmanager.isIpBanned(address.serializeString())){
2031 SendAccessDenied(m_con, peer_id,
2032 L"Your ip is banned. Banned name was "
2033 +narrow_to_wide(m_banmanager.getBanName(
2034 address.serializeString())));
2035 m_con.DeletePeer(peer_id);
2039 catch(con::PeerNotFoundException &e)
2041 infostream<<"Server::ProcessData(): Cancelling: peer "
2042 <<peer_id<<" not found"<<std::endl;
2046 u8 peer_ser_ver = getClient(peer_id)->serialization_version;
2054 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
2056 if(command == TOSERVER_INIT)
2058 // [0] u16 TOSERVER_INIT
2059 // [2] u8 SER_FMT_VER_HIGHEST
2060 // [3] u8[20] player_name
2061 // [23] u8[28] password <--- can be sent without this, from old versions
2063 if(datasize < 2+1+PLAYERNAME_SIZE)
2066 infostream<<"Server: Got TOSERVER_INIT from "
2067 <<peer_id<<std::endl;
2069 // First byte after command is maximum supported
2070 // serialization version
2071 u8 client_max = data[2];
2072 u8 our_max = SER_FMT_VER_HIGHEST;
2073 // Use the highest version supported by both
2074 u8 deployed = core::min_(client_max, our_max);
2075 // If it's lower than the lowest supported, give up.
2076 if(deployed < SER_FMT_VER_LOWEST)
2077 deployed = SER_FMT_VER_INVALID;
2079 //peer->serialization_version = deployed;
2080 getClient(peer_id)->pending_serialization_version = deployed;
2082 if(deployed == SER_FMT_VER_INVALID)
2084 infostream<<"Server: Cannot negotiate "
2085 "serialization version with peer "
2086 <<peer_id<<std::endl;
2087 SendAccessDenied(m_con, peer_id, std::wstring(
2088 L"Your client's version is not supported.\n"
2089 L"Server version is ")
2090 + narrow_to_wide(VERSION_STRING) + L"."
2096 Read and check network protocol version
2099 u16 net_proto_version = 0;
2100 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
2102 net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
2105 getClient(peer_id)->net_proto_version = net_proto_version;
2107 if(net_proto_version == 0)
2109 SendAccessDenied(m_con, peer_id, std::wstring(
2110 L"Your client's version is not supported.\n"
2111 L"Server version is ")
2112 + narrow_to_wide(VERSION_STRING) + L"."
2117 if(g_settings->getBool("strict_protocol_version_checking"))
2119 if(net_proto_version != PROTOCOL_VERSION)
2121 SendAccessDenied(m_con, peer_id, std::wstring(
2122 L"Your client's version is not supported.\n"
2123 L"Server version is ")
2124 + narrow_to_wide(VERSION_STRING) + L"."
2135 char playername[PLAYERNAME_SIZE];
2136 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
2138 playername[i] = data[3+i];
2140 playername[PLAYERNAME_SIZE-1] = 0;
2142 if(playername[0]=='\0')
2144 infostream<<"Server: Player has empty name"<<std::endl;
2145 SendAccessDenied(m_con, peer_id,
2150 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
2152 infostream<<"Server: Player has invalid name"<<std::endl;
2153 SendAccessDenied(m_con, peer_id,
2154 L"Name contains unallowed characters");
2159 char password[PASSWORD_SIZE];
2160 if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
2162 // old version - assume blank password
2167 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2169 password[i] = data[23+i];
2171 password[PASSWORD_SIZE-1] = 0;
2174 std::string checkpwd;
2175 if(m_authmanager.exists(playername))
2177 checkpwd = m_authmanager.getPassword(playername);
2181 checkpwd = g_settings->get("default_password");
2184 /*infostream<<"Server: Client gave password '"<<password
2185 <<"', the correct one is '"<<checkpwd<<"'"<<std::endl;*/
2187 if(password != checkpwd && m_authmanager.exists(playername))
2189 infostream<<"Server: peer_id="<<peer_id
2190 <<": supplied invalid password for "
2191 <<playername<<std::endl;
2192 SendAccessDenied(m_con, peer_id, L"Invalid password");
2196 // Add player to auth manager
2197 if(m_authmanager.exists(playername) == false)
2199 infostream<<"Server: adding player "<<playername
2200 <<" to auth manager"<<std::endl;
2201 m_authmanager.add(playername);
2202 m_authmanager.setPassword(playername, checkpwd);
2203 m_authmanager.setPrivs(playername,
2204 stringToPrivs(g_settings->get("default_privs")));
2205 m_authmanager.save();
2208 // Enforce user limit.
2209 // Don't enforce for users that have some admin right
2210 if(m_clients.size() >= g_settings->getU16("max_users") &&
2211 (m_authmanager.getPrivs(playername)
2212 & (PRIV_SERVER|PRIV_BAN|PRIV_PRIVS)) == 0 &&
2213 playername != g_settings->get("name"))
2215 SendAccessDenied(m_con, peer_id, L"Too many users.");
2220 Player *player = emergePlayer(playername, password, peer_id);
2222 // If failed, cancel
2225 infostream<<"Server: peer_id="<<peer_id
2226 <<": failed to emerge player"<<std::endl;
2231 Answer with a TOCLIENT_INIT
2234 SharedBuffer<u8> reply(2+1+6+8);
2235 writeU16(&reply[0], TOCLIENT_INIT);
2236 writeU8(&reply[2], deployed);
2237 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
2238 writeU64(&reply[2+1+6], m_env->getServerMap().getSeed());
2241 m_con.Send(peer_id, 0, reply, true);
2245 Send complete position information
2247 SendMovePlayer(player);
2252 if(command == TOSERVER_INIT2)
2254 infostream<<"Server: Got TOSERVER_INIT2 from "
2255 <<peer_id<<std::endl;
2258 getClient(peer_id)->serialization_version
2259 = getClient(peer_id)->pending_serialization_version;
2262 Send some initialization data
2265 // Send tool definitions
2266 SendToolDef(m_con, peer_id, m_toolmgr);
2268 // Send node definitions
2269 SendNodeDef(m_con, peer_id, m_nodedef);
2271 // Send CraftItem definitions
2272 SendCraftItemDef(m_con, peer_id, m_craftitemdef);
2275 SendTextures(peer_id);
2277 // Send player info to all players
2280 // Send inventory to player
2281 UpdateCrafting(peer_id);
2282 SendInventory(peer_id);
2284 // Send player items to all players
2287 Player *player = m_env->getPlayer(peer_id);
2290 SendPlayerHP(player);
2294 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2295 m_env->getTimeOfDay());
2296 m_con.Send(peer_id, 0, data, true);
2299 // Send information about server to player in chat
2300 SendChatMessage(peer_id, getStatusString());
2302 // Send information about joining in chat
2304 std::wstring name = L"unknown";
2305 Player *player = m_env->getPlayer(peer_id);
2307 name = narrow_to_wide(player->getName());
2309 std::wstring message;
2312 message += L" joined game";
2313 BroadcastChatMessage(message);
2316 // Warnings about protocol version can be issued here
2317 if(getClient(peer_id)->net_proto_version < PROTOCOL_VERSION)
2319 SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT IS OLD AND MAY WORK PROPERLY WITH THIS SERVER");
2323 Check HP, respawn if necessary
2325 HandlePlayerHP(player, 0);
2331 std::ostringstream os(std::ios_base::binary);
2332 for(core::map<u16, RemoteClient*>::Iterator
2333 i = m_clients.getIterator();
2334 i.atEnd() == false; i++)
2336 RemoteClient *client = i.getNode()->getValue();
2337 assert(client->peer_id == i.getNode()->getKey());
2338 if(client->serialization_version == SER_FMT_VER_INVALID)
2341 Player *player = m_env->getPlayer(client->peer_id);
2344 // Get name of player
2345 os<<player->getName()<<" ";
2348 actionstream<<player->getName()<<" joins game. List of players: "
2349 <<os.str()<<std::endl;
2355 if(peer_ser_ver == SER_FMT_VER_INVALID)
2357 infostream<<"Server::ProcessData(): Cancelling: Peer"
2358 " serialization format invalid or not initialized."
2359 " Skipping incoming command="<<command<<std::endl;
2363 Player *player = m_env->getPlayer(peer_id);
2366 infostream<<"Server::ProcessData(): Cancelling: "
2367 "No player for peer_id="<<peer_id
2371 if(command == TOSERVER_PLAYERPOS)
2373 if(datasize < 2+12+12+4+4)
2377 v3s32 ps = readV3S32(&data[start+2]);
2378 v3s32 ss = readV3S32(&data[start+2+12]);
2379 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2380 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2381 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2382 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2383 pitch = wrapDegrees(pitch);
2384 yaw = wrapDegrees(yaw);
2386 player->setPosition(position);
2387 player->setSpeed(speed);
2388 player->setPitch(pitch);
2389 player->setYaw(yaw);
2391 /*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2392 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2393 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2395 else if(command == TOSERVER_GOTBLOCKS)
2408 u16 count = data[2];
2409 for(u16 i=0; i<count; i++)
2411 if((s16)datasize < 2+1+(i+1)*6)
2412 throw con::InvalidIncomingDataException
2413 ("GOTBLOCKS length is too short");
2414 v3s16 p = readV3S16(&data[2+1+i*6]);
2415 /*infostream<<"Server: GOTBLOCKS ("
2416 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2417 RemoteClient *client = getClient(peer_id);
2418 client->GotBlock(p);
2421 else if(command == TOSERVER_DELETEDBLOCKS)
2434 u16 count = data[2];
2435 for(u16 i=0; i<count; i++)
2437 if((s16)datasize < 2+1+(i+1)*6)
2438 throw con::InvalidIncomingDataException
2439 ("DELETEDBLOCKS length is too short");
2440 v3s16 p = readV3S16(&data[2+1+i*6]);
2441 /*infostream<<"Server: DELETEDBLOCKS ("
2442 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2443 RemoteClient *client = getClient(peer_id);
2444 client->SetBlockNotSent(p);
2447 else if(command == TOSERVER_CLICK_OBJECT)
2449 infostream<<"Server: CLICK_OBJECT not supported anymore"<<std::endl;
2452 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2454 infostream<<"Server: CLICK_ACTIVEOBJECT not supported anymore"<<std::endl;
2457 else if(command == TOSERVER_GROUND_ACTION)
2459 infostream<<"Server: GROUND_ACTION not supported anymore"<<std::endl;
2463 else if(command == TOSERVER_RELEASE)
2465 infostream<<"Server: RELEASE not supported anymore"<<std::endl;
2468 else if(command == TOSERVER_SIGNTEXT)
2470 infostream<<"Server: SIGNTEXT not supported anymore"
2474 else if(command == TOSERVER_SIGNNODETEXT)
2476 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2484 std::string datastring((char*)&data[2], datasize-2);
2485 std::istringstream is(datastring, std::ios_base::binary);
2488 is.read((char*)buf, 6);
2489 v3s16 p = readV3S16(buf);
2490 is.read((char*)buf, 2);
2491 u16 textlen = readU16(buf);
2493 for(u16 i=0; i<textlen; i++)
2495 is.read((char*)buf, 1);
2496 text += (char)buf[0];
2499 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
2503 meta->setText(text);
2505 actionstream<<player->getName()<<" writes \""<<text<<"\" to sign"
2506 <<" at "<<PP(p)<<std::endl;
2508 v3s16 blockpos = getNodeBlockPos(p);
2509 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
2512 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2516 setBlockNotSent(blockpos);
2518 else if(command == TOSERVER_INVENTORY_ACTION)
2520 /*// Ignore inventory changes if in creative mode
2521 if(g_settings->getBool("creative_mode") == true)
2523 infostream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2527 // Strip command and create a stream
2528 std::string datastring((char*)&data[2], datasize-2);
2529 infostream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2530 std::istringstream is(datastring, std::ios_base::binary);
2532 InventoryAction *a = InventoryAction::deSerialize(is);
2537 c.current_player = player;
2540 Handle craftresult specially if not in creative mode
2542 bool disable_action = false;
2543 if(a->getType() == IACTION_MOVE
2544 && g_settings->getBool("creative_mode") == false)
2546 IMoveAction *ma = (IMoveAction*)a;
2547 if(ma->to_inv == "current_player" &&
2548 ma->from_inv == "current_player")
2550 InventoryList *rlist = player->inventory.getList("craftresult");
2552 InventoryList *clist = player->inventory.getList("craft");
2554 InventoryList *mlist = player->inventory.getList("main");
2557 Craftresult is no longer preview if something
2560 if(ma->to_list == "craftresult"
2561 && ma->from_list != "craftresult")
2563 // If it currently is a preview, remove
2565 if(player->craftresult_is_preview)
2567 rlist->deleteItem(0);
2569 player->craftresult_is_preview = false;
2572 Crafting takes place if this condition is true.
2574 if(player->craftresult_is_preview &&
2575 ma->from_list == "craftresult")
2577 player->craftresult_is_preview = false;
2578 clist->decrementMaterials(1);
2580 /* Print out action */
2581 InventoryList *list =
2582 player->inventory.getList("craftresult");
2584 InventoryItem *item = list->getItem(0);
2585 std::string itemname = "NULL";
2587 itemname = item->getName();
2588 actionstream<<player->getName()<<" crafts "
2589 <<itemname<<std::endl;
2592 If the craftresult is placed on itself, move it to
2593 main inventory instead of doing the action
2595 if(ma->to_list == "craftresult"
2596 && ma->from_list == "craftresult")
2598 disable_action = true;
2600 InventoryItem *item1 = rlist->changeItem(0, NULL);
2601 mlist->addItem(item1);
2604 // Disallow moving items if not allowed to build
2605 else if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2607 disable_action = true;
2609 // if it's a locking chest, only allow the owner or server admins to move items
2610 else if (ma->from_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
2612 Strfnd fn(ma->from_inv);
2613 std::string id0 = fn.next(":");
2614 if(id0 == "nodemeta")
2617 p.X = stoi(fn.next(","));
2618 p.Y = stoi(fn.next(","));
2619 p.Z = stoi(fn.next(","));
2620 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
2621 if(meta->getOwner() != ""){
2622 if(meta->getOwner() != player->getName())
2623 disable_action = true;
2627 else if (ma->to_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
2629 Strfnd fn(ma->to_inv);
2630 std::string id0 = fn.next(":");
2631 if(id0 == "nodemeta")
2634 p.X = stoi(fn.next(","));
2635 p.Y = stoi(fn.next(","));
2636 p.Z = stoi(fn.next(","));
2637 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
2638 if(meta->getOwner() != ""){
2639 if(meta->getOwner() != player->getName())
2640 disable_action = true;
2646 if(a->getType() == IACTION_DROP)
2648 IDropAction *da = (IDropAction*)a;
2649 // Disallow dropping items if not allowed to build
2650 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2652 disable_action = true;
2654 // if it's a locking chest, only allow the owner or server admins to drop items
2655 else if (da->from_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
2657 Strfnd fn(da->from_inv);
2658 std::string id0 = fn.next(":");
2659 if(id0 == "nodemeta")
2662 p.X = stoi(fn.next(","));
2663 p.Y = stoi(fn.next(","));
2664 p.Z = stoi(fn.next(","));
2665 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
2666 if(meta->getOwner() != ""){
2667 if(meta->getOwner() != player->getName())
2668 disable_action = true;
2674 if(disable_action == false)
2676 // Feed action to player inventory
2677 a->apply(&c, this, m_env);
2685 infostream<<"TOSERVER_INVENTORY_ACTION: "
2686 <<"InventoryAction::deSerialize() returned NULL"
2690 else if(command == TOSERVER_CHAT_MESSAGE)
2698 std::string datastring((char*)&data[2], datasize-2);
2699 std::istringstream is(datastring, std::ios_base::binary);
2702 is.read((char*)buf, 2);
2703 u16 len = readU16(buf);
2705 std::wstring message;
2706 for(u16 i=0; i<len; i++)
2708 is.read((char*)buf, 2);
2709 message += (wchar_t)readU16(buf);
2712 // Get player name of this client
2713 std::wstring name = narrow_to_wide(player->getName());
2716 bool ate = scriptapi_on_chat_message(m_lua, player->getName(),
2717 wide_to_narrow(message));
2718 // If script ate the message, don't proceed
2722 // Line to send to players
2724 // Whether to send to the player that sent the line
2725 bool send_to_sender = false;
2726 // Whether to send to other players
2727 bool send_to_others = false;
2729 // Local player gets all privileges regardless of
2730 // what's set on their account.
2731 u64 privs = getPlayerPrivs(player);
2734 if(message[0] == L'/')
2736 size_t strip_size = 1;
2737 if (message[1] == L'#') // support old-style commans
2739 message = message.substr(strip_size);
2741 WStrfnd f1(message);
2742 f1.next(L" "); // Skip over /#whatever
2743 std::wstring paramstring = f1.next(L"");
2745 ServerCommandContext *ctx = new ServerCommandContext(
2746 str_split(message, L' '),
2753 std::wstring reply(processServerCommand(ctx));
2754 send_to_sender = ctx->flags & SEND_TO_SENDER;
2755 send_to_others = ctx->flags & SEND_TO_OTHERS;
2757 if (ctx->flags & SEND_NO_PREFIX)
2760 line += L"Server: " + reply;
2767 if(privs & PRIV_SHOUT)
2773 send_to_others = true;
2777 line += L"Server: You are not allowed to shout";
2778 send_to_sender = true;
2785 actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2788 Send the message to clients
2790 for(core::map<u16, RemoteClient*>::Iterator
2791 i = m_clients.getIterator();
2792 i.atEnd() == false; i++)
2794 // Get client and check that it is valid
2795 RemoteClient *client = i.getNode()->getValue();
2796 assert(client->peer_id == i.getNode()->getKey());
2797 if(client->serialization_version == SER_FMT_VER_INVALID)
2801 bool sender_selected = (peer_id == client->peer_id);
2802 if(sender_selected == true && send_to_sender == false)
2804 if(sender_selected == false && send_to_others == false)
2807 SendChatMessage(client->peer_id, line);
2811 else if(command == TOSERVER_DAMAGE)
2813 std::string datastring((char*)&data[2], datasize-2);
2814 std::istringstream is(datastring, std::ios_base::binary);
2815 u8 damage = readU8(is);
2817 if(g_settings->getBool("enable_damage"))
2819 actionstream<<player->getName()<<" damaged by "
2820 <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
2823 HandlePlayerHP(player, damage);
2827 SendPlayerHP(player);
2830 else if(command == TOSERVER_PASSWORD)
2833 [0] u16 TOSERVER_PASSWORD
2834 [2] u8[28] old password
2835 [30] u8[28] new password
2838 if(datasize != 2+PASSWORD_SIZE*2)
2840 /*char password[PASSWORD_SIZE];
2841 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2842 password[i] = data[2+i];
2843 password[PASSWORD_SIZE-1] = 0;*/
2845 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2853 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2855 char c = data[2+PASSWORD_SIZE+i];
2861 infostream<<"Server: Client requests a password change from "
2862 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
2864 std::string playername = player->getName();
2866 if(m_authmanager.exists(playername) == false)
2868 infostream<<"Server: playername not found in authmanager"<<std::endl;
2869 // Wrong old password supplied!!
2870 SendChatMessage(peer_id, L"playername not found in authmanager");
2874 std::string checkpwd = m_authmanager.getPassword(playername);
2876 if(oldpwd != checkpwd)
2878 infostream<<"Server: invalid old password"<<std::endl;
2879 // Wrong old password supplied!!
2880 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
2884 actionstream<<player->getName()<<" changes password"<<std::endl;
2886 m_authmanager.setPassword(playername, newpwd);
2888 infostream<<"Server: password change successful for "<<playername
2890 SendChatMessage(peer_id, L"Password change successful");
2892 else if(command == TOSERVER_PLAYERITEM)
2897 u16 item = readU16(&data[2]);
2898 player->wieldItem(item);
2899 SendWieldedItem(player);
2901 else if(command == TOSERVER_RESPAWN)
2906 RespawnPlayer(player);
2908 actionstream<<player->getName()<<" respawns at "
2909 <<PP(player->getPosition()/BS)<<std::endl;
2911 else if(command == TOSERVER_INTERACT)
2913 std::string datastring((char*)&data[2], datasize-2);
2914 std::istringstream is(datastring, std::ios_base::binary);
2920 [5] u32 length of the next item
2921 [9] serialized PointedThing
2923 0: start digging (from undersurface) or use
2924 1: stop digging (all parameters ignored)
2925 2: digging completed
2926 3: place block or item (to abovesurface)
2929 u8 action = readU8(is);
2930 u16 item_i = readU16(is);
2931 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
2932 PointedThing pointed;
2933 pointed.deSerialize(tmp_is);
2935 infostream<<"TOSERVER_INTERACT: action="<<(int)action<<", item="<<item_i<<", pointed="<<pointed.dump()<<std::endl;
2937 ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
2938 v3f player_pos = srp->m_last_good_position;
2940 // Update wielded item
2941 srp->wieldItem(item_i);
2943 // Get pointed to node (undefined if not POINTEDTYPE_NODE)
2944 v3s16 p_under = pointed.node_undersurface;
2945 v3s16 p_above = pointed.node_abovesurface;
2947 // Get pointed to object (NULL if not POINTEDTYPE_OBJECT)
2948 ServerActiveObject *pointed_object = NULL;
2949 if(pointed.type == POINTEDTHING_OBJECT)
2951 pointed_object = m_env->getActiveObject(pointed.object_id);
2952 if(pointed_object == NULL)
2954 infostream<<"TOSERVER_INTERACT: "
2955 "pointed object is NULL"<<std::endl;
2962 Check that target is reasonably close
2963 (only when digging or placing things)
2965 if(action == 0 || action == 2 || action == 3)
2967 v3f pointed_pos = player_pos;
2968 if(pointed.type == POINTEDTHING_NODE)
2970 pointed_pos = intToFloat(p_under, BS);
2972 else if(pointed.type == POINTEDTHING_OBJECT)
2974 pointed_pos = pointed_object->getBasePosition();
2977 float d = player_pos.getDistanceFrom(pointed_pos);
2978 float max_d = BS * 10; // Just some large enough value
2980 actionstream<<"Player "<<player->getName()
2981 <<" tried to access "<<pointed.dump()
2983 <<"d="<<d<<", max_d="<<max_d
2984 <<". ignoring."<<std::endl;
2985 // Re-send block to revert change on client-side
2986 RemoteClient *client = getClient(peer_id);
2987 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos, BS));
2988 client->SetBlockNotSent(blockpos);
2995 Make sure the player is allowed to do it
2997 bool build_priv = (getPlayerPrivs(player) & PRIV_BUILD) != 0;
3000 infostream<<"Ignoring interaction from player "<<player->getName()
3001 <<" because privileges are "<<getPlayerPrivs(player)
3003 // NOTE: no return; here, fall through
3007 0: start digging or punch object
3011 if(pointed.type == POINTEDTHING_NODE)
3014 NOTE: This can be used in the future to check if
3015 somebody is cheating, by checking the timing.
3017 bool cannot_punch_node = !build_priv;
3019 MapNode n(CONTENT_IGNORE);
3023 n = m_env->getMap().getNode(p_under);
3025 catch(InvalidPositionException &e)
3027 infostream<<"Server: Not punching: Node not found."
3028 <<" Adding block to emerge queue."
3030 m_emerge_queue.addBlock(peer_id,
3031 getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
3032 cannot_punch_node = true;
3035 if(cannot_punch_node)
3041 scriptapi_environment_on_punchnode(m_lua, p_under, n, srp);
3043 else if(pointed.type == POINTEDTHING_OBJECT)
3048 // Skip if object has been removed
3049 if(pointed_object->m_removed)
3052 actionstream<<player->getName()<<" punches object "
3053 <<pointed.object_id<<std::endl;
3056 pointed_object->punch(srp);
3064 else if(action == 1)
3069 2: Digging completed
3071 else if(action == 2)
3073 // Only complete digging of nodes
3074 if(pointed.type != POINTEDTHING_NODE)
3077 // Mandatory parameter; actually used for nothing
3078 core::map<v3s16, MapBlock*> modified_blocks;
3080 content_t material = CONTENT_IGNORE;
3081 u8 mineral = MINERAL_NONE;
3083 bool cannot_remove_node = !build_priv;
3085 MapNode n(CONTENT_IGNORE);
3088 n = m_env->getMap().getNode(p_under);
3090 mineral = n.getMineral(m_nodedef);
3091 // Get material at position
3092 material = n.getContent();
3093 // If not yet cancelled
3094 if(cannot_remove_node == false)
3096 // If it's not diggable, do nothing
3097 if(m_nodedef->get(material).diggable == false)
3099 infostream<<"Server: Not finishing digging: "
3100 <<"Node not diggable"
3102 cannot_remove_node = true;
3105 // If not yet cancelled
3106 if(cannot_remove_node == false)
3108 // Get node metadata
3109 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p_under);
3110 if(meta && meta->nodeRemovalDisabled() == true)
3112 infostream<<"Server: Not finishing digging: "
3113 <<"Node metadata disables removal"
3115 cannot_remove_node = true;
3119 catch(InvalidPositionException &e)
3121 infostream<<"Server: Not finishing digging: Node not found."
3122 <<" Adding block to emerge queue."
3124 m_emerge_queue.addBlock(peer_id,
3125 getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
3126 cannot_remove_node = true;
3130 If node can't be removed, set block to be re-sent to
3133 if(cannot_remove_node)
3135 infostream<<"Server: Not finishing digging."<<std::endl;
3137 // Client probably has wrong data.
3138 // Set block not sent, so that client will get
3140 infostream<<"Client "<<peer_id<<" tried to dig "
3141 <<"node; but node cannot be removed."
3142 <<" setting MapBlock not sent."<<std::endl;
3143 RemoteClient *client = getClient(peer_id);
3144 v3s16 blockpos = getNodeBlockPos(p_under);
3145 client->SetBlockNotSent(blockpos);
3150 actionstream<<player->getName()<<" digs "<<PP(p_under)
3151 <<", gets material "<<(int)material<<", mineral "
3152 <<(int)mineral<<std::endl;
3155 Send the removal to all close-by players.
3156 - If other player is close, send REMOVENODE
3157 - Otherwise set blocks not sent
3159 core::list<u16> far_players;
3160 sendRemoveNode(p_under, peer_id, &far_players, 30);
3163 Update and send inventory
3166 if(g_settings->getBool("creative_mode") == false)
3171 InventoryList *mlist = player->inventory.getList("main");
3174 InventoryItem *item = mlist->getItem(item_i);
3175 if(item && (std::string)item->getName() == "ToolItem")
3177 ToolItem *titem = (ToolItem*)item;
3178 std::string toolname = titem->getToolName();
3180 // Get digging properties for material and tool
3181 ToolDiggingProperties tp =
3182 m_toolmgr->getDiggingProperties(toolname);
3183 DiggingProperties prop =
3184 getDiggingProperties(material, &tp, m_nodedef);
3186 if(prop.diggable == false)
3188 infostream<<"Server: WARNING: Player digged"
3189 <<" with impossible material + tool"
3190 <<" combination"<<std::endl;
3193 bool weared_out = titem->addWear(prop.wear);
3197 mlist->deleteItem(item_i);
3200 srp->m_inventory_not_sent = true;
3205 Add dug item to inventory
3208 InventoryItem *item = NULL;
3210 if(mineral != MINERAL_NONE)
3211 item = getDiggedMineralItem(mineral, this);
3216 const std::string &dug_s = m_nodedef->get(material).dug_item;
3219 std::istringstream is(dug_s, std::ios::binary);
3220 item = InventoryItem::deSerialize(is, this);
3226 // Add a item to inventory
3227 player->inventory.addItem("main", item);
3228 srp->m_inventory_not_sent = true;
3233 if(mineral != MINERAL_NONE)
3234 item = getDiggedMineralItem(mineral, this);
3239 const std::string &extra_dug_s = m_nodedef->get(material).extra_dug_item;
3240 s32 extra_rarity = m_nodedef->get(material).extra_dug_item_rarity;
3241 if(extra_dug_s != "" && extra_rarity != 0
3242 && myrand() % extra_rarity == 0)
3244 std::istringstream is(extra_dug_s, std::ios::binary);
3245 item = InventoryItem::deSerialize(is, this);
3251 // Add a item to inventory
3252 player->inventory.addItem("main", item);
3253 srp->m_inventory_not_sent = true;
3259 (this takes some time so it is done after the quick stuff)
3262 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
3264 m_env->getMap().removeNodeAndUpdate(p_under, modified_blocks);
3267 Set blocks not sent to far players
3269 for(core::list<u16>::Iterator
3270 i = far_players.begin();
3271 i != far_players.end(); i++)
3274 RemoteClient *client = getClient(peer_id);
3277 client->SetBlocksNotSent(modified_blocks);
3283 scriptapi_environment_on_dignode(m_lua, p_under, n, srp);
3287 3: place block or right-click object
3289 else if(action == 3)
3291 if(pointed.type == POINTEDTHING_NODE)
3293 InventoryList *ilist = player->inventory.getList("main");
3298 InventoryItem *item = ilist->getItem(item_i);
3300 // If there is no item, it is not possible to add it anywhere
3305 Handle material items
3307 if(std::string("MaterialItem") == item->getName())
3309 bool cannot_place_node = !build_priv;
3312 // Don't add a node if this is not a free space
3313 MapNode n2 = m_env->getMap().getNode(p_above);
3314 if(m_nodedef->get(n2).buildable_to == false)
3316 infostream<<"Client "<<peer_id<<" tried to place"
3317 <<" node in invalid position."<<std::endl;
3318 cannot_place_node = true;
3321 catch(InvalidPositionException &e)
3323 infostream<<"Server: Ignoring ADDNODE: Node not found"
3324 <<" Adding block to emerge queue."
3326 m_emerge_queue.addBlock(peer_id,
3327 getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
3328 cannot_place_node = true;
3331 if(cannot_place_node)
3333 // Client probably has wrong data.
3334 // Set block not sent, so that client will get
3336 RemoteClient *client = getClient(peer_id);
3337 v3s16 blockpos = getNodeBlockPos(p_above);
3338 client->SetBlockNotSent(blockpos);
3342 // Reset build time counter
3343 getClient(peer_id)->m_time_from_building = 0.0;
3346 MaterialItem *mitem = (MaterialItem*)item;
3348 n.setContent(mitem->getMaterial());
3350 actionstream<<player->getName()<<" places material "
3351 <<(int)mitem->getMaterial()
3352 <<" at "<<PP(p_under)<<std::endl;
3354 // Calculate direction for wall mounted stuff
3355 if(m_nodedef->get(n).wall_mounted)
3356 n.param2 = packDir(p_under - p_above);
3358 // Calculate the direction for furnaces and chests and stuff
3359 if(m_nodedef->get(n).param_type == CPT_FACEDIR_SIMPLE)
3361 v3f playerpos = player->getPosition();
3362 v3f blockpos = intToFloat(p_above, BS) - playerpos;
3363 blockpos = blockpos.normalize();
3365 if (fabs(blockpos.X) > fabs(blockpos.Z)) {
3379 Send to all close-by players
3381 core::list<u16> far_players;
3382 sendAddNode(p_above, n, 0, &far_players, 30);
3387 InventoryList *ilist = player->inventory.getList("main");
3388 if(g_settings->getBool("creative_mode") == false && ilist)
3390 // Remove from inventory and send inventory
3391 if(mitem->getCount() <= 1)
3392 ilist->deleteItem(item_i);
3395 srp->m_inventory_not_sent = true;
3401 This takes some time so it is done after the quick stuff
3403 core::map<v3s16, MapBlock*> modified_blocks;
3405 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
3407 std::string p_name = std::string(player->getName());
3408 m_env->getMap().addNodeAndUpdate(p_above, n, modified_blocks, p_name);
3411 Set blocks not sent to far players
3413 for(core::list<u16>::Iterator
3414 i = far_players.begin();
3415 i != far_players.end(); i++)
3418 RemoteClient *client = getClient(peer_id);
3421 client->SetBlocksNotSent(modified_blocks);
3427 scriptapi_environment_on_placenode(m_lua, p_above, n, srp);
3430 Calculate special events
3433 /*if(n.d == LEGN(m_nodedef, "CONTENT_MESE"))
3436 for(s16 z=-1; z<=1; z++)
3437 for(s16 y=-1; y<=1; y++)
3438 for(s16 x=-1; x<=1; x++)
3445 Place other item (not a block)
3451 infostream<<"Not allowing player to place item: "
3452 "no build privileges"<<std::endl;
3456 // Calculate a position for it
3457 v3f pos = player_pos;
3458 if(pointed.type == POINTEDTHING_NOTHING)
3460 infostream<<"Not allowing player to place item: "
3461 "pointing to nothing"<<std::endl;
3464 else if(pointed.type == POINTEDTHING_NODE)
3466 pos = intToFloat(p_above, BS);
3468 else if(pointed.type == POINTEDTHING_OBJECT)
3470 pos = pointed_object->getBasePosition();
3473 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
3474 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
3478 //pos.Y -= BS*0.25; // let it drop a bit
3481 Check that the block is loaded so that the item
3482 can properly be added to the static list too
3484 v3s16 blockpos = getNodeBlockPos(floatToInt(pos, BS));
3485 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3488 infostream<<"Error while placing item: "
3489 "block not found"<<std::endl;
3493 actionstream<<player->getName()<<" places "<<item->getName()
3494 <<" at "<<PP(pos)<<std::endl;
3499 bool remove = item->dropOrPlace(m_env, srp, pos, true, -1);
3500 if(remove && g_settings->getBool("creative_mode") == false)
3502 InventoryList *ilist = player->inventory.getList("main");
3504 // Remove from inventory and send inventory
3505 ilist->deleteItem(item_i);
3506 srp->m_inventory_not_sent = true;
3511 else if(pointed.type == POINTEDTHING_OBJECT)
3513 // Right click object
3518 // Skip if object has been removed
3519 if(pointed_object->m_removed)
3522 actionstream<<player->getName()<<" right-clicks object "
3523 <<pointed.object_id<<std::endl;
3526 pointed_object->rightClick(srp);
3534 else if(action == 4)
3536 InventoryList *ilist = player->inventory.getList("main");
3541 InventoryItem *item = ilist->getItem(item_i);
3543 // If there is no item, it is not possible to add it anywhere
3547 // Requires build privs
3550 infostream<<"Not allowing player to use item: "
3551 "no build privileges"<<std::endl;
3555 actionstream<<player->getName()<<" uses "<<item->getName()
3556 <<", pointing at "<<pointed.dump()<<std::endl;
3558 bool remove = item->use(m_env, srp, pointed);
3560 if(remove && g_settings->getBool("creative_mode") == false)
3562 InventoryList *ilist = player->inventory.getList("main");
3564 // Remove from inventory and send inventory
3565 ilist->deleteItem(item_i);
3566 srp->m_inventory_not_sent = true;
3573 Catch invalid actions
3577 infostream<<"WARNING: Server: Invalid action "
3578 <<action<<std::endl;
3581 // Complete add_to_inventory_later
3582 srp->completeAddToInventoryLater(item_i);
3586 infostream<<"Server::ProcessData(): Ignoring "
3587 "unknown command "<<command<<std::endl;
3591 catch(SendFailedException &e)
3593 errorstream<<"Server::ProcessData(): SendFailedException: "
3599 void Server::onMapEditEvent(MapEditEvent *event)
3601 //infostream<<"Server::onMapEditEvent()"<<std::endl;
3602 if(m_ignore_map_edit_events)
3604 MapEditEvent *e = event->clone();
3605 m_unsent_map_edit_queue.push_back(e);
3608 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3610 if(id == "current_player")
3612 assert(c->current_player);
3613 return &(c->current_player->inventory);
3617 std::string id0 = fn.next(":");
3619 if(id0 == "nodemeta")
3622 p.X = stoi(fn.next(","));
3623 p.Y = stoi(fn.next(","));
3624 p.Z = stoi(fn.next(","));
3625 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3627 return meta->getInventory();
3628 infostream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3629 <<"no metadata found"<<std::endl;
3633 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3636 void Server::inventoryModified(InventoryContext *c, std::string id)
3638 if(id == "current_player")
3640 assert(c->current_player);
3641 ServerRemotePlayer *srp =
3642 static_cast<ServerRemotePlayer*>(c->current_player);
3643 srp->m_inventory_not_sent = true;
3648 std::string id0 = fn.next(":");
3650 if(id0 == "nodemeta")
3653 p.X = stoi(fn.next(","));
3654 p.Y = stoi(fn.next(","));
3655 p.Z = stoi(fn.next(","));
3656 v3s16 blockpos = getNodeBlockPos(p);
3658 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3660 meta->inventoryModified();
3662 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3664 block->raiseModified(MOD_STATE_WRITE_NEEDED);
3666 setBlockNotSent(blockpos);
3671 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3674 core::list<PlayerInfo> Server::getPlayerInfo()
3676 DSTACK(__FUNCTION_NAME);
3677 JMutexAutoLock envlock(m_env_mutex);
3678 JMutexAutoLock conlock(m_con_mutex);
3680 core::list<PlayerInfo> list;
3682 core::list<Player*> players = m_env->getPlayers();
3684 core::list<Player*>::Iterator i;
3685 for(i = players.begin();
3686 i != players.end(); i++)
3690 Player *player = *i;
3693 // Copy info from connection to info struct
3694 info.id = player->peer_id;
3695 info.address = m_con.GetPeerAddress(player->peer_id);
3696 info.avg_rtt = m_con.GetPeerAvgRTT(player->peer_id);
3698 catch(con::PeerNotFoundException &e)
3700 // Set dummy peer info
3702 info.address = Address(0,0,0,0,0);
3706 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3707 info.position = player->getPosition();
3709 list.push_back(info);
3716 void Server::peerAdded(con::Peer *peer)
3718 DSTACK(__FUNCTION_NAME);
3719 infostream<<"Server::peerAdded(): peer->id="
3720 <<peer->id<<std::endl;
3723 c.type = PEER_ADDED;
3724 c.peer_id = peer->id;
3726 m_peer_change_queue.push_back(c);
3729 void Server::deletingPeer(con::Peer *peer, bool timeout)
3731 DSTACK(__FUNCTION_NAME);
3732 infostream<<"Server::deletingPeer(): peer->id="
3733 <<peer->id<<", timeout="<<timeout<<std::endl;
3736 c.type = PEER_REMOVED;
3737 c.peer_id = peer->id;
3738 c.timeout = timeout;
3739 m_peer_change_queue.push_back(c);
3746 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3748 DSTACK(__FUNCTION_NAME);
3749 std::ostringstream os(std::ios_base::binary);
3751 writeU16(os, TOCLIENT_HP);
3755 std::string s = os.str();
3756 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3758 con.Send(peer_id, 0, data, true);
3761 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3762 const std::wstring &reason)
3764 DSTACK(__FUNCTION_NAME);
3765 std::ostringstream os(std::ios_base::binary);
3767 writeU16(os, TOCLIENT_ACCESS_DENIED);
3768 os<<serializeWideString(reason);
3771 std::string s = os.str();
3772 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3774 con.Send(peer_id, 0, data, true);
3777 void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
3778 bool set_camera_point_target, v3f camera_point_target)
3780 DSTACK(__FUNCTION_NAME);
3781 std::ostringstream os(std::ios_base::binary);
3783 writeU16(os, TOCLIENT_DEATHSCREEN);
3784 writeU8(os, set_camera_point_target);
3785 writeV3F1000(os, camera_point_target);
3788 std::string s = os.str();
3789 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3791 con.Send(peer_id, 0, data, true);
3794 void Server::SendToolDef(con::Connection &con, u16 peer_id,
3795 IToolDefManager *tooldef)
3797 DSTACK(__FUNCTION_NAME);
3798 std::ostringstream os(std::ios_base::binary);
3802 u32 length of the next item
3803 serialized ToolDefManager
3805 writeU16(os, TOCLIENT_TOOLDEF);
3806 std::ostringstream tmp_os(std::ios::binary);
3807 tooldef->serialize(tmp_os);
3808 os<<serializeLongString(tmp_os.str());
3811 std::string s = os.str();
3812 infostream<<"Server::SendToolDef(): Sending tool definitions: size="
3813 <<s.size()<<std::endl;
3814 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3816 con.Send(peer_id, 0, data, true);
3819 void Server::SendNodeDef(con::Connection &con, u16 peer_id,
3820 INodeDefManager *nodedef)
3822 DSTACK(__FUNCTION_NAME);
3823 std::ostringstream os(std::ios_base::binary);
3827 u32 length of the next item
3828 serialized NodeDefManager
3830 writeU16(os, TOCLIENT_NODEDEF);
3831 std::ostringstream tmp_os(std::ios::binary);
3832 nodedef->serialize(tmp_os);
3833 os<<serializeLongString(tmp_os.str());
3836 std::string s = os.str();
3837 infostream<<"Server::SendNodeDef(): Sending node definitions: size="
3838 <<s.size()<<std::endl;
3839 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3841 con.Send(peer_id, 0, data, true);
3844 void Server::SendCraftItemDef(con::Connection &con, u16 peer_id,
3845 ICraftItemDefManager *craftitemdef)
3847 DSTACK(__FUNCTION_NAME);
3848 std::ostringstream os(std::ios_base::binary);
3852 u32 length of the next item
3853 serialized CraftItemDefManager
3855 writeU16(os, TOCLIENT_CRAFTITEMDEF);
3856 std::ostringstream tmp_os(std::ios::binary);
3857 craftitemdef->serialize(tmp_os);
3858 os<<serializeLongString(tmp_os.str());
3861 std::string s = os.str();
3862 infostream<<"Server::SendCraftItemDef(): Sending craft item definitions: size="
3863 <<s.size()<<std::endl;
3864 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3866 con.Send(peer_id, 0, data, true);
3870 Non-static send methods
3873 void Server::SendObjectData(float dtime)
3875 DSTACK(__FUNCTION_NAME);
3877 core::map<v3s16, bool> stepped_blocks;
3879 for(core::map<u16, RemoteClient*>::Iterator
3880 i = m_clients.getIterator();
3881 i.atEnd() == false; i++)
3883 u16 peer_id = i.getNode()->getKey();
3884 RemoteClient *client = i.getNode()->getValue();
3885 assert(client->peer_id == peer_id);
3887 if(client->serialization_version == SER_FMT_VER_INVALID)
3890 client->SendObjectData(this, dtime, stepped_blocks);
3894 void Server::SendPlayerInfos()
3896 DSTACK(__FUNCTION_NAME);
3898 //JMutexAutoLock envlock(m_env_mutex);
3900 // Get connected players
3901 core::list<Player*> players = m_env->getPlayers(true);
3903 u32 player_count = players.getSize();
3904 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3906 SharedBuffer<u8> data(datasize);
3907 writeU16(&data[0], TOCLIENT_PLAYERINFO);
3910 core::list<Player*>::Iterator i;
3911 for(i = players.begin();
3912 i != players.end(); i++)
3914 Player *player = *i;
3916 /*infostream<<"Server sending player info for player with "
3917 "peer_id="<<player->peer_id<<std::endl;*/
3919 writeU16(&data[start], player->peer_id);
3920 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3921 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3922 start += 2+PLAYERNAME_SIZE;
3925 //JMutexAutoLock conlock(m_con_mutex);
3928 m_con.SendToAll(0, data, true);
3931 void Server::SendInventory(u16 peer_id)
3933 DSTACK(__FUNCTION_NAME);
3935 ServerRemotePlayer* player =
3936 static_cast<ServerRemotePlayer*>(m_env->getPlayer(peer_id));
3939 player->m_inventory_not_sent = false;
3945 std::ostringstream os;
3946 //os.imbue(std::locale("C"));
3948 player->inventory.serialize(os);
3950 std::string s = os.str();
3952 SharedBuffer<u8> data(s.size()+2);
3953 writeU16(&data[0], TOCLIENT_INVENTORY);
3954 memcpy(&data[2], s.c_str(), s.size());
3957 m_con.Send(peer_id, 0, data, true);
3960 std::string getWieldedItemString(const Player *player)
3962 const InventoryItem *item = player->getWieldItem();
3964 return std::string("");
3965 std::ostringstream os(std::ios_base::binary);
3966 item->serialize(os);
3970 void Server::SendWieldedItem(const Player* player)
3972 DSTACK(__FUNCTION_NAME);
3976 std::ostringstream os(std::ios_base::binary);
3978 writeU16(os, TOCLIENT_PLAYERITEM);
3980 writeU16(os, player->peer_id);
3981 os<<serializeString(getWieldedItemString(player));
3984 std::string s = os.str();
3985 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3987 m_con.SendToAll(0, data, true);
3990 void Server::SendPlayerItems()
3992 DSTACK(__FUNCTION_NAME);
3994 std::ostringstream os(std::ios_base::binary);
3995 core::list<Player *> players = m_env->getPlayers(true);
3997 writeU16(os, TOCLIENT_PLAYERITEM);
3998 writeU16(os, players.size());
3999 core::list<Player *>::Iterator i;
4000 for(i = players.begin(); i != players.end(); ++i)
4003 writeU16(os, p->peer_id);
4004 os<<serializeString(getWieldedItemString(p));
4008 std::string s = os.str();
4009 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4011 m_con.SendToAll(0, data, true);
4014 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
4016 DSTACK(__FUNCTION_NAME);
4018 std::ostringstream os(std::ios_base::binary);
4022 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
4023 os.write((char*)buf, 2);
4026 writeU16(buf, message.size());
4027 os.write((char*)buf, 2);
4030 for(u32 i=0; i<message.size(); i++)
4034 os.write((char*)buf, 2);
4038 std::string s = os.str();
4039 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4041 m_con.Send(peer_id, 0, data, true);
4044 void Server::BroadcastChatMessage(const std::wstring &message)
4046 for(core::map<u16, RemoteClient*>::Iterator
4047 i = m_clients.getIterator();
4048 i.atEnd() == false; i++)
4050 // Get client and check that it is valid
4051 RemoteClient *client = i.getNode()->getValue();
4052 assert(client->peer_id == i.getNode()->getKey());
4053 if(client->serialization_version == SER_FMT_VER_INVALID)
4056 SendChatMessage(client->peer_id, message);
4060 void Server::SendPlayerHP(Player *player)
4062 SendHP(m_con, player->peer_id, player->hp);
4065 void Server::SendMovePlayer(Player *player)
4067 DSTACK(__FUNCTION_NAME);
4068 std::ostringstream os(std::ios_base::binary);
4070 writeU16(os, TOCLIENT_MOVE_PLAYER);
4071 writeV3F1000(os, player->getPosition());
4072 writeF1000(os, player->getPitch());
4073 writeF1000(os, player->getYaw());
4076 v3f pos = player->getPosition();
4077 f32 pitch = player->getPitch();
4078 f32 yaw = player->getYaw();
4079 infostream<<"Server sending TOCLIENT_MOVE_PLAYER"
4080 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
4087 std::string s = os.str();
4088 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4090 m_con.Send(player->peer_id, 0, data, true);
4093 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
4094 core::list<u16> *far_players, float far_d_nodes)
4096 float maxd = far_d_nodes*BS;
4097 v3f p_f = intToFloat(p, BS);
4101 SharedBuffer<u8> reply(replysize);
4102 writeU16(&reply[0], TOCLIENT_REMOVENODE);
4103 writeS16(&reply[2], p.X);
4104 writeS16(&reply[4], p.Y);
4105 writeS16(&reply[6], p.Z);
4107 for(core::map<u16, RemoteClient*>::Iterator
4108 i = m_clients.getIterator();
4109 i.atEnd() == false; i++)
4111 // Get client and check that it is valid
4112 RemoteClient *client = i.getNode()->getValue();
4113 assert(client->peer_id == i.getNode()->getKey());
4114 if(client->serialization_version == SER_FMT_VER_INVALID)
4117 // Don't send if it's the same one
4118 if(client->peer_id == ignore_id)
4124 Player *player = m_env->getPlayer(client->peer_id);
4127 // If player is far away, only set modified blocks not sent
4128 v3f player_pos = player->getPosition();
4129 if(player_pos.getDistanceFrom(p_f) > maxd)
4131 far_players->push_back(client->peer_id);
4138 m_con.Send(client->peer_id, 0, reply, true);
4142 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
4143 core::list<u16> *far_players, float far_d_nodes)
4145 float maxd = far_d_nodes*BS;
4146 v3f p_f = intToFloat(p, BS);
4148 for(core::map<u16, RemoteClient*>::Iterator
4149 i = m_clients.getIterator();
4150 i.atEnd() == false; i++)
4152 // Get client and check that it is valid
4153 RemoteClient *client = i.getNode()->getValue();
4154 assert(client->peer_id == i.getNode()->getKey());
4155 if(client->serialization_version == SER_FMT_VER_INVALID)
4158 // Don't send if it's the same one
4159 if(client->peer_id == ignore_id)
4165 Player *player = m_env->getPlayer(client->peer_id);
4168 // If player is far away, only set modified blocks not sent
4169 v3f player_pos = player->getPosition();
4170 if(player_pos.getDistanceFrom(p_f) > maxd)
4172 far_players->push_back(client->peer_id);
4179 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
4180 SharedBuffer<u8> reply(replysize);
4181 writeU16(&reply[0], TOCLIENT_ADDNODE);
4182 writeS16(&reply[2], p.X);
4183 writeS16(&reply[4], p.Y);
4184 writeS16(&reply[6], p.Z);
4185 n.serialize(&reply[8], client->serialization_version);
4188 m_con.Send(client->peer_id, 0, reply, true);
4192 void Server::setBlockNotSent(v3s16 p)
4194 for(core::map<u16, RemoteClient*>::Iterator
4195 i = m_clients.getIterator();
4196 i.atEnd()==false; i++)
4198 RemoteClient *client = i.getNode()->getValue();
4199 client->SetBlockNotSent(p);
4203 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
4205 DSTACK(__FUNCTION_NAME);
4207 v3s16 p = block->getPos();
4211 bool completely_air = true;
4212 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4213 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4214 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4216 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
4218 completely_air = false;
4219 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
4224 infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
4226 infostream<<"[completely air] ";
4227 infostream<<std::endl;
4231 Create a packet with the block in the right format
4234 std::ostringstream os(std::ios_base::binary);
4235 block->serialize(os, ver);
4236 std::string s = os.str();
4237 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
4239 u32 replysize = 8 + blockdata.getSize();
4240 SharedBuffer<u8> reply(replysize);
4241 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
4242 writeS16(&reply[2], p.X);
4243 writeS16(&reply[4], p.Y);
4244 writeS16(&reply[6], p.Z);
4245 memcpy(&reply[8], *blockdata, blockdata.getSize());
4247 /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4248 <<": \tpacket size: "<<replysize<<std::endl;*/
4253 m_con.Send(peer_id, 1, reply, true);
4256 void Server::SendBlocks(float dtime)
4258 DSTACK(__FUNCTION_NAME);
4260 JMutexAutoLock envlock(m_env_mutex);
4261 JMutexAutoLock conlock(m_con_mutex);
4263 //TimeTaker timer("Server::SendBlocks");
4265 core::array<PrioritySortedBlockTransfer> queue;
4267 s32 total_sending = 0;
4270 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
4272 for(core::map<u16, RemoteClient*>::Iterator
4273 i = m_clients.getIterator();
4274 i.atEnd() == false; i++)
4276 RemoteClient *client = i.getNode()->getValue();
4277 assert(client->peer_id == i.getNode()->getKey());
4279 total_sending += client->SendingCount();
4281 if(client->serialization_version == SER_FMT_VER_INVALID)
4284 client->GetNextBlocks(this, dtime, queue);
4289 // Lowest priority number comes first.
4290 // Lowest is most important.
4293 for(u32 i=0; i<queue.size(); i++)
4295 //TODO: Calculate limit dynamically
4296 if(total_sending >= g_settings->getS32
4297 ("max_simultaneous_block_sends_server_total"))
4300 PrioritySortedBlockTransfer q = queue[i];
4302 MapBlock *block = NULL;
4305 block = m_env->getMap().getBlockNoCreate(q.pos);
4307 catch(InvalidPositionException &e)
4312 RemoteClient *client = getClient(q.peer_id);
4314 SendBlockNoLock(q.peer_id, block, client->serialization_version);
4316 client->SentBlock(q.pos);
4322 struct SendableTexture
4328 SendableTexture(const std::string &name_="", const std::string path_="",
4329 const std::string &data_=""):
4336 void Server::SendTextures(u16 peer_id)
4338 DSTACK(__FUNCTION_NAME);
4340 infostream<<"Server::SendTextures(): Sending textures to client"<<std::endl;
4344 // Put 5kB in one bunch (this is not accurate)
4345 u32 bytes_per_bunch = 5000;
4347 core::array< core::list<SendableTexture> > texture_bunches;
4348 texture_bunches.push_back(core::list<SendableTexture>());
4350 u32 texture_size_bunch_total = 0;
4351 core::list<ModSpec> mods = getMods(m_modspaths);
4352 for(core::list<ModSpec>::Iterator i = mods.begin();
4353 i != mods.end(); i++){
4355 std::string texturepath = mod.path + DIR_DELIM + "textures";
4356 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(texturepath);
4357 for(u32 j=0; j<dirlist.size(); j++){
4358 if(dirlist[j].dir) // Ignode dirs
4360 std::string tname = dirlist[j].name;
4361 std::string tpath = texturepath + DIR_DELIM + tname;
4363 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
4364 if(fis.good() == false){
4365 errorstream<<"Server::SendTextures(): Could not open \""
4366 <<tname<<"\" for reading"<<std::endl;
4369 std::ostringstream tmp_os(std::ios_base::binary);
4373 fis.read(buf, 1024);
4374 std::streamsize len = fis.gcount();
4375 tmp_os.write(buf, len);
4376 texture_size_bunch_total += len;
4385 errorstream<<"Server::SendTextures(): Failed to read \""
4386 <<tname<<"\""<<std::endl;
4389 /*infostream<<"Server::SendTextures(): Loaded \""
4390 <<tname<<"\""<<std::endl;*/
4392 texture_bunches[texture_bunches.size()-1].push_back(
4393 SendableTexture(tname, tpath, tmp_os.str()));
4395 // Start next bunch if got enough data
4396 if(texture_size_bunch_total >= bytes_per_bunch){
4397 texture_bunches.push_back(core::list<SendableTexture>());
4398 texture_size_bunch_total = 0;
4403 /* Create and send packets */
4405 u32 num_bunches = texture_bunches.size();
4406 for(u32 i=0; i<num_bunches; i++)
4410 u16 total number of texture bunches
4411 u16 index of this bunch
4412 u32 number of textures in this bunch
4420 std::ostringstream os(std::ios_base::binary);
4422 writeU16(os, TOCLIENT_TEXTURES);
4423 writeU16(os, num_bunches);
4425 writeU32(os, texture_bunches[i].size());
4427 for(core::list<SendableTexture>::Iterator
4428 j = texture_bunches[i].begin();
4429 j != texture_bunches[i].end(); j++){
4430 os<<serializeString(j->name);
4431 os<<serializeLongString(j->data);
4435 std::string s = os.str();
4436 infostream<<"Server::SendTextures(): bunch "<<i<<"/"<<num_bunches
4437 <<" textures="<<texture_bunches[i].size()
4438 <<" size=" <<s.size()<<std::endl;
4439 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4441 m_con.Send(peer_id, 0, data, true);
4449 void Server::HandlePlayerHP(Player *player, s16 damage)
4451 if(player->hp > damage)
4453 player->hp -= damage;
4454 SendPlayerHP(player);
4458 infostream<<"Server::HandlePlayerHP(): Player "
4459 <<player->getName()<<" dies"<<std::endl;
4463 //TODO: Throw items around
4465 // Handle players that are not connected
4466 if(player->peer_id == PEER_ID_INEXISTENT){
4467 RespawnPlayer(player);
4471 SendPlayerHP(player);
4473 RemoteClient *client = getClient(player->peer_id);
4474 if(client->net_proto_version >= 3)
4476 SendDeathscreen(m_con, player->peer_id, false, v3f(0,0,0));
4480 RespawnPlayer(player);
4485 void Server::RespawnPlayer(Player *player)
4488 ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
4489 bool repositioned = scriptapi_on_respawnplayer(m_lua, srp);
4491 v3f pos = findSpawnPos(m_env->getServerMap());
4492 player->setPosition(pos);
4493 srp->m_last_good_position = pos;
4494 srp->m_last_good_position_age = 0;
4496 SendMovePlayer(player);
4497 SendPlayerHP(player);
4500 void Server::UpdateCrafting(u16 peer_id)
4502 DSTACK(__FUNCTION_NAME);
4504 Player* player = m_env->getPlayer(peer_id);
4508 Calculate crafting stuff
4510 if(g_settings->getBool("creative_mode") == false)
4512 InventoryList *clist = player->inventory.getList("craft");
4513 InventoryList *rlist = player->inventory.getList("craftresult");
4515 if(rlist && rlist->getUsedSlots() == 0)
4516 player->craftresult_is_preview = true;
4518 if(rlist && player->craftresult_is_preview)
4520 rlist->clearItems();
4522 if(clist && rlist && player->craftresult_is_preview)
4524 // Get result of crafting grid
4526 std::vector<InventoryItem*> items;
4527 for(u16 i=0; i<9; i++){
4528 if(clist->getItem(i) == NULL)
4529 items.push_back(NULL);
4531 items.push_back(clist->getItem(i)->clone());
4533 CraftPointerInput cpi(3, items);
4535 InventoryItem *result = m_craftdef->getCraftResult(cpi, this);
4536 //InventoryItem *result = craft_get_result(items, this);
4538 rlist->addItem(result);
4541 } // if creative_mode == false
4544 RemoteClient* Server::getClient(u16 peer_id)
4546 DSTACK(__FUNCTION_NAME);
4547 //JMutexAutoLock lock(m_con_mutex);
4548 core::map<u16, RemoteClient*>::Node *n;
4549 n = m_clients.find(peer_id);
4550 // A client should exist for all peers
4552 return n->getValue();
4555 std::wstring Server::getStatusString()
4557 std::wostringstream os(std::ios_base::binary);
4560 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4562 os<<L", uptime="<<m_uptime.get();
4563 // Information about clients
4565 for(core::map<u16, RemoteClient*>::Iterator
4566 i = m_clients.getIterator();
4567 i.atEnd() == false; i++)
4569 // Get client and check that it is valid
4570 RemoteClient *client = i.getNode()->getValue();
4571 assert(client->peer_id == i.getNode()->getKey());
4572 if(client->serialization_version == SER_FMT_VER_INVALID)
4575 Player *player = m_env->getPlayer(client->peer_id);
4576 // Get name of player
4577 std::wstring name = L"unknown";
4579 name = narrow_to_wide(player->getName());
4580 // Add name to information string
4584 if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
4585 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4586 if(g_settings->get("motd") != "")
4587 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4591 // Saves g_settings to configpath given at initialization
4592 void Server::saveConfig()
4594 if(m_configpath != "")
4595 g_settings->updateConfigFile(m_configpath.c_str());
4598 void Server::notifyPlayer(const char *name, const std::wstring msg)
4600 Player *player = m_env->getPlayer(name);
4603 SendChatMessage(player->peer_id, std::wstring(L"Server: -!- ")+msg);
4606 void Server::notifyPlayers(const std::wstring msg)
4608 BroadcastChatMessage(msg);
4611 void Server::queueBlockEmerge(v3s16 blockpos, bool allow_generate)
4615 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
4616 m_emerge_queue.addBlock(PEER_ID_INEXISTENT, blockpos, flags);
4619 // IGameDef interface
4621 IToolDefManager* Server::getToolDefManager()
4625 INodeDefManager* Server::getNodeDefManager()
4629 ICraftDefManager* Server::getCraftDefManager()
4633 ICraftItemDefManager* Server::getCraftItemDefManager()
4635 return m_craftitemdef;
4637 ITextureSource* Server::getTextureSource()
4641 u16 Server::allocateUnknownNodeId(const std::string &name)
4643 return m_nodedef->allocateDummy(name);
4646 IWritableToolDefManager* Server::getWritableToolDefManager()
4650 IWritableNodeDefManager* Server::getWritableNodeDefManager()
4654 IWritableCraftDefManager* Server::getWritableCraftDefManager()
4658 IWritableCraftItemDefManager* Server::getWritableCraftItemDefManager()
4660 return m_craftitemdef;
4663 v3f findSpawnPos(ServerMap &map)
4665 //return v3f(50,50,50)*BS;
4670 nodepos = v2s16(0,0);
4675 // Try to find a good place a few times
4676 for(s32 i=0; i<1000; i++)
4679 // We're going to try to throw the player to this position
4680 v2s16 nodepos2d = v2s16(-range + (myrand()%(range*2)),
4681 -range + (myrand()%(range*2)));
4682 //v2s16 sectorpos = getNodeSectorPos(nodepos2d);
4683 // Get ground height at point (fallbacks to heightmap function)
4684 s16 groundheight = map.findGroundLevel(nodepos2d);
4685 // Don't go underwater
4686 if(groundheight < WATER_LEVEL)
4688 //infostream<<"-> Underwater"<<std::endl;
4691 // Don't go to high places
4692 if(groundheight > WATER_LEVEL + 4)
4694 //infostream<<"-> Underwater"<<std::endl;
4698 nodepos = v3s16(nodepos2d.X, groundheight-2, nodepos2d.Y);
4699 bool is_good = false;
4701 for(s32 i=0; i<10; i++){
4702 v3s16 blockpos = getNodeBlockPos(nodepos);
4703 map.emergeBlock(blockpos, true);
4704 MapNode n = map.getNodeNoEx(nodepos);
4705 if(n.getContent() == CONTENT_AIR){
4716 // Found a good place
4717 //infostream<<"Searched through "<<i<<" places."<<std::endl;
4723 return intToFloat(nodepos, BS);
4726 Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id)
4729 Try to get an existing player
4731 Player *player = m_env->getPlayer(name);
4734 // If player is already connected, cancel
4735 if(player->peer_id != 0)
4737 infostream<<"emergePlayer(): Player already connected"<<std::endl;
4742 player->peer_id = peer_id;
4744 // Reset inventory to creative if in creative mode
4745 if(g_settings->getBool("creative_mode"))
4747 // Warning: double code below
4748 // Backup actual inventory
4749 player->inventory_backup = new Inventory();
4750 *(player->inventory_backup) = player->inventory;
4751 // Set creative inventory
4752 craft_set_creative_inventory(player, this);
4759 If player with the wanted peer_id already exists, cancel.
4761 if(m_env->getPlayer(peer_id) != NULL)
4763 infostream<<"emergePlayer(): Player with wrong name but same"
4764 " peer_id already exists"<<std::endl;
4772 // Add authentication stuff
4773 m_authmanager.add(name);
4774 m_authmanager.setPassword(name, password);
4775 m_authmanager.setPrivs(name,
4776 stringToPrivs(g_settings->get("default_privs")));
4778 /* Set player position */
4780 infostream<<"Server: Finding spawn place for player \""
4781 <<name<<"\""<<std::endl;
4783 v3f pos = findSpawnPos(m_env->getServerMap());
4785 player = new ServerRemotePlayer(m_env, pos, peer_id, name);
4787 /* Add player to environment */
4788 m_env->addPlayer(player);
4791 ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
4792 scriptapi_on_newplayer(m_lua, srp);
4794 /* Add stuff to inventory */
4795 if(g_settings->getBool("creative_mode"))
4797 // Warning: double code above
4798 // Backup actual inventory
4799 player->inventory_backup = new Inventory();
4800 *(player->inventory_backup) = player->inventory;
4801 // Set creative inventory
4802 craft_set_creative_inventory(player, this);
4807 } // create new player
4810 void Server::handlePeerChange(PeerChange &c)
4812 JMutexAutoLock envlock(m_env_mutex);
4813 JMutexAutoLock conlock(m_con_mutex);
4815 if(c.type == PEER_ADDED)
4822 core::map<u16, RemoteClient*>::Node *n;
4823 n = m_clients.find(c.peer_id);
4824 // The client shouldn't already exist
4828 RemoteClient *client = new RemoteClient();
4829 client->peer_id = c.peer_id;
4830 m_clients.insert(client->peer_id, client);
4833 else if(c.type == PEER_REMOVED)
4840 core::map<u16, RemoteClient*>::Node *n;
4841 n = m_clients.find(c.peer_id);
4842 // The client should exist
4846 Mark objects to be not known by the client
4848 RemoteClient *client = n->getValue();
4850 for(core::map<u16, bool>::Iterator
4851 i = client->m_known_objects.getIterator();
4852 i.atEnd()==false; i++)
4855 u16 id = i.getNode()->getKey();
4856 ServerActiveObject* obj = m_env->getActiveObject(id);
4858 if(obj && obj->m_known_by_count > 0)
4859 obj->m_known_by_count--;
4862 // Collect information about leaving in chat
4863 std::wstring message;
4865 Player *player = m_env->getPlayer(c.peer_id);
4868 std::wstring name = narrow_to_wide(player->getName());
4871 message += L" left game";
4873 message += L" (timed out)";
4879 m_env->removePlayer(c.peer_id);
4882 // Set player client disconnected
4884 Player *player = m_env->getPlayer(c.peer_id);
4886 player->peer_id = 0;
4893 std::ostringstream os(std::ios_base::binary);
4894 for(core::map<u16, RemoteClient*>::Iterator
4895 i = m_clients.getIterator();
4896 i.atEnd() == false; i++)
4898 RemoteClient *client = i.getNode()->getValue();
4899 assert(client->peer_id == i.getNode()->getKey());
4900 if(client->serialization_version == SER_FMT_VER_INVALID)
4903 Player *player = m_env->getPlayer(client->peer_id);
4906 // Get name of player
4907 os<<player->getName()<<" ";
4910 actionstream<<player->getName()<<" "
4911 <<(c.timeout?"times out.":"leaves game.")
4912 <<" List of players: "
4913 <<os.str()<<std::endl;
4918 delete m_clients[c.peer_id];
4919 m_clients.remove(c.peer_id);
4921 // Send player info to all remaining clients
4924 // Send leave chat message to all remaining clients
4925 BroadcastChatMessage(message);
4934 void Server::handlePeerChanges()
4936 while(m_peer_change_queue.size() > 0)
4938 PeerChange c = m_peer_change_queue.pop_front();
4940 infostream<<"Server: Handling peer change: "
4941 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4944 handlePeerChange(c);
4948 u64 Server::getPlayerPrivs(Player *player)
4952 std::string playername = player->getName();
4953 // Local player gets all privileges regardless of
4954 // what's set on their account.
4955 if(g_settings->get("name") == playername)
4961 return getPlayerAuthPrivs(playername);
4965 void dedicated_server_loop(Server &server, bool &kill)
4967 DSTACK(__FUNCTION_NAME);
4969 infostream<<DTIME<<std::endl;
4970 infostream<<"========================"<<std::endl;
4971 infostream<<"Running dedicated server"<<std::endl;
4972 infostream<<"========================"<<std::endl;
4973 infostream<<std::endl;
4975 IntervalLimiter m_profiler_interval;
4979 // This is kind of a hack but can be done like this
4980 // because server.step() is very light
4982 ScopeProfiler sp(g_profiler, "dedicated server sleep");
4987 if(server.getShutdownRequested() || kill)
4989 infostream<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4996 float profiler_print_interval =
4997 g_settings->getFloat("profiler_print_interval");
4998 if(profiler_print_interval != 0)
5000 if(m_profiler_interval.step(0.030, profiler_print_interval))
5002 infostream<<"Profiler:"<<std::endl;
5003 g_profiler->print(infostream);
5004 g_profiler->clear();
5011 static int counter = 0;
5017 core::list<PlayerInfo> list = server.getPlayerInfo();
5018 core::list<PlayerInfo>::Iterator i;
5019 static u32 sum_old = 0;
5020 u32 sum = PIChecksum(list);
5023 infostream<<DTIME<<"Player info:"<<std::endl;
5024 for(i=list.begin(); i!=list.end(); i++)
5026 i->PrintLine(&infostream);