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());
3425 // Line to send to players
3427 // Whether to send to the player that sent the line
3428 bool send_to_sender = false;
3429 // Whether to send to other players
3430 bool send_to_others = false;
3432 // Local player gets all privileges regardless of
3433 // what's set on their account.
3434 u64 privs = getPlayerPrivs(player);
3437 if(message[0] == L'/')
3439 size_t strip_size = 1;
3440 if (message[1] == L'#') // support old-style commans
3442 message = message.substr(strip_size);
3444 WStrfnd f1(message);
3445 f1.next(L" "); // Skip over /#whatever
3446 std::wstring paramstring = f1.next(L"");
3448 ServerCommandContext *ctx = new ServerCommandContext(
3449 str_split(message, L' '),
3456 std::wstring reply(processServerCommand(ctx));
3457 send_to_sender = ctx->flags & SEND_TO_SENDER;
3458 send_to_others = ctx->flags & SEND_TO_OTHERS;
3460 if (ctx->flags & SEND_NO_PREFIX)
3463 line += L"Server: " + reply;
3470 if(privs & PRIV_SHOUT)
3476 send_to_others = true;
3480 line += L"Server: You are not allowed to shout";
3481 send_to_sender = true;
3488 actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
3491 Send the message to clients
3493 for(core::map<u16, RemoteClient*>::Iterator
3494 i = m_clients.getIterator();
3495 i.atEnd() == false; i++)
3497 // Get client and check that it is valid
3498 RemoteClient *client = i.getNode()->getValue();
3499 assert(client->peer_id == i.getNode()->getKey());
3500 if(client->serialization_version == SER_FMT_VER_INVALID)
3504 bool sender_selected = (peer_id == client->peer_id);
3505 if(sender_selected == true && send_to_sender == false)
3507 if(sender_selected == false && send_to_others == false)
3510 SendChatMessage(client->peer_id, line);
3514 else if(command == TOSERVER_DAMAGE)
3516 std::string datastring((char*)&data[2], datasize-2);
3517 std::istringstream is(datastring, std::ios_base::binary);
3518 u8 damage = readU8(is);
3520 if(g_settings->getBool("enable_damage"))
3522 actionstream<<player->getName()<<" damaged by "
3523 <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
3526 HandlePlayerHP(player, damage);
3530 SendPlayerHP(player);
3533 else if(command == TOSERVER_PASSWORD)
3536 [0] u16 TOSERVER_PASSWORD
3537 [2] u8[28] old password
3538 [30] u8[28] new password
3541 if(datasize != 2+PASSWORD_SIZE*2)
3543 /*char password[PASSWORD_SIZE];
3544 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3545 password[i] = data[2+i];
3546 password[PASSWORD_SIZE-1] = 0;*/
3548 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3556 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3558 char c = data[2+PASSWORD_SIZE+i];
3564 infostream<<"Server: Client requests a password change from "
3565 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
3567 std::string playername = player->getName();
3569 if(m_authmanager.exists(playername) == false)
3571 infostream<<"Server: playername not found in authmanager"<<std::endl;
3572 // Wrong old password supplied!!
3573 SendChatMessage(peer_id, L"playername not found in authmanager");
3577 std::string checkpwd = m_authmanager.getPassword(playername);
3579 if(oldpwd != checkpwd)
3581 infostream<<"Server: invalid old password"<<std::endl;
3582 // Wrong old password supplied!!
3583 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
3587 actionstream<<player->getName()<<" changes password"<<std::endl;
3589 m_authmanager.setPassword(playername, newpwd);
3591 infostream<<"Server: password change successful for "<<playername
3593 SendChatMessage(peer_id, L"Password change successful");
3595 else if(command == TOSERVER_PLAYERITEM)
3600 u16 item = readU16(&data[2]);
3601 player->wieldItem(item);
3602 SendWieldedItem(player);
3604 else if(command == TOSERVER_RESPAWN)
3609 RespawnPlayer(player);
3611 actionstream<<player->getName()<<" respawns at "
3612 <<PP(player->getPosition()/BS)<<std::endl;
3616 infostream<<"Server::ProcessData(): Ignoring "
3617 "unknown command "<<command<<std::endl;
3621 catch(SendFailedException &e)
3623 errorstream<<"Server::ProcessData(): SendFailedException: "
3629 void Server::onMapEditEvent(MapEditEvent *event)
3631 //infostream<<"Server::onMapEditEvent()"<<std::endl;
3632 if(m_ignore_map_edit_events)
3634 MapEditEvent *e = event->clone();
3635 m_unsent_map_edit_queue.push_back(e);
3638 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3640 if(id == "current_player")
3642 assert(c->current_player);
3643 return &(c->current_player->inventory);
3647 std::string id0 = fn.next(":");
3649 if(id0 == "nodemeta")
3652 p.X = stoi(fn.next(","));
3653 p.Y = stoi(fn.next(","));
3654 p.Z = stoi(fn.next(","));
3655 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3657 return meta->getInventory();
3658 infostream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3659 <<"no metadata found"<<std::endl;
3663 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3666 void Server::inventoryModified(InventoryContext *c, std::string id)
3668 if(id == "current_player")
3670 assert(c->current_player);
3672 UpdateCrafting(c->current_player->peer_id);
3673 SendInventory(c->current_player->peer_id);
3678 std::string id0 = fn.next(":");
3680 if(id0 == "nodemeta")
3683 p.X = stoi(fn.next(","));
3684 p.Y = stoi(fn.next(","));
3685 p.Z = stoi(fn.next(","));
3686 v3s16 blockpos = getNodeBlockPos(p);
3688 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3690 meta->inventoryModified();
3692 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3694 block->raiseModified(MOD_STATE_WRITE_NEEDED);
3696 setBlockNotSent(blockpos);
3701 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3704 core::list<PlayerInfo> Server::getPlayerInfo()
3706 DSTACK(__FUNCTION_NAME);
3707 JMutexAutoLock envlock(m_env_mutex);
3708 JMutexAutoLock conlock(m_con_mutex);
3710 core::list<PlayerInfo> list;
3712 core::list<Player*> players = m_env->getPlayers();
3714 core::list<Player*>::Iterator i;
3715 for(i = players.begin();
3716 i != players.end(); i++)
3720 Player *player = *i;
3723 // Copy info from connection to info struct
3724 info.id = player->peer_id;
3725 info.address = m_con.GetPeerAddress(player->peer_id);
3726 info.avg_rtt = m_con.GetPeerAvgRTT(player->peer_id);
3728 catch(con::PeerNotFoundException &e)
3730 // Set dummy peer info
3732 info.address = Address(0,0,0,0,0);
3736 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3737 info.position = player->getPosition();
3739 list.push_back(info);
3746 void Server::peerAdded(con::Peer *peer)
3748 DSTACK(__FUNCTION_NAME);
3749 infostream<<"Server::peerAdded(): peer->id="
3750 <<peer->id<<std::endl;
3753 c.type = PEER_ADDED;
3754 c.peer_id = peer->id;
3756 m_peer_change_queue.push_back(c);
3759 void Server::deletingPeer(con::Peer *peer, bool timeout)
3761 DSTACK(__FUNCTION_NAME);
3762 infostream<<"Server::deletingPeer(): peer->id="
3763 <<peer->id<<", timeout="<<timeout<<std::endl;
3766 c.type = PEER_REMOVED;
3767 c.peer_id = peer->id;
3768 c.timeout = timeout;
3769 m_peer_change_queue.push_back(c);
3776 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3778 DSTACK(__FUNCTION_NAME);
3779 std::ostringstream os(std::ios_base::binary);
3781 writeU16(os, TOCLIENT_HP);
3785 std::string s = os.str();
3786 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3788 con.Send(peer_id, 0, data, true);
3791 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3792 const std::wstring &reason)
3794 DSTACK(__FUNCTION_NAME);
3795 std::ostringstream os(std::ios_base::binary);
3797 writeU16(os, TOCLIENT_ACCESS_DENIED);
3798 os<<serializeWideString(reason);
3801 std::string s = os.str();
3802 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3804 con.Send(peer_id, 0, data, true);
3807 void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
3808 bool set_camera_point_target, v3f camera_point_target)
3810 DSTACK(__FUNCTION_NAME);
3811 std::ostringstream os(std::ios_base::binary);
3813 writeU16(os, TOCLIENT_DEATHSCREEN);
3814 writeU8(os, set_camera_point_target);
3815 writeV3F1000(os, camera_point_target);
3818 std::string s = os.str();
3819 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3821 con.Send(peer_id, 0, data, true);
3824 void Server::SendToolDef(con::Connection &con, u16 peer_id,
3825 IToolDefManager *tooldef)
3827 DSTACK(__FUNCTION_NAME);
3828 std::ostringstream os(std::ios_base::binary);
3832 u32 length of the next item
3833 serialized ToolDefManager
3835 writeU16(os, TOCLIENT_TOOLDEF);
3836 std::ostringstream tmp_os(std::ios::binary);
3837 tooldef->serialize(tmp_os);
3838 os<<serializeLongString(tmp_os.str());
3841 std::string s = os.str();
3842 infostream<<"Server::SendToolDef(): Sending tool definitions: size="
3843 <<s.size()<<std::endl;
3844 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3846 con.Send(peer_id, 0, data, true);
3849 void Server::SendNodeDef(con::Connection &con, u16 peer_id,
3850 INodeDefManager *nodedef)
3852 DSTACK(__FUNCTION_NAME);
3853 std::ostringstream os(std::ios_base::binary);
3857 u32 length of the next item
3858 serialized NodeDefManager
3860 writeU16(os, TOCLIENT_NODEDEF);
3861 std::ostringstream tmp_os(std::ios::binary);
3862 nodedef->serialize(tmp_os);
3863 os<<serializeLongString(tmp_os.str());
3866 std::string s = os.str();
3867 infostream<<"Server::SendNodeDef(): Sending node definitions: size="
3868 <<s.size()<<std::endl;
3869 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3871 con.Send(peer_id, 0, data, true);
3875 Non-static send methods
3878 void Server::SendObjectData(float dtime)
3880 DSTACK(__FUNCTION_NAME);
3882 core::map<v3s16, bool> stepped_blocks;
3884 for(core::map<u16, RemoteClient*>::Iterator
3885 i = m_clients.getIterator();
3886 i.atEnd() == false; i++)
3888 u16 peer_id = i.getNode()->getKey();
3889 RemoteClient *client = i.getNode()->getValue();
3890 assert(client->peer_id == peer_id);
3892 if(client->serialization_version == SER_FMT_VER_INVALID)
3895 client->SendObjectData(this, dtime, stepped_blocks);
3899 void Server::SendPlayerInfos()
3901 DSTACK(__FUNCTION_NAME);
3903 //JMutexAutoLock envlock(m_env_mutex);
3905 // Get connected players
3906 core::list<Player*> players = m_env->getPlayers(true);
3908 u32 player_count = players.getSize();
3909 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3911 SharedBuffer<u8> data(datasize);
3912 writeU16(&data[0], TOCLIENT_PLAYERINFO);
3915 core::list<Player*>::Iterator i;
3916 for(i = players.begin();
3917 i != players.end(); i++)
3919 Player *player = *i;
3921 /*infostream<<"Server sending player info for player with "
3922 "peer_id="<<player->peer_id<<std::endl;*/
3924 writeU16(&data[start], player->peer_id);
3925 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3926 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3927 start += 2+PLAYERNAME_SIZE;
3930 //JMutexAutoLock conlock(m_con_mutex);
3933 m_con.SendToAll(0, data, true);
3936 void Server::SendInventory(u16 peer_id)
3938 DSTACK(__FUNCTION_NAME);
3940 Player* player = m_env->getPlayer(peer_id);
3947 std::ostringstream os;
3948 //os.imbue(std::locale("C"));
3950 player->inventory.serialize(os);
3952 std::string s = os.str();
3954 SharedBuffer<u8> data(s.size()+2);
3955 writeU16(&data[0], TOCLIENT_INVENTORY);
3956 memcpy(&data[2], s.c_str(), s.size());
3959 m_con.Send(peer_id, 0, data, true);
3962 std::string getWieldedItemString(const Player *player)
3964 const InventoryItem *item = player->getWieldItem();
3966 return std::string("");
3967 std::ostringstream os(std::ios_base::binary);
3968 item->serialize(os);
3972 void Server::SendWieldedItem(const Player* player)
3974 DSTACK(__FUNCTION_NAME);
3978 std::ostringstream os(std::ios_base::binary);
3980 writeU16(os, TOCLIENT_PLAYERITEM);
3982 writeU16(os, player->peer_id);
3983 os<<serializeString(getWieldedItemString(player));
3986 std::string s = os.str();
3987 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3989 m_con.SendToAll(0, data, true);
3992 void Server::SendPlayerItems()
3994 DSTACK(__FUNCTION_NAME);
3996 std::ostringstream os(std::ios_base::binary);
3997 core::list<Player *> players = m_env->getPlayers(true);
3999 writeU16(os, TOCLIENT_PLAYERITEM);
4000 writeU16(os, players.size());
4001 core::list<Player *>::Iterator i;
4002 for(i = players.begin(); i != players.end(); ++i)
4005 writeU16(os, p->peer_id);
4006 os<<serializeString(getWieldedItemString(p));
4010 std::string s = os.str();
4011 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4013 m_con.SendToAll(0, data, true);
4016 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
4018 DSTACK(__FUNCTION_NAME);
4020 std::ostringstream os(std::ios_base::binary);
4024 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
4025 os.write((char*)buf, 2);
4028 writeU16(buf, message.size());
4029 os.write((char*)buf, 2);
4032 for(u32 i=0; i<message.size(); i++)
4036 os.write((char*)buf, 2);
4040 std::string s = os.str();
4041 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4043 m_con.Send(peer_id, 0, data, true);
4046 void Server::BroadcastChatMessage(const std::wstring &message)
4048 for(core::map<u16, RemoteClient*>::Iterator
4049 i = m_clients.getIterator();
4050 i.atEnd() == false; i++)
4052 // Get client and check that it is valid
4053 RemoteClient *client = i.getNode()->getValue();
4054 assert(client->peer_id == i.getNode()->getKey());
4055 if(client->serialization_version == SER_FMT_VER_INVALID)
4058 SendChatMessage(client->peer_id, message);
4062 void Server::SendPlayerHP(Player *player)
4064 SendHP(m_con, player->peer_id, player->hp);
4067 void Server::SendMovePlayer(Player *player)
4069 DSTACK(__FUNCTION_NAME);
4070 std::ostringstream os(std::ios_base::binary);
4072 writeU16(os, TOCLIENT_MOVE_PLAYER);
4073 writeV3F1000(os, player->getPosition());
4074 writeF1000(os, player->getPitch());
4075 writeF1000(os, player->getYaw());
4078 v3f pos = player->getPosition();
4079 f32 pitch = player->getPitch();
4080 f32 yaw = player->getYaw();
4081 infostream<<"Server sending TOCLIENT_MOVE_PLAYER"
4082 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
4089 std::string s = os.str();
4090 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4092 m_con.Send(player->peer_id, 0, data, true);
4095 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
4096 core::list<u16> *far_players, float far_d_nodes)
4098 float maxd = far_d_nodes*BS;
4099 v3f p_f = intToFloat(p, BS);
4103 SharedBuffer<u8> reply(replysize);
4104 writeU16(&reply[0], TOCLIENT_REMOVENODE);
4105 writeS16(&reply[2], p.X);
4106 writeS16(&reply[4], p.Y);
4107 writeS16(&reply[6], p.Z);
4109 for(core::map<u16, RemoteClient*>::Iterator
4110 i = m_clients.getIterator();
4111 i.atEnd() == false; i++)
4113 // Get client and check that it is valid
4114 RemoteClient *client = i.getNode()->getValue();
4115 assert(client->peer_id == i.getNode()->getKey());
4116 if(client->serialization_version == SER_FMT_VER_INVALID)
4119 // Don't send if it's the same one
4120 if(client->peer_id == ignore_id)
4126 Player *player = m_env->getPlayer(client->peer_id);
4129 // If player is far away, only set modified blocks not sent
4130 v3f player_pos = player->getPosition();
4131 if(player_pos.getDistanceFrom(p_f) > maxd)
4133 far_players->push_back(client->peer_id);
4140 m_con.Send(client->peer_id, 0, reply, true);
4144 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
4145 core::list<u16> *far_players, float far_d_nodes)
4147 float maxd = far_d_nodes*BS;
4148 v3f p_f = intToFloat(p, BS);
4150 for(core::map<u16, RemoteClient*>::Iterator
4151 i = m_clients.getIterator();
4152 i.atEnd() == false; i++)
4154 // Get client and check that it is valid
4155 RemoteClient *client = i.getNode()->getValue();
4156 assert(client->peer_id == i.getNode()->getKey());
4157 if(client->serialization_version == SER_FMT_VER_INVALID)
4160 // Don't send if it's the same one
4161 if(client->peer_id == ignore_id)
4167 Player *player = m_env->getPlayer(client->peer_id);
4170 // If player is far away, only set modified blocks not sent
4171 v3f player_pos = player->getPosition();
4172 if(player_pos.getDistanceFrom(p_f) > maxd)
4174 far_players->push_back(client->peer_id);
4181 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
4182 SharedBuffer<u8> reply(replysize);
4183 writeU16(&reply[0], TOCLIENT_ADDNODE);
4184 writeS16(&reply[2], p.X);
4185 writeS16(&reply[4], p.Y);
4186 writeS16(&reply[6], p.Z);
4187 n.serialize(&reply[8], client->serialization_version);
4190 m_con.Send(client->peer_id, 0, reply, true);
4194 void Server::setBlockNotSent(v3s16 p)
4196 for(core::map<u16, RemoteClient*>::Iterator
4197 i = m_clients.getIterator();
4198 i.atEnd()==false; i++)
4200 RemoteClient *client = i.getNode()->getValue();
4201 client->SetBlockNotSent(p);
4205 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
4207 DSTACK(__FUNCTION_NAME);
4209 v3s16 p = block->getPos();
4213 bool completely_air = true;
4214 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4215 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4216 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4218 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
4220 completely_air = false;
4221 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
4226 infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
4228 infostream<<"[completely air] ";
4229 infostream<<std::endl;
4233 Create a packet with the block in the right format
4236 std::ostringstream os(std::ios_base::binary);
4237 block->serialize(os, ver);
4238 std::string s = os.str();
4239 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
4241 u32 replysize = 8 + blockdata.getSize();
4242 SharedBuffer<u8> reply(replysize);
4243 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
4244 writeS16(&reply[2], p.X);
4245 writeS16(&reply[4], p.Y);
4246 writeS16(&reply[6], p.Z);
4247 memcpy(&reply[8], *blockdata, blockdata.getSize());
4249 /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4250 <<": \tpacket size: "<<replysize<<std::endl;*/
4255 m_con.Send(peer_id, 1, reply, true);
4258 void Server::SendBlocks(float dtime)
4260 DSTACK(__FUNCTION_NAME);
4262 JMutexAutoLock envlock(m_env_mutex);
4263 JMutexAutoLock conlock(m_con_mutex);
4265 //TimeTaker timer("Server::SendBlocks");
4267 core::array<PrioritySortedBlockTransfer> queue;
4269 s32 total_sending = 0;
4272 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
4274 for(core::map<u16, RemoteClient*>::Iterator
4275 i = m_clients.getIterator();
4276 i.atEnd() == false; i++)
4278 RemoteClient *client = i.getNode()->getValue();
4279 assert(client->peer_id == i.getNode()->getKey());
4281 total_sending += client->SendingCount();
4283 if(client->serialization_version == SER_FMT_VER_INVALID)
4286 client->GetNextBlocks(this, dtime, queue);
4291 // Lowest priority number comes first.
4292 // Lowest is most important.
4295 for(u32 i=0; i<queue.size(); i++)
4297 //TODO: Calculate limit dynamically
4298 if(total_sending >= g_settings->getS32
4299 ("max_simultaneous_block_sends_server_total"))
4302 PrioritySortedBlockTransfer q = queue[i];
4304 MapBlock *block = NULL;
4307 block = m_env->getMap().getBlockNoCreate(q.pos);
4309 catch(InvalidPositionException &e)
4314 RemoteClient *client = getClient(q.peer_id);
4316 SendBlockNoLock(q.peer_id, block, client->serialization_version);
4318 client->SentBlock(q.pos);
4324 struct SendableTexture
4330 SendableTexture(const std::string &name_="", const std::string path_="",
4331 const std::string &data_=""):
4338 void Server::SendTextures(u16 peer_id)
4340 DSTACK(__FUNCTION_NAME);
4342 infostream<<"Server::SendTextures(): Sending textures to client"<<std::endl;
4346 // Put 5kB in one bunch (this is not accurate)
4347 u32 bytes_per_bunch = 5000;
4349 core::array< core::list<SendableTexture> > texture_bunches;
4350 texture_bunches.push_back(core::list<SendableTexture>());
4352 u32 texture_size_bunch_total = 0;
4353 core::list<ModSpec> mods = getMods(m_modspaths);
4354 for(core::list<ModSpec>::Iterator i = mods.begin();
4355 i != mods.end(); i++){
4357 std::string texturepath = mod.path + DIR_DELIM + "textures";
4358 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(texturepath);
4359 for(u32 j=0; j<dirlist.size(); j++){
4360 if(dirlist[j].dir) // Ignode dirs
4362 std::string tname = dirlist[j].name;
4363 std::string tpath = texturepath + DIR_DELIM + tname;
4365 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
4366 if(fis.good() == false){
4367 errorstream<<"Server::SendTextures(): Could not open \""
4368 <<tname<<"\" for reading"<<std::endl;
4371 std::ostringstream tmp_os(std::ios_base::binary);
4375 fis.read(buf, 1024);
4376 std::streamsize len = fis.gcount();
4377 tmp_os.write(buf, len);
4378 texture_size_bunch_total += len;
4387 errorstream<<"Server::SendTextures(): Failed to read \""
4388 <<tname<<"\""<<std::endl;
4391 /*infostream<<"Server::SendTextures(): Loaded \""
4392 <<tname<<"\""<<std::endl;*/
4394 texture_bunches[texture_bunches.size()-1].push_back(
4395 SendableTexture(tname, tpath, tmp_os.str()));
4397 // Start next bunch if got enough data
4398 if(texture_size_bunch_total >= bytes_per_bunch){
4399 texture_bunches.push_back(core::list<SendableTexture>());
4400 texture_size_bunch_total = 0;
4405 /* Create and send packets */
4407 u32 num_bunches = texture_bunches.size();
4408 for(u32 i=0; i<num_bunches; i++)
4412 u16 total number of texture bunches
4413 u16 index of this bunch
4414 u32 number of textures in this bunch
4422 std::ostringstream os(std::ios_base::binary);
4424 writeU16(os, TOCLIENT_TEXTURES);
4425 writeU16(os, num_bunches);
4427 writeU32(os, texture_bunches[i].size());
4429 for(core::list<SendableTexture>::Iterator
4430 j = texture_bunches[i].begin();
4431 j != texture_bunches[i].end(); j++){
4432 os<<serializeString(j->name);
4433 os<<serializeLongString(j->data);
4437 std::string s = os.str();
4438 infostream<<"Server::SendTextures(): bunch "<<i<<"/"<<num_bunches
4439 <<" textures="<<texture_bunches[i].size()
4440 <<" size=" <<s.size()<<std::endl;
4441 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4443 m_con.Send(peer_id, 0, data, true);
4451 void Server::HandlePlayerHP(Player *player, s16 damage)
4453 if(player->hp > damage)
4455 player->hp -= damage;
4456 SendPlayerHP(player);
4460 infostream<<"Server::HandlePlayerHP(): Player "
4461 <<player->getName()<<" dies"<<std::endl;
4465 //TODO: Throw items around
4467 // Handle players that are not connected
4468 if(player->peer_id == PEER_ID_INEXISTENT){
4469 RespawnPlayer(player);
4473 SendPlayerHP(player);
4475 RemoteClient *client = getClient(player->peer_id);
4476 if(client->net_proto_version >= 3)
4478 SendDeathscreen(m_con, player->peer_id, false, v3f(0,0,0));
4482 RespawnPlayer(player);
4487 void Server::RespawnPlayer(Player *player)
4490 ServerRemotePlayer *srp = (ServerRemotePlayer*)player;
4491 bool repositioned = scriptapi_on_respawnplayer(m_lua, srp);
4493 v3f pos = findSpawnPos(m_env->getServerMap());
4494 player->setPosition(pos);
4495 srp->m_last_good_position = pos;
4496 srp->m_last_good_position_age = 0;
4498 SendMovePlayer(player);
4499 SendPlayerHP(player);
4502 void Server::UpdateCrafting(u16 peer_id)
4504 DSTACK(__FUNCTION_NAME);
4506 Player* player = m_env->getPlayer(peer_id);
4510 Calculate crafting stuff
4512 if(g_settings->getBool("creative_mode") == false)
4514 InventoryList *clist = player->inventory.getList("craft");
4515 InventoryList *rlist = player->inventory.getList("craftresult");
4517 if(rlist && rlist->getUsedSlots() == 0)
4518 player->craftresult_is_preview = true;
4520 if(rlist && player->craftresult_is_preview)
4522 rlist->clearItems();
4524 if(clist && rlist && player->craftresult_is_preview)
4526 // Get result of crafting grid
4528 std::vector<InventoryItem*> items;
4529 for(u16 i=0; i<9; i++){
4530 if(clist->getItem(i) == NULL)
4531 items.push_back(NULL);
4533 items.push_back(clist->getItem(i)->clone());
4535 CraftPointerInput cpi(3, items);
4537 InventoryItem *result = m_craftdef->getCraftResult(cpi, this);
4538 //InventoryItem *result = craft_get_result(items, this);
4540 rlist->addItem(result);
4543 } // if creative_mode == false
4546 RemoteClient* Server::getClient(u16 peer_id)
4548 DSTACK(__FUNCTION_NAME);
4549 //JMutexAutoLock lock(m_con_mutex);
4550 core::map<u16, RemoteClient*>::Node *n;
4551 n = m_clients.find(peer_id);
4552 // A client should exist for all peers
4554 return n->getValue();
4557 std::wstring Server::getStatusString()
4559 std::wostringstream os(std::ios_base::binary);
4562 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4564 os<<L", uptime="<<m_uptime.get();
4565 // Information about clients
4567 for(core::map<u16, RemoteClient*>::Iterator
4568 i = m_clients.getIterator();
4569 i.atEnd() == false; i++)
4571 // Get client and check that it is valid
4572 RemoteClient *client = i.getNode()->getValue();
4573 assert(client->peer_id == i.getNode()->getKey());
4574 if(client->serialization_version == SER_FMT_VER_INVALID)
4577 Player *player = m_env->getPlayer(client->peer_id);
4578 // Get name of player
4579 std::wstring name = L"unknown";
4581 name = narrow_to_wide(player->getName());
4582 // Add name to information string
4586 if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
4587 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4588 if(g_settings->get("motd") != "")
4589 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4593 // Saves g_settings to configpath given at initialization
4594 void Server::saveConfig()
4596 if(m_configpath != "")
4597 g_settings->updateConfigFile(m_configpath.c_str());
4600 void Server::notifyPlayer(const char *name, const std::wstring msg)
4602 Player *player = m_env->getPlayer(name);
4605 SendChatMessage(player->peer_id, std::wstring(L"Server: -!- ")+msg);
4608 void Server::notifyPlayers(const std::wstring msg)
4610 BroadcastChatMessage(msg);
4613 void Server::queueBlockEmerge(v3s16 blockpos, bool allow_generate)
4617 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
4618 m_emerge_queue.addBlock(PEER_ID_INEXISTENT, blockpos, flags);
4621 // IGameDef interface
4623 IToolDefManager* Server::getToolDefManager()
4627 INodeDefManager* Server::getNodeDefManager()
4631 ICraftDefManager* Server::getCraftDefManager()
4635 ITextureSource* Server::getTextureSource()
4639 u16 Server::allocateUnknownNodeId(const std::string &name)
4641 return m_nodedef->allocateDummy(name);
4644 IWritableToolDefManager* Server::getWritableToolDefManager()
4648 IWritableNodeDefManager* Server::getWritableNodeDefManager()
4652 IWritableCraftDefManager* Server::getWritableCraftDefManager()
4657 v3f findSpawnPos(ServerMap &map)
4659 //return v3f(50,50,50)*BS;
4664 nodepos = v2s16(0,0);
4669 // Try to find a good place a few times
4670 for(s32 i=0; i<1000; i++)
4673 // We're going to try to throw the player to this position
4674 v2s16 nodepos2d = v2s16(-range + (myrand()%(range*2)),
4675 -range + (myrand()%(range*2)));
4676 //v2s16 sectorpos = getNodeSectorPos(nodepos2d);
4677 // Get ground height at point (fallbacks to heightmap function)
4678 s16 groundheight = map.findGroundLevel(nodepos2d);
4679 // Don't go underwater
4680 if(groundheight < WATER_LEVEL)
4682 //infostream<<"-> Underwater"<<std::endl;
4685 // Don't go to high places
4686 if(groundheight > WATER_LEVEL + 4)
4688 //infostream<<"-> Underwater"<<std::endl;
4692 nodepos = v3s16(nodepos2d.X, groundheight-2, nodepos2d.Y);
4693 bool is_good = false;
4695 for(s32 i=0; i<10; i++){
4696 v3s16 blockpos = getNodeBlockPos(nodepos);
4697 map.emergeBlock(blockpos, true);
4698 MapNode n = map.getNodeNoEx(nodepos);
4699 if(n.getContent() == CONTENT_AIR){
4710 // Found a good place
4711 //infostream<<"Searched through "<<i<<" places."<<std::endl;
4717 return intToFloat(nodepos, BS);
4720 Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id)
4723 Try to get an existing player
4725 Player *player = m_env->getPlayer(name);
4728 // If player is already connected, cancel
4729 if(player->peer_id != 0)
4731 infostream<<"emergePlayer(): Player already connected"<<std::endl;
4736 player->peer_id = peer_id;
4738 // Reset inventory to creative if in creative mode
4739 if(g_settings->getBool("creative_mode"))
4741 // Warning: double code below
4742 // Backup actual inventory
4743 player->inventory_backup = new Inventory();
4744 *(player->inventory_backup) = player->inventory;
4745 // Set creative inventory
4746 craft_set_creative_inventory(player, this);
4753 If player with the wanted peer_id already exists, cancel.
4755 if(m_env->getPlayer(peer_id) != NULL)
4757 infostream<<"emergePlayer(): Player with wrong name but same"
4758 " peer_id already exists"<<std::endl;
4766 // Add authentication stuff
4767 m_authmanager.add(name);
4768 m_authmanager.setPassword(name, password);
4769 m_authmanager.setPrivs(name,
4770 stringToPrivs(g_settings->get("default_privs")));
4772 /* Set player position */
4774 infostream<<"Server: Finding spawn place for player \""
4775 <<name<<"\""<<std::endl;
4777 v3f pos = findSpawnPos(m_env->getServerMap());
4779 player = new ServerRemotePlayer(m_env, pos, peer_id, name);
4781 /* Add player to environment */
4782 m_env->addPlayer(player);
4785 ServerRemotePlayer *srp = (ServerRemotePlayer*)player;
4786 scriptapi_on_newplayer(m_lua, srp);
4788 /* Add stuff to inventory */
4789 if(g_settings->getBool("creative_mode"))
4791 // Warning: double code above
4792 // Backup actual inventory
4793 player->inventory_backup = new Inventory();
4794 *(player->inventory_backup) = player->inventory;
4795 // Set creative inventory
4796 craft_set_creative_inventory(player, this);
4801 } // create new player
4804 void Server::handlePeerChange(PeerChange &c)
4806 JMutexAutoLock envlock(m_env_mutex);
4807 JMutexAutoLock conlock(m_con_mutex);
4809 if(c.type == PEER_ADDED)
4816 core::map<u16, RemoteClient*>::Node *n;
4817 n = m_clients.find(c.peer_id);
4818 // The client shouldn't already exist
4822 RemoteClient *client = new RemoteClient();
4823 client->peer_id = c.peer_id;
4824 m_clients.insert(client->peer_id, client);
4827 else if(c.type == PEER_REMOVED)
4834 core::map<u16, RemoteClient*>::Node *n;
4835 n = m_clients.find(c.peer_id);
4836 // The client should exist
4840 Mark objects to be not known by the client
4842 RemoteClient *client = n->getValue();
4844 for(core::map<u16, bool>::Iterator
4845 i = client->m_known_objects.getIterator();
4846 i.atEnd()==false; i++)
4849 u16 id = i.getNode()->getKey();
4850 ServerActiveObject* obj = m_env->getActiveObject(id);
4852 if(obj && obj->m_known_by_count > 0)
4853 obj->m_known_by_count--;
4856 // Collect information about leaving in chat
4857 std::wstring message;
4859 Player *player = m_env->getPlayer(c.peer_id);
4862 std::wstring name = narrow_to_wide(player->getName());
4865 message += L" left game";
4867 message += L" (timed out)";
4873 m_env->removePlayer(c.peer_id);
4876 // Set player client disconnected
4878 Player *player = m_env->getPlayer(c.peer_id);
4880 player->peer_id = 0;
4887 std::ostringstream os(std::ios_base::binary);
4888 for(core::map<u16, RemoteClient*>::Iterator
4889 i = m_clients.getIterator();
4890 i.atEnd() == false; i++)
4892 RemoteClient *client = i.getNode()->getValue();
4893 assert(client->peer_id == i.getNode()->getKey());
4894 if(client->serialization_version == SER_FMT_VER_INVALID)
4897 Player *player = m_env->getPlayer(client->peer_id);
4900 // Get name of player
4901 os<<player->getName()<<" ";
4904 actionstream<<player->getName()<<" "
4905 <<(c.timeout?"times out.":"leaves game.")
4906 <<" List of players: "
4907 <<os.str()<<std::endl;
4912 delete m_clients[c.peer_id];
4913 m_clients.remove(c.peer_id);
4915 // Send player info to all remaining clients
4918 // Send leave chat message to all remaining clients
4919 BroadcastChatMessage(message);
4928 void Server::handlePeerChanges()
4930 while(m_peer_change_queue.size() > 0)
4932 PeerChange c = m_peer_change_queue.pop_front();
4934 infostream<<"Server: Handling peer change: "
4935 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4938 handlePeerChange(c);
4942 u64 Server::getPlayerPrivs(Player *player)
4946 std::string playername = player->getName();
4947 // Local player gets all privileges regardless of
4948 // what's set on their account.
4949 if(g_settings->get("name") == playername)
4955 return getPlayerAuthPrivs(playername);
4959 void dedicated_server_loop(Server &server, bool &kill)
4961 DSTACK(__FUNCTION_NAME);
4963 infostream<<DTIME<<std::endl;
4964 infostream<<"========================"<<std::endl;
4965 infostream<<"Running dedicated server"<<std::endl;
4966 infostream<<"========================"<<std::endl;
4967 infostream<<std::endl;
4969 IntervalLimiter m_profiler_interval;
4973 // This is kind of a hack but can be done like this
4974 // because server.step() is very light
4976 ScopeProfiler sp(g_profiler, "dedicated server sleep");
4981 if(server.getShutdownRequested() || kill)
4983 infostream<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4990 float profiler_print_interval =
4991 g_settings->getFloat("profiler_print_interval");
4992 if(profiler_print_interval != 0)
4994 if(m_profiler_interval.step(0.030, profiler_print_interval))
4996 infostream<<"Profiler:"<<std::endl;
4997 g_profiler->print(infostream);
4998 g_profiler->clear();
5005 static int counter = 0;
5011 core::list<PlayerInfo> list = server.getPlayerInfo();
5012 core::list<PlayerInfo>::Iterator i;
5013 static u32 sum_old = 0;
5014 u32 sum = PIChecksum(list);
5017 infostream<<DTIME<<"Player info:"<<std::endl;
5018 for(i=list.begin(); i!=list.end(); i++)
5020 i->PrintLine(&infostream);