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"));
1379 Check player movements
1381 NOTE: Actually the server should handle player physics like the
1382 client does and compare player's position to what is calculated
1383 on our side. This is required when eg. players fly due to an
1387 JMutexAutoLock lock(m_env_mutex);
1388 JMutexAutoLock lock2(m_con_mutex);
1390 //float player_max_speed = BS * 4.0; // Normal speed
1391 float player_max_speed = BS * 20; // Fast speed
1392 float player_max_speed_up = BS * 20;
1394 player_max_speed *= 2.5; // Tolerance
1395 player_max_speed_up *= 2.5;
1397 for(core::map<u16, RemoteClient*>::Iterator
1398 i = m_clients.getIterator();
1399 i.atEnd() == false; i++)
1401 RemoteClient *client = i.getNode()->getValue();
1402 ServerRemotePlayer *player =
1403 static_cast<ServerRemotePlayer*>
1404 (m_env->getPlayer(client->peer_id));
1407 player->m_last_good_position_age += dtime;
1408 if(player->m_last_good_position_age >= 2.0){
1409 float age = player->m_last_good_position_age;
1410 v3f diff = (player->getPosition() - player->m_last_good_position);
1411 float d_vert = diff.Y;
1413 float d_horiz = diff.getLength();
1414 /*infostream<<player->getName()<<"'s horizontal speed is "
1415 <<(d_horiz/age)<<std::endl;*/
1416 if(d_horiz <= age * player_max_speed &&
1417 (d_vert < 0 || d_vert < age * player_max_speed_up)){
1418 player->m_last_good_position = player->getPosition();
1420 actionstream<<"Player "<<player->getName()
1421 <<" moved too fast; resetting position"
1423 player->setPosition(player->m_last_good_position);
1424 SendMovePlayer(player);
1426 player->m_last_good_position_age = 0;
1431 /* Transform liquids */
1432 m_liquid_transform_timer += dtime;
1433 if(m_liquid_transform_timer >= 1.00)
1435 m_liquid_transform_timer -= 1.00;
1437 JMutexAutoLock lock(m_env_mutex);
1439 ScopeProfiler sp(g_profiler, "Server: liquid transform");
1441 core::map<v3s16, MapBlock*> modified_blocks;
1442 m_env->getMap().transformLiquids(modified_blocks);
1447 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1448 ServerMap &map = ((ServerMap&)m_env->getMap());
1449 map.updateLighting(modified_blocks, lighting_modified_blocks);
1451 // Add blocks modified by lighting to modified_blocks
1452 for(core::map<v3s16, MapBlock*>::Iterator
1453 i = lighting_modified_blocks.getIterator();
1454 i.atEnd() == false; i++)
1456 MapBlock *block = i.getNode()->getValue();
1457 modified_blocks.insert(block->getPos(), block);
1461 Set the modified blocks unsent for all the clients
1464 JMutexAutoLock lock2(m_con_mutex);
1466 for(core::map<u16, RemoteClient*>::Iterator
1467 i = m_clients.getIterator();
1468 i.atEnd() == false; i++)
1470 RemoteClient *client = i.getNode()->getValue();
1472 if(modified_blocks.size() > 0)
1474 // Remove block from sent history
1475 client->SetBlocksNotSent(modified_blocks);
1480 // Periodically print some info
1482 float &counter = m_print_info_timer;
1488 JMutexAutoLock lock2(m_con_mutex);
1490 if(m_clients.size() != 0)
1491 infostream<<"Players:"<<std::endl;
1492 for(core::map<u16, RemoteClient*>::Iterator
1493 i = m_clients.getIterator();
1494 i.atEnd() == false; i++)
1496 //u16 peer_id = i.getNode()->getKey();
1497 RemoteClient *client = i.getNode()->getValue();
1498 Player *player = m_env->getPlayer(client->peer_id);
1501 infostream<<"* "<<player->getName()<<"\t";
1502 client->PrintInfo(infostream);
1507 //if(g_settings->getBool("enable_experimental"))
1511 Check added and deleted active objects
1514 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
1515 JMutexAutoLock envlock(m_env_mutex);
1516 JMutexAutoLock conlock(m_con_mutex);
1518 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
1520 // Radius inside which objects are active
1521 s16 radius = g_settings->getS16("active_object_send_range_blocks");
1522 radius *= MAP_BLOCKSIZE;
1524 for(core::map<u16, RemoteClient*>::Iterator
1525 i = m_clients.getIterator();
1526 i.atEnd() == false; i++)
1528 RemoteClient *client = i.getNode()->getValue();
1529 Player *player = m_env->getPlayer(client->peer_id);
1532 // This can happen if the client timeouts somehow
1533 /*infostream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
1535 <<" has no associated player"<<std::endl;*/
1538 v3s16 pos = floatToInt(player->getPosition(), BS);
1540 core::map<u16, bool> removed_objects;
1541 core::map<u16, bool> added_objects;
1542 m_env->getRemovedActiveObjects(pos, radius,
1543 client->m_known_objects, removed_objects);
1544 m_env->getAddedActiveObjects(pos, radius,
1545 client->m_known_objects, added_objects);
1547 // Ignore if nothing happened
1548 if(removed_objects.size() == 0 && added_objects.size() == 0)
1550 //infostream<<"active objects: none changed"<<std::endl;
1554 std::string data_buffer;
1558 // Handle removed objects
1559 writeU16((u8*)buf, removed_objects.size());
1560 data_buffer.append(buf, 2);
1561 for(core::map<u16, bool>::Iterator
1562 i = removed_objects.getIterator();
1563 i.atEnd()==false; i++)
1566 u16 id = i.getNode()->getKey();
1567 ServerActiveObject* obj = m_env->getActiveObject(id);
1569 // Add to data buffer for sending
1570 writeU16((u8*)buf, i.getNode()->getKey());
1571 data_buffer.append(buf, 2);
1573 // Remove from known objects
1574 client->m_known_objects.remove(i.getNode()->getKey());
1576 if(obj && obj->m_known_by_count > 0)
1577 obj->m_known_by_count--;
1580 // Handle added objects
1581 writeU16((u8*)buf, added_objects.size());
1582 data_buffer.append(buf, 2);
1583 for(core::map<u16, bool>::Iterator
1584 i = added_objects.getIterator();
1585 i.atEnd()==false; i++)
1588 u16 id = i.getNode()->getKey();
1589 ServerActiveObject* obj = m_env->getActiveObject(id);
1592 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1594 infostream<<"WARNING: "<<__FUNCTION_NAME
1595 <<": NULL object"<<std::endl;
1597 type = obj->getType();
1599 // Add to data buffer for sending
1600 writeU16((u8*)buf, id);
1601 data_buffer.append(buf, 2);
1602 writeU8((u8*)buf, type);
1603 data_buffer.append(buf, 1);
1606 data_buffer.append(serializeLongString(
1607 obj->getClientInitializationData()));
1609 data_buffer.append(serializeLongString(""));
1611 // Add to known objects
1612 client->m_known_objects.insert(i.getNode()->getKey(), false);
1615 obj->m_known_by_count++;
1619 SharedBuffer<u8> reply(2 + data_buffer.size());
1620 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1621 memcpy((char*)&reply[2], data_buffer.c_str(),
1622 data_buffer.size());
1624 m_con.Send(client->peer_id, 0, reply, true);
1626 infostream<<"Server: Sent object remove/add: "
1627 <<removed_objects.size()<<" removed, "
1628 <<added_objects.size()<<" added, "
1629 <<"packet size is "<<reply.getSize()<<std::endl;
1634 Collect a list of all the objects known by the clients
1635 and report it back to the environment.
1638 core::map<u16, bool> all_known_objects;
1640 for(core::map<u16, RemoteClient*>::Iterator
1641 i = m_clients.getIterator();
1642 i.atEnd() == false; i++)
1644 RemoteClient *client = i.getNode()->getValue();
1645 // Go through all known objects of client
1646 for(core::map<u16, bool>::Iterator
1647 i = client->m_known_objects.getIterator();
1648 i.atEnd()==false; i++)
1650 u16 id = i.getNode()->getKey();
1651 all_known_objects[id] = true;
1655 m_env->setKnownActiveObjects(whatever);
1661 Send object messages
1664 JMutexAutoLock envlock(m_env_mutex);
1665 JMutexAutoLock conlock(m_con_mutex);
1667 //ScopeProfiler sp(g_profiler, "Server: sending object messages");
1670 // Value = data sent by object
1671 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1673 // Get active object messages from environment
1676 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
1680 core::list<ActiveObjectMessage>* message_list = NULL;
1681 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1682 n = buffered_messages.find(aom.id);
1685 message_list = new core::list<ActiveObjectMessage>;
1686 buffered_messages.insert(aom.id, message_list);
1690 message_list = n->getValue();
1692 message_list->push_back(aom);
1695 // Route data to every client
1696 for(core::map<u16, RemoteClient*>::Iterator
1697 i = m_clients.getIterator();
1698 i.atEnd()==false; i++)
1700 RemoteClient *client = i.getNode()->getValue();
1701 std::string reliable_data;
1702 std::string unreliable_data;
1703 // Go through all objects in message buffer
1704 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1705 j = buffered_messages.getIterator();
1706 j.atEnd()==false; j++)
1708 // If object is not known by client, skip it
1709 u16 id = j.getNode()->getKey();
1710 if(client->m_known_objects.find(id) == NULL)
1712 // Get message list of object
1713 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1714 // Go through every message
1715 for(core::list<ActiveObjectMessage>::Iterator
1716 k = list->begin(); k != list->end(); k++)
1718 // Compose the full new data with header
1719 ActiveObjectMessage aom = *k;
1720 std::string new_data;
1723 writeU16((u8*)&buf[0], aom.id);
1724 new_data.append(buf, 2);
1726 new_data += serializeString(aom.datastring);
1727 // Add data to buffer
1729 reliable_data += new_data;
1731 unreliable_data += new_data;
1735 reliable_data and unreliable_data are now ready.
1738 if(reliable_data.size() > 0)
1740 SharedBuffer<u8> reply(2 + reliable_data.size());
1741 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1742 memcpy((char*)&reply[2], reliable_data.c_str(),
1743 reliable_data.size());
1745 m_con.Send(client->peer_id, 0, reply, true);
1747 if(unreliable_data.size() > 0)
1749 SharedBuffer<u8> reply(2 + unreliable_data.size());
1750 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1751 memcpy((char*)&reply[2], unreliable_data.c_str(),
1752 unreliable_data.size());
1753 // Send as unreliable
1754 m_con.Send(client->peer_id, 0, reply, false);
1757 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1759 infostream<<"Server: Size of object message data: "
1760 <<"reliable: "<<reliable_data.size()
1761 <<", unreliable: "<<unreliable_data.size()
1766 // Clear buffered_messages
1767 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1768 i = buffered_messages.getIterator();
1769 i.atEnd()==false; i++)
1771 delete i.getNode()->getValue();
1775 } // enable_experimental
1778 Send queued-for-sending map edit events.
1781 // Don't send too many at a time
1784 // Single change sending is disabled if queue size is not small
1785 bool disable_single_change_sending = false;
1786 if(m_unsent_map_edit_queue.size() >= 4)
1787 disable_single_change_sending = true;
1789 bool got_any_events = false;
1791 // We'll log the amount of each
1794 while(m_unsent_map_edit_queue.size() != 0)
1796 got_any_events = true;
1798 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1800 // Players far away from the change are stored here.
1801 // Instead of sending the changes, MapBlocks are set not sent
1803 core::list<u16> far_players;
1805 if(event->type == MEET_ADDNODE)
1807 //infostream<<"Server: MEET_ADDNODE"<<std::endl;
1808 prof.add("MEET_ADDNODE", 1);
1809 if(disable_single_change_sending)
1810 sendAddNode(event->p, event->n, event->already_known_by_peer,
1813 sendAddNode(event->p, event->n, event->already_known_by_peer,
1816 else if(event->type == MEET_REMOVENODE)
1818 //infostream<<"Server: MEET_REMOVENODE"<<std::endl;
1819 prof.add("MEET_REMOVENODE", 1);
1820 if(disable_single_change_sending)
1821 sendRemoveNode(event->p, event->already_known_by_peer,
1824 sendRemoveNode(event->p, event->already_known_by_peer,
1827 else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
1829 infostream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
1830 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
1831 setBlockNotSent(event->p);
1833 else if(event->type == MEET_OTHER)
1835 infostream<<"Server: MEET_OTHER"<<std::endl;
1836 prof.add("MEET_OTHER", 1);
1837 for(core::map<v3s16, bool>::Iterator
1838 i = event->modified_blocks.getIterator();
1839 i.atEnd()==false; i++)
1841 v3s16 p = i.getNode()->getKey();
1847 prof.add("unknown", 1);
1848 infostream<<"WARNING: Server: Unknown MapEditEvent "
1849 <<((u32)event->type)<<std::endl;
1853 Set blocks not sent to far players
1855 if(far_players.size() > 0)
1857 // Convert list format to that wanted by SetBlocksNotSent
1858 core::map<v3s16, MapBlock*> modified_blocks2;
1859 for(core::map<v3s16, bool>::Iterator
1860 i = event->modified_blocks.getIterator();
1861 i.atEnd()==false; i++)
1863 v3s16 p = i.getNode()->getKey();
1864 modified_blocks2.insert(p,
1865 m_env->getMap().getBlockNoCreateNoEx(p));
1867 // Set blocks not sent
1868 for(core::list<u16>::Iterator
1869 i = far_players.begin();
1870 i != far_players.end(); i++)
1873 RemoteClient *client = getClient(peer_id);
1876 client->SetBlocksNotSent(modified_blocks2);
1882 /*// Don't send too many at a time
1884 if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
1890 infostream<<"Server: MapEditEvents:"<<std::endl;
1891 prof.print(infostream);
1897 Send object positions
1900 float &counter = m_objectdata_timer;
1902 if(counter >= g_settings->getFloat("objectdata_interval"))
1904 JMutexAutoLock lock1(m_env_mutex);
1905 JMutexAutoLock lock2(m_con_mutex);
1907 //ScopeProfiler sp(g_profiler, "Server: sending player positions");
1909 SendObjectData(counter);
1916 Trigger emergethread (it somehow gets to a non-triggered but
1917 bysy state sometimes)
1920 float &counter = m_emergethread_trigger_timer;
1926 m_emergethread.trigger();
1930 // Save map, players and auth stuff
1932 float &counter = m_savemap_timer;
1934 if(counter >= g_settings->getFloat("server_map_save_interval"))
1938 ScopeProfiler sp(g_profiler, "Server: saving stuff");
1941 if(m_authmanager.isModified())
1942 m_authmanager.save();
1945 if(m_banmanager.isModified())
1946 m_banmanager.save();
1949 JMutexAutoLock lock(m_env_mutex);
1951 // Save changed parts of map
1952 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
1955 m_env->serializePlayers(m_mapsavedir);
1957 // Save environment metadata
1958 m_env->saveMeta(m_mapsavedir);
1963 void Server::Receive()
1965 DSTACK(__FUNCTION_NAME);
1966 SharedBuffer<u8> data;
1971 JMutexAutoLock conlock(m_con_mutex);
1972 datasize = m_con.Receive(peer_id, data);
1975 // This has to be called so that the client list gets synced
1976 // with the peer list of the connection
1977 handlePeerChanges();
1979 ProcessData(*data, datasize, peer_id);
1981 catch(con::InvalidIncomingDataException &e)
1983 infostream<<"Server::Receive(): "
1984 "InvalidIncomingDataException: what()="
1985 <<e.what()<<std::endl;
1987 catch(con::PeerNotFoundException &e)
1989 //NOTE: This is not needed anymore
1991 // The peer has been disconnected.
1992 // Find the associated player and remove it.
1994 /*JMutexAutoLock envlock(m_env_mutex);
1996 infostream<<"ServerThread: peer_id="<<peer_id
1997 <<" has apparently closed connection. "
1998 <<"Removing player."<<std::endl;
2000 m_env->removePlayer(peer_id);*/
2004 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
2006 DSTACK(__FUNCTION_NAME);
2007 // Environment is locked first.
2008 JMutexAutoLock envlock(m_env_mutex);
2009 JMutexAutoLock conlock(m_con_mutex);
2012 Address address = m_con.GetPeerAddress(peer_id);
2014 // drop player if is ip is banned
2015 if(m_banmanager.isIpBanned(address.serializeString())){
2016 SendAccessDenied(m_con, peer_id,
2017 L"Your ip is banned. Banned name was "
2018 +narrow_to_wide(m_banmanager.getBanName(
2019 address.serializeString())));
2020 m_con.DeletePeer(peer_id);
2024 catch(con::PeerNotFoundException &e)
2026 infostream<<"Server::ProcessData(): Cancelling: peer "
2027 <<peer_id<<" not found"<<std::endl;
2031 u8 peer_ser_ver = getClient(peer_id)->serialization_version;
2039 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
2041 if(command == TOSERVER_INIT)
2043 // [0] u16 TOSERVER_INIT
2044 // [2] u8 SER_FMT_VER_HIGHEST
2045 // [3] u8[20] player_name
2046 // [23] u8[28] password <--- can be sent without this, from old versions
2048 if(datasize < 2+1+PLAYERNAME_SIZE)
2051 infostream<<"Server: Got TOSERVER_INIT from "
2052 <<peer_id<<std::endl;
2054 // First byte after command is maximum supported
2055 // serialization version
2056 u8 client_max = data[2];
2057 u8 our_max = SER_FMT_VER_HIGHEST;
2058 // Use the highest version supported by both
2059 u8 deployed = core::min_(client_max, our_max);
2060 // If it's lower than the lowest supported, give up.
2061 if(deployed < SER_FMT_VER_LOWEST)
2062 deployed = SER_FMT_VER_INVALID;
2064 //peer->serialization_version = deployed;
2065 getClient(peer_id)->pending_serialization_version = deployed;
2067 if(deployed == SER_FMT_VER_INVALID)
2069 infostream<<"Server: Cannot negotiate "
2070 "serialization version with peer "
2071 <<peer_id<<std::endl;
2072 SendAccessDenied(m_con, peer_id,
2073 L"Your client is too old (map format)");
2078 Read and check network protocol version
2081 u16 net_proto_version = 0;
2082 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
2084 net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
2087 getClient(peer_id)->net_proto_version = net_proto_version;
2089 if(net_proto_version == 0)
2091 SendAccessDenied(m_con, peer_id,
2092 L"Your client is too old. Please upgrade.");
2096 /* Uhh... this should actually be a warning but let's do it like this */
2097 if(g_settings->getBool("strict_protocol_version_checking"))
2099 if(net_proto_version < PROTOCOL_VERSION)
2101 SendAccessDenied(m_con, peer_id,
2102 L"Your client is too old. Please upgrade.");
2112 char playername[PLAYERNAME_SIZE];
2113 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
2115 playername[i] = data[3+i];
2117 playername[PLAYERNAME_SIZE-1] = 0;
2119 if(playername[0]=='\0')
2121 infostream<<"Server: Player has empty name"<<std::endl;
2122 SendAccessDenied(m_con, peer_id,
2127 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
2129 infostream<<"Server: Player has invalid name"<<std::endl;
2130 SendAccessDenied(m_con, peer_id,
2131 L"Name contains unallowed characters");
2136 char password[PASSWORD_SIZE];
2137 if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
2139 // old version - assume blank password
2144 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2146 password[i] = data[23+i];
2148 password[PASSWORD_SIZE-1] = 0;
2151 std::string checkpwd;
2152 if(m_authmanager.exists(playername))
2154 checkpwd = m_authmanager.getPassword(playername);
2158 checkpwd = g_settings->get("default_password");
2161 /*infostream<<"Server: Client gave password '"<<password
2162 <<"', the correct one is '"<<checkpwd<<"'"<<std::endl;*/
2164 if(password != checkpwd && m_authmanager.exists(playername))
2166 infostream<<"Server: peer_id="<<peer_id
2167 <<": supplied invalid password for "
2168 <<playername<<std::endl;
2169 SendAccessDenied(m_con, peer_id, L"Invalid password");
2173 // Add player to auth manager
2174 if(m_authmanager.exists(playername) == false)
2176 infostream<<"Server: adding player "<<playername
2177 <<" to auth manager"<<std::endl;
2178 m_authmanager.add(playername);
2179 m_authmanager.setPassword(playername, checkpwd);
2180 m_authmanager.setPrivs(playername,
2181 stringToPrivs(g_settings->get("default_privs")));
2182 m_authmanager.save();
2185 // Enforce user limit.
2186 // Don't enforce for users that have some admin right
2187 if(m_clients.size() >= g_settings->getU16("max_users") &&
2188 (m_authmanager.getPrivs(playername)
2189 & (PRIV_SERVER|PRIV_BAN|PRIV_PRIVS)) == 0 &&
2190 playername != g_settings->get("name"))
2192 SendAccessDenied(m_con, peer_id, L"Too many users.");
2197 Player *player = emergePlayer(playername, password, peer_id);
2199 // If failed, cancel
2202 infostream<<"Server: peer_id="<<peer_id
2203 <<": failed to emerge player"<<std::endl;
2208 Answer with a TOCLIENT_INIT
2211 SharedBuffer<u8> reply(2+1+6+8);
2212 writeU16(&reply[0], TOCLIENT_INIT);
2213 writeU8(&reply[2], deployed);
2214 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
2215 writeU64(&reply[2+1+6], m_env->getServerMap().getSeed());
2218 m_con.Send(peer_id, 0, reply, true);
2222 Send complete position information
2224 SendMovePlayer(player);
2229 if(command == TOSERVER_INIT2)
2231 infostream<<"Server: Got TOSERVER_INIT2 from "
2232 <<peer_id<<std::endl;
2235 getClient(peer_id)->serialization_version
2236 = getClient(peer_id)->pending_serialization_version;
2239 Send some initialization data
2242 // Send tool definitions
2243 SendToolDef(m_con, peer_id, m_toolmgr);
2245 // Send node definitions
2246 SendNodeDef(m_con, peer_id, m_nodedef);
2248 // Send CraftItem definitions
2249 SendCraftItemDef(m_con, peer_id, m_craftitemdef);
2252 SendTextures(peer_id);
2254 // Send player info to all players
2257 // Send inventory to player
2258 UpdateCrafting(peer_id);
2259 SendInventory(peer_id);
2261 // Send player items to all players
2264 Player *player = m_env->getPlayer(peer_id);
2267 SendPlayerHP(player);
2271 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2272 m_env->getTimeOfDay());
2273 m_con.Send(peer_id, 0, data, true);
2276 // Send information about server to player in chat
2277 SendChatMessage(peer_id, getStatusString());
2279 // Send information about joining in chat
2281 std::wstring name = L"unknown";
2282 Player *player = m_env->getPlayer(peer_id);
2284 name = narrow_to_wide(player->getName());
2286 std::wstring message;
2289 message += L" joined game";
2290 BroadcastChatMessage(message);
2293 // Warnings about protocol version can be issued here
2294 if(getClient(peer_id)->net_proto_version < PROTOCOL_VERSION)
2296 SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT IS OLD AND MAY WORK PROPERLY WITH THIS SERVER");
2300 Check HP, respawn if necessary
2302 HandlePlayerHP(player, 0);
2308 std::ostringstream os(std::ios_base::binary);
2309 for(core::map<u16, RemoteClient*>::Iterator
2310 i = m_clients.getIterator();
2311 i.atEnd() == false; i++)
2313 RemoteClient *client = i.getNode()->getValue();
2314 assert(client->peer_id == i.getNode()->getKey());
2315 if(client->serialization_version == SER_FMT_VER_INVALID)
2318 Player *player = m_env->getPlayer(client->peer_id);
2321 // Get name of player
2322 os<<player->getName()<<" ";
2325 actionstream<<player->getName()<<" joins game. List of players: "
2326 <<os.str()<<std::endl;
2332 if(peer_ser_ver == SER_FMT_VER_INVALID)
2334 infostream<<"Server::ProcessData(): Cancelling: Peer"
2335 " serialization format invalid or not initialized."
2336 " Skipping incoming command="<<command<<std::endl;
2340 Player *player = m_env->getPlayer(peer_id);
2343 infostream<<"Server::ProcessData(): Cancelling: "
2344 "No player for peer_id="<<peer_id
2348 if(command == TOSERVER_PLAYERPOS)
2350 if(datasize < 2+12+12+4+4)
2354 v3s32 ps = readV3S32(&data[start+2]);
2355 v3s32 ss = readV3S32(&data[start+2+12]);
2356 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2357 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2358 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2359 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2360 pitch = wrapDegrees(pitch);
2361 yaw = wrapDegrees(yaw);
2363 player->setPosition(position);
2364 player->setSpeed(speed);
2365 player->setPitch(pitch);
2366 player->setYaw(yaw);
2368 /*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2369 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2370 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2372 else if(command == TOSERVER_GOTBLOCKS)
2385 u16 count = data[2];
2386 for(u16 i=0; i<count; i++)
2388 if((s16)datasize < 2+1+(i+1)*6)
2389 throw con::InvalidIncomingDataException
2390 ("GOTBLOCKS length is too short");
2391 v3s16 p = readV3S16(&data[2+1+i*6]);
2392 /*infostream<<"Server: GOTBLOCKS ("
2393 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2394 RemoteClient *client = getClient(peer_id);
2395 client->GotBlock(p);
2398 else if(command == TOSERVER_DELETEDBLOCKS)
2411 u16 count = data[2];
2412 for(u16 i=0; i<count; i++)
2414 if((s16)datasize < 2+1+(i+1)*6)
2415 throw con::InvalidIncomingDataException
2416 ("DELETEDBLOCKS length is too short");
2417 v3s16 p = readV3S16(&data[2+1+i*6]);
2418 /*infostream<<"Server: DELETEDBLOCKS ("
2419 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2420 RemoteClient *client = getClient(peer_id);
2421 client->SetBlockNotSent(p);
2424 else if(command == TOSERVER_CLICK_OBJECT)
2426 infostream<<"Server: CLICK_OBJECT not supported anymore"<<std::endl;
2429 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2431 infostream<<"Server: CLICK_ACTIVEOBJECT not supported anymore"<<std::endl;
2434 else if(command == TOSERVER_GROUND_ACTION)
2436 infostream<<"Server: GROUND_ACTION not supported anymore"<<std::endl;
2440 else if(command == TOSERVER_RELEASE)
2442 infostream<<"Server: RELEASE not supported anymore"<<std::endl;
2445 else if(command == TOSERVER_SIGNTEXT)
2447 infostream<<"Server: SIGNTEXT not supported anymore"
2451 else if(command == TOSERVER_SIGNNODETEXT)
2453 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2461 std::string datastring((char*)&data[2], datasize-2);
2462 std::istringstream is(datastring, std::ios_base::binary);
2465 is.read((char*)buf, 6);
2466 v3s16 p = readV3S16(buf);
2467 is.read((char*)buf, 2);
2468 u16 textlen = readU16(buf);
2470 for(u16 i=0; i<textlen; i++)
2472 is.read((char*)buf, 1);
2473 text += (char)buf[0];
2476 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
2480 meta->setText(text);
2482 actionstream<<player->getName()<<" writes \""<<text<<"\" to sign"
2483 <<" at "<<PP(p)<<std::endl;
2485 v3s16 blockpos = getNodeBlockPos(p);
2486 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
2489 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2493 setBlockNotSent(blockpos);
2495 else if(command == TOSERVER_INVENTORY_ACTION)
2497 /*// Ignore inventory changes if in creative mode
2498 if(g_settings->getBool("creative_mode") == true)
2500 infostream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2504 // Strip command and create a stream
2505 std::string datastring((char*)&data[2], datasize-2);
2506 infostream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2507 std::istringstream is(datastring, std::ios_base::binary);
2509 InventoryAction *a = InventoryAction::deSerialize(is);
2514 c.current_player = player;
2517 Handle craftresult specially if not in creative mode
2519 bool disable_action = false;
2520 if(a->getType() == IACTION_MOVE
2521 && g_settings->getBool("creative_mode") == false)
2523 IMoveAction *ma = (IMoveAction*)a;
2524 if(ma->to_inv == "current_player" &&
2525 ma->from_inv == "current_player")
2527 InventoryList *rlist = player->inventory.getList("craftresult");
2529 InventoryList *clist = player->inventory.getList("craft");
2531 InventoryList *mlist = player->inventory.getList("main");
2534 Craftresult is no longer preview if something
2537 if(ma->to_list == "craftresult"
2538 && ma->from_list != "craftresult")
2540 // If it currently is a preview, remove
2542 if(player->craftresult_is_preview)
2544 rlist->deleteItem(0);
2546 player->craftresult_is_preview = false;
2549 Crafting takes place if this condition is true.
2551 if(player->craftresult_is_preview &&
2552 ma->from_list == "craftresult")
2554 player->craftresult_is_preview = false;
2555 clist->decrementMaterials(1);
2557 /* Print out action */
2558 InventoryList *list =
2559 player->inventory.getList("craftresult");
2561 InventoryItem *item = list->getItem(0);
2562 std::string itemname = "NULL";
2564 itemname = item->getName();
2565 actionstream<<player->getName()<<" crafts "
2566 <<itemname<<std::endl;
2569 If the craftresult is placed on itself, move it to
2570 main inventory instead of doing the action
2572 if(ma->to_list == "craftresult"
2573 && ma->from_list == "craftresult")
2575 disable_action = true;
2577 InventoryItem *item1 = rlist->changeItem(0, NULL);
2578 mlist->addItem(item1);
2581 // Disallow moving items if not allowed to build
2582 else if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2584 disable_action = true;
2586 // if it's a locking chest, only allow the owner or server admins to move items
2587 else if (ma->from_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
2589 Strfnd fn(ma->from_inv);
2590 std::string id0 = fn.next(":");
2591 if(id0 == "nodemeta")
2594 p.X = stoi(fn.next(","));
2595 p.Y = stoi(fn.next(","));
2596 p.Z = stoi(fn.next(","));
2597 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
2598 if(meta->getOwner() != ""){
2599 if(meta->getOwner() != player->getName())
2600 disable_action = true;
2604 else if (ma->to_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
2606 Strfnd fn(ma->to_inv);
2607 std::string id0 = fn.next(":");
2608 if(id0 == "nodemeta")
2611 p.X = stoi(fn.next(","));
2612 p.Y = stoi(fn.next(","));
2613 p.Z = stoi(fn.next(","));
2614 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
2615 if(meta->getOwner() != ""){
2616 if(meta->getOwner() != player->getName())
2617 disable_action = true;
2623 if(a->getType() == IACTION_DROP)
2625 IDropAction *da = (IDropAction*)a;
2626 // Disallow dropping items if not allowed to build
2627 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2629 disable_action = true;
2631 // if it's a locking chest, only allow the owner or server admins to drop items
2632 else if (da->from_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
2634 Strfnd fn(da->from_inv);
2635 std::string id0 = fn.next(":");
2636 if(id0 == "nodemeta")
2639 p.X = stoi(fn.next(","));
2640 p.Y = stoi(fn.next(","));
2641 p.Z = stoi(fn.next(","));
2642 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
2643 if(meta->getOwner() != ""){
2644 if(meta->getOwner() != player->getName())
2645 disable_action = true;
2651 if(disable_action == false)
2653 // Feed action to player inventory
2654 a->apply(&c, this, m_env);
2662 infostream<<"TOSERVER_INVENTORY_ACTION: "
2663 <<"InventoryAction::deSerialize() returned NULL"
2667 else if(command == TOSERVER_CHAT_MESSAGE)
2675 std::string datastring((char*)&data[2], datasize-2);
2676 std::istringstream is(datastring, std::ios_base::binary);
2679 is.read((char*)buf, 2);
2680 u16 len = readU16(buf);
2682 std::wstring message;
2683 for(u16 i=0; i<len; i++)
2685 is.read((char*)buf, 2);
2686 message += (wchar_t)readU16(buf);
2689 // Get player name of this client
2690 std::wstring name = narrow_to_wide(player->getName());
2693 bool ate = scriptapi_on_chat_message(m_lua, player->getName(),
2694 wide_to_narrow(message));
2695 // If script ate the message, don't proceed
2699 // Line to send to players
2701 // Whether to send to the player that sent the line
2702 bool send_to_sender = false;
2703 // Whether to send to other players
2704 bool send_to_others = false;
2706 // Local player gets all privileges regardless of
2707 // what's set on their account.
2708 u64 privs = getPlayerPrivs(player);
2711 if(message[0] == L'/')
2713 size_t strip_size = 1;
2714 if (message[1] == L'#') // support old-style commans
2716 message = message.substr(strip_size);
2718 WStrfnd f1(message);
2719 f1.next(L" "); // Skip over /#whatever
2720 std::wstring paramstring = f1.next(L"");
2722 ServerCommandContext *ctx = new ServerCommandContext(
2723 str_split(message, L' '),
2730 std::wstring reply(processServerCommand(ctx));
2731 send_to_sender = ctx->flags & SEND_TO_SENDER;
2732 send_to_others = ctx->flags & SEND_TO_OTHERS;
2734 if (ctx->flags & SEND_NO_PREFIX)
2737 line += L"Server: " + reply;
2744 if(privs & PRIV_SHOUT)
2750 send_to_others = true;
2754 line += L"Server: You are not allowed to shout";
2755 send_to_sender = true;
2762 actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2765 Send the message to clients
2767 for(core::map<u16, RemoteClient*>::Iterator
2768 i = m_clients.getIterator();
2769 i.atEnd() == false; i++)
2771 // Get client and check that it is valid
2772 RemoteClient *client = i.getNode()->getValue();
2773 assert(client->peer_id == i.getNode()->getKey());
2774 if(client->serialization_version == SER_FMT_VER_INVALID)
2778 bool sender_selected = (peer_id == client->peer_id);
2779 if(sender_selected == true && send_to_sender == false)
2781 if(sender_selected == false && send_to_others == false)
2784 SendChatMessage(client->peer_id, line);
2788 else if(command == TOSERVER_DAMAGE)
2790 std::string datastring((char*)&data[2], datasize-2);
2791 std::istringstream is(datastring, std::ios_base::binary);
2792 u8 damage = readU8(is);
2794 if(g_settings->getBool("enable_damage"))
2796 actionstream<<player->getName()<<" damaged by "
2797 <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
2800 HandlePlayerHP(player, damage);
2804 SendPlayerHP(player);
2807 else if(command == TOSERVER_PASSWORD)
2810 [0] u16 TOSERVER_PASSWORD
2811 [2] u8[28] old password
2812 [30] u8[28] new password
2815 if(datasize != 2+PASSWORD_SIZE*2)
2817 /*char password[PASSWORD_SIZE];
2818 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2819 password[i] = data[2+i];
2820 password[PASSWORD_SIZE-1] = 0;*/
2822 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2830 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2832 char c = data[2+PASSWORD_SIZE+i];
2838 infostream<<"Server: Client requests a password change from "
2839 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
2841 std::string playername = player->getName();
2843 if(m_authmanager.exists(playername) == false)
2845 infostream<<"Server: playername not found in authmanager"<<std::endl;
2846 // Wrong old password supplied!!
2847 SendChatMessage(peer_id, L"playername not found in authmanager");
2851 std::string checkpwd = m_authmanager.getPassword(playername);
2853 if(oldpwd != checkpwd)
2855 infostream<<"Server: invalid old password"<<std::endl;
2856 // Wrong old password supplied!!
2857 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
2861 actionstream<<player->getName()<<" changes password"<<std::endl;
2863 m_authmanager.setPassword(playername, newpwd);
2865 infostream<<"Server: password change successful for "<<playername
2867 SendChatMessage(peer_id, L"Password change successful");
2869 else if(command == TOSERVER_PLAYERITEM)
2874 u16 item = readU16(&data[2]);
2875 player->wieldItem(item);
2876 SendWieldedItem(player);
2878 else if(command == TOSERVER_RESPAWN)
2883 RespawnPlayer(player);
2885 actionstream<<player->getName()<<" respawns at "
2886 <<PP(player->getPosition()/BS)<<std::endl;
2888 else if(command == TOSERVER_INTERACT)
2890 std::string datastring((char*)&data[2], datasize-2);
2891 std::istringstream is(datastring, std::ios_base::binary);
2897 [5] u32 length of the next item
2898 [9] serialized PointedThing
2900 0: start digging (from undersurface) or use
2901 1: stop digging (all parameters ignored)
2902 2: digging completed
2903 3: place block or item (to abovesurface)
2906 u8 action = readU8(is);
2907 u16 item_i = readU16(is);
2908 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
2909 PointedThing pointed;
2910 pointed.deSerialize(tmp_is);
2912 infostream<<"TOSERVER_INTERACT: action="<<(int)action<<", item="<<item_i<<", pointed="<<pointed.dump()<<std::endl;
2914 ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
2915 v3f player_pos = srp->m_last_good_position;
2917 // Update wielded item
2918 srp->wieldItem(item_i);
2920 // Get pointed to node (undefined if not POINTEDTYPE_NODE)
2921 v3s16 p_under = pointed.node_undersurface;
2922 v3s16 p_above = pointed.node_abovesurface;
2924 // Get pointed to object (NULL if not POINTEDTYPE_OBJECT)
2925 ServerActiveObject *pointed_object = NULL;
2926 if(pointed.type == POINTEDTHING_OBJECT)
2928 pointed_object = m_env->getActiveObject(pointed.object_id);
2929 if(pointed_object == NULL)
2931 infostream<<"TOSERVER_INTERACT: "
2932 "pointed object is NULL"<<std::endl;
2939 Check that target is reasonably close
2940 (only when digging or placing things)
2942 if(action == 0 || action == 2 || action == 3)
2944 v3f pointed_pos = player_pos;
2945 if(pointed.type == POINTEDTHING_NODE)
2947 pointed_pos = intToFloat(p_under, BS);
2949 else if(pointed.type == POINTEDTHING_OBJECT)
2951 pointed_pos = pointed_object->getBasePosition();
2954 float d = player_pos.getDistanceFrom(pointed_pos);
2955 float max_d = BS * 10; // Just some large enough value
2957 actionstream<<"Player "<<player->getName()
2958 <<" tried to access "<<pointed.dump()
2960 <<"d="<<d<<", max_d="<<max_d
2961 <<". ignoring."<<std::endl;
2962 // Re-send block to revert change on client-side
2963 RemoteClient *client = getClient(peer_id);
2964 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos, BS));
2965 client->SetBlockNotSent(blockpos);
2972 Make sure the player is allowed to do it
2974 bool build_priv = (getPlayerPrivs(player) & PRIV_BUILD) != 0;
2977 infostream<<"Ignoring interaction from player "<<player->getName()
2978 <<" because privileges are "<<getPlayerPrivs(player)
2980 // NOTE: no return; here, fall through
2984 0: start digging or punch object
2988 if(pointed.type == POINTEDTHING_NODE)
2991 NOTE: This can be used in the future to check if
2992 somebody is cheating, by checking the timing.
2994 bool cannot_punch_node = !build_priv;
2996 MapNode n(CONTENT_IGNORE);
3000 n = m_env->getMap().getNode(p_under);
3002 catch(InvalidPositionException &e)
3004 infostream<<"Server: Not punching: Node not found."
3005 <<" Adding block to emerge queue."
3007 m_emerge_queue.addBlock(peer_id,
3008 getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
3009 cannot_punch_node = true;
3012 if(cannot_punch_node)
3018 scriptapi_environment_on_punchnode(m_lua, p_under, n, srp);
3020 else if(pointed.type == POINTEDTHING_OBJECT)
3025 // Skip if object has been removed
3026 if(pointed_object->m_removed)
3029 actionstream<<player->getName()<<" punches object "
3030 <<pointed.object_id<<std::endl;
3033 pointed_object->punch(srp);
3041 else if(action == 1)
3046 2: Digging completed
3048 else if(action == 2)
3050 // Only complete digging of nodes
3051 if(pointed.type != POINTEDTHING_NODE)
3054 // Mandatory parameter; actually used for nothing
3055 core::map<v3s16, MapBlock*> modified_blocks;
3057 content_t material = CONTENT_IGNORE;
3058 u8 mineral = MINERAL_NONE;
3060 bool cannot_remove_node = !build_priv;
3062 MapNode n(CONTENT_IGNORE);
3065 n = m_env->getMap().getNode(p_under);
3067 mineral = n.getMineral(m_nodedef);
3068 // Get material at position
3069 material = n.getContent();
3070 // If not yet cancelled
3071 if(cannot_remove_node == false)
3073 // If it's not diggable, do nothing
3074 if(m_nodedef->get(material).diggable == false)
3076 infostream<<"Server: Not finishing digging: "
3077 <<"Node not diggable"
3079 cannot_remove_node = true;
3082 // If not yet cancelled
3083 if(cannot_remove_node == false)
3085 // Get node metadata
3086 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p_under);
3087 if(meta && meta->nodeRemovalDisabled() == true)
3089 infostream<<"Server: Not finishing digging: "
3090 <<"Node metadata disables removal"
3092 cannot_remove_node = true;
3096 catch(InvalidPositionException &e)
3098 infostream<<"Server: Not finishing digging: Node not found."
3099 <<" Adding block to emerge queue."
3101 m_emerge_queue.addBlock(peer_id,
3102 getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
3103 cannot_remove_node = true;
3107 If node can't be removed, set block to be re-sent to
3110 if(cannot_remove_node)
3112 infostream<<"Server: Not finishing digging."<<std::endl;
3114 // Client probably has wrong data.
3115 // Set block not sent, so that client will get
3117 infostream<<"Client "<<peer_id<<" tried to dig "
3118 <<"node; but node cannot be removed."
3119 <<" setting MapBlock not sent."<<std::endl;
3120 RemoteClient *client = getClient(peer_id);
3121 v3s16 blockpos = getNodeBlockPos(p_under);
3122 client->SetBlockNotSent(blockpos);
3127 actionstream<<player->getName()<<" digs "<<PP(p_under)
3128 <<", gets material "<<(int)material<<", mineral "
3129 <<(int)mineral<<std::endl;
3132 Send the removal to all close-by players.
3133 - If other player is close, send REMOVENODE
3134 - Otherwise set blocks not sent
3136 core::list<u16> far_players;
3137 sendRemoveNode(p_under, peer_id, &far_players, 30);
3140 Update and send inventory
3143 if(g_settings->getBool("creative_mode") == false)
3148 InventoryList *mlist = player->inventory.getList("main");
3151 InventoryItem *item = mlist->getItem(item_i);
3152 if(item && (std::string)item->getName() == "ToolItem")
3154 ToolItem *titem = (ToolItem*)item;
3155 std::string toolname = titem->getToolName();
3157 // Get digging properties for material and tool
3158 ToolDiggingProperties tp =
3159 m_toolmgr->getDiggingProperties(toolname);
3160 DiggingProperties prop =
3161 getDiggingProperties(material, &tp, m_nodedef);
3163 if(prop.diggable == false)
3165 infostream<<"Server: WARNING: Player digged"
3166 <<" with impossible material + tool"
3167 <<" combination"<<std::endl;
3170 bool weared_out = titem->addWear(prop.wear);
3174 mlist->deleteItem(item_i);
3180 Add dug item to inventory
3183 InventoryItem *item = NULL;
3185 if(mineral != MINERAL_NONE)
3186 item = getDiggedMineralItem(mineral, this);
3191 const std::string &dug_s = m_nodedef->get(material).dug_item;
3194 std::istringstream is(dug_s, std::ios::binary);
3195 item = InventoryItem::deSerialize(is, this);
3201 // Add a item to inventory
3202 player->inventory.addItem("main", item);
3207 if(mineral != MINERAL_NONE)
3208 item = getDiggedMineralItem(mineral, this);
3213 const std::string &extra_dug_s = m_nodedef->get(material).extra_dug_item;
3214 s32 extra_rarity = m_nodedef->get(material).extra_dug_item_rarity;
3215 if(extra_dug_s != "" && extra_rarity != 0
3216 && myrand() % extra_rarity == 0)
3218 std::istringstream is(extra_dug_s, std::ios::binary);
3219 item = InventoryItem::deSerialize(is, this);
3225 // Add a item to inventory
3226 player->inventory.addItem("main", item);
3232 (this takes some time so it is done after the quick stuff)
3235 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
3237 m_env->getMap().removeNodeAndUpdate(p_under, modified_blocks);
3240 Set blocks not sent to far players
3242 for(core::list<u16>::Iterator
3243 i = far_players.begin();
3244 i != far_players.end(); i++)
3247 RemoteClient *client = getClient(peer_id);
3250 client->SetBlocksNotSent(modified_blocks);
3256 scriptapi_environment_on_dignode(m_lua, p_under, n, srp);
3260 3: place block or right-click object
3262 else if(action == 3)
3264 if(pointed.type == POINTEDTHING_NODE)
3266 InventoryList *ilist = player->inventory.getList("main");
3271 InventoryItem *item = ilist->getItem(item_i);
3273 // If there is no item, it is not possible to add it anywhere
3278 Handle material items
3280 if(std::string("MaterialItem") == item->getName())
3282 bool cannot_place_node = !build_priv;
3285 // Don't add a node if this is not a free space
3286 MapNode n2 = m_env->getMap().getNode(p_above);
3287 if(m_nodedef->get(n2).buildable_to == false)
3289 infostream<<"Client "<<peer_id<<" tried to place"
3290 <<" node in invalid position."<<std::endl;
3291 cannot_place_node = true;
3294 catch(InvalidPositionException &e)
3296 infostream<<"Server: Ignoring ADDNODE: Node not found"
3297 <<" Adding block to emerge queue."
3299 m_emerge_queue.addBlock(peer_id,
3300 getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
3301 cannot_place_node = true;
3304 if(cannot_place_node)
3306 // Client probably has wrong data.
3307 // Set block not sent, so that client will get
3309 RemoteClient *client = getClient(peer_id);
3310 v3s16 blockpos = getNodeBlockPos(p_above);
3311 client->SetBlockNotSent(blockpos);
3315 // Reset build time counter
3316 getClient(peer_id)->m_time_from_building = 0.0;
3319 MaterialItem *mitem = (MaterialItem*)item;
3321 n.setContent(mitem->getMaterial());
3323 actionstream<<player->getName()<<" places material "
3324 <<(int)mitem->getMaterial()
3325 <<" at "<<PP(p_under)<<std::endl;
3327 // Calculate direction for wall mounted stuff
3328 if(m_nodedef->get(n).wall_mounted)
3329 n.param2 = packDir(p_under - p_above);
3331 // Calculate the direction for furnaces and chests and stuff
3332 if(m_nodedef->get(n).param_type == CPT_FACEDIR_SIMPLE)
3334 v3f playerpos = player->getPosition();
3335 v3f blockpos = intToFloat(p_above, BS) - playerpos;
3336 blockpos = blockpos.normalize();
3338 if (fabs(blockpos.X) > fabs(blockpos.Z)) {
3352 Send to all close-by players
3354 core::list<u16> far_players;
3355 sendAddNode(p_above, n, 0, &far_players, 30);
3360 InventoryList *ilist = player->inventory.getList("main");
3361 if(g_settings->getBool("creative_mode") == false && ilist)
3363 // Remove from inventory and send inventory
3364 if(mitem->getCount() == 1)
3365 ilist->deleteItem(item_i);
3373 This takes some time so it is done after the quick stuff
3375 core::map<v3s16, MapBlock*> modified_blocks;
3377 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
3379 std::string p_name = std::string(player->getName());
3380 m_env->getMap().addNodeAndUpdate(p_above, n, modified_blocks, p_name);
3383 Set blocks not sent to far players
3385 for(core::list<u16>::Iterator
3386 i = far_players.begin();
3387 i != far_players.end(); i++)
3390 RemoteClient *client = getClient(peer_id);
3393 client->SetBlocksNotSent(modified_blocks);
3399 scriptapi_environment_on_placenode(m_lua, p_above, n, srp);
3402 Calculate special events
3405 /*if(n.d == LEGN(m_nodedef, "CONTENT_MESE"))
3408 for(s16 z=-1; z<=1; z++)
3409 for(s16 y=-1; y<=1; y++)
3410 for(s16 x=-1; x<=1; x++)
3417 Place other item (not a block)
3423 infostream<<"Not allowing player to place item: "
3424 "no build privileges"<<std::endl;
3428 // Calculate a position for it
3429 v3f pos = player_pos;
3430 if(pointed.type == POINTEDTHING_NOTHING)
3432 infostream<<"Not allowing player to place item: "
3433 "pointing to nothing"<<std::endl;
3436 else if(pointed.type == POINTEDTHING_NODE)
3438 pos = intToFloat(p_above, BS);
3440 else if(pointed.type == POINTEDTHING_OBJECT)
3442 pos = pointed_object->getBasePosition();
3445 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
3446 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
3450 //pos.Y -= BS*0.25; // let it drop a bit
3453 Check that the block is loaded so that the item
3454 can properly be added to the static list too
3456 v3s16 blockpos = getNodeBlockPos(floatToInt(pos, BS));
3457 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3460 infostream<<"Error while placing item: "
3461 "block not found"<<std::endl;
3465 actionstream<<player->getName()<<" places "<<item->getName()
3466 <<" at "<<PP(pos)<<std::endl;
3471 bool remove = item->dropOrPlace(m_env, srp, pos, true, -1);
3472 if(remove && g_settings->getBool("creative_mode") == false)
3474 InventoryList *ilist = player->inventory.getList("main");
3476 // Remove from inventory and send inventory
3477 ilist->deleteItem(item_i);
3481 else if(pointed.type == POINTEDTHING_OBJECT)
3483 // Right click object
3488 // Skip if object has been removed
3489 if(pointed_object->m_removed)
3492 actionstream<<player->getName()<<" right-clicks object "
3493 <<pointed.object_id<<std::endl;
3496 pointed_object->rightClick(srp);
3504 else if(action == 4)
3506 InventoryList *ilist = player->inventory.getList("main");
3511 InventoryItem *item = ilist->getItem(item_i);
3513 // If there is no item, it is not possible to add it anywhere
3517 // Requires build privs
3520 infostream<<"Not allowing player to use item: "
3521 "no build privileges"<<std::endl;
3525 actionstream<<player->getName()<<" uses "<<item->getName()
3526 <<", pointing at "<<pointed.dump()<<std::endl;
3528 bool remove = item->use(m_env, srp, pointed);
3529 if(remove && g_settings->getBool("creative_mode") == false)
3531 InventoryList *ilist = player->inventory.getList("main");
3533 // Remove from inventory and send inventory
3534 ilist->deleteItem(item_i);
3540 Catch invalid actions
3544 infostream<<"WARNING: Server: Invalid action "
3545 <<action<<std::endl;
3548 // Complete add_to_inventory_later
3549 srp->completeAddToInventoryLater(item_i);
3552 // FIXME: Shouldn't be done unless something changed.
3553 UpdateCrafting(player->peer_id);
3554 SendInventory(player->peer_id);
3558 infostream<<"Server::ProcessData(): Ignoring "
3559 "unknown command "<<command<<std::endl;
3563 catch(SendFailedException &e)
3565 errorstream<<"Server::ProcessData(): SendFailedException: "
3571 void Server::onMapEditEvent(MapEditEvent *event)
3573 //infostream<<"Server::onMapEditEvent()"<<std::endl;
3574 if(m_ignore_map_edit_events)
3576 MapEditEvent *e = event->clone();
3577 m_unsent_map_edit_queue.push_back(e);
3580 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3582 if(id == "current_player")
3584 assert(c->current_player);
3585 return &(c->current_player->inventory);
3589 std::string id0 = fn.next(":");
3591 if(id0 == "nodemeta")
3594 p.X = stoi(fn.next(","));
3595 p.Y = stoi(fn.next(","));
3596 p.Z = stoi(fn.next(","));
3597 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3599 return meta->getInventory();
3600 infostream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3601 <<"no metadata found"<<std::endl;
3605 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3608 void Server::inventoryModified(InventoryContext *c, std::string id)
3610 if(id == "current_player")
3612 assert(c->current_player);
3614 UpdateCrafting(c->current_player->peer_id);
3615 SendInventory(c->current_player->peer_id);
3620 std::string id0 = fn.next(":");
3622 if(id0 == "nodemeta")
3625 p.X = stoi(fn.next(","));
3626 p.Y = stoi(fn.next(","));
3627 p.Z = stoi(fn.next(","));
3628 v3s16 blockpos = getNodeBlockPos(p);
3630 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3632 meta->inventoryModified();
3634 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3636 block->raiseModified(MOD_STATE_WRITE_NEEDED);
3638 setBlockNotSent(blockpos);
3643 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3646 core::list<PlayerInfo> Server::getPlayerInfo()
3648 DSTACK(__FUNCTION_NAME);
3649 JMutexAutoLock envlock(m_env_mutex);
3650 JMutexAutoLock conlock(m_con_mutex);
3652 core::list<PlayerInfo> list;
3654 core::list<Player*> players = m_env->getPlayers();
3656 core::list<Player*>::Iterator i;
3657 for(i = players.begin();
3658 i != players.end(); i++)
3662 Player *player = *i;
3665 // Copy info from connection to info struct
3666 info.id = player->peer_id;
3667 info.address = m_con.GetPeerAddress(player->peer_id);
3668 info.avg_rtt = m_con.GetPeerAvgRTT(player->peer_id);
3670 catch(con::PeerNotFoundException &e)
3672 // Set dummy peer info
3674 info.address = Address(0,0,0,0,0);
3678 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3679 info.position = player->getPosition();
3681 list.push_back(info);
3688 void Server::peerAdded(con::Peer *peer)
3690 DSTACK(__FUNCTION_NAME);
3691 infostream<<"Server::peerAdded(): peer->id="
3692 <<peer->id<<std::endl;
3695 c.type = PEER_ADDED;
3696 c.peer_id = peer->id;
3698 m_peer_change_queue.push_back(c);
3701 void Server::deletingPeer(con::Peer *peer, bool timeout)
3703 DSTACK(__FUNCTION_NAME);
3704 infostream<<"Server::deletingPeer(): peer->id="
3705 <<peer->id<<", timeout="<<timeout<<std::endl;
3708 c.type = PEER_REMOVED;
3709 c.peer_id = peer->id;
3710 c.timeout = timeout;
3711 m_peer_change_queue.push_back(c);
3718 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3720 DSTACK(__FUNCTION_NAME);
3721 std::ostringstream os(std::ios_base::binary);
3723 writeU16(os, TOCLIENT_HP);
3727 std::string s = os.str();
3728 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3730 con.Send(peer_id, 0, data, true);
3733 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3734 const std::wstring &reason)
3736 DSTACK(__FUNCTION_NAME);
3737 std::ostringstream os(std::ios_base::binary);
3739 writeU16(os, TOCLIENT_ACCESS_DENIED);
3740 os<<serializeWideString(reason);
3743 std::string s = os.str();
3744 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3746 con.Send(peer_id, 0, data, true);
3749 void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
3750 bool set_camera_point_target, v3f camera_point_target)
3752 DSTACK(__FUNCTION_NAME);
3753 std::ostringstream os(std::ios_base::binary);
3755 writeU16(os, TOCLIENT_DEATHSCREEN);
3756 writeU8(os, set_camera_point_target);
3757 writeV3F1000(os, camera_point_target);
3760 std::string s = os.str();
3761 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3763 con.Send(peer_id, 0, data, true);
3766 void Server::SendToolDef(con::Connection &con, u16 peer_id,
3767 IToolDefManager *tooldef)
3769 DSTACK(__FUNCTION_NAME);
3770 std::ostringstream os(std::ios_base::binary);
3774 u32 length of the next item
3775 serialized ToolDefManager
3777 writeU16(os, TOCLIENT_TOOLDEF);
3778 std::ostringstream tmp_os(std::ios::binary);
3779 tooldef->serialize(tmp_os);
3780 os<<serializeLongString(tmp_os.str());
3783 std::string s = os.str();
3784 infostream<<"Server::SendToolDef(): Sending tool definitions: size="
3785 <<s.size()<<std::endl;
3786 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3788 con.Send(peer_id, 0, data, true);
3791 void Server::SendNodeDef(con::Connection &con, u16 peer_id,
3792 INodeDefManager *nodedef)
3794 DSTACK(__FUNCTION_NAME);
3795 std::ostringstream os(std::ios_base::binary);
3799 u32 length of the next item
3800 serialized NodeDefManager
3802 writeU16(os, TOCLIENT_NODEDEF);
3803 std::ostringstream tmp_os(std::ios::binary);
3804 nodedef->serialize(tmp_os);
3805 os<<serializeLongString(tmp_os.str());
3808 std::string s = os.str();
3809 infostream<<"Server::SendNodeDef(): Sending node definitions: size="
3810 <<s.size()<<std::endl;
3811 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3813 con.Send(peer_id, 0, data, true);
3816 void Server::SendCraftItemDef(con::Connection &con, u16 peer_id,
3817 ICraftItemDefManager *craftitemdef)
3819 DSTACK(__FUNCTION_NAME);
3820 std::ostringstream os(std::ios_base::binary);
3824 u32 length of the next item
3825 serialized CraftItemDefManager
3827 writeU16(os, TOCLIENT_CRAFTITEMDEF);
3828 std::ostringstream tmp_os(std::ios::binary);
3829 craftitemdef->serialize(tmp_os);
3830 os<<serializeLongString(tmp_os.str());
3833 std::string s = os.str();
3834 infostream<<"Server::SendCraftItemDef(): Sending craft item definitions: size="
3835 <<s.size()<<std::endl;
3836 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3838 con.Send(peer_id, 0, data, true);
3842 Non-static send methods
3845 void Server::SendObjectData(float dtime)
3847 DSTACK(__FUNCTION_NAME);
3849 core::map<v3s16, bool> stepped_blocks;
3851 for(core::map<u16, RemoteClient*>::Iterator
3852 i = m_clients.getIterator();
3853 i.atEnd() == false; i++)
3855 u16 peer_id = i.getNode()->getKey();
3856 RemoteClient *client = i.getNode()->getValue();
3857 assert(client->peer_id == peer_id);
3859 if(client->serialization_version == SER_FMT_VER_INVALID)
3862 client->SendObjectData(this, dtime, stepped_blocks);
3866 void Server::SendPlayerInfos()
3868 DSTACK(__FUNCTION_NAME);
3870 //JMutexAutoLock envlock(m_env_mutex);
3872 // Get connected players
3873 core::list<Player*> players = m_env->getPlayers(true);
3875 u32 player_count = players.getSize();
3876 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3878 SharedBuffer<u8> data(datasize);
3879 writeU16(&data[0], TOCLIENT_PLAYERINFO);
3882 core::list<Player*>::Iterator i;
3883 for(i = players.begin();
3884 i != players.end(); i++)
3886 Player *player = *i;
3888 /*infostream<<"Server sending player info for player with "
3889 "peer_id="<<player->peer_id<<std::endl;*/
3891 writeU16(&data[start], player->peer_id);
3892 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3893 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3894 start += 2+PLAYERNAME_SIZE;
3897 //JMutexAutoLock conlock(m_con_mutex);
3900 m_con.SendToAll(0, data, true);
3903 void Server::SendInventory(u16 peer_id)
3905 DSTACK(__FUNCTION_NAME);
3907 Player* player = m_env->getPlayer(peer_id);
3914 std::ostringstream os;
3915 //os.imbue(std::locale("C"));
3917 player->inventory.serialize(os);
3919 std::string s = os.str();
3921 SharedBuffer<u8> data(s.size()+2);
3922 writeU16(&data[0], TOCLIENT_INVENTORY);
3923 memcpy(&data[2], s.c_str(), s.size());
3926 m_con.Send(peer_id, 0, data, true);
3929 std::string getWieldedItemString(const Player *player)
3931 const InventoryItem *item = player->getWieldItem();
3933 return std::string("");
3934 std::ostringstream os(std::ios_base::binary);
3935 item->serialize(os);
3939 void Server::SendWieldedItem(const Player* player)
3941 DSTACK(__FUNCTION_NAME);
3945 std::ostringstream os(std::ios_base::binary);
3947 writeU16(os, TOCLIENT_PLAYERITEM);
3949 writeU16(os, player->peer_id);
3950 os<<serializeString(getWieldedItemString(player));
3953 std::string s = os.str();
3954 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3956 m_con.SendToAll(0, data, true);
3959 void Server::SendPlayerItems()
3961 DSTACK(__FUNCTION_NAME);
3963 std::ostringstream os(std::ios_base::binary);
3964 core::list<Player *> players = m_env->getPlayers(true);
3966 writeU16(os, TOCLIENT_PLAYERITEM);
3967 writeU16(os, players.size());
3968 core::list<Player *>::Iterator i;
3969 for(i = players.begin(); i != players.end(); ++i)
3972 writeU16(os, p->peer_id);
3973 os<<serializeString(getWieldedItemString(p));
3977 std::string s = os.str();
3978 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3980 m_con.SendToAll(0, data, true);
3983 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3985 DSTACK(__FUNCTION_NAME);
3987 std::ostringstream os(std::ios_base::binary);
3991 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3992 os.write((char*)buf, 2);
3995 writeU16(buf, message.size());
3996 os.write((char*)buf, 2);
3999 for(u32 i=0; i<message.size(); i++)
4003 os.write((char*)buf, 2);
4007 std::string s = os.str();
4008 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4010 m_con.Send(peer_id, 0, data, true);
4013 void Server::BroadcastChatMessage(const std::wstring &message)
4015 for(core::map<u16, RemoteClient*>::Iterator
4016 i = m_clients.getIterator();
4017 i.atEnd() == false; i++)
4019 // Get client and check that it is valid
4020 RemoteClient *client = i.getNode()->getValue();
4021 assert(client->peer_id == i.getNode()->getKey());
4022 if(client->serialization_version == SER_FMT_VER_INVALID)
4025 SendChatMessage(client->peer_id, message);
4029 void Server::SendPlayerHP(Player *player)
4031 SendHP(m_con, player->peer_id, player->hp);
4034 void Server::SendMovePlayer(Player *player)
4036 DSTACK(__FUNCTION_NAME);
4037 std::ostringstream os(std::ios_base::binary);
4039 writeU16(os, TOCLIENT_MOVE_PLAYER);
4040 writeV3F1000(os, player->getPosition());
4041 writeF1000(os, player->getPitch());
4042 writeF1000(os, player->getYaw());
4045 v3f pos = player->getPosition();
4046 f32 pitch = player->getPitch();
4047 f32 yaw = player->getYaw();
4048 infostream<<"Server sending TOCLIENT_MOVE_PLAYER"
4049 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
4056 std::string s = os.str();
4057 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4059 m_con.Send(player->peer_id, 0, data, true);
4062 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
4063 core::list<u16> *far_players, float far_d_nodes)
4065 float maxd = far_d_nodes*BS;
4066 v3f p_f = intToFloat(p, BS);
4070 SharedBuffer<u8> reply(replysize);
4071 writeU16(&reply[0], TOCLIENT_REMOVENODE);
4072 writeS16(&reply[2], p.X);
4073 writeS16(&reply[4], p.Y);
4074 writeS16(&reply[6], p.Z);
4076 for(core::map<u16, RemoteClient*>::Iterator
4077 i = m_clients.getIterator();
4078 i.atEnd() == false; i++)
4080 // Get client and check that it is valid
4081 RemoteClient *client = i.getNode()->getValue();
4082 assert(client->peer_id == i.getNode()->getKey());
4083 if(client->serialization_version == SER_FMT_VER_INVALID)
4086 // Don't send if it's the same one
4087 if(client->peer_id == ignore_id)
4093 Player *player = m_env->getPlayer(client->peer_id);
4096 // If player is far away, only set modified blocks not sent
4097 v3f player_pos = player->getPosition();
4098 if(player_pos.getDistanceFrom(p_f) > maxd)
4100 far_players->push_back(client->peer_id);
4107 m_con.Send(client->peer_id, 0, reply, true);
4111 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
4112 core::list<u16> *far_players, float far_d_nodes)
4114 float maxd = far_d_nodes*BS;
4115 v3f p_f = intToFloat(p, BS);
4117 for(core::map<u16, RemoteClient*>::Iterator
4118 i = m_clients.getIterator();
4119 i.atEnd() == false; i++)
4121 // Get client and check that it is valid
4122 RemoteClient *client = i.getNode()->getValue();
4123 assert(client->peer_id == i.getNode()->getKey());
4124 if(client->serialization_version == SER_FMT_VER_INVALID)
4127 // Don't send if it's the same one
4128 if(client->peer_id == ignore_id)
4134 Player *player = m_env->getPlayer(client->peer_id);
4137 // If player is far away, only set modified blocks not sent
4138 v3f player_pos = player->getPosition();
4139 if(player_pos.getDistanceFrom(p_f) > maxd)
4141 far_players->push_back(client->peer_id);
4148 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
4149 SharedBuffer<u8> reply(replysize);
4150 writeU16(&reply[0], TOCLIENT_ADDNODE);
4151 writeS16(&reply[2], p.X);
4152 writeS16(&reply[4], p.Y);
4153 writeS16(&reply[6], p.Z);
4154 n.serialize(&reply[8], client->serialization_version);
4157 m_con.Send(client->peer_id, 0, reply, true);
4161 void Server::setBlockNotSent(v3s16 p)
4163 for(core::map<u16, RemoteClient*>::Iterator
4164 i = m_clients.getIterator();
4165 i.atEnd()==false; i++)
4167 RemoteClient *client = i.getNode()->getValue();
4168 client->SetBlockNotSent(p);
4172 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
4174 DSTACK(__FUNCTION_NAME);
4176 v3s16 p = block->getPos();
4180 bool completely_air = true;
4181 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4182 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4183 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4185 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
4187 completely_air = false;
4188 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
4193 infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
4195 infostream<<"[completely air] ";
4196 infostream<<std::endl;
4200 Create a packet with the block in the right format
4203 std::ostringstream os(std::ios_base::binary);
4204 block->serialize(os, ver);
4205 std::string s = os.str();
4206 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
4208 u32 replysize = 8 + blockdata.getSize();
4209 SharedBuffer<u8> reply(replysize);
4210 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
4211 writeS16(&reply[2], p.X);
4212 writeS16(&reply[4], p.Y);
4213 writeS16(&reply[6], p.Z);
4214 memcpy(&reply[8], *blockdata, blockdata.getSize());
4216 /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4217 <<": \tpacket size: "<<replysize<<std::endl;*/
4222 m_con.Send(peer_id, 1, reply, true);
4225 void Server::SendBlocks(float dtime)
4227 DSTACK(__FUNCTION_NAME);
4229 JMutexAutoLock envlock(m_env_mutex);
4230 JMutexAutoLock conlock(m_con_mutex);
4232 //TimeTaker timer("Server::SendBlocks");
4234 core::array<PrioritySortedBlockTransfer> queue;
4236 s32 total_sending = 0;
4239 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
4241 for(core::map<u16, RemoteClient*>::Iterator
4242 i = m_clients.getIterator();
4243 i.atEnd() == false; i++)
4245 RemoteClient *client = i.getNode()->getValue();
4246 assert(client->peer_id == i.getNode()->getKey());
4248 total_sending += client->SendingCount();
4250 if(client->serialization_version == SER_FMT_VER_INVALID)
4253 client->GetNextBlocks(this, dtime, queue);
4258 // Lowest priority number comes first.
4259 // Lowest is most important.
4262 for(u32 i=0; i<queue.size(); i++)
4264 //TODO: Calculate limit dynamically
4265 if(total_sending >= g_settings->getS32
4266 ("max_simultaneous_block_sends_server_total"))
4269 PrioritySortedBlockTransfer q = queue[i];
4271 MapBlock *block = NULL;
4274 block = m_env->getMap().getBlockNoCreate(q.pos);
4276 catch(InvalidPositionException &e)
4281 RemoteClient *client = getClient(q.peer_id);
4283 SendBlockNoLock(q.peer_id, block, client->serialization_version);
4285 client->SentBlock(q.pos);
4291 struct SendableTexture
4297 SendableTexture(const std::string &name_="", const std::string path_="",
4298 const std::string &data_=""):
4305 void Server::SendTextures(u16 peer_id)
4307 DSTACK(__FUNCTION_NAME);
4309 infostream<<"Server::SendTextures(): Sending textures to client"<<std::endl;
4313 // Put 5kB in one bunch (this is not accurate)
4314 u32 bytes_per_bunch = 5000;
4316 core::array< core::list<SendableTexture> > texture_bunches;
4317 texture_bunches.push_back(core::list<SendableTexture>());
4319 u32 texture_size_bunch_total = 0;
4320 core::list<ModSpec> mods = getMods(m_modspaths);
4321 for(core::list<ModSpec>::Iterator i = mods.begin();
4322 i != mods.end(); i++){
4324 std::string texturepath = mod.path + DIR_DELIM + "textures";
4325 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(texturepath);
4326 for(u32 j=0; j<dirlist.size(); j++){
4327 if(dirlist[j].dir) // Ignode dirs
4329 std::string tname = dirlist[j].name;
4330 std::string tpath = texturepath + DIR_DELIM + tname;
4332 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
4333 if(fis.good() == false){
4334 errorstream<<"Server::SendTextures(): Could not open \""
4335 <<tname<<"\" for reading"<<std::endl;
4338 std::ostringstream tmp_os(std::ios_base::binary);
4342 fis.read(buf, 1024);
4343 std::streamsize len = fis.gcount();
4344 tmp_os.write(buf, len);
4345 texture_size_bunch_total += len;
4354 errorstream<<"Server::SendTextures(): Failed to read \""
4355 <<tname<<"\""<<std::endl;
4358 /*infostream<<"Server::SendTextures(): Loaded \""
4359 <<tname<<"\""<<std::endl;*/
4361 texture_bunches[texture_bunches.size()-1].push_back(
4362 SendableTexture(tname, tpath, tmp_os.str()));
4364 // Start next bunch if got enough data
4365 if(texture_size_bunch_total >= bytes_per_bunch){
4366 texture_bunches.push_back(core::list<SendableTexture>());
4367 texture_size_bunch_total = 0;
4372 /* Create and send packets */
4374 u32 num_bunches = texture_bunches.size();
4375 for(u32 i=0; i<num_bunches; i++)
4379 u16 total number of texture bunches
4380 u16 index of this bunch
4381 u32 number of textures in this bunch
4389 std::ostringstream os(std::ios_base::binary);
4391 writeU16(os, TOCLIENT_TEXTURES);
4392 writeU16(os, num_bunches);
4394 writeU32(os, texture_bunches[i].size());
4396 for(core::list<SendableTexture>::Iterator
4397 j = texture_bunches[i].begin();
4398 j != texture_bunches[i].end(); j++){
4399 os<<serializeString(j->name);
4400 os<<serializeLongString(j->data);
4404 std::string s = os.str();
4405 infostream<<"Server::SendTextures(): bunch "<<i<<"/"<<num_bunches
4406 <<" textures="<<texture_bunches[i].size()
4407 <<" size=" <<s.size()<<std::endl;
4408 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4410 m_con.Send(peer_id, 0, data, true);
4418 void Server::HandlePlayerHP(Player *player, s16 damage)
4420 if(player->hp > damage)
4422 player->hp -= damage;
4423 SendPlayerHP(player);
4427 infostream<<"Server::HandlePlayerHP(): Player "
4428 <<player->getName()<<" dies"<<std::endl;
4432 //TODO: Throw items around
4434 // Handle players that are not connected
4435 if(player->peer_id == PEER_ID_INEXISTENT){
4436 RespawnPlayer(player);
4440 SendPlayerHP(player);
4442 RemoteClient *client = getClient(player->peer_id);
4443 if(client->net_proto_version >= 3)
4445 SendDeathscreen(m_con, player->peer_id, false, v3f(0,0,0));
4449 RespawnPlayer(player);
4454 void Server::RespawnPlayer(Player *player)
4457 ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
4458 bool repositioned = scriptapi_on_respawnplayer(m_lua, srp);
4460 v3f pos = findSpawnPos(m_env->getServerMap());
4461 player->setPosition(pos);
4462 srp->m_last_good_position = pos;
4463 srp->m_last_good_position_age = 0;
4465 SendMovePlayer(player);
4466 SendPlayerHP(player);
4469 void Server::UpdateCrafting(u16 peer_id)
4471 DSTACK(__FUNCTION_NAME);
4473 Player* player = m_env->getPlayer(peer_id);
4477 Calculate crafting stuff
4479 if(g_settings->getBool("creative_mode") == false)
4481 InventoryList *clist = player->inventory.getList("craft");
4482 InventoryList *rlist = player->inventory.getList("craftresult");
4484 if(rlist && rlist->getUsedSlots() == 0)
4485 player->craftresult_is_preview = true;
4487 if(rlist && player->craftresult_is_preview)
4489 rlist->clearItems();
4491 if(clist && rlist && player->craftresult_is_preview)
4493 // Get result of crafting grid
4495 std::vector<InventoryItem*> items;
4496 for(u16 i=0; i<9; i++){
4497 if(clist->getItem(i) == NULL)
4498 items.push_back(NULL);
4500 items.push_back(clist->getItem(i)->clone());
4502 CraftPointerInput cpi(3, items);
4504 InventoryItem *result = m_craftdef->getCraftResult(cpi, this);
4505 //InventoryItem *result = craft_get_result(items, this);
4507 rlist->addItem(result);
4510 } // if creative_mode == false
4513 RemoteClient* Server::getClient(u16 peer_id)
4515 DSTACK(__FUNCTION_NAME);
4516 //JMutexAutoLock lock(m_con_mutex);
4517 core::map<u16, RemoteClient*>::Node *n;
4518 n = m_clients.find(peer_id);
4519 // A client should exist for all peers
4521 return n->getValue();
4524 std::wstring Server::getStatusString()
4526 std::wostringstream os(std::ios_base::binary);
4529 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4531 os<<L", uptime="<<m_uptime.get();
4532 // Information about clients
4534 for(core::map<u16, RemoteClient*>::Iterator
4535 i = m_clients.getIterator();
4536 i.atEnd() == false; i++)
4538 // Get client and check that it is valid
4539 RemoteClient *client = i.getNode()->getValue();
4540 assert(client->peer_id == i.getNode()->getKey());
4541 if(client->serialization_version == SER_FMT_VER_INVALID)
4544 Player *player = m_env->getPlayer(client->peer_id);
4545 // Get name of player
4546 std::wstring name = L"unknown";
4548 name = narrow_to_wide(player->getName());
4549 // Add name to information string
4553 if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
4554 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4555 if(g_settings->get("motd") != "")
4556 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4560 // Saves g_settings to configpath given at initialization
4561 void Server::saveConfig()
4563 if(m_configpath != "")
4564 g_settings->updateConfigFile(m_configpath.c_str());
4567 void Server::notifyPlayer(const char *name, const std::wstring msg)
4569 Player *player = m_env->getPlayer(name);
4572 SendChatMessage(player->peer_id, std::wstring(L"Server: -!- ")+msg);
4575 void Server::notifyPlayers(const std::wstring msg)
4577 BroadcastChatMessage(msg);
4580 void Server::queueBlockEmerge(v3s16 blockpos, bool allow_generate)
4584 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
4585 m_emerge_queue.addBlock(PEER_ID_INEXISTENT, blockpos, flags);
4588 // IGameDef interface
4590 IToolDefManager* Server::getToolDefManager()
4594 INodeDefManager* Server::getNodeDefManager()
4598 ICraftDefManager* Server::getCraftDefManager()
4602 ICraftItemDefManager* Server::getCraftItemDefManager()
4604 return m_craftitemdef;
4606 ITextureSource* Server::getTextureSource()
4610 u16 Server::allocateUnknownNodeId(const std::string &name)
4612 return m_nodedef->allocateDummy(name);
4615 IWritableToolDefManager* Server::getWritableToolDefManager()
4619 IWritableNodeDefManager* Server::getWritableNodeDefManager()
4623 IWritableCraftDefManager* Server::getWritableCraftDefManager()
4627 IWritableCraftItemDefManager* Server::getWritableCraftItemDefManager()
4629 return m_craftitemdef;
4632 v3f findSpawnPos(ServerMap &map)
4634 //return v3f(50,50,50)*BS;
4639 nodepos = v2s16(0,0);
4644 // Try to find a good place a few times
4645 for(s32 i=0; i<1000; i++)
4648 // We're going to try to throw the player to this position
4649 v2s16 nodepos2d = v2s16(-range + (myrand()%(range*2)),
4650 -range + (myrand()%(range*2)));
4651 //v2s16 sectorpos = getNodeSectorPos(nodepos2d);
4652 // Get ground height at point (fallbacks to heightmap function)
4653 s16 groundheight = map.findGroundLevel(nodepos2d);
4654 // Don't go underwater
4655 if(groundheight < WATER_LEVEL)
4657 //infostream<<"-> Underwater"<<std::endl;
4660 // Don't go to high places
4661 if(groundheight > WATER_LEVEL + 4)
4663 //infostream<<"-> Underwater"<<std::endl;
4667 nodepos = v3s16(nodepos2d.X, groundheight-2, nodepos2d.Y);
4668 bool is_good = false;
4670 for(s32 i=0; i<10; i++){
4671 v3s16 blockpos = getNodeBlockPos(nodepos);
4672 map.emergeBlock(blockpos, true);
4673 MapNode n = map.getNodeNoEx(nodepos);
4674 if(n.getContent() == CONTENT_AIR){
4685 // Found a good place
4686 //infostream<<"Searched through "<<i<<" places."<<std::endl;
4692 return intToFloat(nodepos, BS);
4695 Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id)
4698 Try to get an existing player
4700 Player *player = m_env->getPlayer(name);
4703 // If player is already connected, cancel
4704 if(player->peer_id != 0)
4706 infostream<<"emergePlayer(): Player already connected"<<std::endl;
4711 player->peer_id = peer_id;
4713 // Reset inventory to creative if in creative mode
4714 if(g_settings->getBool("creative_mode"))
4716 // Warning: double code below
4717 // Backup actual inventory
4718 player->inventory_backup = new Inventory();
4719 *(player->inventory_backup) = player->inventory;
4720 // Set creative inventory
4721 craft_set_creative_inventory(player, this);
4728 If player with the wanted peer_id already exists, cancel.
4730 if(m_env->getPlayer(peer_id) != NULL)
4732 infostream<<"emergePlayer(): Player with wrong name but same"
4733 " peer_id already exists"<<std::endl;
4741 // Add authentication stuff
4742 m_authmanager.add(name);
4743 m_authmanager.setPassword(name, password);
4744 m_authmanager.setPrivs(name,
4745 stringToPrivs(g_settings->get("default_privs")));
4747 /* Set player position */
4749 infostream<<"Server: Finding spawn place for player \""
4750 <<name<<"\""<<std::endl;
4752 v3f pos = findSpawnPos(m_env->getServerMap());
4754 player = new ServerRemotePlayer(m_env, pos, peer_id, name);
4756 /* Add player to environment */
4757 m_env->addPlayer(player);
4760 ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
4761 scriptapi_on_newplayer(m_lua, srp);
4763 /* Add stuff to inventory */
4764 if(g_settings->getBool("creative_mode"))
4766 // Warning: double code above
4767 // Backup actual inventory
4768 player->inventory_backup = new Inventory();
4769 *(player->inventory_backup) = player->inventory;
4770 // Set creative inventory
4771 craft_set_creative_inventory(player, this);
4776 } // create new player
4779 void Server::handlePeerChange(PeerChange &c)
4781 JMutexAutoLock envlock(m_env_mutex);
4782 JMutexAutoLock conlock(m_con_mutex);
4784 if(c.type == PEER_ADDED)
4791 core::map<u16, RemoteClient*>::Node *n;
4792 n = m_clients.find(c.peer_id);
4793 // The client shouldn't already exist
4797 RemoteClient *client = new RemoteClient();
4798 client->peer_id = c.peer_id;
4799 m_clients.insert(client->peer_id, client);
4802 else if(c.type == PEER_REMOVED)
4809 core::map<u16, RemoteClient*>::Node *n;
4810 n = m_clients.find(c.peer_id);
4811 // The client should exist
4815 Mark objects to be not known by the client
4817 RemoteClient *client = n->getValue();
4819 for(core::map<u16, bool>::Iterator
4820 i = client->m_known_objects.getIterator();
4821 i.atEnd()==false; i++)
4824 u16 id = i.getNode()->getKey();
4825 ServerActiveObject* obj = m_env->getActiveObject(id);
4827 if(obj && obj->m_known_by_count > 0)
4828 obj->m_known_by_count--;
4831 // Collect information about leaving in chat
4832 std::wstring message;
4834 Player *player = m_env->getPlayer(c.peer_id);
4837 std::wstring name = narrow_to_wide(player->getName());
4840 message += L" left game";
4842 message += L" (timed out)";
4848 m_env->removePlayer(c.peer_id);
4851 // Set player client disconnected
4853 Player *player = m_env->getPlayer(c.peer_id);
4855 player->peer_id = 0;
4862 std::ostringstream os(std::ios_base::binary);
4863 for(core::map<u16, RemoteClient*>::Iterator
4864 i = m_clients.getIterator();
4865 i.atEnd() == false; i++)
4867 RemoteClient *client = i.getNode()->getValue();
4868 assert(client->peer_id == i.getNode()->getKey());
4869 if(client->serialization_version == SER_FMT_VER_INVALID)
4872 Player *player = m_env->getPlayer(client->peer_id);
4875 // Get name of player
4876 os<<player->getName()<<" ";
4879 actionstream<<player->getName()<<" "
4880 <<(c.timeout?"times out.":"leaves game.")
4881 <<" List of players: "
4882 <<os.str()<<std::endl;
4887 delete m_clients[c.peer_id];
4888 m_clients.remove(c.peer_id);
4890 // Send player info to all remaining clients
4893 // Send leave chat message to all remaining clients
4894 BroadcastChatMessage(message);
4903 void Server::handlePeerChanges()
4905 while(m_peer_change_queue.size() > 0)
4907 PeerChange c = m_peer_change_queue.pop_front();
4909 infostream<<"Server: Handling peer change: "
4910 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4913 handlePeerChange(c);
4917 u64 Server::getPlayerPrivs(Player *player)
4921 std::string playername = player->getName();
4922 // Local player gets all privileges regardless of
4923 // what's set on their account.
4924 if(g_settings->get("name") == playername)
4930 return getPlayerAuthPrivs(playername);
4934 void dedicated_server_loop(Server &server, bool &kill)
4936 DSTACK(__FUNCTION_NAME);
4938 infostream<<DTIME<<std::endl;
4939 infostream<<"========================"<<std::endl;
4940 infostream<<"Running dedicated server"<<std::endl;
4941 infostream<<"========================"<<std::endl;
4942 infostream<<std::endl;
4944 IntervalLimiter m_profiler_interval;
4948 // This is kind of a hack but can be done like this
4949 // because server.step() is very light
4951 ScopeProfiler sp(g_profiler, "dedicated server sleep");
4956 if(server.getShutdownRequested() || kill)
4958 infostream<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4965 float profiler_print_interval =
4966 g_settings->getFloat("profiler_print_interval");
4967 if(profiler_print_interval != 0)
4969 if(m_profiler_interval.step(0.030, profiler_print_interval))
4971 infostream<<"Profiler:"<<std::endl;
4972 g_profiler->print(infostream);
4973 g_profiler->clear();
4980 static int counter = 0;
4986 core::list<PlayerInfo> list = server.getPlayerInfo();
4987 core::list<PlayerInfo>::Iterator i;
4988 static u32 sum_old = 0;
4989 u32 sum = PIChecksum(list);
4992 infostream<<DTIME<<"Player info:"<<std::endl;
4993 for(i=list.begin(); i!=list.end(); i++)
4995 i->PrintLine(&infostream);