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)
331 void RemoteClient::GetNextBlocks(Server *server, float dtime,
332 core::array<PrioritySortedBlockTransfer> &dest)
334 DSTACK(__FUNCTION_NAME);
337 TimeTaker timer("RemoteClient::GetNextBlocks", &timer_result);*/
340 m_nothing_to_send_pause_timer -= dtime;
341 m_nearest_unsent_reset_timer += dtime;
343 if(m_nothing_to_send_pause_timer >= 0)
348 // Won't send anything if already sending
349 if(m_blocks_sending.size() >= g_settings->getU16
350 ("max_simultaneous_block_sends_per_client"))
352 //infostream<<"Not sending any blocks, Queue full."<<std::endl;
356 //TimeTaker timer("RemoteClient::GetNextBlocks");
358 Player *player = server->m_env->getPlayer(peer_id);
360 assert(player != NULL);
362 v3f playerpos = player->getPosition();
363 v3f playerspeed = player->getSpeed();
364 v3f playerspeeddir(0,0,0);
365 if(playerspeed.getLength() > 1.0*BS)
366 playerspeeddir = playerspeed / playerspeed.getLength();
367 // Predict to next block
368 v3f playerpos_predicted = playerpos + playerspeeddir*MAP_BLOCKSIZE*BS;
370 v3s16 center_nodepos = floatToInt(playerpos_predicted, BS);
372 v3s16 center = getNodeBlockPos(center_nodepos);
374 // Camera position and direction
375 v3f camera_pos = player->getEyePosition();
376 v3f camera_dir = v3f(0,0,1);
377 camera_dir.rotateYZBy(player->getPitch());
378 camera_dir.rotateXZBy(player->getYaw());
380 /*infostream<<"camera_dir=("<<camera_dir.X<<","<<camera_dir.Y<<","
381 <<camera_dir.Z<<")"<<std::endl;*/
384 Get the starting value of the block finder radius.
387 if(m_last_center != center)
389 m_nearest_unsent_d = 0;
390 m_last_center = center;
393 /*infostream<<"m_nearest_unsent_reset_timer="
394 <<m_nearest_unsent_reset_timer<<std::endl;*/
396 // Reset periodically to workaround for some bugs or stuff
397 if(m_nearest_unsent_reset_timer > 20.0)
399 m_nearest_unsent_reset_timer = 0;
400 m_nearest_unsent_d = 0;
401 //infostream<<"Resetting m_nearest_unsent_d for "
402 // <<server->getPlayerName(peer_id)<<std::endl;
405 //s16 last_nearest_unsent_d = m_nearest_unsent_d;
406 s16 d_start = m_nearest_unsent_d;
408 //infostream<<"d_start="<<d_start<<std::endl;
410 u16 max_simul_sends_setting = g_settings->getU16
411 ("max_simultaneous_block_sends_per_client");
412 u16 max_simul_sends_usually = max_simul_sends_setting;
415 Check the time from last addNode/removeNode.
417 Decrease send rate if player is building stuff.
419 m_time_from_building += dtime;
420 if(m_time_from_building < g_settings->getFloat(
421 "full_block_send_enable_min_time_from_building"))
423 max_simul_sends_usually
424 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
428 Number of blocks sending + number of blocks selected for sending
430 u32 num_blocks_selected = m_blocks_sending.size();
433 next time d will be continued from the d from which the nearest
434 unsent block was found this time.
436 This is because not necessarily any of the blocks found this
437 time are actually sent.
439 s32 new_nearest_unsent_d = -1;
441 s16 d_max = g_settings->getS16("max_block_send_distance");
442 s16 d_max_gen = g_settings->getS16("max_block_generate_distance");
444 // Don't loop very much at a time
445 s16 max_d_increment_at_time = 2;
446 if(d_max > d_start + max_d_increment_at_time)
447 d_max = d_start + max_d_increment_at_time;
448 /*if(d_max_gen > d_start+2)
449 d_max_gen = d_start+2;*/
451 //infostream<<"Starting from "<<d_start<<std::endl;
453 s32 nearest_emerged_d = -1;
454 s32 nearest_emergefull_d = -1;
455 s32 nearest_sent_d = -1;
456 bool queue_is_full = false;
459 for(d = d_start; d <= d_max; d++)
461 /*errorstream<<"checking d="<<d<<" for "
462 <<server->getPlayerName(peer_id)<<std::endl;*/
463 //infostream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
466 If m_nearest_unsent_d was changed by the EmergeThread
467 (it can change it to 0 through SetBlockNotSent),
469 Else update m_nearest_unsent_d
471 /*if(m_nearest_unsent_d != last_nearest_unsent_d)
473 d = m_nearest_unsent_d;
474 last_nearest_unsent_d = m_nearest_unsent_d;
478 Get the border/face dot coordinates of a "d-radiused"
481 core::list<v3s16> list;
482 getFacePositions(list, d);
484 core::list<v3s16>::Iterator li;
485 for(li=list.begin(); li!=list.end(); li++)
487 v3s16 p = *li + center;
491 - Don't allow too many simultaneous transfers
492 - EXCEPT when the blocks are very close
494 Also, don't send blocks that are already flying.
497 // Start with the usual maximum
498 u16 max_simul_dynamic = max_simul_sends_usually;
500 // If block is very close, allow full maximum
501 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
502 max_simul_dynamic = max_simul_sends_setting;
504 // Don't select too many blocks for sending
505 if(num_blocks_selected >= max_simul_dynamic)
507 queue_is_full = true;
508 goto queue_full_break;
511 // Don't send blocks that are currently being transferred
512 if(m_blocks_sending.find(p) != NULL)
518 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
519 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
520 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
521 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
522 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
523 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
526 // If this is true, inexistent block will be made from scratch
527 bool generate = d <= d_max_gen;
530 /*// Limit the generating area vertically to 2/3
531 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
534 // Limit the send area vertically to 1/2
535 if(abs(p.Y - center.Y) > d_max / 2)
541 If block is far away, don't generate it unless it is
547 // Block center y in nodes
548 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
549 // Don't generate if it's very high or very low
550 if(y < -64 || y > 64)
554 v2s16 p2d_nodes_center(
558 // Get ground height in nodes
559 s16 gh = server->m_env->getServerMap().findGroundLevel(
562 // If differs a lot, don't generate
563 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
565 // Actually, don't even send it
571 //infostream<<"d="<<d<<std::endl;
574 Don't generate or send if not in sight
575 FIXME This only works if the client uses a small enough
576 FOV setting. The default of 72 degrees is fine.
579 float camera_fov = (72.0*PI/180) * 4./3.;
580 if(isBlockInSight(p, camera_pos, camera_dir, camera_fov, 10000*BS) == false)
586 Don't send already sent blocks
589 if(m_blocks_sent.find(p) != NULL)
596 Check if map has this block
598 MapBlock *block = server->m_env->getMap().getBlockNoCreateNoEx(p);
600 bool surely_not_found_on_disk = false;
601 bool block_is_invalid = false;
604 // Reset usage timer, this block will be of use in the future.
605 block->resetUsageTimer();
607 // Block is dummy if data doesn't exist.
608 // It means it has been not found from disk and not generated
611 surely_not_found_on_disk = true;
614 // Block is valid if lighting is up-to-date and data exists
615 if(block->isValid() == false)
617 block_is_invalid = true;
620 /*if(block->isFullyGenerated() == false)
622 block_is_invalid = true;
627 ServerMap *map = (ServerMap*)(&server->m_env->getMap());
628 v2s16 chunkpos = map->sector_to_chunk(p2d);
629 if(map->chunkNonVolatile(chunkpos) == false)
630 block_is_invalid = true;
632 if(block->isGenerated() == false)
633 block_is_invalid = true;
636 If block is not close, don't send it unless it is near
639 Block is near ground level if night-time mesh
640 differs from day-time mesh.
644 if(block->dayNightDiffed() == false)
651 If block has been marked to not exist on disk (dummy)
652 and generating new ones is not wanted, skip block.
654 if(generate == false && surely_not_found_on_disk == true)
661 Add inexistent block to emerge queue.
663 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
665 //TODO: Get value from somewhere
666 // Allow only one block in emerge queue
667 //if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
668 // Allow two blocks in queue per client
669 //if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
671 // Make it more responsive when needing to generate stuff
672 if(surely_not_found_on_disk)
674 if(server->m_emerge_queue.peerItemCount(peer_id) < max_emerge)
676 //infostream<<"Adding block to emerge queue"<<std::endl;
678 // Add it to the emerge queue and trigger the thread
681 if(generate == false)
682 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
684 server->m_emerge_queue.addBlock(peer_id, p, flags);
685 server->m_emergethread.trigger();
687 if(nearest_emerged_d == -1)
688 nearest_emerged_d = d;
690 if(nearest_emergefull_d == -1)
691 nearest_emergefull_d = d;
698 if(nearest_sent_d == -1)
702 Add block to send queue
705 /*errorstream<<"sending from d="<<d<<" to "
706 <<server->getPlayerName(peer_id)<<std::endl;*/
708 PrioritySortedBlockTransfer q((float)d, p, peer_id);
712 num_blocks_selected += 1;
717 //infostream<<"Stopped at "<<d<<std::endl;
719 // If nothing was found for sending and nothing was queued for
720 // emerging, continue next time browsing from here
721 if(nearest_emerged_d != -1){
722 new_nearest_unsent_d = nearest_emerged_d;
723 } else if(nearest_emergefull_d != -1){
724 new_nearest_unsent_d = nearest_emergefull_d;
726 if(d > g_settings->getS16("max_block_send_distance")){
727 new_nearest_unsent_d = 0;
728 m_nothing_to_send_pause_timer = 2.0;
729 /*infostream<<"GetNextBlocks(): d wrapped around for "
730 <<server->getPlayerName(peer_id)
731 <<"; setting to 0 and pausing"<<std::endl;*/
733 if(nearest_sent_d != -1)
734 new_nearest_unsent_d = nearest_sent_d;
736 new_nearest_unsent_d = d;
740 if(new_nearest_unsent_d != -1)
741 m_nearest_unsent_d = new_nearest_unsent_d;
743 /*timer_result = timer.stop(true);
744 if(timer_result != 0)
745 infostream<<"GetNextBlocks duration: "<<timer_result<<" (!=0)"<<std::endl;*/
748 void RemoteClient::SendObjectData(
751 core::map<v3s16, bool> &stepped_blocks
754 DSTACK(__FUNCTION_NAME);
756 // Can't send anything without knowing version
757 if(serialization_version == SER_FMT_VER_INVALID)
759 infostream<<"RemoteClient::SendObjectData(): Not sending, no version."
765 Send a TOCLIENT_OBJECTDATA packet.
769 u16 number of player positions
781 std::ostringstream os(std::ios_base::binary);
785 writeU16(buf, TOCLIENT_OBJECTDATA);
786 os.write((char*)buf, 2);
789 Get and write player data
792 // Get connected players
793 core::list<Player*> players = server->m_env->getPlayers(true);
795 // Write player count
796 u16 playercount = players.size();
797 writeU16(buf, playercount);
798 os.write((char*)buf, 2);
800 core::list<Player*>::Iterator i;
801 for(i = players.begin();
802 i != players.end(); i++)
806 v3f pf = player->getPosition();
807 v3f sf = player->getSpeed();
809 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
810 v3s32 speed_i (sf.X*100, sf.Y*100, sf.Z*100);
811 s32 pitch_i (player->getPitch() * 100);
812 s32 yaw_i (player->getYaw() * 100);
814 writeU16(buf, player->peer_id);
815 os.write((char*)buf, 2);
816 writeV3S32(buf, position_i);
817 os.write((char*)buf, 12);
818 writeV3S32(buf, speed_i);
819 os.write((char*)buf, 12);
820 writeS32(buf, pitch_i);
821 os.write((char*)buf, 4);
822 writeS32(buf, yaw_i);
823 os.write((char*)buf, 4);
827 Get and write object data (dummy, for compatibility)
832 os.write((char*)buf, 2);
838 //infostream<<"Server: Sending object data to "<<peer_id<<std::endl;
841 std::string s = os.str();
842 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
843 // Send as unreliable
844 server->m_con.Send(peer_id, 0, data, false);
847 void RemoteClient::GotBlock(v3s16 p)
849 if(m_blocks_sending.find(p) != NULL)
850 m_blocks_sending.remove(p);
853 /*infostream<<"RemoteClient::GotBlock(): Didn't find in"
854 " m_blocks_sending"<<std::endl;*/
855 m_excess_gotblocks++;
857 m_blocks_sent.insert(p, true);
860 void RemoteClient::SentBlock(v3s16 p)
862 if(m_blocks_sending.find(p) == NULL)
863 m_blocks_sending.insert(p, 0.0);
865 infostream<<"RemoteClient::SentBlock(): Sent block"
866 " already in m_blocks_sending"<<std::endl;
869 void RemoteClient::SetBlockNotSent(v3s16 p)
871 m_nearest_unsent_d = 0;
873 if(m_blocks_sending.find(p) != NULL)
874 m_blocks_sending.remove(p);
875 if(m_blocks_sent.find(p) != NULL)
876 m_blocks_sent.remove(p);
879 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
881 m_nearest_unsent_d = 0;
883 for(core::map<v3s16, MapBlock*>::Iterator
884 i = blocks.getIterator();
885 i.atEnd()==false; i++)
887 v3s16 p = i.getNode()->getKey();
889 if(m_blocks_sending.find(p) != NULL)
890 m_blocks_sending.remove(p);
891 if(m_blocks_sent.find(p) != NULL)
892 m_blocks_sent.remove(p);
900 PlayerInfo::PlayerInfo()
906 void PlayerInfo::PrintLine(std::ostream *s)
909 (*s)<<"\""<<name<<"\" ("
910 <<(position.X/10)<<","<<(position.Y/10)
911 <<","<<(position.Z/10)<<") ";
913 (*s)<<" avg_rtt="<<avg_rtt;
917 u32 PIChecksum(core::list<PlayerInfo> &l)
919 core::list<PlayerInfo>::Iterator i;
922 for(i=l.begin(); i!=l.end(); i++)
924 checksum += a * (i->id+1);
925 checksum ^= 0x435aafcd;
939 std::set<std::string> depends;
940 std::set<std::string> unsatisfied_depends;
942 ModSpec(const std::string &name_="", const std::string path_="",
943 const std::set<std::string> &depends_=std::set<std::string>()):
947 unsatisfied_depends(depends_)
951 // Get a dependency-sorted list of ModSpecs
952 static core::list<ModSpec> getMods(core::list<std::string> &modspaths)
954 std::queue<ModSpec> mods_satisfied;
955 core::list<ModSpec> mods_unsorted;
956 core::list<ModSpec> mods_sorted;
957 for(core::list<std::string>::Iterator i = modspaths.begin();
958 i != modspaths.end(); i++){
959 std::string modspath = *i;
960 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(modspath);
961 for(u32 j=0; j<dirlist.size(); j++){
964 std::string modname = dirlist[j].name;
965 std::string modpath = modspath + DIR_DELIM + modname;
966 std::set<std::string> depends;
967 std::ifstream is((modpath+DIR_DELIM+"depends.txt").c_str(),
968 std::ios_base::binary);
971 std::getline(is, dep);
976 ModSpec spec(modname, modpath, depends);
977 mods_unsorted.push_back(spec);
979 mods_satisfied.push(spec);
982 // Sort by depencencies
983 while(!mods_satisfied.empty()){
984 ModSpec mod = mods_satisfied.front();
985 mods_satisfied.pop();
986 mods_sorted.push_back(mod);
987 for(core::list<ModSpec>::Iterator i = mods_unsorted.begin();
988 i != mods_unsorted.end(); i++){
990 if(mod2.unsatisfied_depends.empty())
992 mod2.unsatisfied_depends.erase(mod.name);
993 if(!mod2.unsatisfied_depends.empty())
995 mods_satisfied.push(mod2);
998 // Check unsatisfied dependencies
999 for(core::list<ModSpec>::Iterator i = mods_unsorted.begin();
1000 i != mods_unsorted.end(); i++){
1002 if(mod.unsatisfied_depends.empty())
1004 errorstream<<"mod \""<<mod.name
1005 <<"\" has unsatisfied dependencies:";
1006 for(std::set<std::string>::iterator
1007 i = mod.unsatisfied_depends.begin();
1008 i != mod.unsatisfied_depends.end(); i++){
1009 errorstream<<" \""<<(*i)<<"\"";
1011 errorstream<<". Loading nevertheless."<<std::endl;
1012 mods_sorted.push_back(mod);
1022 std::string mapsavedir,
1023 std::string configpath
1026 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
1027 m_authmanager(mapsavedir+DIR_DELIM+"auth.txt"),
1028 m_banmanager(mapsavedir+DIR_DELIM+"ipban.txt"),
1030 m_toolmgr(createToolDefManager()),
1031 m_nodedef(createNodeDefManager()),
1032 m_craftdef(createCraftDefManager()),
1034 m_emergethread(this),
1036 m_time_of_day_send_timer(0),
1038 m_mapsavedir(mapsavedir),
1039 m_configpath(configpath),
1040 m_shutdown_requested(false),
1041 m_ignore_map_edit_events(false),
1042 m_ignore_map_edit_events_peer_id(0)
1044 m_liquid_transform_timer = 0.0;
1045 m_print_info_timer = 0.0;
1046 m_objectdata_timer = 0.0;
1047 m_emergethread_trigger_timer = 0.0;
1048 m_savemap_timer = 0.0;
1052 m_step_dtime_mutex.Init();
1055 JMutexAutoLock envlock(m_env_mutex);
1056 JMutexAutoLock conlock(m_con_mutex);
1058 infostream<<"m_nodedef="<<m_nodedef<<std::endl;
1060 // Path to builtin.lua
1061 std::string builtinpath = porting::path_data + DIR_DELIM + "builtin.lua";
1062 // Add default global mod path
1063 m_modspaths.push_back(porting::path_data + DIR_DELIM + "mods");
1065 // Initialize scripting
1067 infostream<<"Server: Initializing scripting"<<std::endl;
1068 m_lua = script_init();
1071 scriptapi_export(m_lua, this);
1072 // Load and run builtin.lua
1073 infostream<<"Server: Loading builtin Lua stuff from \""<<builtinpath
1075 bool success = script_load(m_lua, builtinpath.c_str());
1077 errorstream<<"Server: Failed to load and run "
1078 <<builtinpath<<std::endl;
1081 // Load and run "mod" scripts
1082 core::list<ModSpec> mods = getMods(m_modspaths);
1083 for(core::list<ModSpec>::Iterator i = mods.begin();
1084 i != mods.end(); i++){
1086 infostream<<"Server: Loading mod \""<<mod.name<<"\""<<std::endl;
1087 std::string scriptpath = mod.path + DIR_DELIM + "init.lua";
1088 bool success = script_load(m_lua, scriptpath.c_str());
1090 errorstream<<"Server: Failed to load and run "
1091 <<scriptpath<<std::endl;
1096 // Initialize Environment
1098 m_env = new ServerEnvironment(new ServerMap(mapsavedir, this), m_lua,
1101 // Give environment reference to scripting api
1102 scriptapi_add_environment(m_lua, m_env);
1104 // Register us to receive map edit events
1105 m_env->getMap().addEventReceiver(this);
1107 // If file exists, load environment metadata
1108 if(fs::PathExists(m_mapsavedir+DIR_DELIM+"env_meta.txt"))
1110 infostream<<"Server: Loading environment metadata"<<std::endl;
1111 m_env->loadMeta(m_mapsavedir);
1115 infostream<<"Server: Loading players"<<std::endl;
1116 m_env->deSerializePlayers(m_mapsavedir);
1121 infostream<<"Server::~Server()"<<std::endl;
1124 Send shutdown message
1127 JMutexAutoLock conlock(m_con_mutex);
1129 std::wstring line = L"*** Server shutting down";
1132 Send the message to clients
1134 for(core::map<u16, RemoteClient*>::Iterator
1135 i = m_clients.getIterator();
1136 i.atEnd() == false; i++)
1138 // Get client and check that it is valid
1139 RemoteClient *client = i.getNode()->getValue();
1140 assert(client->peer_id == i.getNode()->getKey());
1141 if(client->serialization_version == SER_FMT_VER_INVALID)
1145 SendChatMessage(client->peer_id, line);
1147 catch(con::PeerNotFoundException &e)
1153 JMutexAutoLock envlock(m_env_mutex);
1158 infostream<<"Server: Saving players"<<std::endl;
1159 m_env->serializePlayers(m_mapsavedir);
1162 Save environment metadata
1164 infostream<<"Server: Saving environment metadata"<<std::endl;
1165 m_env->saveMeta(m_mapsavedir);
1177 JMutexAutoLock clientslock(m_con_mutex);
1179 for(core::map<u16, RemoteClient*>::Iterator
1180 i = m_clients.getIterator();
1181 i.atEnd() == false; i++)
1184 // NOTE: These are removed by env destructor
1186 u16 peer_id = i.getNode()->getKey();
1187 JMutexAutoLock envlock(m_env_mutex);
1188 m_env->removePlayer(peer_id);
1192 delete i.getNode()->getValue();
1196 // Delete Environment
1202 // Deinitialize scripting
1203 infostream<<"Server: Deinitializing scripting"<<std::endl;
1204 script_deinit(m_lua);
1207 void Server::start(unsigned short port)
1209 DSTACK(__FUNCTION_NAME);
1210 // Stop thread if already running
1213 // Initialize connection
1214 m_con.SetTimeoutMs(30);
1218 m_thread.setRun(true);
1221 infostream<<"Server: Started on port "<<port<<std::endl;
1226 DSTACK(__FUNCTION_NAME);
1228 infostream<<"Server: Stopping and waiting threads"<<std::endl;
1230 // Stop threads (set run=false first so both start stopping)
1231 m_thread.setRun(false);
1232 m_emergethread.setRun(false);
1234 m_emergethread.stop();
1236 infostream<<"Server: Threads stopped"<<std::endl;
1239 void Server::step(float dtime)
1241 DSTACK(__FUNCTION_NAME);
1246 JMutexAutoLock lock(m_step_dtime_mutex);
1247 m_step_dtime += dtime;
1251 void Server::AsyncRunStep()
1253 DSTACK(__FUNCTION_NAME);
1255 g_profiler->add("Server::AsyncRunStep (num)", 1);
1259 JMutexAutoLock lock1(m_step_dtime_mutex);
1260 dtime = m_step_dtime;
1264 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
1265 // Send blocks to clients
1272 g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
1274 //infostream<<"Server steps "<<dtime<<std::endl;
1275 //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1278 JMutexAutoLock lock1(m_step_dtime_mutex);
1279 m_step_dtime -= dtime;
1286 m_uptime.set(m_uptime.get() + dtime);
1290 // Process connection's timeouts
1291 JMutexAutoLock lock2(m_con_mutex);
1292 ScopeProfiler sp(g_profiler, "Server: connection timeout processing");
1293 m_con.RunTimeouts(dtime);
1297 // This has to be called so that the client list gets synced
1298 // with the peer list of the connection
1299 handlePeerChanges();
1303 Update m_time_of_day and overall game time
1306 JMutexAutoLock envlock(m_env_mutex);
1308 m_time_counter += dtime;
1309 f32 speed = g_settings->getFloat("time_speed") * 24000./(24.*3600);
1310 u32 units = (u32)(m_time_counter*speed);
1311 m_time_counter -= (f32)units / speed;
1313 m_env->setTimeOfDay((m_env->getTimeOfDay() + units) % 24000);
1315 //infostream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1318 Send to clients at constant intervals
1321 m_time_of_day_send_timer -= dtime;
1322 if(m_time_of_day_send_timer < 0.0)
1324 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
1326 //JMutexAutoLock envlock(m_env_mutex);
1327 JMutexAutoLock conlock(m_con_mutex);
1329 for(core::map<u16, RemoteClient*>::Iterator
1330 i = m_clients.getIterator();
1331 i.atEnd() == false; i++)
1333 RemoteClient *client = i.getNode()->getValue();
1334 //Player *player = m_env->getPlayer(client->peer_id);
1336 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1337 m_env->getTimeOfDay());
1339 m_con.Send(client->peer_id, 0, data, true);
1345 JMutexAutoLock lock(m_env_mutex);
1347 ScopeProfiler sp(g_profiler, "SEnv step");
1348 ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
1352 const float map_timer_and_unload_dtime = 2.92;
1353 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
1355 JMutexAutoLock lock(m_env_mutex);
1356 // Run Map's timers and unload unused data
1357 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
1358 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
1359 g_settings->getFloat("server_unload_unused_data_timeout"));
1367 Check player movements
1369 NOTE: Actually the server should handle player physics like the
1370 client does and compare player's position to what is calculated
1371 on our side. This is required when eg. players fly due to an
1375 JMutexAutoLock lock(m_env_mutex);
1376 JMutexAutoLock lock2(m_con_mutex);
1378 //float player_max_speed = BS * 4.0; // Normal speed
1379 float player_max_speed = BS * 20; // Fast speed
1380 float player_max_speed_up = BS * 20;
1382 player_max_speed *= 2.5; // Tolerance
1383 player_max_speed_up *= 2.5;
1385 for(core::map<u16, RemoteClient*>::Iterator
1386 i = m_clients.getIterator();
1387 i.atEnd() == false; i++)
1389 RemoteClient *client = i.getNode()->getValue();
1390 ServerRemotePlayer *player =
1391 (ServerRemotePlayer*)m_env->getPlayer(client->peer_id);
1394 player->m_last_good_position_age += dtime;
1395 if(player->m_last_good_position_age >= 2.0){
1396 float age = player->m_last_good_position_age;
1397 v3f diff = (player->getPosition() - player->m_last_good_position);
1398 float d_vert = diff.Y;
1400 float d_horiz = diff.getLength();
1401 /*infostream<<player->getName()<<"'s horizontal speed is "
1402 <<(d_horiz/age)<<std::endl;*/
1403 if(d_horiz <= age * player_max_speed &&
1404 (d_vert < 0 || d_vert < age * player_max_speed_up)){
1405 player->m_last_good_position = player->getPosition();
1407 actionstream<<"Player "<<player->getName()
1408 <<" moved too fast; resetting position"
1410 player->setPosition(player->m_last_good_position);
1411 SendMovePlayer(player);
1413 player->m_last_good_position_age = 0;
1418 /* Transform liquids */
1419 m_liquid_transform_timer += dtime;
1420 if(m_liquid_transform_timer >= 1.00)
1422 m_liquid_transform_timer -= 1.00;
1424 JMutexAutoLock lock(m_env_mutex);
1426 ScopeProfiler sp(g_profiler, "Server: liquid transform");
1428 core::map<v3s16, MapBlock*> modified_blocks;
1429 m_env->getMap().transformLiquids(modified_blocks);
1434 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1435 ServerMap &map = ((ServerMap&)m_env->getMap());
1436 map.updateLighting(modified_blocks, lighting_modified_blocks);
1438 // Add blocks modified by lighting to modified_blocks
1439 for(core::map<v3s16, MapBlock*>::Iterator
1440 i = lighting_modified_blocks.getIterator();
1441 i.atEnd() == false; i++)
1443 MapBlock *block = i.getNode()->getValue();
1444 modified_blocks.insert(block->getPos(), block);
1448 Set the modified blocks unsent for all the clients
1451 JMutexAutoLock lock2(m_con_mutex);
1453 for(core::map<u16, RemoteClient*>::Iterator
1454 i = m_clients.getIterator();
1455 i.atEnd() == false; i++)
1457 RemoteClient *client = i.getNode()->getValue();
1459 if(modified_blocks.size() > 0)
1461 // Remove block from sent history
1462 client->SetBlocksNotSent(modified_blocks);
1467 // Periodically print some info
1469 float &counter = m_print_info_timer;
1475 JMutexAutoLock lock2(m_con_mutex);
1477 if(m_clients.size() != 0)
1478 infostream<<"Players:"<<std::endl;
1479 for(core::map<u16, RemoteClient*>::Iterator
1480 i = m_clients.getIterator();
1481 i.atEnd() == false; i++)
1483 //u16 peer_id = i.getNode()->getKey();
1484 RemoteClient *client = i.getNode()->getValue();
1485 Player *player = m_env->getPlayer(client->peer_id);
1488 infostream<<"* "<<player->getName()<<"\t";
1489 client->PrintInfo(infostream);
1494 //if(g_settings->getBool("enable_experimental"))
1498 Check added and deleted active objects
1501 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
1502 JMutexAutoLock envlock(m_env_mutex);
1503 JMutexAutoLock conlock(m_con_mutex);
1505 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
1507 // Radius inside which objects are active
1508 s16 radius = g_settings->getS16("active_object_send_range_blocks");
1509 radius *= MAP_BLOCKSIZE;
1511 for(core::map<u16, RemoteClient*>::Iterator
1512 i = m_clients.getIterator();
1513 i.atEnd() == false; i++)
1515 RemoteClient *client = i.getNode()->getValue();
1516 Player *player = m_env->getPlayer(client->peer_id);
1519 // This can happen if the client timeouts somehow
1520 /*infostream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
1522 <<" has no associated player"<<std::endl;*/
1525 v3s16 pos = floatToInt(player->getPosition(), BS);
1527 core::map<u16, bool> removed_objects;
1528 core::map<u16, bool> added_objects;
1529 m_env->getRemovedActiveObjects(pos, radius,
1530 client->m_known_objects, removed_objects);
1531 m_env->getAddedActiveObjects(pos, radius,
1532 client->m_known_objects, added_objects);
1534 // Ignore if nothing happened
1535 if(removed_objects.size() == 0 && added_objects.size() == 0)
1537 //infostream<<"active objects: none changed"<<std::endl;
1541 std::string data_buffer;
1545 // Handle removed objects
1546 writeU16((u8*)buf, removed_objects.size());
1547 data_buffer.append(buf, 2);
1548 for(core::map<u16, bool>::Iterator
1549 i = removed_objects.getIterator();
1550 i.atEnd()==false; i++)
1553 u16 id = i.getNode()->getKey();
1554 ServerActiveObject* obj = m_env->getActiveObject(id);
1556 // Add to data buffer for sending
1557 writeU16((u8*)buf, i.getNode()->getKey());
1558 data_buffer.append(buf, 2);
1560 // Remove from known objects
1561 client->m_known_objects.remove(i.getNode()->getKey());
1563 if(obj && obj->m_known_by_count > 0)
1564 obj->m_known_by_count--;
1567 // Handle added objects
1568 writeU16((u8*)buf, added_objects.size());
1569 data_buffer.append(buf, 2);
1570 for(core::map<u16, bool>::Iterator
1571 i = added_objects.getIterator();
1572 i.atEnd()==false; i++)
1575 u16 id = i.getNode()->getKey();
1576 ServerActiveObject* obj = m_env->getActiveObject(id);
1579 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1581 infostream<<"WARNING: "<<__FUNCTION_NAME
1582 <<": NULL object"<<std::endl;
1584 type = obj->getType();
1586 // Add to data buffer for sending
1587 writeU16((u8*)buf, id);
1588 data_buffer.append(buf, 2);
1589 writeU8((u8*)buf, type);
1590 data_buffer.append(buf, 1);
1593 data_buffer.append(serializeLongString(
1594 obj->getClientInitializationData()));
1596 data_buffer.append(serializeLongString(""));
1598 // Add to known objects
1599 client->m_known_objects.insert(i.getNode()->getKey(), false);
1602 obj->m_known_by_count++;
1606 SharedBuffer<u8> reply(2 + data_buffer.size());
1607 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1608 memcpy((char*)&reply[2], data_buffer.c_str(),
1609 data_buffer.size());
1611 m_con.Send(client->peer_id, 0, reply, true);
1613 infostream<<"Server: Sent object remove/add: "
1614 <<removed_objects.size()<<" removed, "
1615 <<added_objects.size()<<" added, "
1616 <<"packet size is "<<reply.getSize()<<std::endl;
1621 Collect a list of all the objects known by the clients
1622 and report it back to the environment.
1625 core::map<u16, bool> all_known_objects;
1627 for(core::map<u16, RemoteClient*>::Iterator
1628 i = m_clients.getIterator();
1629 i.atEnd() == false; i++)
1631 RemoteClient *client = i.getNode()->getValue();
1632 // Go through all known objects of client
1633 for(core::map<u16, bool>::Iterator
1634 i = client->m_known_objects.getIterator();
1635 i.atEnd()==false; i++)
1637 u16 id = i.getNode()->getKey();
1638 all_known_objects[id] = true;
1642 m_env->setKnownActiveObjects(whatever);
1648 Send object messages
1651 JMutexAutoLock envlock(m_env_mutex);
1652 JMutexAutoLock conlock(m_con_mutex);
1654 //ScopeProfiler sp(g_profiler, "Server: sending object messages");
1657 // Value = data sent by object
1658 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1660 // Get active object messages from environment
1663 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
1667 core::list<ActiveObjectMessage>* message_list = NULL;
1668 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1669 n = buffered_messages.find(aom.id);
1672 message_list = new core::list<ActiveObjectMessage>;
1673 buffered_messages.insert(aom.id, message_list);
1677 message_list = n->getValue();
1679 message_list->push_back(aom);
1682 // Route data to every client
1683 for(core::map<u16, RemoteClient*>::Iterator
1684 i = m_clients.getIterator();
1685 i.atEnd()==false; i++)
1687 RemoteClient *client = i.getNode()->getValue();
1688 std::string reliable_data;
1689 std::string unreliable_data;
1690 // Go through all objects in message buffer
1691 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1692 j = buffered_messages.getIterator();
1693 j.atEnd()==false; j++)
1695 // If object is not known by client, skip it
1696 u16 id = j.getNode()->getKey();
1697 if(client->m_known_objects.find(id) == NULL)
1699 // Get message list of object
1700 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1701 // Go through every message
1702 for(core::list<ActiveObjectMessage>::Iterator
1703 k = list->begin(); k != list->end(); k++)
1705 // Compose the full new data with header
1706 ActiveObjectMessage aom = *k;
1707 std::string new_data;
1710 writeU16((u8*)&buf[0], aom.id);
1711 new_data.append(buf, 2);
1713 new_data += serializeString(aom.datastring);
1714 // Add data to buffer
1716 reliable_data += new_data;
1718 unreliable_data += new_data;
1722 reliable_data and unreliable_data are now ready.
1725 if(reliable_data.size() > 0)
1727 SharedBuffer<u8> reply(2 + reliable_data.size());
1728 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1729 memcpy((char*)&reply[2], reliable_data.c_str(),
1730 reliable_data.size());
1732 m_con.Send(client->peer_id, 0, reply, true);
1734 if(unreliable_data.size() > 0)
1736 SharedBuffer<u8> reply(2 + unreliable_data.size());
1737 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1738 memcpy((char*)&reply[2], unreliable_data.c_str(),
1739 unreliable_data.size());
1740 // Send as unreliable
1741 m_con.Send(client->peer_id, 0, reply, false);
1744 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1746 infostream<<"Server: Size of object message data: "
1747 <<"reliable: "<<reliable_data.size()
1748 <<", unreliable: "<<unreliable_data.size()
1753 // Clear buffered_messages
1754 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1755 i = buffered_messages.getIterator();
1756 i.atEnd()==false; i++)
1758 delete i.getNode()->getValue();
1762 } // enable_experimental
1765 Send queued-for-sending map edit events.
1768 // Don't send too many at a time
1771 // Single change sending is disabled if queue size is not small
1772 bool disable_single_change_sending = false;
1773 if(m_unsent_map_edit_queue.size() >= 4)
1774 disable_single_change_sending = true;
1776 bool got_any_events = false;
1778 // We'll log the amount of each
1781 while(m_unsent_map_edit_queue.size() != 0)
1783 got_any_events = true;
1785 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1787 // Players far away from the change are stored here.
1788 // Instead of sending the changes, MapBlocks are set not sent
1790 core::list<u16> far_players;
1792 if(event->type == MEET_ADDNODE)
1794 //infostream<<"Server: MEET_ADDNODE"<<std::endl;
1795 prof.add("MEET_ADDNODE", 1);
1796 if(disable_single_change_sending)
1797 sendAddNode(event->p, event->n, event->already_known_by_peer,
1800 sendAddNode(event->p, event->n, event->already_known_by_peer,
1803 else if(event->type == MEET_REMOVENODE)
1805 //infostream<<"Server: MEET_REMOVENODE"<<std::endl;
1806 prof.add("MEET_REMOVENODE", 1);
1807 if(disable_single_change_sending)
1808 sendRemoveNode(event->p, event->already_known_by_peer,
1811 sendRemoveNode(event->p, event->already_known_by_peer,
1814 else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
1816 infostream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
1817 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
1818 setBlockNotSent(event->p);
1820 else if(event->type == MEET_OTHER)
1822 infostream<<"Server: MEET_OTHER"<<std::endl;
1823 prof.add("MEET_OTHER", 1);
1824 for(core::map<v3s16, bool>::Iterator
1825 i = event->modified_blocks.getIterator();
1826 i.atEnd()==false; i++)
1828 v3s16 p = i.getNode()->getKey();
1834 prof.add("unknown", 1);
1835 infostream<<"WARNING: Server: Unknown MapEditEvent "
1836 <<((u32)event->type)<<std::endl;
1840 Set blocks not sent to far players
1842 if(far_players.size() > 0)
1844 // Convert list format to that wanted by SetBlocksNotSent
1845 core::map<v3s16, MapBlock*> modified_blocks2;
1846 for(core::map<v3s16, bool>::Iterator
1847 i = event->modified_blocks.getIterator();
1848 i.atEnd()==false; i++)
1850 v3s16 p = i.getNode()->getKey();
1851 modified_blocks2.insert(p,
1852 m_env->getMap().getBlockNoCreateNoEx(p));
1854 // Set blocks not sent
1855 for(core::list<u16>::Iterator
1856 i = far_players.begin();
1857 i != far_players.end(); i++)
1860 RemoteClient *client = getClient(peer_id);
1863 client->SetBlocksNotSent(modified_blocks2);
1869 /*// Don't send too many at a time
1871 if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
1877 infostream<<"Server: MapEditEvents:"<<std::endl;
1878 prof.print(infostream);
1884 Send object positions
1887 float &counter = m_objectdata_timer;
1889 if(counter >= g_settings->getFloat("objectdata_interval"))
1891 JMutexAutoLock lock1(m_env_mutex);
1892 JMutexAutoLock lock2(m_con_mutex);
1894 //ScopeProfiler sp(g_profiler, "Server: sending player positions");
1896 SendObjectData(counter);
1903 Trigger emergethread (it somehow gets to a non-triggered but
1904 bysy state sometimes)
1907 float &counter = m_emergethread_trigger_timer;
1913 m_emergethread.trigger();
1917 // Save map, players and auth stuff
1919 float &counter = m_savemap_timer;
1921 if(counter >= g_settings->getFloat("server_map_save_interval"))
1925 ScopeProfiler sp(g_profiler, "Server: saving stuff");
1928 if(m_authmanager.isModified())
1929 m_authmanager.save();
1932 if(m_banmanager.isModified())
1933 m_banmanager.save();
1936 JMutexAutoLock lock(m_env_mutex);
1938 /*// Unload unused data (delete from memory)
1939 m_env->getMap().unloadUnusedData(
1940 g_settings->getFloat("server_unload_unused_sectors_timeout"));
1942 /*u32 deleted_count = m_env->getMap().unloadUnusedData(
1943 g_settings->getFloat("server_unload_unused_sectors_timeout"));
1946 // Save only changed parts
1947 m_env->getMap().save(true);
1949 /*if(deleted_count > 0)
1951 infostream<<"Server: Unloaded "<<deleted_count
1952 <<" blocks from memory"<<std::endl;
1956 m_env->serializePlayers(m_mapsavedir);
1958 // Save environment metadata
1959 m_env->saveMeta(m_mapsavedir);
1964 void Server::Receive()
1966 DSTACK(__FUNCTION_NAME);
1967 SharedBuffer<u8> data;
1972 JMutexAutoLock conlock(m_con_mutex);
1973 datasize = m_con.Receive(peer_id, data);
1976 // This has to be called so that the client list gets synced
1977 // with the peer list of the connection
1978 handlePeerChanges();
1980 ProcessData(*data, datasize, peer_id);
1982 catch(con::InvalidIncomingDataException &e)
1984 infostream<<"Server::Receive(): "
1985 "InvalidIncomingDataException: what()="
1986 <<e.what()<<std::endl;
1988 catch(con::PeerNotFoundException &e)
1990 //NOTE: This is not needed anymore
1992 // The peer has been disconnected.
1993 // Find the associated player and remove it.
1995 /*JMutexAutoLock envlock(m_env_mutex);
1997 infostream<<"ServerThread: peer_id="<<peer_id
1998 <<" has apparently closed connection. "
1999 <<"Removing player."<<std::endl;
2001 m_env->removePlayer(peer_id);*/
2005 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
2007 DSTACK(__FUNCTION_NAME);
2008 // Environment is locked first.
2009 JMutexAutoLock envlock(m_env_mutex);
2010 JMutexAutoLock conlock(m_con_mutex);
2013 Address address = m_con.GetPeerAddress(peer_id);
2015 // drop player if is ip is banned
2016 if(m_banmanager.isIpBanned(address.serializeString())){
2017 SendAccessDenied(m_con, peer_id,
2018 L"Your ip is banned. Banned name was "
2019 +narrow_to_wide(m_banmanager.getBanName(
2020 address.serializeString())));
2021 m_con.DeletePeer(peer_id);
2025 catch(con::PeerNotFoundException &e)
2027 infostream<<"Server::ProcessData(): Cancelling: peer "
2028 <<peer_id<<" not found"<<std::endl;
2032 u8 peer_ser_ver = getClient(peer_id)->serialization_version;
2040 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
2042 if(command == TOSERVER_INIT)
2044 // [0] u16 TOSERVER_INIT
2045 // [2] u8 SER_FMT_VER_HIGHEST
2046 // [3] u8[20] player_name
2047 // [23] u8[28] password <--- can be sent without this, from old versions
2049 if(datasize < 2+1+PLAYERNAME_SIZE)
2052 infostream<<"Server: Got TOSERVER_INIT from "
2053 <<peer_id<<std::endl;
2055 // First byte after command is maximum supported
2056 // serialization version
2057 u8 client_max = data[2];
2058 u8 our_max = SER_FMT_VER_HIGHEST;
2059 // Use the highest version supported by both
2060 u8 deployed = core::min_(client_max, our_max);
2061 // If it's lower than the lowest supported, give up.
2062 if(deployed < SER_FMT_VER_LOWEST)
2063 deployed = SER_FMT_VER_INVALID;
2065 //peer->serialization_version = deployed;
2066 getClient(peer_id)->pending_serialization_version = deployed;
2068 if(deployed == SER_FMT_VER_INVALID)
2070 infostream<<"Server: Cannot negotiate "
2071 "serialization version with peer "
2072 <<peer_id<<std::endl;
2073 SendAccessDenied(m_con, peer_id,
2074 L"Your client is too old (map format)");
2079 Read and check network protocol version
2082 u16 net_proto_version = 0;
2083 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
2085 net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
2088 getClient(peer_id)->net_proto_version = net_proto_version;
2090 if(net_proto_version == 0)
2092 SendAccessDenied(m_con, peer_id,
2093 L"Your client is too old. Please upgrade.");
2097 /* Uhh... this should actually be a warning but let's do it like this */
2098 if(g_settings->getBool("strict_protocol_version_checking"))
2100 if(net_proto_version < PROTOCOL_VERSION)
2102 SendAccessDenied(m_con, peer_id,
2103 L"Your client is too old. Please upgrade.");
2113 char playername[PLAYERNAME_SIZE];
2114 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
2116 playername[i] = data[3+i];
2118 playername[PLAYERNAME_SIZE-1] = 0;
2120 if(playername[0]=='\0')
2122 infostream<<"Server: Player has empty name"<<std::endl;
2123 SendAccessDenied(m_con, peer_id,
2128 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
2130 infostream<<"Server: Player has invalid name"<<std::endl;
2131 SendAccessDenied(m_con, peer_id,
2132 L"Name contains unallowed characters");
2137 char password[PASSWORD_SIZE];
2138 if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
2140 // old version - assume blank password
2145 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2147 password[i] = data[23+i];
2149 password[PASSWORD_SIZE-1] = 0;
2152 std::string checkpwd;
2153 if(m_authmanager.exists(playername))
2155 checkpwd = m_authmanager.getPassword(playername);
2159 checkpwd = g_settings->get("default_password");
2162 /*infostream<<"Server: Client gave password '"<<password
2163 <<"', the correct one is '"<<checkpwd<<"'"<<std::endl;*/
2165 if(password != checkpwd && m_authmanager.exists(playername))
2167 infostream<<"Server: peer_id="<<peer_id
2168 <<": supplied invalid password for "
2169 <<playername<<std::endl;
2170 SendAccessDenied(m_con, peer_id, L"Invalid password");
2174 // Add player to auth manager
2175 if(m_authmanager.exists(playername) == false)
2177 infostream<<"Server: adding player "<<playername
2178 <<" to auth manager"<<std::endl;
2179 m_authmanager.add(playername);
2180 m_authmanager.setPassword(playername, checkpwd);
2181 m_authmanager.setPrivs(playername,
2182 stringToPrivs(g_settings->get("default_privs")));
2183 m_authmanager.save();
2186 // Enforce user limit.
2187 // Don't enforce for users that have some admin right
2188 if(m_clients.size() >= g_settings->getU16("max_users") &&
2189 (m_authmanager.getPrivs(playername)
2190 & (PRIV_SERVER|PRIV_BAN|PRIV_PRIVS)) == 0 &&
2191 playername != g_settings->get("name"))
2193 SendAccessDenied(m_con, peer_id, L"Too many users.");
2198 Player *player = emergePlayer(playername, password, peer_id);
2200 // If failed, cancel
2203 infostream<<"Server: peer_id="<<peer_id
2204 <<": failed to emerge player"<<std::endl;
2209 Answer with a TOCLIENT_INIT
2212 SharedBuffer<u8> reply(2+1+6+8);
2213 writeU16(&reply[0], TOCLIENT_INIT);
2214 writeU8(&reply[2], deployed);
2215 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
2216 writeU64(&reply[2+1+6], m_env->getServerMap().getSeed());
2219 m_con.Send(peer_id, 0, reply, true);
2223 Send complete position information
2225 SendMovePlayer(player);
2230 if(command == TOSERVER_INIT2)
2232 infostream<<"Server: Got TOSERVER_INIT2 from "
2233 <<peer_id<<std::endl;
2236 getClient(peer_id)->serialization_version
2237 = getClient(peer_id)->pending_serialization_version;
2240 Send some initialization data
2243 // Send tool definitions
2244 SendToolDef(m_con, peer_id, m_toolmgr);
2246 // Send node definitions
2247 SendNodeDef(m_con, peer_id, m_nodedef);
2250 SendTextures(peer_id);
2252 // Send player info to all players
2255 // Send inventory to player
2256 UpdateCrafting(peer_id);
2257 SendInventory(peer_id);
2259 // Send player items to all players
2262 Player *player = m_env->getPlayer(peer_id);
2265 SendPlayerHP(player);
2269 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2270 m_env->getTimeOfDay());
2271 m_con.Send(peer_id, 0, data, true);
2274 // Send information about server to player in chat
2275 SendChatMessage(peer_id, getStatusString());
2277 // Send information about joining in chat
2279 std::wstring name = L"unknown";
2280 Player *player = m_env->getPlayer(peer_id);
2282 name = narrow_to_wide(player->getName());
2284 std::wstring message;
2287 message += L" joined game";
2288 BroadcastChatMessage(message);
2291 // Warnings about protocol version can be issued here
2292 if(getClient(peer_id)->net_proto_version < PROTOCOL_VERSION)
2294 SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT IS OLD AND MAY WORK PROPERLY WITH THIS SERVER");
2298 Check HP, respawn if necessary
2300 HandlePlayerHP(player, 0);
2306 std::ostringstream os(std::ios_base::binary);
2307 for(core::map<u16, RemoteClient*>::Iterator
2308 i = m_clients.getIterator();
2309 i.atEnd() == false; i++)
2311 RemoteClient *client = i.getNode()->getValue();
2312 assert(client->peer_id == i.getNode()->getKey());
2313 if(client->serialization_version == SER_FMT_VER_INVALID)
2316 Player *player = m_env->getPlayer(client->peer_id);
2319 // Get name of player
2320 os<<player->getName()<<" ";
2323 actionstream<<player->getName()<<" joins game. List of players: "
2324 <<os.str()<<std::endl;
2330 if(peer_ser_ver == SER_FMT_VER_INVALID)
2332 infostream<<"Server::ProcessData(): Cancelling: Peer"
2333 " serialization format invalid or not initialized."
2334 " Skipping incoming command="<<command<<std::endl;
2338 Player *player = m_env->getPlayer(peer_id);
2341 infostream<<"Server::ProcessData(): Cancelling: "
2342 "No player for peer_id="<<peer_id
2346 if(command == TOSERVER_PLAYERPOS)
2348 if(datasize < 2+12+12+4+4)
2352 v3s32 ps = readV3S32(&data[start+2]);
2353 v3s32 ss = readV3S32(&data[start+2+12]);
2354 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2355 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2356 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2357 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2358 pitch = wrapDegrees(pitch);
2359 yaw = wrapDegrees(yaw);
2361 player->setPosition(position);
2362 player->setSpeed(speed);
2363 player->setPitch(pitch);
2364 player->setYaw(yaw);
2366 /*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2367 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2368 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2370 else if(command == TOSERVER_GOTBLOCKS)
2383 u16 count = data[2];
2384 for(u16 i=0; i<count; i++)
2386 if((s16)datasize < 2+1+(i+1)*6)
2387 throw con::InvalidIncomingDataException
2388 ("GOTBLOCKS length is too short");
2389 v3s16 p = readV3S16(&data[2+1+i*6]);
2390 /*infostream<<"Server: GOTBLOCKS ("
2391 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2392 RemoteClient *client = getClient(peer_id);
2393 client->GotBlock(p);
2396 else if(command == TOSERVER_DELETEDBLOCKS)
2409 u16 count = data[2];
2410 for(u16 i=0; i<count; i++)
2412 if((s16)datasize < 2+1+(i+1)*6)
2413 throw con::InvalidIncomingDataException
2414 ("DELETEDBLOCKS length is too short");
2415 v3s16 p = readV3S16(&data[2+1+i*6]);
2416 /*infostream<<"Server: DELETEDBLOCKS ("
2417 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2418 RemoteClient *client = getClient(peer_id);
2419 client->SetBlockNotSent(p);
2422 else if(command == TOSERVER_CLICK_OBJECT)
2424 infostream<<"Server: CLICK_OBJECT not supported anymore"<<std::endl;
2427 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2432 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2438 [2] u8 button (0=left, 1=right)
2442 u8 button = readU8(&data[2]);
2443 u16 id = readS16(&data[3]);
2444 u16 item_i = readU16(&data[5]);
2446 ServerActiveObject *obj = m_env->getActiveObject(id);
2450 infostream<<"Server: CLICK_ACTIVEOBJECT: object not found"
2455 // Skip if object has been removed
2459 //TODO: Check that object is reasonably close
2461 // Get ServerRemotePlayer
2462 ServerRemotePlayer *srp = (ServerRemotePlayer*)player;
2464 // Update wielded item
2465 srp->wieldItem(item_i);
2467 // Left click, pick/punch
2470 actionstream<<player->getName()<<" punches object "
2471 <<obj->getId()<<std::endl;
2478 Try creating inventory item
2480 InventoryItem *item = obj->createPickedUpItem();
2484 InventoryList *ilist = player->inventory.getList("main");
2487 actionstream<<player->getName()<<" picked up "
2488 <<item->getName()<<std::endl;
2489 if(g_settings->getBool("creative_mode") == false)
2491 // Skip if inventory has no free space
2492 if(ilist->roomForItem(item) == false)
2494 infostream<<"Player inventory has no free space"<<std::endl;
2498 // Add to inventory and send inventory
2499 ilist->addItem(item);
2500 UpdateCrafting(player->peer_id);
2501 SendInventory(player->peer_id);
2504 // Remove object from environment
2505 obj->m_removed = true;
2511 Item cannot be picked up. Punch it instead.
2514 actionstream<<player->getName()<<" punches object "
2515 <<obj->getId()<<std::endl;
2517 ToolItem *titem = NULL;
2518 std::string toolname = "";
2520 InventoryList *mlist = player->inventory.getList("main");
2523 InventoryItem *item = mlist->getItem(item_i);
2524 if(item && (std::string)item->getName() == "ToolItem")
2526 titem = (ToolItem*)item;
2527 toolname = titem->getToolName();
2531 v3f playerpos = player->getPosition();
2532 v3f objpos = obj->getBasePosition();
2533 v3f dir = (objpos - playerpos).normalize();
2535 u16 wear = obj->punch(toolname, dir, player->getName());
2539 bool weared_out = titem->addWear(wear);
2541 mlist->deleteItem(item_i);
2542 SendInventory(player->peer_id);
2547 // Right click, do something with object
2550 actionstream<<player->getName()<<" right clicks object "
2551 <<obj->getId()<<std::endl;
2554 obj->rightClick(srp);
2558 Update player state to client
2560 SendPlayerHP(player);
2561 UpdateCrafting(player->peer_id);
2562 SendInventory(player->peer_id);
2564 else if(command == TOSERVER_GROUND_ACTION)
2572 [3] v3s16 nodepos_undersurface
2573 [9] v3s16 nodepos_abovesurface
2578 2: stop digging (all parameters ignored)
2579 3: digging completed
2581 u8 action = readU8(&data[2]);
2583 p_under.X = readS16(&data[3]);
2584 p_under.Y = readS16(&data[5]);
2585 p_under.Z = readS16(&data[7]);
2587 p_over.X = readS16(&data[9]);
2588 p_over.Y = readS16(&data[11]);
2589 p_over.Z = readS16(&data[13]);
2590 u16 item_i = readU16(&data[15]);
2592 ServerRemotePlayer *srp = (ServerRemotePlayer*)player;
2595 Check that target is reasonably close
2597 if(action != 2) // action 2 has always position (0,0,0)
2599 v3f np_f = intToFloat(p_under, BS);
2600 float max_d = BS * 10; // Just some large enough value
2601 float d = srp->m_last_good_position.getDistanceFrom(np_f);
2603 actionstream<<"Player "<<player->getName()
2604 <<" tried to access node from too far: "
2605 <<"d="<<d<<", max_d="<<max_d
2606 <<". ignoring."<<std::endl;
2607 // Re-send block to revert change on client-side
2608 RemoteClient *client = getClient(peer_id);
2609 v3s16 blockpos = getNodeBlockPos(p_under);
2610 client->SetBlockNotSent(blockpos);
2622 NOTE: This can be used in the future to check if
2623 somebody is cheating, by checking the timing.
2625 bool cannot_punch_node = false;
2627 MapNode n(CONTENT_IGNORE);
2631 n = m_env->getMap().getNode(p_under);
2633 catch(InvalidPositionException &e)
2635 infostream<<"Server: Not punching: Node not found."
2636 <<" Adding block to emerge queue."
2638 m_emerge_queue.addBlock(peer_id,
2639 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2640 cannot_punch_node = true;
2643 if(cannot_punch_node)
2649 scriptapi_environment_on_punchnode(m_lua, p_under, n, srp);
2656 else if(action == 2)
2659 RemoteClient *client = getClient(peer_id);
2660 JMutexAutoLock digmutex(client->m_dig_mutex);
2661 client->m_dig_tool_item = -1;
2666 3: Digging completed
2668 else if(action == 3)
2670 // Mandatory parameter; actually used for nothing
2671 core::map<v3s16, MapBlock*> modified_blocks;
2673 content_t material = CONTENT_IGNORE;
2674 u8 mineral = MINERAL_NONE;
2676 bool cannot_remove_node = false;
2678 MapNode n(CONTENT_IGNORE);
2681 n = m_env->getMap().getNode(p_under);
2683 mineral = n.getMineral(m_nodedef);
2684 // Get material at position
2685 material = n.getContent();
2686 // If not yet cancelled
2687 if(cannot_remove_node == false)
2689 // If it's not diggable, do nothing
2690 if(m_nodedef->get(material).diggable == false)
2692 infostream<<"Server: Not finishing digging: "
2693 <<"Node not diggable"
2695 cannot_remove_node = true;
2698 // If not yet cancelled
2699 if(cannot_remove_node == false)
2701 // Get node metadata
2702 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p_under);
2703 if(meta && meta->nodeRemovalDisabled() == true)
2705 infostream<<"Server: Not finishing digging: "
2706 <<"Node metadata disables removal"
2708 cannot_remove_node = true;
2712 catch(InvalidPositionException &e)
2714 infostream<<"Server: Not finishing digging: Node not found."
2715 <<" Adding block to emerge queue."
2717 m_emerge_queue.addBlock(peer_id,
2718 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2719 cannot_remove_node = true;
2722 // Make sure the player is allowed to do it
2723 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2725 infostream<<"Player "<<player->getName()<<" cannot remove node"
2726 <<" because privileges are "<<getPlayerPrivs(player)
2728 cannot_remove_node = true;
2732 If node can't be removed, set block to be re-sent to
2735 if(cannot_remove_node)
2737 infostream<<"Server: Not finishing digging."<<std::endl;
2739 // Client probably has wrong data.
2740 // Set block not sent, so that client will get
2742 infostream<<"Client "<<peer_id<<" tried to dig "
2743 <<"node; but node cannot be removed."
2744 <<" setting MapBlock not sent."<<std::endl;
2745 RemoteClient *client = getClient(peer_id);
2746 v3s16 blockpos = getNodeBlockPos(p_under);
2747 client->SetBlockNotSent(blockpos);
2752 actionstream<<player->getName()<<" digs "<<PP(p_under)
2753 <<", gets material "<<(int)material<<", mineral "
2754 <<(int)mineral<<std::endl;
2757 Send the removal to all close-by players.
2758 - If other player is close, send REMOVENODE
2759 - Otherwise set blocks not sent
2761 core::list<u16> far_players;
2762 sendRemoveNode(p_under, peer_id, &far_players, 30);
2765 Update and send inventory
2768 if(g_settings->getBool("creative_mode") == false)
2773 InventoryList *mlist = player->inventory.getList("main");
2776 InventoryItem *item = mlist->getItem(item_i);
2777 if(item && (std::string)item->getName() == "ToolItem")
2779 ToolItem *titem = (ToolItem*)item;
2780 std::string toolname = titem->getToolName();
2782 // Get digging properties for material and tool
2783 ToolDiggingProperties tp =
2784 m_toolmgr->getDiggingProperties(toolname);
2785 DiggingProperties prop =
2786 getDiggingProperties(material, &tp, m_nodedef);
2788 if(prop.diggable == false)
2790 infostream<<"Server: WARNING: Player digged"
2791 <<" with impossible material + tool"
2792 <<" combination"<<std::endl;
2795 bool weared_out = titem->addWear(prop.wear);
2799 mlist->deleteItem(item_i);
2805 Add dug item to inventory
2808 InventoryItem *item = NULL;
2810 if(mineral != MINERAL_NONE)
2811 item = getDiggedMineralItem(mineral, this);
2816 const std::string &dug_s = m_nodedef->get(material).dug_item;
2819 std::istringstream is(dug_s, std::ios::binary);
2820 item = InventoryItem::deSerialize(is, this);
2826 // Add a item to inventory
2827 player->inventory.addItem("main", item);
2830 UpdateCrafting(player->peer_id);
2831 SendInventory(player->peer_id);
2836 if(mineral != MINERAL_NONE)
2837 item = getDiggedMineralItem(mineral, this);
2842 const std::string &extra_dug_s = m_nodedef->get(material).extra_dug_item;
2843 s32 extra_rarity = m_nodedef->get(material).extra_dug_item_rarity;
2844 if(extra_dug_s != "" && extra_rarity != 0
2845 && myrand() % extra_rarity == 0)
2847 std::istringstream is(extra_dug_s, std::ios::binary);
2848 item = InventoryItem::deSerialize(is, this);
2854 // Add a item to inventory
2855 player->inventory.addItem("main", item);
2858 UpdateCrafting(player->peer_id);
2859 SendInventory(player->peer_id);
2865 (this takes some time so it is done after the quick stuff)
2868 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
2870 m_env->getMap().removeNodeAndUpdate(p_under, modified_blocks);
2873 Set blocks not sent to far players
2875 for(core::list<u16>::Iterator
2876 i = far_players.begin();
2877 i != far_players.end(); i++)
2880 RemoteClient *client = getClient(peer_id);
2883 client->SetBlocksNotSent(modified_blocks);
2889 scriptapi_environment_on_dignode(m_lua, p_under, n, srp);
2895 else if(action == 1)
2898 InventoryList *ilist = player->inventory.getList("main");
2903 InventoryItem *item = ilist->getItem(item_i);
2905 // If there is no item, it is not possible to add it anywhere
2910 Handle material items
2912 if(std::string("MaterialItem") == item->getName())
2915 // Don't add a node if this is not a free space
2916 MapNode n2 = m_env->getMap().getNode(p_over);
2917 bool no_enough_privs =
2918 ((getPlayerPrivs(player) & PRIV_BUILD)==0);
2920 infostream<<"Player "<<player->getName()<<" cannot add node"
2921 <<" because privileges are "<<getPlayerPrivs(player)
2924 if(m_nodedef->get(n2).buildable_to == false
2927 // Client probably has wrong data.
2928 // Set block not sent, so that client will get
2930 infostream<<"Client "<<peer_id<<" tried to place"
2931 <<" node in invalid position; setting"
2932 <<" MapBlock not sent."<<std::endl;
2933 RemoteClient *client = getClient(peer_id);
2934 v3s16 blockpos = getNodeBlockPos(p_over);
2935 client->SetBlockNotSent(blockpos);
2939 catch(InvalidPositionException &e)
2941 infostream<<"Server: Ignoring ADDNODE: Node not found"
2942 <<" Adding block to emerge queue."
2944 m_emerge_queue.addBlock(peer_id,
2945 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2949 // Reset build time counter
2950 getClient(peer_id)->m_time_from_building = 0.0;
2953 MaterialItem *mitem = (MaterialItem*)item;
2955 n.setContent(mitem->getMaterial());
2957 actionstream<<player->getName()<<" places material "
2958 <<(int)mitem->getMaterial()
2959 <<" at "<<PP(p_under)<<std::endl;
2961 // Calculate direction for wall mounted stuff
2962 if(m_nodedef->get(n).wall_mounted)
2963 n.param2 = packDir(p_under - p_over);
2965 // Calculate the direction for furnaces and chests and stuff
2966 if(m_nodedef->get(n).param_type == CPT_FACEDIR_SIMPLE)
2968 v3f playerpos = player->getPosition();
2969 v3f blockpos = intToFloat(p_over, BS) - playerpos;
2970 blockpos = blockpos.normalize();
2972 if (fabs(blockpos.X) > fabs(blockpos.Z)) {
2986 Send to all close-by players
2988 core::list<u16> far_players;
2989 sendAddNode(p_over, n, 0, &far_players, 30);
2994 InventoryList *ilist = player->inventory.getList("main");
2995 if(g_settings->getBool("creative_mode") == false && ilist)
2997 // Remove from inventory and send inventory
2998 if(mitem->getCount() == 1)
2999 ilist->deleteItem(item_i);
3003 UpdateCrafting(peer_id);
3004 SendInventory(peer_id);
3010 This takes some time so it is done after the quick stuff
3012 core::map<v3s16, MapBlock*> modified_blocks;
3014 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
3016 std::string p_name = std::string(player->getName());
3017 m_env->getMap().addNodeAndUpdate(p_over, n, modified_blocks, p_name);
3020 Set blocks not sent to far players
3022 for(core::list<u16>::Iterator
3023 i = far_players.begin();
3024 i != far_players.end(); i++)
3027 RemoteClient *client = getClient(peer_id);
3030 client->SetBlocksNotSent(modified_blocks);
3036 scriptapi_environment_on_placenode(m_lua, p_over, n, srp);
3039 Calculate special events
3042 /*if(n.d == LEGN(m_nodedef, "CONTENT_MESE"))
3045 for(s16 z=-1; z<=1; z++)
3046 for(s16 y=-1; y<=1; y++)
3047 for(s16 x=-1; x<=1; x++)
3054 Place other item (not a block)
3058 v3s16 blockpos = getNodeBlockPos(p_over);
3061 Check that the block is loaded so that the item
3062 can properly be added to the static list too
3064 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3067 infostream<<"Error while placing object: "
3068 "block not found"<<std::endl;
3073 If in creative mode, item dropping is disabled unless
3074 player has build privileges
3076 if(g_settings->getBool("creative_mode") &&
3077 (getPlayerPrivs(player) & PRIV_BUILD) == 0)
3079 infostream<<"Not allowing player to drop item: "
3080 "creative mode and no build privs"<<std::endl;
3084 // Calculate a position for it
3085 v3f pos = intToFloat(p_over, BS);
3087 /*pos.Y -= BS*0.25; // let it drop a bit
3089 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
3090 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;*/
3095 ServerActiveObject *obj = item->createSAO(m_env, pos);
3099 infostream<<"WARNING: item resulted in NULL object, "
3100 <<"not placing onto map"
3105 actionstream<<player->getName()<<" places "<<item->getName()
3106 <<" at "<<PP(p_over)<<std::endl;
3108 // Add the object to the environment
3109 m_env->addActiveObject(obj);
3111 infostream<<"Placed object"<<std::endl;
3113 if(g_settings->getBool("creative_mode") == false)
3115 // Delete the right amount of items from the slot
3116 u16 dropcount = item->getDropCount();
3118 // Delete item if all gone
3119 if(item->getCount() <= dropcount)
3121 if(item->getCount() < dropcount)
3122 infostream<<"WARNING: Server: dropped more items"
3123 <<" than the slot contains"<<std::endl;
3125 InventoryList *ilist = player->inventory.getList("main");
3127 // Remove from inventory and send inventory
3128 ilist->deleteItem(item_i);
3130 // Else decrement it
3132 item->remove(dropcount);
3135 UpdateCrafting(peer_id);
3136 SendInventory(peer_id);
3144 Catch invalid actions
3148 infostream<<"WARNING: Server: Invalid action "
3149 <<action<<std::endl;
3153 else if(command == TOSERVER_RELEASE)
3162 infostream<<"TOSERVER_RELEASE ignored"<<std::endl;
3165 else if(command == TOSERVER_SIGNTEXT)
3167 infostream<<"Server: TOSERVER_SIGNTEXT not supported anymore"
3171 else if(command == TOSERVER_SIGNNODETEXT)
3173 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
3181 std::string datastring((char*)&data[2], datasize-2);
3182 std::istringstream is(datastring, std::ios_base::binary);
3185 is.read((char*)buf, 6);
3186 v3s16 p = readV3S16(buf);
3187 is.read((char*)buf, 2);
3188 u16 textlen = readU16(buf);
3190 for(u16 i=0; i<textlen; i++)
3192 is.read((char*)buf, 1);
3193 text += (char)buf[0];
3196 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3199 if(meta->typeId() != LEGN(m_nodedef, "CONTENT_SIGN_WALL"))
3201 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
3202 signmeta->setText(text);
3204 actionstream<<player->getName()<<" writes \""<<text<<"\" to sign "
3205 <<" at "<<PP(p)<<std::endl;
3207 v3s16 blockpos = getNodeBlockPos(p);
3208 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3211 block->raiseModified(MOD_STATE_WRITE_NEEDED,
3215 setBlockNotSent(blockpos);
3217 else if(command == TOSERVER_INVENTORY_ACTION)
3219 /*// Ignore inventory changes if in creative mode
3220 if(g_settings->getBool("creative_mode") == true)
3222 infostream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
3226 // Strip command and create a stream
3227 std::string datastring((char*)&data[2], datasize-2);
3228 infostream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
3229 std::istringstream is(datastring, std::ios_base::binary);
3231 InventoryAction *a = InventoryAction::deSerialize(is);
3236 c.current_player = player;
3239 Handle craftresult specially if not in creative mode
3241 bool disable_action = false;
3242 if(a->getType() == IACTION_MOVE
3243 && g_settings->getBool("creative_mode") == false)
3245 IMoveAction *ma = (IMoveAction*)a;
3246 if(ma->to_inv == "current_player" &&
3247 ma->from_inv == "current_player")
3249 InventoryList *rlist = player->inventory.getList("craftresult");
3251 InventoryList *clist = player->inventory.getList("craft");
3253 InventoryList *mlist = player->inventory.getList("main");
3256 Craftresult is no longer preview if something
3259 if(ma->to_list == "craftresult"
3260 && ma->from_list != "craftresult")
3262 // If it currently is a preview, remove
3264 if(player->craftresult_is_preview)
3266 rlist->deleteItem(0);
3268 player->craftresult_is_preview = false;
3271 Crafting takes place if this condition is true.
3273 if(player->craftresult_is_preview &&
3274 ma->from_list == "craftresult")
3276 player->craftresult_is_preview = false;
3277 clist->decrementMaterials(1);
3279 /* Print out action */
3280 InventoryList *list =
3281 player->inventory.getList("craftresult");
3283 InventoryItem *item = list->getItem(0);
3284 std::string itemname = "NULL";
3286 itemname = item->getName();
3287 actionstream<<player->getName()<<" crafts "
3288 <<itemname<<std::endl;
3291 If the craftresult is placed on itself, move it to
3292 main inventory instead of doing the action
3294 if(ma->to_list == "craftresult"
3295 && ma->from_list == "craftresult")
3297 disable_action = true;
3299 InventoryItem *item1 = rlist->changeItem(0, NULL);
3300 mlist->addItem(item1);
3303 // Disallow moving items if not allowed to build
3304 else if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
3306 disable_action = true;
3308 // if it's a locking chest, only allow the owner or server admins to move items
3309 else if (ma->from_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
3311 Strfnd fn(ma->from_inv);
3312 std::string id0 = fn.next(":");
3313 if(id0 == "nodemeta")
3316 p.X = stoi(fn.next(","));
3317 p.Y = stoi(fn.next(","));
3318 p.Z = stoi(fn.next(","));
3319 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3320 if(meta && meta->typeId() == LEGN(m_nodedef, "CONTENT_LOCKABLE_CHEST")) {
3321 LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
3322 if (lcm->getOwner() != player->getName())
3323 disable_action = true;
3327 else if (ma->to_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
3329 Strfnd fn(ma->to_inv);
3330 std::string id0 = fn.next(":");
3331 if(id0 == "nodemeta")
3334 p.X = stoi(fn.next(","));
3335 p.Y = stoi(fn.next(","));
3336 p.Z = stoi(fn.next(","));
3337 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3338 if(meta && meta->typeId() == LEGN(m_nodedef, "CONTENT_LOCKABLE_CHEST")) {
3339 LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
3340 if (lcm->getOwner() != player->getName())
3341 disable_action = true;
3347 if(a->getType() == IACTION_DROP)
3349 IDropAction *da = (IDropAction*)a;
3350 // Disallow dropping items if not allowed to build
3351 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
3353 disable_action = true;
3355 // if it's a locking chest, only allow the owner or server admins to drop items
3356 else if (da->from_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
3358 Strfnd fn(da->from_inv);
3359 std::string id0 = fn.next(":");
3360 if(id0 == "nodemeta")
3363 p.X = stoi(fn.next(","));
3364 p.Y = stoi(fn.next(","));
3365 p.Z = stoi(fn.next(","));
3366 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3367 if(meta && meta->typeId() == LEGN(m_nodedef, "CONTENT_LOCKABLE_CHEST")) {
3368 LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
3369 if (lcm->getOwner() != player->getName())
3370 disable_action = true;
3376 if(disable_action == false)
3378 // Feed action to player inventory
3379 a->apply(&c, this, m_env);
3384 UpdateCrafting(player->peer_id);
3385 SendInventory(player->peer_id);
3393 infostream<<"TOSERVER_INVENTORY_ACTION: "
3394 <<"InventoryAction::deSerialize() returned NULL"
3398 else if(command == TOSERVER_CHAT_MESSAGE)
3406 std::string datastring((char*)&data[2], datasize-2);
3407 std::istringstream is(datastring, std::ios_base::binary);
3410 is.read((char*)buf, 2);
3411 u16 len = readU16(buf);
3413 std::wstring message;
3414 for(u16 i=0; i<len; i++)
3416 is.read((char*)buf, 2);
3417 message += (wchar_t)readU16(buf);
3420 // Get player name of this client
3421 std::wstring name = narrow_to_wide(player->getName());
3423 // Line to send to players
3425 // Whether to send to the player that sent the line
3426 bool send_to_sender = false;
3427 // Whether to send to other players
3428 bool send_to_others = false;
3430 // Local player gets all privileges regardless of
3431 // what's set on their account.
3432 u64 privs = getPlayerPrivs(player);
3435 if(message[0] == L'/')
3437 size_t strip_size = 1;
3438 if (message[1] == L'#') // support old-style commans
3440 message = message.substr(strip_size);
3442 WStrfnd f1(message);
3443 f1.next(L" "); // Skip over /#whatever
3444 std::wstring paramstring = f1.next(L"");
3446 ServerCommandContext *ctx = new ServerCommandContext(
3447 str_split(message, L' '),
3454 std::wstring reply(processServerCommand(ctx));
3455 send_to_sender = ctx->flags & SEND_TO_SENDER;
3456 send_to_others = ctx->flags & SEND_TO_OTHERS;
3458 if (ctx->flags & SEND_NO_PREFIX)
3461 line += L"Server: " + reply;
3468 if(privs & PRIV_SHOUT)
3474 send_to_others = true;
3478 line += L"Server: You are not allowed to shout";
3479 send_to_sender = true;
3486 actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
3489 Send the message to clients
3491 for(core::map<u16, RemoteClient*>::Iterator
3492 i = m_clients.getIterator();
3493 i.atEnd() == false; i++)
3495 // Get client and check that it is valid
3496 RemoteClient *client = i.getNode()->getValue();
3497 assert(client->peer_id == i.getNode()->getKey());
3498 if(client->serialization_version == SER_FMT_VER_INVALID)
3502 bool sender_selected = (peer_id == client->peer_id);
3503 if(sender_selected == true && send_to_sender == false)
3505 if(sender_selected == false && send_to_others == false)
3508 SendChatMessage(client->peer_id, line);
3512 else if(command == TOSERVER_DAMAGE)
3514 std::string datastring((char*)&data[2], datasize-2);
3515 std::istringstream is(datastring, std::ios_base::binary);
3516 u8 damage = readU8(is);
3518 if(g_settings->getBool("enable_damage"))
3520 actionstream<<player->getName()<<" damaged by "
3521 <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
3524 HandlePlayerHP(player, damage);
3528 SendPlayerHP(player);
3531 else if(command == TOSERVER_PASSWORD)
3534 [0] u16 TOSERVER_PASSWORD
3535 [2] u8[28] old password
3536 [30] u8[28] new password
3539 if(datasize != 2+PASSWORD_SIZE*2)
3541 /*char password[PASSWORD_SIZE];
3542 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3543 password[i] = data[2+i];
3544 password[PASSWORD_SIZE-1] = 0;*/
3546 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3554 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3556 char c = data[2+PASSWORD_SIZE+i];
3562 infostream<<"Server: Client requests a password change from "
3563 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
3565 std::string playername = player->getName();
3567 if(m_authmanager.exists(playername) == false)
3569 infostream<<"Server: playername not found in authmanager"<<std::endl;
3570 // Wrong old password supplied!!
3571 SendChatMessage(peer_id, L"playername not found in authmanager");
3575 std::string checkpwd = m_authmanager.getPassword(playername);
3577 if(oldpwd != checkpwd)
3579 infostream<<"Server: invalid old password"<<std::endl;
3580 // Wrong old password supplied!!
3581 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
3585 actionstream<<player->getName()<<" changes password"<<std::endl;
3587 m_authmanager.setPassword(playername, newpwd);
3589 infostream<<"Server: password change successful for "<<playername
3591 SendChatMessage(peer_id, L"Password change successful");
3593 else if(command == TOSERVER_PLAYERITEM)
3598 u16 item = readU16(&data[2]);
3599 player->wieldItem(item);
3600 SendWieldedItem(player);
3602 else if(command == TOSERVER_RESPAWN)
3607 RespawnPlayer(player);
3609 actionstream<<player->getName()<<" respawns at "
3610 <<PP(player->getPosition()/BS)<<std::endl;
3614 infostream<<"Server::ProcessData(): Ignoring "
3615 "unknown command "<<command<<std::endl;
3619 catch(SendFailedException &e)
3621 errorstream<<"Server::ProcessData(): SendFailedException: "
3627 void Server::onMapEditEvent(MapEditEvent *event)
3629 //infostream<<"Server::onMapEditEvent()"<<std::endl;
3630 if(m_ignore_map_edit_events)
3632 MapEditEvent *e = event->clone();
3633 m_unsent_map_edit_queue.push_back(e);
3636 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3638 if(id == "current_player")
3640 assert(c->current_player);
3641 return &(c->current_player->inventory);
3645 std::string id0 = fn.next(":");
3647 if(id0 == "nodemeta")
3650 p.X = stoi(fn.next(","));
3651 p.Y = stoi(fn.next(","));
3652 p.Z = stoi(fn.next(","));
3653 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3655 return meta->getInventory();
3656 infostream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3657 <<"no metadata found"<<std::endl;
3661 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3664 void Server::inventoryModified(InventoryContext *c, std::string id)
3666 if(id == "current_player")
3668 assert(c->current_player);
3670 UpdateCrafting(c->current_player->peer_id);
3671 SendInventory(c->current_player->peer_id);
3676 std::string id0 = fn.next(":");
3678 if(id0 == "nodemeta")
3681 p.X = stoi(fn.next(","));
3682 p.Y = stoi(fn.next(","));
3683 p.Z = stoi(fn.next(","));
3684 v3s16 blockpos = getNodeBlockPos(p);
3686 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3688 meta->inventoryModified();
3690 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3692 block->raiseModified(MOD_STATE_WRITE_NEEDED);
3694 setBlockNotSent(blockpos);
3699 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3702 core::list<PlayerInfo> Server::getPlayerInfo()
3704 DSTACK(__FUNCTION_NAME);
3705 JMutexAutoLock envlock(m_env_mutex);
3706 JMutexAutoLock conlock(m_con_mutex);
3708 core::list<PlayerInfo> list;
3710 core::list<Player*> players = m_env->getPlayers();
3712 core::list<Player*>::Iterator i;
3713 for(i = players.begin();
3714 i != players.end(); i++)
3718 Player *player = *i;
3721 // Copy info from connection to info struct
3722 info.id = player->peer_id;
3723 info.address = m_con.GetPeerAddress(player->peer_id);
3724 info.avg_rtt = m_con.GetPeerAvgRTT(player->peer_id);
3726 catch(con::PeerNotFoundException &e)
3728 // Set dummy peer info
3730 info.address = Address(0,0,0,0,0);
3734 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3735 info.position = player->getPosition();
3737 list.push_back(info);
3744 void Server::peerAdded(con::Peer *peer)
3746 DSTACK(__FUNCTION_NAME);
3747 infostream<<"Server::peerAdded(): peer->id="
3748 <<peer->id<<std::endl;
3751 c.type = PEER_ADDED;
3752 c.peer_id = peer->id;
3754 m_peer_change_queue.push_back(c);
3757 void Server::deletingPeer(con::Peer *peer, bool timeout)
3759 DSTACK(__FUNCTION_NAME);
3760 infostream<<"Server::deletingPeer(): peer->id="
3761 <<peer->id<<", timeout="<<timeout<<std::endl;
3764 c.type = PEER_REMOVED;
3765 c.peer_id = peer->id;
3766 c.timeout = timeout;
3767 m_peer_change_queue.push_back(c);
3774 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3776 DSTACK(__FUNCTION_NAME);
3777 std::ostringstream os(std::ios_base::binary);
3779 writeU16(os, TOCLIENT_HP);
3783 std::string s = os.str();
3784 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3786 con.Send(peer_id, 0, data, true);
3789 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3790 const std::wstring &reason)
3792 DSTACK(__FUNCTION_NAME);
3793 std::ostringstream os(std::ios_base::binary);
3795 writeU16(os, TOCLIENT_ACCESS_DENIED);
3796 os<<serializeWideString(reason);
3799 std::string s = os.str();
3800 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3802 con.Send(peer_id, 0, data, true);
3805 void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
3806 bool set_camera_point_target, v3f camera_point_target)
3808 DSTACK(__FUNCTION_NAME);
3809 std::ostringstream os(std::ios_base::binary);
3811 writeU16(os, TOCLIENT_DEATHSCREEN);
3812 writeU8(os, set_camera_point_target);
3813 writeV3F1000(os, camera_point_target);
3816 std::string s = os.str();
3817 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3819 con.Send(peer_id, 0, data, true);
3822 void Server::SendToolDef(con::Connection &con, u16 peer_id,
3823 IToolDefManager *tooldef)
3825 DSTACK(__FUNCTION_NAME);
3826 std::ostringstream os(std::ios_base::binary);
3830 u32 length of the next item
3831 serialized ToolDefManager
3833 writeU16(os, TOCLIENT_TOOLDEF);
3834 std::ostringstream tmp_os(std::ios::binary);
3835 tooldef->serialize(tmp_os);
3836 os<<serializeLongString(tmp_os.str());
3839 std::string s = os.str();
3840 infostream<<"Server::SendToolDef(): Sending tool definitions: size="
3841 <<s.size()<<std::endl;
3842 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3844 con.Send(peer_id, 0, data, true);
3847 void Server::SendNodeDef(con::Connection &con, u16 peer_id,
3848 INodeDefManager *nodedef)
3850 DSTACK(__FUNCTION_NAME);
3851 std::ostringstream os(std::ios_base::binary);
3855 u32 length of the next item
3856 serialized NodeDefManager
3858 writeU16(os, TOCLIENT_NODEDEF);
3859 std::ostringstream tmp_os(std::ios::binary);
3860 nodedef->serialize(tmp_os);
3861 os<<serializeLongString(tmp_os.str());
3864 std::string s = os.str();
3865 infostream<<"Server::SendNodeDef(): Sending node definitions: size="
3866 <<s.size()<<std::endl;
3867 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3869 con.Send(peer_id, 0, data, true);
3873 Non-static send methods
3876 void Server::SendObjectData(float dtime)
3878 DSTACK(__FUNCTION_NAME);
3880 core::map<v3s16, bool> stepped_blocks;
3882 for(core::map<u16, RemoteClient*>::Iterator
3883 i = m_clients.getIterator();
3884 i.atEnd() == false; i++)
3886 u16 peer_id = i.getNode()->getKey();
3887 RemoteClient *client = i.getNode()->getValue();
3888 assert(client->peer_id == peer_id);
3890 if(client->serialization_version == SER_FMT_VER_INVALID)
3893 client->SendObjectData(this, dtime, stepped_blocks);
3897 void Server::SendPlayerInfos()
3899 DSTACK(__FUNCTION_NAME);
3901 //JMutexAutoLock envlock(m_env_mutex);
3903 // Get connected players
3904 core::list<Player*> players = m_env->getPlayers(true);
3906 u32 player_count = players.getSize();
3907 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3909 SharedBuffer<u8> data(datasize);
3910 writeU16(&data[0], TOCLIENT_PLAYERINFO);
3913 core::list<Player*>::Iterator i;
3914 for(i = players.begin();
3915 i != players.end(); i++)
3917 Player *player = *i;
3919 /*infostream<<"Server sending player info for player with "
3920 "peer_id="<<player->peer_id<<std::endl;*/
3922 writeU16(&data[start], player->peer_id);
3923 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3924 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3925 start += 2+PLAYERNAME_SIZE;
3928 //JMutexAutoLock conlock(m_con_mutex);
3931 m_con.SendToAll(0, data, true);
3934 void Server::SendInventory(u16 peer_id)
3936 DSTACK(__FUNCTION_NAME);
3938 Player* player = m_env->getPlayer(peer_id);
3945 std::ostringstream os;
3946 //os.imbue(std::locale("C"));
3948 player->inventory.serialize(os);
3950 std::string s = os.str();
3952 SharedBuffer<u8> data(s.size()+2);
3953 writeU16(&data[0], TOCLIENT_INVENTORY);
3954 memcpy(&data[2], s.c_str(), s.size());
3957 m_con.Send(peer_id, 0, data, true);
3960 std::string getWieldedItemString(const Player *player)
3962 const InventoryItem *item = player->getWieldItem();
3964 return std::string("");
3965 std::ostringstream os(std::ios_base::binary);
3966 item->serialize(os);
3970 void Server::SendWieldedItem(const Player* player)
3972 DSTACK(__FUNCTION_NAME);
3976 std::ostringstream os(std::ios_base::binary);
3978 writeU16(os, TOCLIENT_PLAYERITEM);
3980 writeU16(os, player->peer_id);
3981 os<<serializeString(getWieldedItemString(player));
3984 std::string s = os.str();
3985 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3987 m_con.SendToAll(0, data, true);
3990 void Server::SendPlayerItems()
3992 DSTACK(__FUNCTION_NAME);
3994 std::ostringstream os(std::ios_base::binary);
3995 core::list<Player *> players = m_env->getPlayers(true);
3997 writeU16(os, TOCLIENT_PLAYERITEM);
3998 writeU16(os, players.size());
3999 core::list<Player *>::Iterator i;
4000 for(i = players.begin(); i != players.end(); ++i)
4003 writeU16(os, p->peer_id);
4004 os<<serializeString(getWieldedItemString(p));
4008 std::string s = os.str();
4009 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4011 m_con.SendToAll(0, data, true);
4014 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
4016 DSTACK(__FUNCTION_NAME);
4018 std::ostringstream os(std::ios_base::binary);
4022 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
4023 os.write((char*)buf, 2);
4026 writeU16(buf, message.size());
4027 os.write((char*)buf, 2);
4030 for(u32 i=0; i<message.size(); i++)
4034 os.write((char*)buf, 2);
4038 std::string s = os.str();
4039 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4041 m_con.Send(peer_id, 0, data, true);
4044 void Server::BroadcastChatMessage(const std::wstring &message)
4046 for(core::map<u16, RemoteClient*>::Iterator
4047 i = m_clients.getIterator();
4048 i.atEnd() == false; i++)
4050 // Get client and check that it is valid
4051 RemoteClient *client = i.getNode()->getValue();
4052 assert(client->peer_id == i.getNode()->getKey());
4053 if(client->serialization_version == SER_FMT_VER_INVALID)
4056 SendChatMessage(client->peer_id, message);
4060 void Server::SendPlayerHP(Player *player)
4062 SendHP(m_con, player->peer_id, player->hp);
4065 void Server::SendMovePlayer(Player *player)
4067 DSTACK(__FUNCTION_NAME);
4068 std::ostringstream os(std::ios_base::binary);
4070 writeU16(os, TOCLIENT_MOVE_PLAYER);
4071 writeV3F1000(os, player->getPosition());
4072 writeF1000(os, player->getPitch());
4073 writeF1000(os, player->getYaw());
4076 v3f pos = player->getPosition();
4077 f32 pitch = player->getPitch();
4078 f32 yaw = player->getYaw();
4079 infostream<<"Server sending TOCLIENT_MOVE_PLAYER"
4080 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
4087 std::string s = os.str();
4088 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4090 m_con.Send(player->peer_id, 0, data, true);
4093 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
4094 core::list<u16> *far_players, float far_d_nodes)
4096 float maxd = far_d_nodes*BS;
4097 v3f p_f = intToFloat(p, BS);
4101 SharedBuffer<u8> reply(replysize);
4102 writeU16(&reply[0], TOCLIENT_REMOVENODE);
4103 writeS16(&reply[2], p.X);
4104 writeS16(&reply[4], p.Y);
4105 writeS16(&reply[6], p.Z);
4107 for(core::map<u16, RemoteClient*>::Iterator
4108 i = m_clients.getIterator();
4109 i.atEnd() == false; i++)
4111 // Get client and check that it is valid
4112 RemoteClient *client = i.getNode()->getValue();
4113 assert(client->peer_id == i.getNode()->getKey());
4114 if(client->serialization_version == SER_FMT_VER_INVALID)
4117 // Don't send if it's the same one
4118 if(client->peer_id == ignore_id)
4124 Player *player = m_env->getPlayer(client->peer_id);
4127 // If player is far away, only set modified blocks not sent
4128 v3f player_pos = player->getPosition();
4129 if(player_pos.getDistanceFrom(p_f) > maxd)
4131 far_players->push_back(client->peer_id);
4138 m_con.Send(client->peer_id, 0, reply, true);
4142 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
4143 core::list<u16> *far_players, float far_d_nodes)
4145 float maxd = far_d_nodes*BS;
4146 v3f p_f = intToFloat(p, BS);
4148 for(core::map<u16, RemoteClient*>::Iterator
4149 i = m_clients.getIterator();
4150 i.atEnd() == false; i++)
4152 // Get client and check that it is valid
4153 RemoteClient *client = i.getNode()->getValue();
4154 assert(client->peer_id == i.getNode()->getKey());
4155 if(client->serialization_version == SER_FMT_VER_INVALID)
4158 // Don't send if it's the same one
4159 if(client->peer_id == ignore_id)
4165 Player *player = m_env->getPlayer(client->peer_id);
4168 // If player is far away, only set modified blocks not sent
4169 v3f player_pos = player->getPosition();
4170 if(player_pos.getDistanceFrom(p_f) > maxd)
4172 far_players->push_back(client->peer_id);
4179 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
4180 SharedBuffer<u8> reply(replysize);
4181 writeU16(&reply[0], TOCLIENT_ADDNODE);
4182 writeS16(&reply[2], p.X);
4183 writeS16(&reply[4], p.Y);
4184 writeS16(&reply[6], p.Z);
4185 n.serialize(&reply[8], client->serialization_version);
4188 m_con.Send(client->peer_id, 0, reply, true);
4192 void Server::setBlockNotSent(v3s16 p)
4194 for(core::map<u16, RemoteClient*>::Iterator
4195 i = m_clients.getIterator();
4196 i.atEnd()==false; i++)
4198 RemoteClient *client = i.getNode()->getValue();
4199 client->SetBlockNotSent(p);
4203 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
4205 DSTACK(__FUNCTION_NAME);
4207 v3s16 p = block->getPos();
4211 bool completely_air = true;
4212 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4213 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4214 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4216 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
4218 completely_air = false;
4219 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
4224 infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
4226 infostream<<"[completely air] ";
4227 infostream<<std::endl;
4231 Create a packet with the block in the right format
4234 std::ostringstream os(std::ios_base::binary);
4235 block->serialize(os, ver);
4236 std::string s = os.str();
4237 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
4239 u32 replysize = 8 + blockdata.getSize();
4240 SharedBuffer<u8> reply(replysize);
4241 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
4242 writeS16(&reply[2], p.X);
4243 writeS16(&reply[4], p.Y);
4244 writeS16(&reply[6], p.Z);
4245 memcpy(&reply[8], *blockdata, blockdata.getSize());
4247 /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4248 <<": \tpacket size: "<<replysize<<std::endl;*/
4253 m_con.Send(peer_id, 1, reply, true);
4256 void Server::SendBlocks(float dtime)
4258 DSTACK(__FUNCTION_NAME);
4260 JMutexAutoLock envlock(m_env_mutex);
4261 JMutexAutoLock conlock(m_con_mutex);
4263 //TimeTaker timer("Server::SendBlocks");
4265 core::array<PrioritySortedBlockTransfer> queue;
4267 s32 total_sending = 0;
4270 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
4272 for(core::map<u16, RemoteClient*>::Iterator
4273 i = m_clients.getIterator();
4274 i.atEnd() == false; i++)
4276 RemoteClient *client = i.getNode()->getValue();
4277 assert(client->peer_id == i.getNode()->getKey());
4279 total_sending += client->SendingCount();
4281 if(client->serialization_version == SER_FMT_VER_INVALID)
4284 client->GetNextBlocks(this, dtime, queue);
4289 // Lowest priority number comes first.
4290 // Lowest is most important.
4293 for(u32 i=0; i<queue.size(); i++)
4295 //TODO: Calculate limit dynamically
4296 if(total_sending >= g_settings->getS32
4297 ("max_simultaneous_block_sends_server_total"))
4300 PrioritySortedBlockTransfer q = queue[i];
4302 MapBlock *block = NULL;
4305 block = m_env->getMap().getBlockNoCreate(q.pos);
4307 catch(InvalidPositionException &e)
4312 RemoteClient *client = getClient(q.peer_id);
4314 SendBlockNoLock(q.peer_id, block, client->serialization_version);
4316 client->SentBlock(q.pos);
4322 struct SendableTexture
4328 SendableTexture(const std::string &name_="", const std::string path_="",
4329 const std::string &data_=""):
4336 void Server::SendTextures(u16 peer_id)
4338 DSTACK(__FUNCTION_NAME);
4340 infostream<<"Server::SendTextures(): Sending textures to client"<<std::endl;
4344 // Put 5kB in one bunch (this is not accurate)
4345 u32 bytes_per_bunch = 5000;
4347 core::array< core::list<SendableTexture> > texture_bunches;
4348 texture_bunches.push_back(core::list<SendableTexture>());
4350 u32 texture_size_bunch_total = 0;
4351 core::list<ModSpec> mods = getMods(m_modspaths);
4352 for(core::list<ModSpec>::Iterator i = mods.begin();
4353 i != mods.end(); i++){
4355 std::string texturepath = mod.path + DIR_DELIM + "textures";
4356 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(texturepath);
4357 for(u32 j=0; j<dirlist.size(); j++){
4358 if(dirlist[j].dir) // Ignode dirs
4360 std::string tname = dirlist[j].name;
4361 std::string tpath = texturepath + DIR_DELIM + tname;
4363 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
4364 if(fis.good() == false){
4365 errorstream<<"Server::SendTextures(): Could not open \""
4366 <<tname<<"\" for reading"<<std::endl;
4369 std::ostringstream tmp_os(std::ios_base::binary);
4373 fis.read(buf, 1024);
4374 std::streamsize len = fis.gcount();
4375 tmp_os.write(buf, len);
4376 texture_size_bunch_total += len;
4385 errorstream<<"Server::SendTextures(): Failed to read \""
4386 <<tname<<"\""<<std::endl;
4389 /*infostream<<"Server::SendTextures(): Loaded \""
4390 <<tname<<"\""<<std::endl;*/
4392 texture_bunches[texture_bunches.size()-1].push_back(
4393 SendableTexture(tname, tpath, tmp_os.str()));
4395 // Start next bunch if got enough data
4396 if(texture_size_bunch_total >= bytes_per_bunch){
4397 texture_bunches.push_back(core::list<SendableTexture>());
4398 texture_size_bunch_total = 0;
4403 /* Create and send packets */
4405 u32 num_bunches = texture_bunches.size();
4406 for(u32 i=0; i<num_bunches; i++)
4410 u16 total number of texture bunches
4411 u16 index of this bunch
4412 u32 number of textures in this bunch
4420 std::ostringstream os(std::ios_base::binary);
4422 writeU16(os, TOCLIENT_TEXTURES);
4423 writeU16(os, num_bunches);
4425 writeU32(os, texture_bunches[i].size());
4427 for(core::list<SendableTexture>::Iterator
4428 j = texture_bunches[i].begin();
4429 j != texture_bunches[i].end(); j++){
4430 os<<serializeString(j->name);
4431 os<<serializeLongString(j->data);
4435 std::string s = os.str();
4436 infostream<<"Server::SendTextures(): bunch "<<i<<"/"<<num_bunches
4437 <<" textures="<<texture_bunches[i].size()
4438 <<" size=" <<s.size()<<std::endl;
4439 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4441 m_con.Send(peer_id, 0, data, true);
4449 void Server::HandlePlayerHP(Player *player, s16 damage)
4451 if(player->hp > damage)
4453 player->hp -= damage;
4454 SendPlayerHP(player);
4458 infostream<<"Server::HandlePlayerHP(): Player "
4459 <<player->getName()<<" dies"<<std::endl;
4463 //TODO: Throw items around
4465 // Handle players that are not connected
4466 if(player->peer_id == PEER_ID_INEXISTENT){
4467 RespawnPlayer(player);
4471 SendPlayerHP(player);
4473 RemoteClient *client = getClient(player->peer_id);
4474 if(client->net_proto_version >= 3)
4476 SendDeathscreen(m_con, player->peer_id, false, v3f(0,0,0));
4480 RespawnPlayer(player);
4485 void Server::RespawnPlayer(Player *player)
4488 ServerRemotePlayer *srp = (ServerRemotePlayer*)player;
4489 bool repositioned = scriptapi_on_respawnplayer(m_lua, srp);
4491 v3f pos = findSpawnPos(m_env->getServerMap());
4492 player->setPosition(pos);
4493 srp->m_last_good_position = pos;
4494 srp->m_last_good_position_age = 0;
4496 SendMovePlayer(player);
4497 SendPlayerHP(player);
4500 void Server::UpdateCrafting(u16 peer_id)
4502 DSTACK(__FUNCTION_NAME);
4504 Player* player = m_env->getPlayer(peer_id);
4508 Calculate crafting stuff
4510 if(g_settings->getBool("creative_mode") == false)
4512 InventoryList *clist = player->inventory.getList("craft");
4513 InventoryList *rlist = player->inventory.getList("craftresult");
4515 if(rlist && rlist->getUsedSlots() == 0)
4516 player->craftresult_is_preview = true;
4518 if(rlist && player->craftresult_is_preview)
4520 rlist->clearItems();
4522 if(clist && rlist && player->craftresult_is_preview)
4524 // Get result of crafting grid
4526 std::vector<InventoryItem*> items;
4527 for(u16 i=0; i<9; i++){
4528 if(clist->getItem(i) == NULL)
4529 items.push_back(NULL);
4531 items.push_back(clist->getItem(i)->clone());
4533 CraftPointerInput cpi(3, items);
4535 InventoryItem *result = m_craftdef->getCraftResult(cpi, this);
4536 //InventoryItem *result = craft_get_result(items, this);
4538 rlist->addItem(result);
4541 } // if creative_mode == false
4544 RemoteClient* Server::getClient(u16 peer_id)
4546 DSTACK(__FUNCTION_NAME);
4547 //JMutexAutoLock lock(m_con_mutex);
4548 core::map<u16, RemoteClient*>::Node *n;
4549 n = m_clients.find(peer_id);
4550 // A client should exist for all peers
4552 return n->getValue();
4555 std::wstring Server::getStatusString()
4557 std::wostringstream os(std::ios_base::binary);
4560 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4562 os<<L", uptime="<<m_uptime.get();
4563 // Information about clients
4565 for(core::map<u16, RemoteClient*>::Iterator
4566 i = m_clients.getIterator();
4567 i.atEnd() == false; i++)
4569 // Get client and check that it is valid
4570 RemoteClient *client = i.getNode()->getValue();
4571 assert(client->peer_id == i.getNode()->getKey());
4572 if(client->serialization_version == SER_FMT_VER_INVALID)
4575 Player *player = m_env->getPlayer(client->peer_id);
4576 // Get name of player
4577 std::wstring name = L"unknown";
4579 name = narrow_to_wide(player->getName());
4580 // Add name to information string
4584 if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
4585 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4586 if(g_settings->get("motd") != "")
4587 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4591 // Saves g_settings to configpath given at initialization
4592 void Server::saveConfig()
4594 if(m_configpath != "")
4595 g_settings->updateConfigFile(m_configpath.c_str());
4598 void Server::notifyPlayer(const char *name, const std::wstring msg)
4600 Player *player = m_env->getPlayer(name);
4603 SendChatMessage(player->peer_id, std::wstring(L"Server: -!- ")+msg);
4606 void Server::notifyPlayers(const std::wstring msg)
4608 BroadcastChatMessage(msg);
4611 void Server::queueBlockEmerge(v3s16 blockpos, bool allow_generate)
4615 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
4616 m_emerge_queue.addBlock(PEER_ID_INEXISTENT, blockpos, flags);
4619 // IGameDef interface
4621 IToolDefManager* Server::getToolDefManager()
4625 INodeDefManager* Server::getNodeDefManager()
4629 ICraftDefManager* Server::getCraftDefManager()
4633 ITextureSource* Server::getTextureSource()
4637 u16 Server::allocateUnknownNodeId(const std::string &name)
4639 return m_nodedef->allocateDummy(name);
4642 IWritableToolDefManager* Server::getWritableToolDefManager()
4646 IWritableNodeDefManager* Server::getWritableNodeDefManager()
4650 IWritableCraftDefManager* Server::getWritableCraftDefManager()
4655 v3f findSpawnPos(ServerMap &map)
4657 //return v3f(50,50,50)*BS;
4662 nodepos = v2s16(0,0);
4667 // Try to find a good place a few times
4668 for(s32 i=0; i<1000; i++)
4671 // We're going to try to throw the player to this position
4672 v2s16 nodepos2d = v2s16(-range + (myrand()%(range*2)),
4673 -range + (myrand()%(range*2)));
4674 //v2s16 sectorpos = getNodeSectorPos(nodepos2d);
4675 // Get ground height at point (fallbacks to heightmap function)
4676 s16 groundheight = map.findGroundLevel(nodepos2d);
4677 // Don't go underwater
4678 if(groundheight < WATER_LEVEL)
4680 //infostream<<"-> Underwater"<<std::endl;
4683 // Don't go to high places
4684 if(groundheight > WATER_LEVEL + 4)
4686 //infostream<<"-> Underwater"<<std::endl;
4690 nodepos = v3s16(nodepos2d.X, groundheight-2, nodepos2d.Y);
4691 bool is_good = false;
4693 for(s32 i=0; i<10; i++){
4694 v3s16 blockpos = getNodeBlockPos(nodepos);
4695 map.emergeBlock(blockpos, true);
4696 MapNode n = map.getNodeNoEx(nodepos);
4697 if(n.getContent() == CONTENT_AIR){
4708 // Found a good place
4709 //infostream<<"Searched through "<<i<<" places."<<std::endl;
4715 return intToFloat(nodepos, BS);
4718 Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id)
4721 Try to get an existing player
4723 Player *player = m_env->getPlayer(name);
4726 // If player is already connected, cancel
4727 if(player->peer_id != 0)
4729 infostream<<"emergePlayer(): Player already connected"<<std::endl;
4734 player->peer_id = peer_id;
4736 // Reset inventory to creative if in creative mode
4737 if(g_settings->getBool("creative_mode"))
4739 // Warning: double code below
4740 // Backup actual inventory
4741 player->inventory_backup = new Inventory();
4742 *(player->inventory_backup) = player->inventory;
4743 // Set creative inventory
4744 craft_set_creative_inventory(player, this);
4751 If player with the wanted peer_id already exists, cancel.
4753 if(m_env->getPlayer(peer_id) != NULL)
4755 infostream<<"emergePlayer(): Player with wrong name but same"
4756 " peer_id already exists"<<std::endl;
4764 // Add authentication stuff
4765 m_authmanager.add(name);
4766 m_authmanager.setPassword(name, password);
4767 m_authmanager.setPrivs(name,
4768 stringToPrivs(g_settings->get("default_privs")));
4770 /* Set player position */
4772 infostream<<"Server: Finding spawn place for player \""
4773 <<name<<"\""<<std::endl;
4775 v3f pos = findSpawnPos(m_env->getServerMap());
4777 player = new ServerRemotePlayer(m_env, pos, peer_id, name);
4779 /* Add player to environment */
4780 m_env->addPlayer(player);
4783 ServerRemotePlayer *srp = (ServerRemotePlayer*)player;
4784 scriptapi_on_newplayer(m_lua, srp);
4786 /* Add stuff to inventory */
4787 if(g_settings->getBool("creative_mode"))
4789 // Warning: double code above
4790 // Backup actual inventory
4791 player->inventory_backup = new Inventory();
4792 *(player->inventory_backup) = player->inventory;
4793 // Set creative inventory
4794 craft_set_creative_inventory(player, this);
4799 } // create new player
4802 void Server::handlePeerChange(PeerChange &c)
4804 JMutexAutoLock envlock(m_env_mutex);
4805 JMutexAutoLock conlock(m_con_mutex);
4807 if(c.type == PEER_ADDED)
4814 core::map<u16, RemoteClient*>::Node *n;
4815 n = m_clients.find(c.peer_id);
4816 // The client shouldn't already exist
4820 RemoteClient *client = new RemoteClient();
4821 client->peer_id = c.peer_id;
4822 m_clients.insert(client->peer_id, client);
4825 else if(c.type == PEER_REMOVED)
4832 core::map<u16, RemoteClient*>::Node *n;
4833 n = m_clients.find(c.peer_id);
4834 // The client should exist
4838 Mark objects to be not known by the client
4840 RemoteClient *client = n->getValue();
4842 for(core::map<u16, bool>::Iterator
4843 i = client->m_known_objects.getIterator();
4844 i.atEnd()==false; i++)
4847 u16 id = i.getNode()->getKey();
4848 ServerActiveObject* obj = m_env->getActiveObject(id);
4850 if(obj && obj->m_known_by_count > 0)
4851 obj->m_known_by_count--;
4854 // Collect information about leaving in chat
4855 std::wstring message;
4857 Player *player = m_env->getPlayer(c.peer_id);
4860 std::wstring name = narrow_to_wide(player->getName());
4863 message += L" left game";
4865 message += L" (timed out)";
4871 m_env->removePlayer(c.peer_id);
4874 // Set player client disconnected
4876 Player *player = m_env->getPlayer(c.peer_id);
4878 player->peer_id = 0;
4885 std::ostringstream os(std::ios_base::binary);
4886 for(core::map<u16, RemoteClient*>::Iterator
4887 i = m_clients.getIterator();
4888 i.atEnd() == false; i++)
4890 RemoteClient *client = i.getNode()->getValue();
4891 assert(client->peer_id == i.getNode()->getKey());
4892 if(client->serialization_version == SER_FMT_VER_INVALID)
4895 Player *player = m_env->getPlayer(client->peer_id);
4898 // Get name of player
4899 os<<player->getName()<<" ";
4902 actionstream<<player->getName()<<" "
4903 <<(c.timeout?"times out.":"leaves game.")
4904 <<" List of players: "
4905 <<os.str()<<std::endl;
4910 delete m_clients[c.peer_id];
4911 m_clients.remove(c.peer_id);
4913 // Send player info to all remaining clients
4916 // Send leave chat message to all remaining clients
4917 BroadcastChatMessage(message);
4926 void Server::handlePeerChanges()
4928 while(m_peer_change_queue.size() > 0)
4930 PeerChange c = m_peer_change_queue.pop_front();
4932 infostream<<"Server: Handling peer change: "
4933 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4936 handlePeerChange(c);
4940 u64 Server::getPlayerPrivs(Player *player)
4944 std::string playername = player->getName();
4945 // Local player gets all privileges regardless of
4946 // what's set on their account.
4947 if(g_settings->get("name") == playername)
4953 return getPlayerAuthPrivs(playername);
4957 void dedicated_server_loop(Server &server, bool &kill)
4959 DSTACK(__FUNCTION_NAME);
4961 infostream<<DTIME<<std::endl;
4962 infostream<<"========================"<<std::endl;
4963 infostream<<"Running dedicated server"<<std::endl;
4964 infostream<<"========================"<<std::endl;
4965 infostream<<std::endl;
4967 IntervalLimiter m_profiler_interval;
4971 // This is kind of a hack but can be done like this
4972 // because server.step() is very light
4974 ScopeProfiler sp(g_profiler, "dedicated server sleep");
4979 if(server.getShutdownRequested() || kill)
4981 infostream<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4988 float profiler_print_interval =
4989 g_settings->getFloat("profiler_print_interval");
4990 if(profiler_print_interval != 0)
4992 if(m_profiler_interval.step(0.030, profiler_print_interval))
4994 infostream<<"Profiler:"<<std::endl;
4995 g_profiler->print(infostream);
4996 g_profiler->clear();
5003 static int counter = 0;
5009 core::list<PlayerInfo> list = server.getPlayerInfo();
5010 core::list<PlayerInfo>::Iterator i;
5011 static u32 sum_old = 0;
5012 u32 sum = PIChecksum(list);
5015 infostream<<DTIME<<"Player info:"<<std::endl;
5016 for(i=list.begin(); i!=list.end(); i++)
5018 i->PrintLine(&infostream);