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"
50 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
52 #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
54 class MapEditEventIgnorer
57 MapEditEventIgnorer(bool *flag):
66 ~MapEditEventIgnorer()
79 void * ServerThread::Thread()
83 log_register_thread("ServerThread");
85 DSTACK(__FUNCTION_NAME);
87 BEGIN_DEBUG_EXCEPTION_HANDLER
92 //TimeTaker timer("AsyncRunStep() + Receive()");
95 //TimeTaker timer("AsyncRunStep()");
96 m_server->AsyncRunStep();
99 //infostream<<"Running m_server->Receive()"<<std::endl;
102 catch(con::NoIncomingDataException &e)
105 catch(con::PeerNotFoundException &e)
107 infostream<<"Server: PeerNotFoundException"<<std::endl;
111 END_DEBUG_EXCEPTION_HANDLER(errorstream)
116 void * EmergeThread::Thread()
120 log_register_thread("EmergeThread");
122 DSTACK(__FUNCTION_NAME);
124 BEGIN_DEBUG_EXCEPTION_HANDLER
126 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
129 Get block info from queue, emerge them and send them
132 After queue is empty, exit.
136 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
140 SharedPtr<QueuedBlockEmerge> q(qptr);
146 Do not generate over-limit
148 if(blockpos_over_limit(p))
151 //infostream<<"EmergeThread::Thread(): running"<<std::endl;
153 //TimeTaker timer("block emerge");
156 Try to emerge it from somewhere.
158 If it is only wanted as optional, only loading from disk
163 Check if any peer wants it as non-optional. In that case it
166 Also decrement the emerge queue count in clients.
169 bool only_from_disk = true;
172 core::map<u16, u8>::Iterator i;
173 for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
175 //u16 peer_id = i.getNode()->getKey();
178 u8 flags = i.getNode()->getValue();
179 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
180 only_from_disk = false;
185 if(enable_mapgen_debug_info)
186 infostream<<"EmergeThread: p="
187 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
188 <<"only_from_disk="<<only_from_disk<<std::endl;
190 ServerMap &map = ((ServerMap&)m_server->m_env->getMap());
192 MapBlock *block = NULL;
193 bool got_block = true;
194 core::map<v3s16, MapBlock*> modified_blocks;
197 Try to fetch block from memory or disk.
198 If not found and asked to generate, initialize generator.
201 bool started_generate = false;
202 mapgen::BlockMakeData data;
205 JMutexAutoLock envlock(m_server->m_env_mutex);
207 // Load sector if it isn't loaded
208 if(map.getSectorNoGenerateNoEx(p2d) == NULL)
209 map.loadSectorMeta(p2d);
211 // Attempt to load block
212 block = map.getBlockNoCreateNoEx(p);
213 if(!block || block->isDummy() || !block->isGenerated())
215 if(enable_mapgen_debug_info)
216 infostream<<"EmergeThread: not in memory, "
217 <<"attempting to load from disk"<<std::endl;
219 block = map.loadBlock(p);
222 // If could not load and allowed to generate, start generation
223 // inside this same envlock
224 if(only_from_disk == false &&
225 (block == NULL || block->isGenerated() == false)){
226 if(enable_mapgen_debug_info)
227 infostream<<"EmergeThread: generating"<<std::endl;
228 started_generate = true;
230 map.initBlockMake(&data, p);
235 If generator was initialized, generate now when envlock is free.
240 ScopeProfiler sp(g_profiler, "EmergeThread: mapgen::make_block",
242 TimeTaker t("mapgen::make_block()");
244 mapgen::make_block(&data);
246 if(enable_mapgen_debug_info == false)
247 t.stop(true); // Hide output
251 // Lock environment again to access the map
252 JMutexAutoLock envlock(m_server->m_env_mutex);
254 ScopeProfiler sp(g_profiler, "EmergeThread: after "
255 "mapgen::make_block (envlock)", SPT_AVG);
257 // Blit data back on map, update lighting, add mobs and
258 // whatever this does
259 map.finishBlockMake(&data, modified_blocks);
262 block = map.getBlockNoCreateNoEx(p);
265 Do some post-generate stuff
268 v3s16 minp = block->getPos()*MAP_BLOCKSIZE;
269 v3s16 maxp = minp + v3s16(1,1,1)*(MAP_BLOCKSIZE-1);
270 scriptapi_environment_on_generated(m_server->m_lua,
273 if(enable_mapgen_debug_info)
274 infostream<<"EmergeThread: ended up with: "
275 <<analyze_block(block)<<std::endl;
278 Ignore map edit events, they will not need to be
279 sent to anybody because the block hasn't been sent
282 MapEditEventIgnorer ign(&m_server->m_ignore_map_edit_events);
284 // Activate objects and stuff
285 m_server->m_env->activateBlock(block, 3600);
293 Set sent status of modified blocks on clients
296 // NOTE: Server's clients are also behind the connection mutex
297 JMutexAutoLock lock(m_server->m_con_mutex);
300 Add the originally fetched block to the modified list
304 modified_blocks.insert(p, block);
308 Set the modified blocks unsent for all the clients
311 for(core::map<u16, RemoteClient*>::Iterator
312 i = m_server->m_clients.getIterator();
313 i.atEnd() == false; i++)
315 RemoteClient *client = i.getNode()->getValue();
317 if(modified_blocks.size() > 0)
319 // Remove block from sent history
320 client->SetBlocksNotSent(modified_blocks);
326 END_DEBUG_EXCEPTION_HANDLER(errorstream)
328 log_deregister_thread();
333 void RemoteClient::GetNextBlocks(Server *server, float dtime,
334 core::array<PrioritySortedBlockTransfer> &dest)
336 DSTACK(__FUNCTION_NAME);
339 TimeTaker timer("RemoteClient::GetNextBlocks", &timer_result);*/
342 m_nothing_to_send_pause_timer -= dtime;
343 m_nearest_unsent_reset_timer += dtime;
345 if(m_nothing_to_send_pause_timer >= 0)
350 // Won't send anything if already sending
351 if(m_blocks_sending.size() >= g_settings->getU16
352 ("max_simultaneous_block_sends_per_client"))
354 //infostream<<"Not sending any blocks, Queue full."<<std::endl;
358 //TimeTaker timer("RemoteClient::GetNextBlocks");
360 Player *player = server->m_env->getPlayer(peer_id);
362 assert(player != NULL);
364 v3f playerpos = player->getPosition();
365 v3f playerspeed = player->getSpeed();
366 v3f playerspeeddir(0,0,0);
367 if(playerspeed.getLength() > 1.0*BS)
368 playerspeeddir = playerspeed / playerspeed.getLength();
369 // Predict to next block
370 v3f playerpos_predicted = playerpos + playerspeeddir*MAP_BLOCKSIZE*BS;
372 v3s16 center_nodepos = floatToInt(playerpos_predicted, BS);
374 v3s16 center = getNodeBlockPos(center_nodepos);
376 // Camera position and direction
377 v3f camera_pos = player->getEyePosition();
378 v3f camera_dir = v3f(0,0,1);
379 camera_dir.rotateYZBy(player->getPitch());
380 camera_dir.rotateXZBy(player->getYaw());
382 /*infostream<<"camera_dir=("<<camera_dir.X<<","<<camera_dir.Y<<","
383 <<camera_dir.Z<<")"<<std::endl;*/
386 Get the starting value of the block finder radius.
389 if(m_last_center != center)
391 m_nearest_unsent_d = 0;
392 m_last_center = center;
395 /*infostream<<"m_nearest_unsent_reset_timer="
396 <<m_nearest_unsent_reset_timer<<std::endl;*/
398 // Reset periodically to workaround for some bugs or stuff
399 if(m_nearest_unsent_reset_timer > 20.0)
401 m_nearest_unsent_reset_timer = 0;
402 m_nearest_unsent_d = 0;
403 //infostream<<"Resetting m_nearest_unsent_d for "
404 // <<server->getPlayerName(peer_id)<<std::endl;
407 //s16 last_nearest_unsent_d = m_nearest_unsent_d;
408 s16 d_start = m_nearest_unsent_d;
410 //infostream<<"d_start="<<d_start<<std::endl;
412 u16 max_simul_sends_setting = g_settings->getU16
413 ("max_simultaneous_block_sends_per_client");
414 u16 max_simul_sends_usually = max_simul_sends_setting;
417 Check the time from last addNode/removeNode.
419 Decrease send rate if player is building stuff.
421 m_time_from_building += dtime;
422 if(m_time_from_building < g_settings->getFloat(
423 "full_block_send_enable_min_time_from_building"))
425 max_simul_sends_usually
426 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
430 Number of blocks sending + number of blocks selected for sending
432 u32 num_blocks_selected = m_blocks_sending.size();
435 next time d will be continued from the d from which the nearest
436 unsent block was found this time.
438 This is because not necessarily any of the blocks found this
439 time are actually sent.
441 s32 new_nearest_unsent_d = -1;
443 s16 d_max = g_settings->getS16("max_block_send_distance");
444 s16 d_max_gen = g_settings->getS16("max_block_generate_distance");
446 // Don't loop very much at a time
447 s16 max_d_increment_at_time = 2;
448 if(d_max > d_start + max_d_increment_at_time)
449 d_max = d_start + max_d_increment_at_time;
450 /*if(d_max_gen > d_start+2)
451 d_max_gen = d_start+2;*/
453 //infostream<<"Starting from "<<d_start<<std::endl;
455 s32 nearest_emerged_d = -1;
456 s32 nearest_emergefull_d = -1;
457 s32 nearest_sent_d = -1;
458 bool queue_is_full = false;
461 for(d = d_start; d <= d_max; d++)
463 /*errorstream<<"checking d="<<d<<" for "
464 <<server->getPlayerName(peer_id)<<std::endl;*/
465 //infostream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
468 If m_nearest_unsent_d was changed by the EmergeThread
469 (it can change it to 0 through SetBlockNotSent),
471 Else update m_nearest_unsent_d
473 /*if(m_nearest_unsent_d != last_nearest_unsent_d)
475 d = m_nearest_unsent_d;
476 last_nearest_unsent_d = m_nearest_unsent_d;
480 Get the border/face dot coordinates of a "d-radiused"
483 core::list<v3s16> list;
484 getFacePositions(list, d);
486 core::list<v3s16>::Iterator li;
487 for(li=list.begin(); li!=list.end(); li++)
489 v3s16 p = *li + center;
493 - Don't allow too many simultaneous transfers
494 - EXCEPT when the blocks are very close
496 Also, don't send blocks that are already flying.
499 // Start with the usual maximum
500 u16 max_simul_dynamic = max_simul_sends_usually;
502 // If block is very close, allow full maximum
503 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
504 max_simul_dynamic = max_simul_sends_setting;
506 // Don't select too many blocks for sending
507 if(num_blocks_selected >= max_simul_dynamic)
509 queue_is_full = true;
510 goto queue_full_break;
513 // Don't send blocks that are currently being transferred
514 if(m_blocks_sending.find(p) != NULL)
520 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
521 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
522 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
523 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
524 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
525 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
528 // If this is true, inexistent block will be made from scratch
529 bool generate = d <= d_max_gen;
532 /*// Limit the generating area vertically to 2/3
533 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
536 // Limit the send area vertically to 1/2
537 if(abs(p.Y - center.Y) > d_max / 2)
543 If block is far away, don't generate it unless it is
549 // Block center y in nodes
550 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
551 // Don't generate if it's very high or very low
552 if(y < -64 || y > 64)
556 v2s16 p2d_nodes_center(
560 // Get ground height in nodes
561 s16 gh = server->m_env->getServerMap().findGroundLevel(
564 // If differs a lot, don't generate
565 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
567 // Actually, don't even send it
573 //infostream<<"d="<<d<<std::endl;
576 Don't generate or send if not in sight
577 FIXME This only works if the client uses a small enough
578 FOV setting. The default of 72 degrees is fine.
581 float camera_fov = (72.0*PI/180) * 4./3.;
582 if(isBlockInSight(p, camera_pos, camera_dir, camera_fov, 10000*BS) == false)
588 Don't send already sent blocks
591 if(m_blocks_sent.find(p) != NULL)
598 Check if map has this block
600 MapBlock *block = server->m_env->getMap().getBlockNoCreateNoEx(p);
602 bool surely_not_found_on_disk = false;
603 bool block_is_invalid = false;
606 // Reset usage timer, this block will be of use in the future.
607 block->resetUsageTimer();
609 // Block is dummy if data doesn't exist.
610 // It means it has been not found from disk and not generated
613 surely_not_found_on_disk = true;
616 // Block is valid if lighting is up-to-date and data exists
617 if(block->isValid() == false)
619 block_is_invalid = true;
622 /*if(block->isFullyGenerated() == false)
624 block_is_invalid = true;
629 ServerMap *map = (ServerMap*)(&server->m_env->getMap());
630 v2s16 chunkpos = map->sector_to_chunk(p2d);
631 if(map->chunkNonVolatile(chunkpos) == false)
632 block_is_invalid = true;
634 if(block->isGenerated() == false)
635 block_is_invalid = true;
638 If block is not close, don't send it unless it is near
641 Block is near ground level if night-time mesh
642 differs from day-time mesh.
646 if(block->dayNightDiffed() == false)
653 If block has been marked to not exist on disk (dummy)
654 and generating new ones is not wanted, skip block.
656 if(generate == false && surely_not_found_on_disk == true)
663 Add inexistent block to emerge queue.
665 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
667 //TODO: Get value from somewhere
668 // Allow only one block in emerge queue
669 //if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
670 // Allow two blocks in queue per client
671 //if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
673 // Make it more responsive when needing to generate stuff
674 if(surely_not_found_on_disk)
676 if(server->m_emerge_queue.peerItemCount(peer_id) < max_emerge)
678 //infostream<<"Adding block to emerge queue"<<std::endl;
680 // Add it to the emerge queue and trigger the thread
683 if(generate == false)
684 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
686 server->m_emerge_queue.addBlock(peer_id, p, flags);
687 server->m_emergethread.trigger();
689 if(nearest_emerged_d == -1)
690 nearest_emerged_d = d;
692 if(nearest_emergefull_d == -1)
693 nearest_emergefull_d = d;
700 if(nearest_sent_d == -1)
704 Add block to send queue
707 /*errorstream<<"sending from d="<<d<<" to "
708 <<server->getPlayerName(peer_id)<<std::endl;*/
710 PrioritySortedBlockTransfer q((float)d, p, peer_id);
714 num_blocks_selected += 1;
719 //infostream<<"Stopped at "<<d<<std::endl;
721 // If nothing was found for sending and nothing was queued for
722 // emerging, continue next time browsing from here
723 if(nearest_emerged_d != -1){
724 new_nearest_unsent_d = nearest_emerged_d;
725 } else if(nearest_emergefull_d != -1){
726 new_nearest_unsent_d = nearest_emergefull_d;
728 if(d > g_settings->getS16("max_block_send_distance")){
729 new_nearest_unsent_d = 0;
730 m_nothing_to_send_pause_timer = 2.0;
731 /*infostream<<"GetNextBlocks(): d wrapped around for "
732 <<server->getPlayerName(peer_id)
733 <<"; setting to 0 and pausing"<<std::endl;*/
735 if(nearest_sent_d != -1)
736 new_nearest_unsent_d = nearest_sent_d;
738 new_nearest_unsent_d = d;
742 if(new_nearest_unsent_d != -1)
743 m_nearest_unsent_d = new_nearest_unsent_d;
745 /*timer_result = timer.stop(true);
746 if(timer_result != 0)
747 infostream<<"GetNextBlocks duration: "<<timer_result<<" (!=0)"<<std::endl;*/
750 void RemoteClient::SendObjectData(
753 core::map<v3s16, bool> &stepped_blocks
756 DSTACK(__FUNCTION_NAME);
758 // Can't send anything without knowing version
759 if(serialization_version == SER_FMT_VER_INVALID)
761 infostream<<"RemoteClient::SendObjectData(): Not sending, no version."
767 Send a TOCLIENT_OBJECTDATA packet.
771 u16 number of player positions
783 std::ostringstream os(std::ios_base::binary);
787 writeU16(buf, TOCLIENT_OBJECTDATA);
788 os.write((char*)buf, 2);
791 Get and write player data
794 // Get connected players
795 core::list<Player*> players = server->m_env->getPlayers(true);
797 // Write player count
798 u16 playercount = players.size();
799 writeU16(buf, playercount);
800 os.write((char*)buf, 2);
802 core::list<Player*>::Iterator i;
803 for(i = players.begin();
804 i != players.end(); i++)
808 v3f pf = player->getPosition();
809 v3f sf = player->getSpeed();
811 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
812 v3s32 speed_i (sf.X*100, sf.Y*100, sf.Z*100);
813 s32 pitch_i (player->getPitch() * 100);
814 s32 yaw_i (player->getYaw() * 100);
816 writeU16(buf, player->peer_id);
817 os.write((char*)buf, 2);
818 writeV3S32(buf, position_i);
819 os.write((char*)buf, 12);
820 writeV3S32(buf, speed_i);
821 os.write((char*)buf, 12);
822 writeS32(buf, pitch_i);
823 os.write((char*)buf, 4);
824 writeS32(buf, yaw_i);
825 os.write((char*)buf, 4);
829 Get and write object data (dummy, for compatibility)
834 os.write((char*)buf, 2);
840 //infostream<<"Server: Sending object data to "<<peer_id<<std::endl;
843 std::string s = os.str();
844 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
845 // Send as unreliable
846 server->m_con.Send(peer_id, 0, data, false);
849 void RemoteClient::GotBlock(v3s16 p)
851 if(m_blocks_sending.find(p) != NULL)
852 m_blocks_sending.remove(p);
855 /*infostream<<"RemoteClient::GotBlock(): Didn't find in"
856 " m_blocks_sending"<<std::endl;*/
857 m_excess_gotblocks++;
859 m_blocks_sent.insert(p, true);
862 void RemoteClient::SentBlock(v3s16 p)
864 if(m_blocks_sending.find(p) == NULL)
865 m_blocks_sending.insert(p, 0.0);
867 infostream<<"RemoteClient::SentBlock(): Sent block"
868 " already in m_blocks_sending"<<std::endl;
871 void RemoteClient::SetBlockNotSent(v3s16 p)
873 m_nearest_unsent_d = 0;
875 if(m_blocks_sending.find(p) != NULL)
876 m_blocks_sending.remove(p);
877 if(m_blocks_sent.find(p) != NULL)
878 m_blocks_sent.remove(p);
881 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
883 m_nearest_unsent_d = 0;
885 for(core::map<v3s16, MapBlock*>::Iterator
886 i = blocks.getIterator();
887 i.atEnd()==false; i++)
889 v3s16 p = i.getNode()->getKey();
891 if(m_blocks_sending.find(p) != NULL)
892 m_blocks_sending.remove(p);
893 if(m_blocks_sent.find(p) != NULL)
894 m_blocks_sent.remove(p);
902 PlayerInfo::PlayerInfo()
908 void PlayerInfo::PrintLine(std::ostream *s)
911 (*s)<<"\""<<name<<"\" ("
912 <<(position.X/10)<<","<<(position.Y/10)
913 <<","<<(position.Z/10)<<") ";
915 (*s)<<" avg_rtt="<<avg_rtt;
919 u32 PIChecksum(core::list<PlayerInfo> &l)
921 core::list<PlayerInfo>::Iterator i;
924 for(i=l.begin(); i!=l.end(); i++)
926 checksum += a * (i->id+1);
927 checksum ^= 0x435aafcd;
941 std::set<std::string> depends;
942 std::set<std::string> unsatisfied_depends;
944 ModSpec(const std::string &name_="", const std::string path_="",
945 const std::set<std::string> &depends_=std::set<std::string>()):
949 unsatisfied_depends(depends_)
953 // Get a dependency-sorted list of ModSpecs
954 static core::list<ModSpec> getMods(core::list<std::string> &modspaths)
956 std::queue<ModSpec> mods_satisfied;
957 core::list<ModSpec> mods_unsorted;
958 core::list<ModSpec> mods_sorted;
959 for(core::list<std::string>::Iterator i = modspaths.begin();
960 i != modspaths.end(); i++){
961 std::string modspath = *i;
962 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(modspath);
963 for(u32 j=0; j<dirlist.size(); j++){
966 std::string modname = dirlist[j].name;
967 std::string modpath = modspath + DIR_DELIM + modname;
968 std::set<std::string> depends;
969 std::ifstream is((modpath+DIR_DELIM+"depends.txt").c_str(),
970 std::ios_base::binary);
973 std::getline(is, dep);
978 ModSpec spec(modname, modpath, depends);
979 mods_unsorted.push_back(spec);
981 mods_satisfied.push(spec);
984 // Sort by depencencies
985 while(!mods_satisfied.empty()){
986 ModSpec mod = mods_satisfied.front();
987 mods_satisfied.pop();
988 mods_sorted.push_back(mod);
989 for(core::list<ModSpec>::Iterator i = mods_unsorted.begin();
990 i != mods_unsorted.end(); i++){
992 if(mod2.unsatisfied_depends.empty())
994 mod2.unsatisfied_depends.erase(mod.name);
995 if(!mod2.unsatisfied_depends.empty())
997 mods_satisfied.push(mod2);
1000 // Check unsatisfied dependencies
1001 for(core::list<ModSpec>::Iterator i = mods_unsorted.begin();
1002 i != mods_unsorted.end(); i++){
1004 if(mod.unsatisfied_depends.empty())
1006 errorstream<<"mod \""<<mod.name
1007 <<"\" has unsatisfied dependencies:";
1008 for(std::set<std::string>::iterator
1009 i = mod.unsatisfied_depends.begin();
1010 i != mod.unsatisfied_depends.end(); i++){
1011 errorstream<<" \""<<(*i)<<"\"";
1013 errorstream<<". Loading nevertheless."<<std::endl;
1014 mods_sorted.push_back(mod);
1024 std::string mapsavedir,
1025 std::string configpath
1028 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
1029 m_authmanager(mapsavedir+DIR_DELIM+"auth.txt"),
1030 m_banmanager(mapsavedir+DIR_DELIM+"ipban.txt"),
1032 m_toolmgr(createToolDefManager()),
1033 m_nodedef(createNodeDefManager()),
1034 m_craftdef(createCraftDefManager()),
1036 m_emergethread(this),
1038 m_time_of_day_send_timer(0),
1040 m_mapsavedir(mapsavedir),
1041 m_configpath(configpath),
1042 m_shutdown_requested(false),
1043 m_ignore_map_edit_events(false),
1044 m_ignore_map_edit_events_peer_id(0)
1046 m_liquid_transform_timer = 0.0;
1047 m_print_info_timer = 0.0;
1048 m_objectdata_timer = 0.0;
1049 m_emergethread_trigger_timer = 0.0;
1050 m_savemap_timer = 0.0;
1054 m_step_dtime_mutex.Init();
1057 JMutexAutoLock envlock(m_env_mutex);
1058 JMutexAutoLock conlock(m_con_mutex);
1060 infostream<<"m_nodedef="<<m_nodedef<<std::endl;
1062 // Path to builtin.lua
1063 std::string builtinpath = porting::path_data + DIR_DELIM + "builtin.lua";
1064 // Add default global mod path
1065 m_modspaths.push_back(porting::path_data + DIR_DELIM + "mods");
1067 // Initialize scripting
1069 infostream<<"Server: Initializing scripting"<<std::endl;
1070 m_lua = script_init();
1073 scriptapi_export(m_lua, this);
1074 // Load and run builtin.lua
1075 infostream<<"Server: Loading builtin Lua stuff from \""<<builtinpath
1077 bool success = script_load(m_lua, builtinpath.c_str());
1079 errorstream<<"Server: Failed to load and run "
1080 <<builtinpath<<std::endl;
1083 // Load and run "mod" scripts
1084 core::list<ModSpec> mods = getMods(m_modspaths);
1085 for(core::list<ModSpec>::Iterator i = mods.begin();
1086 i != mods.end(); i++){
1088 infostream<<"Server: Loading mod \""<<mod.name<<"\""<<std::endl;
1089 std::string scriptpath = mod.path + DIR_DELIM + "init.lua";
1090 bool success = script_load(m_lua, scriptpath.c_str());
1092 errorstream<<"Server: Failed to load and run "
1093 <<scriptpath<<std::endl;
1098 // Initialize Environment
1100 m_env = new ServerEnvironment(new ServerMap(mapsavedir, this), m_lua,
1103 // Give environment reference to scripting api
1104 scriptapi_add_environment(m_lua, m_env);
1106 // Register us to receive map edit events
1107 m_env->getMap().addEventReceiver(this);
1109 // If file exists, load environment metadata
1110 if(fs::PathExists(m_mapsavedir+DIR_DELIM+"env_meta.txt"))
1112 infostream<<"Server: Loading environment metadata"<<std::endl;
1113 m_env->loadMeta(m_mapsavedir);
1117 infostream<<"Server: Loading players"<<std::endl;
1118 m_env->deSerializePlayers(m_mapsavedir);
1123 infostream<<"Server::~Server()"<<std::endl;
1126 Send shutdown message
1129 JMutexAutoLock conlock(m_con_mutex);
1131 std::wstring line = L"*** Server shutting down";
1134 Send the message to clients
1136 for(core::map<u16, RemoteClient*>::Iterator
1137 i = m_clients.getIterator();
1138 i.atEnd() == false; i++)
1140 // Get client and check that it is valid
1141 RemoteClient *client = i.getNode()->getValue();
1142 assert(client->peer_id == i.getNode()->getKey());
1143 if(client->serialization_version == SER_FMT_VER_INVALID)
1147 SendChatMessage(client->peer_id, line);
1149 catch(con::PeerNotFoundException &e)
1155 JMutexAutoLock envlock(m_env_mutex);
1160 infostream<<"Server: Saving players"<<std::endl;
1161 m_env->serializePlayers(m_mapsavedir);
1164 Save environment metadata
1166 infostream<<"Server: Saving environment metadata"<<std::endl;
1167 m_env->saveMeta(m_mapsavedir);
1179 JMutexAutoLock clientslock(m_con_mutex);
1181 for(core::map<u16, RemoteClient*>::Iterator
1182 i = m_clients.getIterator();
1183 i.atEnd() == false; i++)
1186 // NOTE: These are removed by env destructor
1188 u16 peer_id = i.getNode()->getKey();
1189 JMutexAutoLock envlock(m_env_mutex);
1190 m_env->removePlayer(peer_id);
1194 delete i.getNode()->getValue();
1198 // Delete Environment
1204 // Deinitialize scripting
1205 infostream<<"Server: Deinitializing scripting"<<std::endl;
1206 script_deinit(m_lua);
1209 void Server::start(unsigned short port)
1211 DSTACK(__FUNCTION_NAME);
1212 // Stop thread if already running
1215 // Initialize connection
1216 m_con.SetTimeoutMs(30);
1220 m_thread.setRun(true);
1223 infostream<<"Server: Started on port "<<port<<std::endl;
1228 DSTACK(__FUNCTION_NAME);
1230 infostream<<"Server: Stopping and waiting threads"<<std::endl;
1232 // Stop threads (set run=false first so both start stopping)
1233 m_thread.setRun(false);
1234 m_emergethread.setRun(false);
1236 m_emergethread.stop();
1238 infostream<<"Server: Threads stopped"<<std::endl;
1241 void Server::step(float dtime)
1243 DSTACK(__FUNCTION_NAME);
1248 JMutexAutoLock lock(m_step_dtime_mutex);
1249 m_step_dtime += dtime;
1253 void Server::AsyncRunStep()
1255 DSTACK(__FUNCTION_NAME);
1257 g_profiler->add("Server::AsyncRunStep (num)", 1);
1261 JMutexAutoLock lock1(m_step_dtime_mutex);
1262 dtime = m_step_dtime;
1266 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
1267 // Send blocks to clients
1274 g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
1276 //infostream<<"Server steps "<<dtime<<std::endl;
1277 //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1280 JMutexAutoLock lock1(m_step_dtime_mutex);
1281 m_step_dtime -= dtime;
1288 m_uptime.set(m_uptime.get() + dtime);
1292 // Process connection's timeouts
1293 JMutexAutoLock lock2(m_con_mutex);
1294 ScopeProfiler sp(g_profiler, "Server: connection timeout processing");
1295 m_con.RunTimeouts(dtime);
1299 // This has to be called so that the client list gets synced
1300 // with the peer list of the connection
1301 handlePeerChanges();
1305 Update m_time_of_day and overall game time
1308 JMutexAutoLock envlock(m_env_mutex);
1310 m_time_counter += dtime;
1311 f32 speed = g_settings->getFloat("time_speed") * 24000./(24.*3600);
1312 u32 units = (u32)(m_time_counter*speed);
1313 m_time_counter -= (f32)units / speed;
1315 m_env->setTimeOfDay((m_env->getTimeOfDay() + units) % 24000);
1317 //infostream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1320 Send to clients at constant intervals
1323 m_time_of_day_send_timer -= dtime;
1324 if(m_time_of_day_send_timer < 0.0)
1326 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
1328 //JMutexAutoLock envlock(m_env_mutex);
1329 JMutexAutoLock conlock(m_con_mutex);
1331 for(core::map<u16, RemoteClient*>::Iterator
1332 i = m_clients.getIterator();
1333 i.atEnd() == false; i++)
1335 RemoteClient *client = i.getNode()->getValue();
1336 //Player *player = m_env->getPlayer(client->peer_id);
1338 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1339 m_env->getTimeOfDay());
1341 m_con.Send(client->peer_id, 0, data, true);
1347 JMutexAutoLock lock(m_env_mutex);
1349 ScopeProfiler sp(g_profiler, "SEnv step");
1350 ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
1354 const float map_timer_and_unload_dtime = 2.92;
1355 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
1357 JMutexAutoLock lock(m_env_mutex);
1358 // Run Map's timers and unload unused data
1359 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
1360 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
1361 g_settings->getFloat("server_unload_unused_data_timeout"));
1369 Check player movements
1371 NOTE: Actually the server should handle player physics like the
1372 client does and compare player's position to what is calculated
1373 on our side. This is required when eg. players fly due to an
1377 JMutexAutoLock lock(m_env_mutex);
1378 JMutexAutoLock lock2(m_con_mutex);
1380 //float player_max_speed = BS * 4.0; // Normal speed
1381 float player_max_speed = BS * 20; // Fast speed
1382 float player_max_speed_up = BS * 20;
1384 player_max_speed *= 2.5; // Tolerance
1385 player_max_speed_up *= 2.5;
1387 for(core::map<u16, RemoteClient*>::Iterator
1388 i = m_clients.getIterator();
1389 i.atEnd() == false; i++)
1391 RemoteClient *client = i.getNode()->getValue();
1392 ServerRemotePlayer *player =
1393 (ServerRemotePlayer*)m_env->getPlayer(client->peer_id);
1396 player->m_last_good_position_age += dtime;
1397 if(player->m_last_good_position_age >= 2.0){
1398 float age = player->m_last_good_position_age;
1399 v3f diff = (player->getPosition() - player->m_last_good_position);
1400 float d_vert = diff.Y;
1402 float d_horiz = diff.getLength();
1403 /*infostream<<player->getName()<<"'s horizontal speed is "
1404 <<(d_horiz/age)<<std::endl;*/
1405 if(d_horiz <= age * player_max_speed &&
1406 (d_vert < 0 || d_vert < age * player_max_speed_up)){
1407 player->m_last_good_position = player->getPosition();
1409 actionstream<<"Player "<<player->getName()
1410 <<" moved too fast; resetting position"
1412 player->setPosition(player->m_last_good_position);
1413 SendMovePlayer(player);
1415 player->m_last_good_position_age = 0;
1420 /* Transform liquids */
1421 m_liquid_transform_timer += dtime;
1422 if(m_liquid_transform_timer >= 1.00)
1424 m_liquid_transform_timer -= 1.00;
1426 JMutexAutoLock lock(m_env_mutex);
1428 ScopeProfiler sp(g_profiler, "Server: liquid transform");
1430 core::map<v3s16, MapBlock*> modified_blocks;
1431 m_env->getMap().transformLiquids(modified_blocks);
1436 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1437 ServerMap &map = ((ServerMap&)m_env->getMap());
1438 map.updateLighting(modified_blocks, lighting_modified_blocks);
1440 // Add blocks modified by lighting to modified_blocks
1441 for(core::map<v3s16, MapBlock*>::Iterator
1442 i = lighting_modified_blocks.getIterator();
1443 i.atEnd() == false; i++)
1445 MapBlock *block = i.getNode()->getValue();
1446 modified_blocks.insert(block->getPos(), block);
1450 Set the modified blocks unsent for all the clients
1453 JMutexAutoLock lock2(m_con_mutex);
1455 for(core::map<u16, RemoteClient*>::Iterator
1456 i = m_clients.getIterator();
1457 i.atEnd() == false; i++)
1459 RemoteClient *client = i.getNode()->getValue();
1461 if(modified_blocks.size() > 0)
1463 // Remove block from sent history
1464 client->SetBlocksNotSent(modified_blocks);
1469 // Periodically print some info
1471 float &counter = m_print_info_timer;
1477 JMutexAutoLock lock2(m_con_mutex);
1479 if(m_clients.size() != 0)
1480 infostream<<"Players:"<<std::endl;
1481 for(core::map<u16, RemoteClient*>::Iterator
1482 i = m_clients.getIterator();
1483 i.atEnd() == false; i++)
1485 //u16 peer_id = i.getNode()->getKey();
1486 RemoteClient *client = i.getNode()->getValue();
1487 Player *player = m_env->getPlayer(client->peer_id);
1490 infostream<<"* "<<player->getName()<<"\t";
1491 client->PrintInfo(infostream);
1496 //if(g_settings->getBool("enable_experimental"))
1500 Check added and deleted active objects
1503 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
1504 JMutexAutoLock envlock(m_env_mutex);
1505 JMutexAutoLock conlock(m_con_mutex);
1507 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
1509 // Radius inside which objects are active
1510 s16 radius = g_settings->getS16("active_object_send_range_blocks");
1511 radius *= MAP_BLOCKSIZE;
1513 for(core::map<u16, RemoteClient*>::Iterator
1514 i = m_clients.getIterator();
1515 i.atEnd() == false; i++)
1517 RemoteClient *client = i.getNode()->getValue();
1518 Player *player = m_env->getPlayer(client->peer_id);
1521 // This can happen if the client timeouts somehow
1522 /*infostream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
1524 <<" has no associated player"<<std::endl;*/
1527 v3s16 pos = floatToInt(player->getPosition(), BS);
1529 core::map<u16, bool> removed_objects;
1530 core::map<u16, bool> added_objects;
1531 m_env->getRemovedActiveObjects(pos, radius,
1532 client->m_known_objects, removed_objects);
1533 m_env->getAddedActiveObjects(pos, radius,
1534 client->m_known_objects, added_objects);
1536 // Ignore if nothing happened
1537 if(removed_objects.size() == 0 && added_objects.size() == 0)
1539 //infostream<<"active objects: none changed"<<std::endl;
1543 std::string data_buffer;
1547 // Handle removed objects
1548 writeU16((u8*)buf, removed_objects.size());
1549 data_buffer.append(buf, 2);
1550 for(core::map<u16, bool>::Iterator
1551 i = removed_objects.getIterator();
1552 i.atEnd()==false; i++)
1555 u16 id = i.getNode()->getKey();
1556 ServerActiveObject* obj = m_env->getActiveObject(id);
1558 // Add to data buffer for sending
1559 writeU16((u8*)buf, i.getNode()->getKey());
1560 data_buffer.append(buf, 2);
1562 // Remove from known objects
1563 client->m_known_objects.remove(i.getNode()->getKey());
1565 if(obj && obj->m_known_by_count > 0)
1566 obj->m_known_by_count--;
1569 // Handle added objects
1570 writeU16((u8*)buf, added_objects.size());
1571 data_buffer.append(buf, 2);
1572 for(core::map<u16, bool>::Iterator
1573 i = added_objects.getIterator();
1574 i.atEnd()==false; i++)
1577 u16 id = i.getNode()->getKey();
1578 ServerActiveObject* obj = m_env->getActiveObject(id);
1581 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1583 infostream<<"WARNING: "<<__FUNCTION_NAME
1584 <<": NULL object"<<std::endl;
1586 type = obj->getType();
1588 // Add to data buffer for sending
1589 writeU16((u8*)buf, id);
1590 data_buffer.append(buf, 2);
1591 writeU8((u8*)buf, type);
1592 data_buffer.append(buf, 1);
1595 data_buffer.append(serializeLongString(
1596 obj->getClientInitializationData()));
1598 data_buffer.append(serializeLongString(""));
1600 // Add to known objects
1601 client->m_known_objects.insert(i.getNode()->getKey(), false);
1604 obj->m_known_by_count++;
1608 SharedBuffer<u8> reply(2 + data_buffer.size());
1609 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1610 memcpy((char*)&reply[2], data_buffer.c_str(),
1611 data_buffer.size());
1613 m_con.Send(client->peer_id, 0, reply, true);
1615 infostream<<"Server: Sent object remove/add: "
1616 <<removed_objects.size()<<" removed, "
1617 <<added_objects.size()<<" added, "
1618 <<"packet size is "<<reply.getSize()<<std::endl;
1623 Collect a list of all the objects known by the clients
1624 and report it back to the environment.
1627 core::map<u16, bool> all_known_objects;
1629 for(core::map<u16, RemoteClient*>::Iterator
1630 i = m_clients.getIterator();
1631 i.atEnd() == false; i++)
1633 RemoteClient *client = i.getNode()->getValue();
1634 // Go through all known objects of client
1635 for(core::map<u16, bool>::Iterator
1636 i = client->m_known_objects.getIterator();
1637 i.atEnd()==false; i++)
1639 u16 id = i.getNode()->getKey();
1640 all_known_objects[id] = true;
1644 m_env->setKnownActiveObjects(whatever);
1650 Send object messages
1653 JMutexAutoLock envlock(m_env_mutex);
1654 JMutexAutoLock conlock(m_con_mutex);
1656 //ScopeProfiler sp(g_profiler, "Server: sending object messages");
1659 // Value = data sent by object
1660 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1662 // Get active object messages from environment
1665 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
1669 core::list<ActiveObjectMessage>* message_list = NULL;
1670 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1671 n = buffered_messages.find(aom.id);
1674 message_list = new core::list<ActiveObjectMessage>;
1675 buffered_messages.insert(aom.id, message_list);
1679 message_list = n->getValue();
1681 message_list->push_back(aom);
1684 // Route data to every client
1685 for(core::map<u16, RemoteClient*>::Iterator
1686 i = m_clients.getIterator();
1687 i.atEnd()==false; i++)
1689 RemoteClient *client = i.getNode()->getValue();
1690 std::string reliable_data;
1691 std::string unreliable_data;
1692 // Go through all objects in message buffer
1693 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1694 j = buffered_messages.getIterator();
1695 j.atEnd()==false; j++)
1697 // If object is not known by client, skip it
1698 u16 id = j.getNode()->getKey();
1699 if(client->m_known_objects.find(id) == NULL)
1701 // Get message list of object
1702 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1703 // Go through every message
1704 for(core::list<ActiveObjectMessage>::Iterator
1705 k = list->begin(); k != list->end(); k++)
1707 // Compose the full new data with header
1708 ActiveObjectMessage aom = *k;
1709 std::string new_data;
1712 writeU16((u8*)&buf[0], aom.id);
1713 new_data.append(buf, 2);
1715 new_data += serializeString(aom.datastring);
1716 // Add data to buffer
1718 reliable_data += new_data;
1720 unreliable_data += new_data;
1724 reliable_data and unreliable_data are now ready.
1727 if(reliable_data.size() > 0)
1729 SharedBuffer<u8> reply(2 + reliable_data.size());
1730 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1731 memcpy((char*)&reply[2], reliable_data.c_str(),
1732 reliable_data.size());
1734 m_con.Send(client->peer_id, 0, reply, true);
1736 if(unreliable_data.size() > 0)
1738 SharedBuffer<u8> reply(2 + unreliable_data.size());
1739 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1740 memcpy((char*)&reply[2], unreliable_data.c_str(),
1741 unreliable_data.size());
1742 // Send as unreliable
1743 m_con.Send(client->peer_id, 0, reply, false);
1746 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1748 infostream<<"Server: Size of object message data: "
1749 <<"reliable: "<<reliable_data.size()
1750 <<", unreliable: "<<unreliable_data.size()
1755 // Clear buffered_messages
1756 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1757 i = buffered_messages.getIterator();
1758 i.atEnd()==false; i++)
1760 delete i.getNode()->getValue();
1764 } // enable_experimental
1767 Send queued-for-sending map edit events.
1770 // Don't send too many at a time
1773 // Single change sending is disabled if queue size is not small
1774 bool disable_single_change_sending = false;
1775 if(m_unsent_map_edit_queue.size() >= 4)
1776 disable_single_change_sending = true;
1778 bool got_any_events = false;
1780 // We'll log the amount of each
1783 while(m_unsent_map_edit_queue.size() != 0)
1785 got_any_events = true;
1787 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1789 // Players far away from the change are stored here.
1790 // Instead of sending the changes, MapBlocks are set not sent
1792 core::list<u16> far_players;
1794 if(event->type == MEET_ADDNODE)
1796 //infostream<<"Server: MEET_ADDNODE"<<std::endl;
1797 prof.add("MEET_ADDNODE", 1);
1798 if(disable_single_change_sending)
1799 sendAddNode(event->p, event->n, event->already_known_by_peer,
1802 sendAddNode(event->p, event->n, event->already_known_by_peer,
1805 else if(event->type == MEET_REMOVENODE)
1807 //infostream<<"Server: MEET_REMOVENODE"<<std::endl;
1808 prof.add("MEET_REMOVENODE", 1);
1809 if(disable_single_change_sending)
1810 sendRemoveNode(event->p, event->already_known_by_peer,
1813 sendRemoveNode(event->p, event->already_known_by_peer,
1816 else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
1818 infostream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
1819 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
1820 setBlockNotSent(event->p);
1822 else if(event->type == MEET_OTHER)
1824 infostream<<"Server: MEET_OTHER"<<std::endl;
1825 prof.add("MEET_OTHER", 1);
1826 for(core::map<v3s16, bool>::Iterator
1827 i = event->modified_blocks.getIterator();
1828 i.atEnd()==false; i++)
1830 v3s16 p = i.getNode()->getKey();
1836 prof.add("unknown", 1);
1837 infostream<<"WARNING: Server: Unknown MapEditEvent "
1838 <<((u32)event->type)<<std::endl;
1842 Set blocks not sent to far players
1844 if(far_players.size() > 0)
1846 // Convert list format to that wanted by SetBlocksNotSent
1847 core::map<v3s16, MapBlock*> modified_blocks2;
1848 for(core::map<v3s16, bool>::Iterator
1849 i = event->modified_blocks.getIterator();
1850 i.atEnd()==false; i++)
1852 v3s16 p = i.getNode()->getKey();
1853 modified_blocks2.insert(p,
1854 m_env->getMap().getBlockNoCreateNoEx(p));
1856 // Set blocks not sent
1857 for(core::list<u16>::Iterator
1858 i = far_players.begin();
1859 i != far_players.end(); i++)
1862 RemoteClient *client = getClient(peer_id);
1865 client->SetBlocksNotSent(modified_blocks2);
1871 /*// Don't send too many at a time
1873 if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
1879 infostream<<"Server: MapEditEvents:"<<std::endl;
1880 prof.print(infostream);
1886 Send object positions
1889 float &counter = m_objectdata_timer;
1891 if(counter >= g_settings->getFloat("objectdata_interval"))
1893 JMutexAutoLock lock1(m_env_mutex);
1894 JMutexAutoLock lock2(m_con_mutex);
1896 //ScopeProfiler sp(g_profiler, "Server: sending player positions");
1898 SendObjectData(counter);
1905 Trigger emergethread (it somehow gets to a non-triggered but
1906 bysy state sometimes)
1909 float &counter = m_emergethread_trigger_timer;
1915 m_emergethread.trigger();
1919 // Save map, players and auth stuff
1921 float &counter = m_savemap_timer;
1923 if(counter >= g_settings->getFloat("server_map_save_interval"))
1927 ScopeProfiler sp(g_profiler, "Server: saving stuff");
1930 if(m_authmanager.isModified())
1931 m_authmanager.save();
1934 if(m_banmanager.isModified())
1935 m_banmanager.save();
1938 JMutexAutoLock lock(m_env_mutex);
1940 /*// Unload unused data (delete from memory)
1941 m_env->getMap().unloadUnusedData(
1942 g_settings->getFloat("server_unload_unused_sectors_timeout"));
1944 /*u32 deleted_count = m_env->getMap().unloadUnusedData(
1945 g_settings->getFloat("server_unload_unused_sectors_timeout"));
1948 // Save only changed parts
1949 m_env->getMap().save(true);
1951 /*if(deleted_count > 0)
1953 infostream<<"Server: Unloaded "<<deleted_count
1954 <<" blocks from memory"<<std::endl;
1958 m_env->serializePlayers(m_mapsavedir);
1960 // Save environment metadata
1961 m_env->saveMeta(m_mapsavedir);
1966 void Server::Receive()
1968 DSTACK(__FUNCTION_NAME);
1969 SharedBuffer<u8> data;
1974 JMutexAutoLock conlock(m_con_mutex);
1975 datasize = m_con.Receive(peer_id, data);
1978 // This has to be called so that the client list gets synced
1979 // with the peer list of the connection
1980 handlePeerChanges();
1982 ProcessData(*data, datasize, peer_id);
1984 catch(con::InvalidIncomingDataException &e)
1986 infostream<<"Server::Receive(): "
1987 "InvalidIncomingDataException: what()="
1988 <<e.what()<<std::endl;
1990 catch(con::PeerNotFoundException &e)
1992 //NOTE: This is not needed anymore
1994 // The peer has been disconnected.
1995 // Find the associated player and remove it.
1997 /*JMutexAutoLock envlock(m_env_mutex);
1999 infostream<<"ServerThread: peer_id="<<peer_id
2000 <<" has apparently closed connection. "
2001 <<"Removing player."<<std::endl;
2003 m_env->removePlayer(peer_id);*/
2007 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
2009 DSTACK(__FUNCTION_NAME);
2010 // Environment is locked first.
2011 JMutexAutoLock envlock(m_env_mutex);
2012 JMutexAutoLock conlock(m_con_mutex);
2015 Address address = m_con.GetPeerAddress(peer_id);
2017 // drop player if is ip is banned
2018 if(m_banmanager.isIpBanned(address.serializeString())){
2019 SendAccessDenied(m_con, peer_id,
2020 L"Your ip is banned. Banned name was "
2021 +narrow_to_wide(m_banmanager.getBanName(
2022 address.serializeString())));
2023 m_con.DeletePeer(peer_id);
2027 catch(con::PeerNotFoundException &e)
2029 infostream<<"Server::ProcessData(): Cancelling: peer "
2030 <<peer_id<<" not found"<<std::endl;
2034 u8 peer_ser_ver = getClient(peer_id)->serialization_version;
2042 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
2044 if(command == TOSERVER_INIT)
2046 // [0] u16 TOSERVER_INIT
2047 // [2] u8 SER_FMT_VER_HIGHEST
2048 // [3] u8[20] player_name
2049 // [23] u8[28] password <--- can be sent without this, from old versions
2051 if(datasize < 2+1+PLAYERNAME_SIZE)
2054 infostream<<"Server: Got TOSERVER_INIT from "
2055 <<peer_id<<std::endl;
2057 // First byte after command is maximum supported
2058 // serialization version
2059 u8 client_max = data[2];
2060 u8 our_max = SER_FMT_VER_HIGHEST;
2061 // Use the highest version supported by both
2062 u8 deployed = core::min_(client_max, our_max);
2063 // If it's lower than the lowest supported, give up.
2064 if(deployed < SER_FMT_VER_LOWEST)
2065 deployed = SER_FMT_VER_INVALID;
2067 //peer->serialization_version = deployed;
2068 getClient(peer_id)->pending_serialization_version = deployed;
2070 if(deployed == SER_FMT_VER_INVALID)
2072 infostream<<"Server: Cannot negotiate "
2073 "serialization version with peer "
2074 <<peer_id<<std::endl;
2075 SendAccessDenied(m_con, peer_id,
2076 L"Your client is too old (map format)");
2081 Read and check network protocol version
2084 u16 net_proto_version = 0;
2085 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
2087 net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
2090 getClient(peer_id)->net_proto_version = net_proto_version;
2092 if(net_proto_version == 0)
2094 SendAccessDenied(m_con, peer_id,
2095 L"Your client is too old. Please upgrade.");
2099 /* Uhh... this should actually be a warning but let's do it like this */
2100 if(g_settings->getBool("strict_protocol_version_checking"))
2102 if(net_proto_version < PROTOCOL_VERSION)
2104 SendAccessDenied(m_con, peer_id,
2105 L"Your client is too old. Please upgrade.");
2115 char playername[PLAYERNAME_SIZE];
2116 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
2118 playername[i] = data[3+i];
2120 playername[PLAYERNAME_SIZE-1] = 0;
2122 if(playername[0]=='\0')
2124 infostream<<"Server: Player has empty name"<<std::endl;
2125 SendAccessDenied(m_con, peer_id,
2130 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
2132 infostream<<"Server: Player has invalid name"<<std::endl;
2133 SendAccessDenied(m_con, peer_id,
2134 L"Name contains unallowed characters");
2139 char password[PASSWORD_SIZE];
2140 if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
2142 // old version - assume blank password
2147 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2149 password[i] = data[23+i];
2151 password[PASSWORD_SIZE-1] = 0;
2154 std::string checkpwd;
2155 if(m_authmanager.exists(playername))
2157 checkpwd = m_authmanager.getPassword(playername);
2161 checkpwd = g_settings->get("default_password");
2164 /*infostream<<"Server: Client gave password '"<<password
2165 <<"', the correct one is '"<<checkpwd<<"'"<<std::endl;*/
2167 if(password != checkpwd && m_authmanager.exists(playername))
2169 infostream<<"Server: peer_id="<<peer_id
2170 <<": supplied invalid password for "
2171 <<playername<<std::endl;
2172 SendAccessDenied(m_con, peer_id, L"Invalid password");
2176 // Add player to auth manager
2177 if(m_authmanager.exists(playername) == false)
2179 infostream<<"Server: adding player "<<playername
2180 <<" to auth manager"<<std::endl;
2181 m_authmanager.add(playername);
2182 m_authmanager.setPassword(playername, checkpwd);
2183 m_authmanager.setPrivs(playername,
2184 stringToPrivs(g_settings->get("default_privs")));
2185 m_authmanager.save();
2188 // Enforce user limit.
2189 // Don't enforce for users that have some admin right
2190 if(m_clients.size() >= g_settings->getU16("max_users") &&
2191 (m_authmanager.getPrivs(playername)
2192 & (PRIV_SERVER|PRIV_BAN|PRIV_PRIVS)) == 0 &&
2193 playername != g_settings->get("name"))
2195 SendAccessDenied(m_con, peer_id, L"Too many users.");
2200 Player *player = emergePlayer(playername, password, peer_id);
2202 // If failed, cancel
2205 infostream<<"Server: peer_id="<<peer_id
2206 <<": failed to emerge player"<<std::endl;
2211 Answer with a TOCLIENT_INIT
2214 SharedBuffer<u8> reply(2+1+6+8);
2215 writeU16(&reply[0], TOCLIENT_INIT);
2216 writeU8(&reply[2], deployed);
2217 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
2218 writeU64(&reply[2+1+6], m_env->getServerMap().getSeed());
2221 m_con.Send(peer_id, 0, reply, true);
2225 Send complete position information
2227 SendMovePlayer(player);
2232 if(command == TOSERVER_INIT2)
2234 infostream<<"Server: Got TOSERVER_INIT2 from "
2235 <<peer_id<<std::endl;
2238 getClient(peer_id)->serialization_version
2239 = getClient(peer_id)->pending_serialization_version;
2242 Send some initialization data
2245 // Send tool definitions
2246 SendToolDef(m_con, peer_id, m_toolmgr);
2248 // Send node definitions
2249 SendNodeDef(m_con, peer_id, m_nodedef);
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)
2434 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2440 [2] u8 button (0=left, 1=right)
2444 u8 button = readU8(&data[2]);
2445 u16 id = readS16(&data[3]);
2446 u16 item_i = readU16(&data[5]);
2448 ServerActiveObject *obj = m_env->getActiveObject(id);
2452 infostream<<"Server: CLICK_ACTIVEOBJECT: object not found"
2457 // Skip if object has been removed
2461 //TODO: Check that object is reasonably close
2463 // Get ServerRemotePlayer
2464 ServerRemotePlayer *srp = (ServerRemotePlayer*)player;
2466 // Update wielded item
2467 srp->wieldItem(item_i);
2469 // Left click, pick/punch
2472 actionstream<<player->getName()<<" punches object "
2473 <<obj->getId()<<std::endl;
2480 Try creating inventory item
2482 InventoryItem *item = obj->createPickedUpItem();
2486 InventoryList *ilist = player->inventory.getList("main");
2489 actionstream<<player->getName()<<" picked up "
2490 <<item->getName()<<std::endl;
2491 if(g_settings->getBool("creative_mode") == false)
2493 // Skip if inventory has no free space
2494 if(ilist->roomForItem(item) == false)
2496 infostream<<"Player inventory has no free space"<<std::endl;
2500 // Add to inventory and send inventory
2501 ilist->addItem(item);
2502 UpdateCrafting(player->peer_id);
2503 SendInventory(player->peer_id);
2506 // Remove object from environment
2507 obj->m_removed = true;
2513 Item cannot be picked up. Punch it instead.
2516 actionstream<<player->getName()<<" punches object "
2517 <<obj->getId()<<std::endl;
2519 ToolItem *titem = NULL;
2520 std::string toolname = "";
2522 InventoryList *mlist = player->inventory.getList("main");
2525 InventoryItem *item = mlist->getItem(item_i);
2526 if(item && (std::string)item->getName() == "ToolItem")
2528 titem = (ToolItem*)item;
2529 toolname = titem->getToolName();
2533 v3f playerpos = player->getPosition();
2534 v3f objpos = obj->getBasePosition();
2535 v3f dir = (objpos - playerpos).normalize();
2537 u16 wear = obj->punch(toolname, dir, player->getName());
2541 bool weared_out = titem->addWear(wear);
2543 mlist->deleteItem(item_i);
2544 SendInventory(player->peer_id);
2549 // Right click, do something with object
2552 actionstream<<player->getName()<<" right clicks object "
2553 <<obj->getId()<<std::endl;
2556 obj->rightClick(srp);
2560 Update player state to client
2562 SendPlayerHP(player);
2563 UpdateCrafting(player->peer_id);
2564 SendInventory(player->peer_id);
2566 else if(command == TOSERVER_GROUND_ACTION)
2574 [3] v3s16 nodepos_undersurface
2575 [9] v3s16 nodepos_abovesurface
2580 2: stop digging (all parameters ignored)
2581 3: digging completed
2583 u8 action = readU8(&data[2]);
2585 p_under.X = readS16(&data[3]);
2586 p_under.Y = readS16(&data[5]);
2587 p_under.Z = readS16(&data[7]);
2589 p_over.X = readS16(&data[9]);
2590 p_over.Y = readS16(&data[11]);
2591 p_over.Z = readS16(&data[13]);
2592 u16 item_i = readU16(&data[15]);
2594 ServerRemotePlayer *srp = (ServerRemotePlayer*)player;
2597 Check that target is reasonably close
2599 if(action != 2) // action 2 has always position (0,0,0)
2601 v3f np_f = intToFloat(p_under, BS);
2602 float max_d = BS * 10; // Just some large enough value
2603 float d = srp->m_last_good_position.getDistanceFrom(np_f);
2605 actionstream<<"Player "<<player->getName()
2606 <<" tried to access node from too far: "
2607 <<"d="<<d<<", max_d="<<max_d
2608 <<". ignoring."<<std::endl;
2609 // Re-send block to revert change on client-side
2610 RemoteClient *client = getClient(peer_id);
2611 v3s16 blockpos = getNodeBlockPos(p_under);
2612 client->SetBlockNotSent(blockpos);
2624 NOTE: This can be used in the future to check if
2625 somebody is cheating, by checking the timing.
2627 bool cannot_punch_node = false;
2629 MapNode n(CONTENT_IGNORE);
2633 n = m_env->getMap().getNode(p_under);
2635 catch(InvalidPositionException &e)
2637 infostream<<"Server: Not punching: Node not found."
2638 <<" Adding block to emerge queue."
2640 m_emerge_queue.addBlock(peer_id,
2641 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2642 cannot_punch_node = true;
2645 if(cannot_punch_node)
2651 scriptapi_environment_on_punchnode(m_lua, p_under, n, srp);
2658 else if(action == 2)
2661 RemoteClient *client = getClient(peer_id);
2662 JMutexAutoLock digmutex(client->m_dig_mutex);
2663 client->m_dig_tool_item = -1;
2668 3: Digging completed
2670 else if(action == 3)
2672 // Mandatory parameter; actually used for nothing
2673 core::map<v3s16, MapBlock*> modified_blocks;
2675 content_t material = CONTENT_IGNORE;
2676 u8 mineral = MINERAL_NONE;
2678 bool cannot_remove_node = false;
2680 MapNode n(CONTENT_IGNORE);
2683 n = m_env->getMap().getNode(p_under);
2685 mineral = n.getMineral(m_nodedef);
2686 // Get material at position
2687 material = n.getContent();
2688 // If not yet cancelled
2689 if(cannot_remove_node == false)
2691 // If it's not diggable, do nothing
2692 if(m_nodedef->get(material).diggable == false)
2694 infostream<<"Server: Not finishing digging: "
2695 <<"Node not diggable"
2697 cannot_remove_node = true;
2700 // If not yet cancelled
2701 if(cannot_remove_node == false)
2703 // Get node metadata
2704 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p_under);
2705 if(meta && meta->nodeRemovalDisabled() == true)
2707 infostream<<"Server: Not finishing digging: "
2708 <<"Node metadata disables removal"
2710 cannot_remove_node = true;
2714 catch(InvalidPositionException &e)
2716 infostream<<"Server: Not finishing digging: Node not found."
2717 <<" Adding block to emerge queue."
2719 m_emerge_queue.addBlock(peer_id,
2720 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2721 cannot_remove_node = true;
2724 // Make sure the player is allowed to do it
2725 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2727 infostream<<"Player "<<player->getName()<<" cannot remove node"
2728 <<" because privileges are "<<getPlayerPrivs(player)
2730 cannot_remove_node = true;
2734 If node can't be removed, set block to be re-sent to
2737 if(cannot_remove_node)
2739 infostream<<"Server: Not finishing digging."<<std::endl;
2741 // Client probably has wrong data.
2742 // Set block not sent, so that client will get
2744 infostream<<"Client "<<peer_id<<" tried to dig "
2745 <<"node; but node cannot be removed."
2746 <<" setting MapBlock not sent."<<std::endl;
2747 RemoteClient *client = getClient(peer_id);
2748 v3s16 blockpos = getNodeBlockPos(p_under);
2749 client->SetBlockNotSent(blockpos);
2754 actionstream<<player->getName()<<" digs "<<PP(p_under)
2755 <<", gets material "<<(int)material<<", mineral "
2756 <<(int)mineral<<std::endl;
2759 Send the removal to all close-by players.
2760 - If other player is close, send REMOVENODE
2761 - Otherwise set blocks not sent
2763 core::list<u16> far_players;
2764 sendRemoveNode(p_under, peer_id, &far_players, 30);
2767 Update and send inventory
2770 if(g_settings->getBool("creative_mode") == false)
2775 InventoryList *mlist = player->inventory.getList("main");
2778 InventoryItem *item = mlist->getItem(item_i);
2779 if(item && (std::string)item->getName() == "ToolItem")
2781 ToolItem *titem = (ToolItem*)item;
2782 std::string toolname = titem->getToolName();
2784 // Get digging properties for material and tool
2785 ToolDiggingProperties tp =
2786 m_toolmgr->getDiggingProperties(toolname);
2787 DiggingProperties prop =
2788 getDiggingProperties(material, &tp, m_nodedef);
2790 if(prop.diggable == false)
2792 infostream<<"Server: WARNING: Player digged"
2793 <<" with impossible material + tool"
2794 <<" combination"<<std::endl;
2797 bool weared_out = titem->addWear(prop.wear);
2801 mlist->deleteItem(item_i);
2807 Add dug item to inventory
2810 InventoryItem *item = NULL;
2812 if(mineral != MINERAL_NONE)
2813 item = getDiggedMineralItem(mineral, this);
2818 const std::string &dug_s = m_nodedef->get(material).dug_item;
2821 std::istringstream is(dug_s, std::ios::binary);
2822 item = InventoryItem::deSerialize(is, this);
2828 // Add a item to inventory
2829 player->inventory.addItem("main", item);
2832 UpdateCrafting(player->peer_id);
2833 SendInventory(player->peer_id);
2838 if(mineral != MINERAL_NONE)
2839 item = getDiggedMineralItem(mineral, this);
2844 const std::string &extra_dug_s = m_nodedef->get(material).extra_dug_item;
2845 s32 extra_rarity = m_nodedef->get(material).extra_dug_item_rarity;
2846 if(extra_dug_s != "" && extra_rarity != 0
2847 && myrand() % extra_rarity == 0)
2849 std::istringstream is(extra_dug_s, std::ios::binary);
2850 item = InventoryItem::deSerialize(is, this);
2856 // Add a item to inventory
2857 player->inventory.addItem("main", item);
2860 UpdateCrafting(player->peer_id);
2861 SendInventory(player->peer_id);
2867 (this takes some time so it is done after the quick stuff)
2870 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
2872 m_env->getMap().removeNodeAndUpdate(p_under, modified_blocks);
2875 Set blocks not sent to far players
2877 for(core::list<u16>::Iterator
2878 i = far_players.begin();
2879 i != far_players.end(); i++)
2882 RemoteClient *client = getClient(peer_id);
2885 client->SetBlocksNotSent(modified_blocks);
2891 scriptapi_environment_on_dignode(m_lua, p_under, n, srp);
2897 else if(action == 1)
2900 InventoryList *ilist = player->inventory.getList("main");
2905 InventoryItem *item = ilist->getItem(item_i);
2907 // If there is no item, it is not possible to add it anywhere
2912 Handle material items
2914 if(std::string("MaterialItem") == item->getName())
2917 // Don't add a node if this is not a free space
2918 MapNode n2 = m_env->getMap().getNode(p_over);
2919 bool no_enough_privs =
2920 ((getPlayerPrivs(player) & PRIV_BUILD)==0);
2922 infostream<<"Player "<<player->getName()<<" cannot add node"
2923 <<" because privileges are "<<getPlayerPrivs(player)
2926 if(m_nodedef->get(n2).buildable_to == false
2929 // Client probably has wrong data.
2930 // Set block not sent, so that client will get
2932 infostream<<"Client "<<peer_id<<" tried to place"
2933 <<" node in invalid position; setting"
2934 <<" MapBlock not sent."<<std::endl;
2935 RemoteClient *client = getClient(peer_id);
2936 v3s16 blockpos = getNodeBlockPos(p_over);
2937 client->SetBlockNotSent(blockpos);
2941 catch(InvalidPositionException &e)
2943 infostream<<"Server: Ignoring ADDNODE: Node not found"
2944 <<" Adding block to emerge queue."
2946 m_emerge_queue.addBlock(peer_id,
2947 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2951 // Reset build time counter
2952 getClient(peer_id)->m_time_from_building = 0.0;
2955 MaterialItem *mitem = (MaterialItem*)item;
2957 n.setContent(mitem->getMaterial());
2959 actionstream<<player->getName()<<" places material "
2960 <<(int)mitem->getMaterial()
2961 <<" at "<<PP(p_under)<<std::endl;
2963 // Calculate direction for wall mounted stuff
2964 if(m_nodedef->get(n).wall_mounted)
2965 n.param2 = packDir(p_under - p_over);
2967 // Calculate the direction for furnaces and chests and stuff
2968 if(m_nodedef->get(n).param_type == CPT_FACEDIR_SIMPLE)
2970 v3f playerpos = player->getPosition();
2971 v3f blockpos = intToFloat(p_over, BS) - playerpos;
2972 blockpos = blockpos.normalize();
2974 if (fabs(blockpos.X) > fabs(blockpos.Z)) {
2988 Send to all close-by players
2990 core::list<u16> far_players;
2991 sendAddNode(p_over, n, 0, &far_players, 30);
2996 InventoryList *ilist = player->inventory.getList("main");
2997 if(g_settings->getBool("creative_mode") == false && ilist)
2999 // Remove from inventory and send inventory
3000 if(mitem->getCount() == 1)
3001 ilist->deleteItem(item_i);
3005 UpdateCrafting(peer_id);
3006 SendInventory(peer_id);
3012 This takes some time so it is done after the quick stuff
3014 core::map<v3s16, MapBlock*> modified_blocks;
3016 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
3018 std::string p_name = std::string(player->getName());
3019 m_env->getMap().addNodeAndUpdate(p_over, n, modified_blocks, p_name);
3022 Set blocks not sent to far players
3024 for(core::list<u16>::Iterator
3025 i = far_players.begin();
3026 i != far_players.end(); i++)
3029 RemoteClient *client = getClient(peer_id);
3032 client->SetBlocksNotSent(modified_blocks);
3038 scriptapi_environment_on_placenode(m_lua, p_over, n, srp);
3041 Calculate special events
3044 /*if(n.d == LEGN(m_nodedef, "CONTENT_MESE"))
3047 for(s16 z=-1; z<=1; z++)
3048 for(s16 y=-1; y<=1; y++)
3049 for(s16 x=-1; x<=1; x++)
3056 Place other item (not a block)
3060 v3s16 blockpos = getNodeBlockPos(p_over);
3063 Check that the block is loaded so that the item
3064 can properly be added to the static list too
3066 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3069 infostream<<"Error while placing object: "
3070 "block not found"<<std::endl;
3075 If in creative mode, item dropping is disabled unless
3076 player has build privileges
3078 if(g_settings->getBool("creative_mode") &&
3079 (getPlayerPrivs(player) & PRIV_BUILD) == 0)
3081 infostream<<"Not allowing player to drop item: "
3082 "creative mode and no build privs"<<std::endl;
3086 // Calculate a position for it
3087 v3f pos = intToFloat(p_over, BS);
3089 /*pos.Y -= BS*0.25; // let it drop a bit
3091 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
3092 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;*/
3097 ServerActiveObject *obj = item->createSAO(m_env, pos);
3101 infostream<<"WARNING: item resulted in NULL object, "
3102 <<"not placing onto map"
3107 actionstream<<player->getName()<<" places "<<item->getName()
3108 <<" at "<<PP(p_over)<<std::endl;
3110 // Add the object to the environment
3111 m_env->addActiveObject(obj);
3113 infostream<<"Placed object"<<std::endl;
3115 if(g_settings->getBool("creative_mode") == false)
3117 // Delete the right amount of items from the slot
3118 u16 dropcount = item->getDropCount();
3120 // Delete item if all gone
3121 if(item->getCount() <= dropcount)
3123 if(item->getCount() < dropcount)
3124 infostream<<"WARNING: Server: dropped more items"
3125 <<" than the slot contains"<<std::endl;
3127 InventoryList *ilist = player->inventory.getList("main");
3129 // Remove from inventory and send inventory
3130 ilist->deleteItem(item_i);
3132 // Else decrement it
3134 item->remove(dropcount);
3137 UpdateCrafting(peer_id);
3138 SendInventory(peer_id);
3146 Catch invalid actions
3150 infostream<<"WARNING: Server: Invalid action "
3151 <<action<<std::endl;
3155 else if(command == TOSERVER_RELEASE)
3164 infostream<<"TOSERVER_RELEASE ignored"<<std::endl;
3167 else if(command == TOSERVER_SIGNTEXT)
3169 infostream<<"Server: TOSERVER_SIGNTEXT not supported anymore"
3173 else if(command == TOSERVER_SIGNNODETEXT)
3175 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
3183 std::string datastring((char*)&data[2], datasize-2);
3184 std::istringstream is(datastring, std::ios_base::binary);
3187 is.read((char*)buf, 6);
3188 v3s16 p = readV3S16(buf);
3189 is.read((char*)buf, 2);
3190 u16 textlen = readU16(buf);
3192 for(u16 i=0; i<textlen; i++)
3194 is.read((char*)buf, 1);
3195 text += (char)buf[0];
3198 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3201 if(meta->typeId() != LEGN(m_nodedef, "CONTENT_SIGN_WALL"))
3203 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
3204 signmeta->setText(text);
3206 actionstream<<player->getName()<<" writes \""<<text<<"\" to sign "
3207 <<" at "<<PP(p)<<std::endl;
3209 v3s16 blockpos = getNodeBlockPos(p);
3210 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3213 block->raiseModified(MOD_STATE_WRITE_NEEDED,
3217 setBlockNotSent(blockpos);
3219 else if(command == TOSERVER_INVENTORY_ACTION)
3221 /*// Ignore inventory changes if in creative mode
3222 if(g_settings->getBool("creative_mode") == true)
3224 infostream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
3228 // Strip command and create a stream
3229 std::string datastring((char*)&data[2], datasize-2);
3230 infostream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
3231 std::istringstream is(datastring, std::ios_base::binary);
3233 InventoryAction *a = InventoryAction::deSerialize(is);
3238 c.current_player = player;
3241 Handle craftresult specially if not in creative mode
3243 bool disable_action = false;
3244 if(a->getType() == IACTION_MOVE
3245 && g_settings->getBool("creative_mode") == false)
3247 IMoveAction *ma = (IMoveAction*)a;
3248 if(ma->to_inv == "current_player" &&
3249 ma->from_inv == "current_player")
3251 InventoryList *rlist = player->inventory.getList("craftresult");
3253 InventoryList *clist = player->inventory.getList("craft");
3255 InventoryList *mlist = player->inventory.getList("main");
3258 Craftresult is no longer preview if something
3261 if(ma->to_list == "craftresult"
3262 && ma->from_list != "craftresult")
3264 // If it currently is a preview, remove
3266 if(player->craftresult_is_preview)
3268 rlist->deleteItem(0);
3270 player->craftresult_is_preview = false;
3273 Crafting takes place if this condition is true.
3275 if(player->craftresult_is_preview &&
3276 ma->from_list == "craftresult")
3278 player->craftresult_is_preview = false;
3279 clist->decrementMaterials(1);
3281 /* Print out action */
3282 InventoryList *list =
3283 player->inventory.getList("craftresult");
3285 InventoryItem *item = list->getItem(0);
3286 std::string itemname = "NULL";
3288 itemname = item->getName();
3289 actionstream<<player->getName()<<" crafts "
3290 <<itemname<<std::endl;
3293 If the craftresult is placed on itself, move it to
3294 main inventory instead of doing the action
3296 if(ma->to_list == "craftresult"
3297 && ma->from_list == "craftresult")
3299 disable_action = true;
3301 InventoryItem *item1 = rlist->changeItem(0, NULL);
3302 mlist->addItem(item1);
3305 // Disallow moving items if not allowed to build
3306 else if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
3308 disable_action = true;
3310 // if it's a locking chest, only allow the owner or server admins to move items
3311 else if (ma->from_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
3313 Strfnd fn(ma->from_inv);
3314 std::string id0 = fn.next(":");
3315 if(id0 == "nodemeta")
3318 p.X = stoi(fn.next(","));
3319 p.Y = stoi(fn.next(","));
3320 p.Z = stoi(fn.next(","));
3321 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3322 if(meta && meta->typeId() == LEGN(m_nodedef, "CONTENT_LOCKABLE_CHEST")) {
3323 LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
3324 if (lcm->getOwner() != player->getName())
3325 disable_action = true;
3329 else if (ma->to_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
3331 Strfnd fn(ma->to_inv);
3332 std::string id0 = fn.next(":");
3333 if(id0 == "nodemeta")
3336 p.X = stoi(fn.next(","));
3337 p.Y = stoi(fn.next(","));
3338 p.Z = stoi(fn.next(","));
3339 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3340 if(meta && meta->typeId() == LEGN(m_nodedef, "CONTENT_LOCKABLE_CHEST")) {
3341 LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
3342 if (lcm->getOwner() != player->getName())
3343 disable_action = true;
3349 if(a->getType() == IACTION_DROP)
3351 IDropAction *da = (IDropAction*)a;
3352 // Disallow dropping items if not allowed to build
3353 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
3355 disable_action = true;
3357 // if it's a locking chest, only allow the owner or server admins to drop items
3358 else if (da->from_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
3360 Strfnd fn(da->from_inv);
3361 std::string id0 = fn.next(":");
3362 if(id0 == "nodemeta")
3365 p.X = stoi(fn.next(","));
3366 p.Y = stoi(fn.next(","));
3367 p.Z = stoi(fn.next(","));
3368 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3369 if(meta && meta->typeId() == LEGN(m_nodedef, "CONTENT_LOCKABLE_CHEST")) {
3370 LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
3371 if (lcm->getOwner() != player->getName())
3372 disable_action = true;
3378 if(disable_action == false)
3380 // Feed action to player inventory
3381 a->apply(&c, this, m_env);
3386 UpdateCrafting(player->peer_id);
3387 SendInventory(player->peer_id);
3395 infostream<<"TOSERVER_INVENTORY_ACTION: "
3396 <<"InventoryAction::deSerialize() returned NULL"
3400 else if(command == TOSERVER_CHAT_MESSAGE)
3408 std::string datastring((char*)&data[2], datasize-2);
3409 std::istringstream is(datastring, std::ios_base::binary);
3412 is.read((char*)buf, 2);
3413 u16 len = readU16(buf);
3415 std::wstring message;
3416 for(u16 i=0; i<len; i++)
3418 is.read((char*)buf, 2);
3419 message += (wchar_t)readU16(buf);
3422 // Get player name of this client
3423 std::wstring name = narrow_to_wide(player->getName());
3426 bool ate = scriptapi_on_chat_message(m_lua, player->getName(),
3427 wide_to_narrow(message));
3428 // If script ate the message, don't proceed
3432 // Line to send to players
3434 // Whether to send to the player that sent the line
3435 bool send_to_sender = false;
3436 // Whether to send to other players
3437 bool send_to_others = false;
3439 // Local player gets all privileges regardless of
3440 // what's set on their account.
3441 u64 privs = getPlayerPrivs(player);
3444 if(message[0] == L'/')
3446 size_t strip_size = 1;
3447 if (message[1] == L'#') // support old-style commans
3449 message = message.substr(strip_size);
3451 WStrfnd f1(message);
3452 f1.next(L" "); // Skip over /#whatever
3453 std::wstring paramstring = f1.next(L"");
3455 ServerCommandContext *ctx = new ServerCommandContext(
3456 str_split(message, L' '),
3463 std::wstring reply(processServerCommand(ctx));
3464 send_to_sender = ctx->flags & SEND_TO_SENDER;
3465 send_to_others = ctx->flags & SEND_TO_OTHERS;
3467 if (ctx->flags & SEND_NO_PREFIX)
3470 line += L"Server: " + reply;
3477 if(privs & PRIV_SHOUT)
3483 send_to_others = true;
3487 line += L"Server: You are not allowed to shout";
3488 send_to_sender = true;
3495 actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
3498 Send the message to clients
3500 for(core::map<u16, RemoteClient*>::Iterator
3501 i = m_clients.getIterator();
3502 i.atEnd() == false; i++)
3504 // Get client and check that it is valid
3505 RemoteClient *client = i.getNode()->getValue();
3506 assert(client->peer_id == i.getNode()->getKey());
3507 if(client->serialization_version == SER_FMT_VER_INVALID)
3511 bool sender_selected = (peer_id == client->peer_id);
3512 if(sender_selected == true && send_to_sender == false)
3514 if(sender_selected == false && send_to_others == false)
3517 SendChatMessage(client->peer_id, line);
3521 else if(command == TOSERVER_DAMAGE)
3523 std::string datastring((char*)&data[2], datasize-2);
3524 std::istringstream is(datastring, std::ios_base::binary);
3525 u8 damage = readU8(is);
3527 if(g_settings->getBool("enable_damage"))
3529 actionstream<<player->getName()<<" damaged by "
3530 <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
3533 HandlePlayerHP(player, damage);
3537 SendPlayerHP(player);
3540 else if(command == TOSERVER_PASSWORD)
3543 [0] u16 TOSERVER_PASSWORD
3544 [2] u8[28] old password
3545 [30] u8[28] new password
3548 if(datasize != 2+PASSWORD_SIZE*2)
3550 /*char password[PASSWORD_SIZE];
3551 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3552 password[i] = data[2+i];
3553 password[PASSWORD_SIZE-1] = 0;*/
3555 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3563 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3565 char c = data[2+PASSWORD_SIZE+i];
3571 infostream<<"Server: Client requests a password change from "
3572 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
3574 std::string playername = player->getName();
3576 if(m_authmanager.exists(playername) == false)
3578 infostream<<"Server: playername not found in authmanager"<<std::endl;
3579 // Wrong old password supplied!!
3580 SendChatMessage(peer_id, L"playername not found in authmanager");
3584 std::string checkpwd = m_authmanager.getPassword(playername);
3586 if(oldpwd != checkpwd)
3588 infostream<<"Server: invalid old password"<<std::endl;
3589 // Wrong old password supplied!!
3590 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
3594 actionstream<<player->getName()<<" changes password"<<std::endl;
3596 m_authmanager.setPassword(playername, newpwd);
3598 infostream<<"Server: password change successful for "<<playername
3600 SendChatMessage(peer_id, L"Password change successful");
3602 else if(command == TOSERVER_PLAYERITEM)
3607 u16 item = readU16(&data[2]);
3608 player->wieldItem(item);
3609 SendWieldedItem(player);
3611 else if(command == TOSERVER_RESPAWN)
3616 RespawnPlayer(player);
3618 actionstream<<player->getName()<<" respawns at "
3619 <<PP(player->getPosition()/BS)<<std::endl;
3623 infostream<<"Server::ProcessData(): Ignoring "
3624 "unknown command "<<command<<std::endl;
3628 catch(SendFailedException &e)
3630 errorstream<<"Server::ProcessData(): SendFailedException: "
3636 void Server::onMapEditEvent(MapEditEvent *event)
3638 //infostream<<"Server::onMapEditEvent()"<<std::endl;
3639 if(m_ignore_map_edit_events)
3641 MapEditEvent *e = event->clone();
3642 m_unsent_map_edit_queue.push_back(e);
3645 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3647 if(id == "current_player")
3649 assert(c->current_player);
3650 return &(c->current_player->inventory);
3654 std::string id0 = fn.next(":");
3656 if(id0 == "nodemeta")
3659 p.X = stoi(fn.next(","));
3660 p.Y = stoi(fn.next(","));
3661 p.Z = stoi(fn.next(","));
3662 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3664 return meta->getInventory();
3665 infostream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3666 <<"no metadata found"<<std::endl;
3670 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3673 void Server::inventoryModified(InventoryContext *c, std::string id)
3675 if(id == "current_player")
3677 assert(c->current_player);
3679 UpdateCrafting(c->current_player->peer_id);
3680 SendInventory(c->current_player->peer_id);
3685 std::string id0 = fn.next(":");
3687 if(id0 == "nodemeta")
3690 p.X = stoi(fn.next(","));
3691 p.Y = stoi(fn.next(","));
3692 p.Z = stoi(fn.next(","));
3693 v3s16 blockpos = getNodeBlockPos(p);
3695 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3697 meta->inventoryModified();
3699 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3701 block->raiseModified(MOD_STATE_WRITE_NEEDED);
3703 setBlockNotSent(blockpos);
3708 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3711 core::list<PlayerInfo> Server::getPlayerInfo()
3713 DSTACK(__FUNCTION_NAME);
3714 JMutexAutoLock envlock(m_env_mutex);
3715 JMutexAutoLock conlock(m_con_mutex);
3717 core::list<PlayerInfo> list;
3719 core::list<Player*> players = m_env->getPlayers();
3721 core::list<Player*>::Iterator i;
3722 for(i = players.begin();
3723 i != players.end(); i++)
3727 Player *player = *i;
3730 // Copy info from connection to info struct
3731 info.id = player->peer_id;
3732 info.address = m_con.GetPeerAddress(player->peer_id);
3733 info.avg_rtt = m_con.GetPeerAvgRTT(player->peer_id);
3735 catch(con::PeerNotFoundException &e)
3737 // Set dummy peer info
3739 info.address = Address(0,0,0,0,0);
3743 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3744 info.position = player->getPosition();
3746 list.push_back(info);
3753 void Server::peerAdded(con::Peer *peer)
3755 DSTACK(__FUNCTION_NAME);
3756 infostream<<"Server::peerAdded(): peer->id="
3757 <<peer->id<<std::endl;
3760 c.type = PEER_ADDED;
3761 c.peer_id = peer->id;
3763 m_peer_change_queue.push_back(c);
3766 void Server::deletingPeer(con::Peer *peer, bool timeout)
3768 DSTACK(__FUNCTION_NAME);
3769 infostream<<"Server::deletingPeer(): peer->id="
3770 <<peer->id<<", timeout="<<timeout<<std::endl;
3773 c.type = PEER_REMOVED;
3774 c.peer_id = peer->id;
3775 c.timeout = timeout;
3776 m_peer_change_queue.push_back(c);
3783 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3785 DSTACK(__FUNCTION_NAME);
3786 std::ostringstream os(std::ios_base::binary);
3788 writeU16(os, TOCLIENT_HP);
3792 std::string s = os.str();
3793 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3795 con.Send(peer_id, 0, data, true);
3798 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3799 const std::wstring &reason)
3801 DSTACK(__FUNCTION_NAME);
3802 std::ostringstream os(std::ios_base::binary);
3804 writeU16(os, TOCLIENT_ACCESS_DENIED);
3805 os<<serializeWideString(reason);
3808 std::string s = os.str();
3809 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3811 con.Send(peer_id, 0, data, true);
3814 void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
3815 bool set_camera_point_target, v3f camera_point_target)
3817 DSTACK(__FUNCTION_NAME);
3818 std::ostringstream os(std::ios_base::binary);
3820 writeU16(os, TOCLIENT_DEATHSCREEN);
3821 writeU8(os, set_camera_point_target);
3822 writeV3F1000(os, camera_point_target);
3825 std::string s = os.str();
3826 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3828 con.Send(peer_id, 0, data, true);
3831 void Server::SendToolDef(con::Connection &con, u16 peer_id,
3832 IToolDefManager *tooldef)
3834 DSTACK(__FUNCTION_NAME);
3835 std::ostringstream os(std::ios_base::binary);
3839 u32 length of the next item
3840 serialized ToolDefManager
3842 writeU16(os, TOCLIENT_TOOLDEF);
3843 std::ostringstream tmp_os(std::ios::binary);
3844 tooldef->serialize(tmp_os);
3845 os<<serializeLongString(tmp_os.str());
3848 std::string s = os.str();
3849 infostream<<"Server::SendToolDef(): Sending tool definitions: size="
3850 <<s.size()<<std::endl;
3851 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3853 con.Send(peer_id, 0, data, true);
3856 void Server::SendNodeDef(con::Connection &con, u16 peer_id,
3857 INodeDefManager *nodedef)
3859 DSTACK(__FUNCTION_NAME);
3860 std::ostringstream os(std::ios_base::binary);
3864 u32 length of the next item
3865 serialized NodeDefManager
3867 writeU16(os, TOCLIENT_NODEDEF);
3868 std::ostringstream tmp_os(std::ios::binary);
3869 nodedef->serialize(tmp_os);
3870 os<<serializeLongString(tmp_os.str());
3873 std::string s = os.str();
3874 infostream<<"Server::SendNodeDef(): Sending node definitions: size="
3875 <<s.size()<<std::endl;
3876 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3878 con.Send(peer_id, 0, data, true);
3882 Non-static send methods
3885 void Server::SendObjectData(float dtime)
3887 DSTACK(__FUNCTION_NAME);
3889 core::map<v3s16, bool> stepped_blocks;
3891 for(core::map<u16, RemoteClient*>::Iterator
3892 i = m_clients.getIterator();
3893 i.atEnd() == false; i++)
3895 u16 peer_id = i.getNode()->getKey();
3896 RemoteClient *client = i.getNode()->getValue();
3897 assert(client->peer_id == peer_id);
3899 if(client->serialization_version == SER_FMT_VER_INVALID)
3902 client->SendObjectData(this, dtime, stepped_blocks);
3906 void Server::SendPlayerInfos()
3908 DSTACK(__FUNCTION_NAME);
3910 //JMutexAutoLock envlock(m_env_mutex);
3912 // Get connected players
3913 core::list<Player*> players = m_env->getPlayers(true);
3915 u32 player_count = players.getSize();
3916 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3918 SharedBuffer<u8> data(datasize);
3919 writeU16(&data[0], TOCLIENT_PLAYERINFO);
3922 core::list<Player*>::Iterator i;
3923 for(i = players.begin();
3924 i != players.end(); i++)
3926 Player *player = *i;
3928 /*infostream<<"Server sending player info for player with "
3929 "peer_id="<<player->peer_id<<std::endl;*/
3931 writeU16(&data[start], player->peer_id);
3932 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3933 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3934 start += 2+PLAYERNAME_SIZE;
3937 //JMutexAutoLock conlock(m_con_mutex);
3940 m_con.SendToAll(0, data, true);
3943 void Server::SendInventory(u16 peer_id)
3945 DSTACK(__FUNCTION_NAME);
3947 Player* player = m_env->getPlayer(peer_id);
3954 std::ostringstream os;
3955 //os.imbue(std::locale("C"));
3957 player->inventory.serialize(os);
3959 std::string s = os.str();
3961 SharedBuffer<u8> data(s.size()+2);
3962 writeU16(&data[0], TOCLIENT_INVENTORY);
3963 memcpy(&data[2], s.c_str(), s.size());
3966 m_con.Send(peer_id, 0, data, true);
3969 std::string getWieldedItemString(const Player *player)
3971 const InventoryItem *item = player->getWieldItem();
3973 return std::string("");
3974 std::ostringstream os(std::ios_base::binary);
3975 item->serialize(os);
3979 void Server::SendWieldedItem(const Player* player)
3981 DSTACK(__FUNCTION_NAME);
3985 std::ostringstream os(std::ios_base::binary);
3987 writeU16(os, TOCLIENT_PLAYERITEM);
3989 writeU16(os, player->peer_id);
3990 os<<serializeString(getWieldedItemString(player));
3993 std::string s = os.str();
3994 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3996 m_con.SendToAll(0, data, true);
3999 void Server::SendPlayerItems()
4001 DSTACK(__FUNCTION_NAME);
4003 std::ostringstream os(std::ios_base::binary);
4004 core::list<Player *> players = m_env->getPlayers(true);
4006 writeU16(os, TOCLIENT_PLAYERITEM);
4007 writeU16(os, players.size());
4008 core::list<Player *>::Iterator i;
4009 for(i = players.begin(); i != players.end(); ++i)
4012 writeU16(os, p->peer_id);
4013 os<<serializeString(getWieldedItemString(p));
4017 std::string s = os.str();
4018 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4020 m_con.SendToAll(0, data, true);
4023 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
4025 DSTACK(__FUNCTION_NAME);
4027 std::ostringstream os(std::ios_base::binary);
4031 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
4032 os.write((char*)buf, 2);
4035 writeU16(buf, message.size());
4036 os.write((char*)buf, 2);
4039 for(u32 i=0; i<message.size(); i++)
4043 os.write((char*)buf, 2);
4047 std::string s = os.str();
4048 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4050 m_con.Send(peer_id, 0, data, true);
4053 void Server::BroadcastChatMessage(const std::wstring &message)
4055 for(core::map<u16, RemoteClient*>::Iterator
4056 i = m_clients.getIterator();
4057 i.atEnd() == false; i++)
4059 // Get client and check that it is valid
4060 RemoteClient *client = i.getNode()->getValue();
4061 assert(client->peer_id == i.getNode()->getKey());
4062 if(client->serialization_version == SER_FMT_VER_INVALID)
4065 SendChatMessage(client->peer_id, message);
4069 void Server::SendPlayerHP(Player *player)
4071 SendHP(m_con, player->peer_id, player->hp);
4074 void Server::SendMovePlayer(Player *player)
4076 DSTACK(__FUNCTION_NAME);
4077 std::ostringstream os(std::ios_base::binary);
4079 writeU16(os, TOCLIENT_MOVE_PLAYER);
4080 writeV3F1000(os, player->getPosition());
4081 writeF1000(os, player->getPitch());
4082 writeF1000(os, player->getYaw());
4085 v3f pos = player->getPosition();
4086 f32 pitch = player->getPitch();
4087 f32 yaw = player->getYaw();
4088 infostream<<"Server sending TOCLIENT_MOVE_PLAYER"
4089 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
4096 std::string s = os.str();
4097 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4099 m_con.Send(player->peer_id, 0, data, true);
4102 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
4103 core::list<u16> *far_players, float far_d_nodes)
4105 float maxd = far_d_nodes*BS;
4106 v3f p_f = intToFloat(p, BS);
4110 SharedBuffer<u8> reply(replysize);
4111 writeU16(&reply[0], TOCLIENT_REMOVENODE);
4112 writeS16(&reply[2], p.X);
4113 writeS16(&reply[4], p.Y);
4114 writeS16(&reply[6], p.Z);
4116 for(core::map<u16, RemoteClient*>::Iterator
4117 i = m_clients.getIterator();
4118 i.atEnd() == false; i++)
4120 // Get client and check that it is valid
4121 RemoteClient *client = i.getNode()->getValue();
4122 assert(client->peer_id == i.getNode()->getKey());
4123 if(client->serialization_version == SER_FMT_VER_INVALID)
4126 // Don't send if it's the same one
4127 if(client->peer_id == ignore_id)
4133 Player *player = m_env->getPlayer(client->peer_id);
4136 // If player is far away, only set modified blocks not sent
4137 v3f player_pos = player->getPosition();
4138 if(player_pos.getDistanceFrom(p_f) > maxd)
4140 far_players->push_back(client->peer_id);
4147 m_con.Send(client->peer_id, 0, reply, true);
4151 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
4152 core::list<u16> *far_players, float far_d_nodes)
4154 float maxd = far_d_nodes*BS;
4155 v3f p_f = intToFloat(p, BS);
4157 for(core::map<u16, RemoteClient*>::Iterator
4158 i = m_clients.getIterator();
4159 i.atEnd() == false; i++)
4161 // Get client and check that it is valid
4162 RemoteClient *client = i.getNode()->getValue();
4163 assert(client->peer_id == i.getNode()->getKey());
4164 if(client->serialization_version == SER_FMT_VER_INVALID)
4167 // Don't send if it's the same one
4168 if(client->peer_id == ignore_id)
4174 Player *player = m_env->getPlayer(client->peer_id);
4177 // If player is far away, only set modified blocks not sent
4178 v3f player_pos = player->getPosition();
4179 if(player_pos.getDistanceFrom(p_f) > maxd)
4181 far_players->push_back(client->peer_id);
4188 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
4189 SharedBuffer<u8> reply(replysize);
4190 writeU16(&reply[0], TOCLIENT_ADDNODE);
4191 writeS16(&reply[2], p.X);
4192 writeS16(&reply[4], p.Y);
4193 writeS16(&reply[6], p.Z);
4194 n.serialize(&reply[8], client->serialization_version);
4197 m_con.Send(client->peer_id, 0, reply, true);
4201 void Server::setBlockNotSent(v3s16 p)
4203 for(core::map<u16, RemoteClient*>::Iterator
4204 i = m_clients.getIterator();
4205 i.atEnd()==false; i++)
4207 RemoteClient *client = i.getNode()->getValue();
4208 client->SetBlockNotSent(p);
4212 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
4214 DSTACK(__FUNCTION_NAME);
4216 v3s16 p = block->getPos();
4220 bool completely_air = true;
4221 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4222 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4223 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4225 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
4227 completely_air = false;
4228 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
4233 infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
4235 infostream<<"[completely air] ";
4236 infostream<<std::endl;
4240 Create a packet with the block in the right format
4243 std::ostringstream os(std::ios_base::binary);
4244 block->serialize(os, ver);
4245 std::string s = os.str();
4246 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
4248 u32 replysize = 8 + blockdata.getSize();
4249 SharedBuffer<u8> reply(replysize);
4250 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
4251 writeS16(&reply[2], p.X);
4252 writeS16(&reply[4], p.Y);
4253 writeS16(&reply[6], p.Z);
4254 memcpy(&reply[8], *blockdata, blockdata.getSize());
4256 /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4257 <<": \tpacket size: "<<replysize<<std::endl;*/
4262 m_con.Send(peer_id, 1, reply, true);
4265 void Server::SendBlocks(float dtime)
4267 DSTACK(__FUNCTION_NAME);
4269 JMutexAutoLock envlock(m_env_mutex);
4270 JMutexAutoLock conlock(m_con_mutex);
4272 //TimeTaker timer("Server::SendBlocks");
4274 core::array<PrioritySortedBlockTransfer> queue;
4276 s32 total_sending = 0;
4279 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
4281 for(core::map<u16, RemoteClient*>::Iterator
4282 i = m_clients.getIterator();
4283 i.atEnd() == false; i++)
4285 RemoteClient *client = i.getNode()->getValue();
4286 assert(client->peer_id == i.getNode()->getKey());
4288 total_sending += client->SendingCount();
4290 if(client->serialization_version == SER_FMT_VER_INVALID)
4293 client->GetNextBlocks(this, dtime, queue);
4298 // Lowest priority number comes first.
4299 // Lowest is most important.
4302 for(u32 i=0; i<queue.size(); i++)
4304 //TODO: Calculate limit dynamically
4305 if(total_sending >= g_settings->getS32
4306 ("max_simultaneous_block_sends_server_total"))
4309 PrioritySortedBlockTransfer q = queue[i];
4311 MapBlock *block = NULL;
4314 block = m_env->getMap().getBlockNoCreate(q.pos);
4316 catch(InvalidPositionException &e)
4321 RemoteClient *client = getClient(q.peer_id);
4323 SendBlockNoLock(q.peer_id, block, client->serialization_version);
4325 client->SentBlock(q.pos);
4331 struct SendableTexture
4337 SendableTexture(const std::string &name_="", const std::string path_="",
4338 const std::string &data_=""):
4345 void Server::SendTextures(u16 peer_id)
4347 DSTACK(__FUNCTION_NAME);
4349 infostream<<"Server::SendTextures(): Sending textures to client"<<std::endl;
4353 // Put 5kB in one bunch (this is not accurate)
4354 u32 bytes_per_bunch = 5000;
4356 core::array< core::list<SendableTexture> > texture_bunches;
4357 texture_bunches.push_back(core::list<SendableTexture>());
4359 u32 texture_size_bunch_total = 0;
4360 core::list<ModSpec> mods = getMods(m_modspaths);
4361 for(core::list<ModSpec>::Iterator i = mods.begin();
4362 i != mods.end(); i++){
4364 std::string texturepath = mod.path + DIR_DELIM + "textures";
4365 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(texturepath);
4366 for(u32 j=0; j<dirlist.size(); j++){
4367 if(dirlist[j].dir) // Ignode dirs
4369 std::string tname = dirlist[j].name;
4370 std::string tpath = texturepath + DIR_DELIM + tname;
4372 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
4373 if(fis.good() == false){
4374 errorstream<<"Server::SendTextures(): Could not open \""
4375 <<tname<<"\" for reading"<<std::endl;
4378 std::ostringstream tmp_os(std::ios_base::binary);
4382 fis.read(buf, 1024);
4383 std::streamsize len = fis.gcount();
4384 tmp_os.write(buf, len);
4385 texture_size_bunch_total += len;
4394 errorstream<<"Server::SendTextures(): Failed to read \""
4395 <<tname<<"\""<<std::endl;
4398 /*infostream<<"Server::SendTextures(): Loaded \""
4399 <<tname<<"\""<<std::endl;*/
4401 texture_bunches[texture_bunches.size()-1].push_back(
4402 SendableTexture(tname, tpath, tmp_os.str()));
4404 // Start next bunch if got enough data
4405 if(texture_size_bunch_total >= bytes_per_bunch){
4406 texture_bunches.push_back(core::list<SendableTexture>());
4407 texture_size_bunch_total = 0;
4412 /* Create and send packets */
4414 u32 num_bunches = texture_bunches.size();
4415 for(u32 i=0; i<num_bunches; i++)
4419 u16 total number of texture bunches
4420 u16 index of this bunch
4421 u32 number of textures in this bunch
4429 std::ostringstream os(std::ios_base::binary);
4431 writeU16(os, TOCLIENT_TEXTURES);
4432 writeU16(os, num_bunches);
4434 writeU32(os, texture_bunches[i].size());
4436 for(core::list<SendableTexture>::Iterator
4437 j = texture_bunches[i].begin();
4438 j != texture_bunches[i].end(); j++){
4439 os<<serializeString(j->name);
4440 os<<serializeLongString(j->data);
4444 std::string s = os.str();
4445 infostream<<"Server::SendTextures(): bunch "<<i<<"/"<<num_bunches
4446 <<" textures="<<texture_bunches[i].size()
4447 <<" size=" <<s.size()<<std::endl;
4448 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4450 m_con.Send(peer_id, 0, data, true);
4458 void Server::HandlePlayerHP(Player *player, s16 damage)
4460 if(player->hp > damage)
4462 player->hp -= damage;
4463 SendPlayerHP(player);
4467 infostream<<"Server::HandlePlayerHP(): Player "
4468 <<player->getName()<<" dies"<<std::endl;
4472 //TODO: Throw items around
4474 // Handle players that are not connected
4475 if(player->peer_id == PEER_ID_INEXISTENT){
4476 RespawnPlayer(player);
4480 SendPlayerHP(player);
4482 RemoteClient *client = getClient(player->peer_id);
4483 if(client->net_proto_version >= 3)
4485 SendDeathscreen(m_con, player->peer_id, false, v3f(0,0,0));
4489 RespawnPlayer(player);
4494 void Server::RespawnPlayer(Player *player)
4497 ServerRemotePlayer *srp = (ServerRemotePlayer*)player;
4498 bool repositioned = scriptapi_on_respawnplayer(m_lua, srp);
4500 v3f pos = findSpawnPos(m_env->getServerMap());
4501 player->setPosition(pos);
4502 srp->m_last_good_position = pos;
4503 srp->m_last_good_position_age = 0;
4505 SendMovePlayer(player);
4506 SendPlayerHP(player);
4509 void Server::UpdateCrafting(u16 peer_id)
4511 DSTACK(__FUNCTION_NAME);
4513 Player* player = m_env->getPlayer(peer_id);
4517 Calculate crafting stuff
4519 if(g_settings->getBool("creative_mode") == false)
4521 InventoryList *clist = player->inventory.getList("craft");
4522 InventoryList *rlist = player->inventory.getList("craftresult");
4524 if(rlist && rlist->getUsedSlots() == 0)
4525 player->craftresult_is_preview = true;
4527 if(rlist && player->craftresult_is_preview)
4529 rlist->clearItems();
4531 if(clist && rlist && player->craftresult_is_preview)
4533 // Get result of crafting grid
4535 std::vector<InventoryItem*> items;
4536 for(u16 i=0; i<9; i++){
4537 if(clist->getItem(i) == NULL)
4538 items.push_back(NULL);
4540 items.push_back(clist->getItem(i)->clone());
4542 CraftPointerInput cpi(3, items);
4544 InventoryItem *result = m_craftdef->getCraftResult(cpi, this);
4545 //InventoryItem *result = craft_get_result(items, this);
4547 rlist->addItem(result);
4550 } // if creative_mode == false
4553 RemoteClient* Server::getClient(u16 peer_id)
4555 DSTACK(__FUNCTION_NAME);
4556 //JMutexAutoLock lock(m_con_mutex);
4557 core::map<u16, RemoteClient*>::Node *n;
4558 n = m_clients.find(peer_id);
4559 // A client should exist for all peers
4561 return n->getValue();
4564 std::wstring Server::getStatusString()
4566 std::wostringstream os(std::ios_base::binary);
4569 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4571 os<<L", uptime="<<m_uptime.get();
4572 // Information about clients
4574 for(core::map<u16, RemoteClient*>::Iterator
4575 i = m_clients.getIterator();
4576 i.atEnd() == false; i++)
4578 // Get client and check that it is valid
4579 RemoteClient *client = i.getNode()->getValue();
4580 assert(client->peer_id == i.getNode()->getKey());
4581 if(client->serialization_version == SER_FMT_VER_INVALID)
4584 Player *player = m_env->getPlayer(client->peer_id);
4585 // Get name of player
4586 std::wstring name = L"unknown";
4588 name = narrow_to_wide(player->getName());
4589 // Add name to information string
4593 if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
4594 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4595 if(g_settings->get("motd") != "")
4596 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4600 // Saves g_settings to configpath given at initialization
4601 void Server::saveConfig()
4603 if(m_configpath != "")
4604 g_settings->updateConfigFile(m_configpath.c_str());
4607 void Server::notifyPlayer(const char *name, const std::wstring msg)
4609 Player *player = m_env->getPlayer(name);
4612 SendChatMessage(player->peer_id, std::wstring(L"Server: -!- ")+msg);
4615 void Server::notifyPlayers(const std::wstring msg)
4617 BroadcastChatMessage(msg);
4620 void Server::queueBlockEmerge(v3s16 blockpos, bool allow_generate)
4624 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
4625 m_emerge_queue.addBlock(PEER_ID_INEXISTENT, blockpos, flags);
4628 // IGameDef interface
4630 IToolDefManager* Server::getToolDefManager()
4634 INodeDefManager* Server::getNodeDefManager()
4638 ICraftDefManager* Server::getCraftDefManager()
4642 ITextureSource* Server::getTextureSource()
4646 u16 Server::allocateUnknownNodeId(const std::string &name)
4648 return m_nodedef->allocateDummy(name);
4651 IWritableToolDefManager* Server::getWritableToolDefManager()
4655 IWritableNodeDefManager* Server::getWritableNodeDefManager()
4659 IWritableCraftDefManager* Server::getWritableCraftDefManager()
4664 v3f findSpawnPos(ServerMap &map)
4666 //return v3f(50,50,50)*BS;
4671 nodepos = v2s16(0,0);
4676 // Try to find a good place a few times
4677 for(s32 i=0; i<1000; i++)
4680 // We're going to try to throw the player to this position
4681 v2s16 nodepos2d = v2s16(-range + (myrand()%(range*2)),
4682 -range + (myrand()%(range*2)));
4683 //v2s16 sectorpos = getNodeSectorPos(nodepos2d);
4684 // Get ground height at point (fallbacks to heightmap function)
4685 s16 groundheight = map.findGroundLevel(nodepos2d);
4686 // Don't go underwater
4687 if(groundheight < WATER_LEVEL)
4689 //infostream<<"-> Underwater"<<std::endl;
4692 // Don't go to high places
4693 if(groundheight > WATER_LEVEL + 4)
4695 //infostream<<"-> Underwater"<<std::endl;
4699 nodepos = v3s16(nodepos2d.X, groundheight-2, nodepos2d.Y);
4700 bool is_good = false;
4702 for(s32 i=0; i<10; i++){
4703 v3s16 blockpos = getNodeBlockPos(nodepos);
4704 map.emergeBlock(blockpos, true);
4705 MapNode n = map.getNodeNoEx(nodepos);
4706 if(n.getContent() == CONTENT_AIR){
4717 // Found a good place
4718 //infostream<<"Searched through "<<i<<" places."<<std::endl;
4724 return intToFloat(nodepos, BS);
4727 Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id)
4730 Try to get an existing player
4732 Player *player = m_env->getPlayer(name);
4735 // If player is already connected, cancel
4736 if(player->peer_id != 0)
4738 infostream<<"emergePlayer(): Player already connected"<<std::endl;
4743 player->peer_id = peer_id;
4745 // Reset inventory to creative if in creative mode
4746 if(g_settings->getBool("creative_mode"))
4748 // Warning: double code below
4749 // Backup actual inventory
4750 player->inventory_backup = new Inventory();
4751 *(player->inventory_backup) = player->inventory;
4752 // Set creative inventory
4753 craft_set_creative_inventory(player, this);
4760 If player with the wanted peer_id already exists, cancel.
4762 if(m_env->getPlayer(peer_id) != NULL)
4764 infostream<<"emergePlayer(): Player with wrong name but same"
4765 " peer_id already exists"<<std::endl;
4773 // Add authentication stuff
4774 m_authmanager.add(name);
4775 m_authmanager.setPassword(name, password);
4776 m_authmanager.setPrivs(name,
4777 stringToPrivs(g_settings->get("default_privs")));
4779 /* Set player position */
4781 infostream<<"Server: Finding spawn place for player \""
4782 <<name<<"\""<<std::endl;
4784 v3f pos = findSpawnPos(m_env->getServerMap());
4786 player = new ServerRemotePlayer(m_env, pos, peer_id, name);
4788 /* Add player to environment */
4789 m_env->addPlayer(player);
4792 ServerRemotePlayer *srp = (ServerRemotePlayer*)player;
4793 scriptapi_on_newplayer(m_lua, srp);
4795 /* Add stuff to inventory */
4796 if(g_settings->getBool("creative_mode"))
4798 // Warning: double code above
4799 // Backup actual inventory
4800 player->inventory_backup = new Inventory();
4801 *(player->inventory_backup) = player->inventory;
4802 // Set creative inventory
4803 craft_set_creative_inventory(player, this);
4808 } // create new player
4811 void Server::handlePeerChange(PeerChange &c)
4813 JMutexAutoLock envlock(m_env_mutex);
4814 JMutexAutoLock conlock(m_con_mutex);
4816 if(c.type == PEER_ADDED)
4823 core::map<u16, RemoteClient*>::Node *n;
4824 n = m_clients.find(c.peer_id);
4825 // The client shouldn't already exist
4829 RemoteClient *client = new RemoteClient();
4830 client->peer_id = c.peer_id;
4831 m_clients.insert(client->peer_id, client);
4834 else if(c.type == PEER_REMOVED)
4841 core::map<u16, RemoteClient*>::Node *n;
4842 n = m_clients.find(c.peer_id);
4843 // The client should exist
4847 Mark objects to be not known by the client
4849 RemoteClient *client = n->getValue();
4851 for(core::map<u16, bool>::Iterator
4852 i = client->m_known_objects.getIterator();
4853 i.atEnd()==false; i++)
4856 u16 id = i.getNode()->getKey();
4857 ServerActiveObject* obj = m_env->getActiveObject(id);
4859 if(obj && obj->m_known_by_count > 0)
4860 obj->m_known_by_count--;
4863 // Collect information about leaving in chat
4864 std::wstring message;
4866 Player *player = m_env->getPlayer(c.peer_id);
4869 std::wstring name = narrow_to_wide(player->getName());
4872 message += L" left game";
4874 message += L" (timed out)";
4880 m_env->removePlayer(c.peer_id);
4883 // Set player client disconnected
4885 Player *player = m_env->getPlayer(c.peer_id);
4887 player->peer_id = 0;
4894 std::ostringstream os(std::ios_base::binary);
4895 for(core::map<u16, RemoteClient*>::Iterator
4896 i = m_clients.getIterator();
4897 i.atEnd() == false; i++)
4899 RemoteClient *client = i.getNode()->getValue();
4900 assert(client->peer_id == i.getNode()->getKey());
4901 if(client->serialization_version == SER_FMT_VER_INVALID)
4904 Player *player = m_env->getPlayer(client->peer_id);
4907 // Get name of player
4908 os<<player->getName()<<" ";
4911 actionstream<<player->getName()<<" "
4912 <<(c.timeout?"times out.":"leaves game.")
4913 <<" List of players: "
4914 <<os.str()<<std::endl;
4919 delete m_clients[c.peer_id];
4920 m_clients.remove(c.peer_id);
4922 // Send player info to all remaining clients
4925 // Send leave chat message to all remaining clients
4926 BroadcastChatMessage(message);
4935 void Server::handlePeerChanges()
4937 while(m_peer_change_queue.size() > 0)
4939 PeerChange c = m_peer_change_queue.pop_front();
4941 infostream<<"Server: Handling peer change: "
4942 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4945 handlePeerChange(c);
4949 u64 Server::getPlayerPrivs(Player *player)
4953 std::string playername = player->getName();
4954 // Local player gets all privileges regardless of
4955 // what's set on their account.
4956 if(g_settings->get("name") == playername)
4962 return getPlayerAuthPrivs(playername);
4966 void dedicated_server_loop(Server &server, bool &kill)
4968 DSTACK(__FUNCTION_NAME);
4970 infostream<<DTIME<<std::endl;
4971 infostream<<"========================"<<std::endl;
4972 infostream<<"Running dedicated server"<<std::endl;
4973 infostream<<"========================"<<std::endl;
4974 infostream<<std::endl;
4976 IntervalLimiter m_profiler_interval;
4980 // This is kind of a hack but can be done like this
4981 // because server.step() is very light
4983 ScopeProfiler sp(g_profiler, "dedicated server sleep");
4988 if(server.getShutdownRequested() || kill)
4990 infostream<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4997 float profiler_print_interval =
4998 g_settings->getFloat("profiler_print_interval");
4999 if(profiler_print_interval != 0)
5001 if(m_profiler_interval.step(0.030, profiler_print_interval))
5003 infostream<<"Profiler:"<<std::endl;
5004 g_profiler->print(infostream);
5005 g_profiler->clear();
5012 static int counter = 0;
5018 core::list<PlayerInfo> list = server.getPlayerInfo();
5019 core::list<PlayerInfo>::Iterator i;
5020 static u32 sum_old = 0;
5021 u32 sum = PIChecksum(list);
5024 infostream<<DTIME<<"Player info:"<<std::endl;
5025 for(i=list.begin(); i!=list.end(); i++)
5027 i->PrintLine(&infostream);