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"
31 #include "servercommand.h"
34 #include "serverobject.h"
39 #include "scriptapi.h"
44 #include "content_mapnode.h"
45 #include "content_nodemeta.h"
46 #include "content_abm.h"
47 #include "content_sao.h"
52 #include "utility_string.h"
53 #include "sound.h" // dummySoundManager
54 #include "event_manager.h"
57 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
59 #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
61 class MapEditEventIgnorer
64 MapEditEventIgnorer(bool *flag):
73 ~MapEditEventIgnorer()
86 class MapEditEventAreaIgnorer
89 MapEditEventAreaIgnorer(VoxelArea *ignorevariable, const VoxelArea &a):
90 m_ignorevariable(ignorevariable)
92 if(m_ignorevariable->getVolume() == 0)
93 *m_ignorevariable = a;
95 m_ignorevariable = NULL;
98 ~MapEditEventAreaIgnorer()
102 assert(m_ignorevariable->getVolume() != 0);
103 *m_ignorevariable = VoxelArea();
108 VoxelArea *m_ignorevariable;
111 void * ServerThread::Thread()
115 log_register_thread("ServerThread");
117 DSTACK(__FUNCTION_NAME);
119 BEGIN_DEBUG_EXCEPTION_HANDLER
124 //TimeTaker timer("AsyncRunStep() + Receive()");
127 //TimeTaker timer("AsyncRunStep()");
128 m_server->AsyncRunStep();
131 //infostream<<"Running m_server->Receive()"<<std::endl;
134 catch(con::NoIncomingDataException &e)
137 catch(con::PeerNotFoundException &e)
139 infostream<<"Server: PeerNotFoundException"<<std::endl;
141 catch(con::ConnectionBindFailed &e)
143 m_server->setAsyncFatalError(e.what());
147 END_DEBUG_EXCEPTION_HANDLER(errorstream)
152 void * EmergeThread::Thread()
156 log_register_thread("EmergeThread");
158 DSTACK(__FUNCTION_NAME);
160 BEGIN_DEBUG_EXCEPTION_HANDLER
162 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
165 Get block info from queue, emerge them and send them
168 After queue is empty, exit.
172 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
176 SharedPtr<QueuedBlockEmerge> q(qptr);
182 Do not generate over-limit
184 if(blockpos_over_limit(p))
187 //infostream<<"EmergeThread::Thread(): running"<<std::endl;
189 //TimeTaker timer("block emerge");
192 Try to emerge it from somewhere.
194 If it is only wanted as optional, only loading from disk
199 Check if any peer wants it as non-optional. In that case it
202 Also decrement the emerge queue count in clients.
205 bool only_from_disk = true;
208 core::map<u16, u8>::Iterator i;
209 for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
211 //u16 peer_id = i.getNode()->getKey();
214 u8 flags = i.getNode()->getValue();
215 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
216 only_from_disk = false;
221 if(enable_mapgen_debug_info)
222 infostream<<"EmergeThread: p="
223 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
224 <<"only_from_disk="<<only_from_disk<<std::endl;
226 ServerMap &map = ((ServerMap&)m_server->m_env->getMap());
228 MapBlock *block = NULL;
229 bool got_block = true;
230 core::map<v3s16, MapBlock*> modified_blocks;
233 Try to fetch block from memory or disk.
234 If not found and asked to generate, initialize generator.
237 bool started_generate = false;
238 mapgen::BlockMakeData data;
241 JMutexAutoLock envlock(m_server->m_env_mutex);
243 // Load sector if it isn't loaded
244 if(map.getSectorNoGenerateNoEx(p2d) == NULL)
245 map.loadSectorMeta(p2d);
247 // Attempt to load block
248 block = map.getBlockNoCreateNoEx(p);
249 if(!block || block->isDummy() || !block->isGenerated())
251 if(enable_mapgen_debug_info)
252 infostream<<"EmergeThread: not in memory, "
253 <<"attempting to load from disk"<<std::endl;
255 block = map.loadBlock(p);
258 // If could not load and allowed to generate, start generation
259 // inside this same envlock
260 if(only_from_disk == false &&
261 (block == NULL || block->isGenerated() == false)){
262 if(enable_mapgen_debug_info)
263 infostream<<"EmergeThread: generating"<<std::endl;
264 started_generate = true;
266 map.initBlockMake(&data, p);
271 If generator was initialized, generate now when envlock is free.
276 ScopeProfiler sp(g_profiler, "EmergeThread: mapgen::make_block",
278 TimeTaker t("mapgen::make_block()");
280 mapgen::make_block(&data);
282 if(enable_mapgen_debug_info == false)
283 t.stop(true); // Hide output
287 // Lock environment again to access the map
288 JMutexAutoLock envlock(m_server->m_env_mutex);
290 ScopeProfiler sp(g_profiler, "EmergeThread: after "
291 "mapgen::make_block (envlock)", SPT_AVG);
293 // Blit data back on map, update lighting, add mobs and
294 // whatever this does
295 map.finishBlockMake(&data, modified_blocks);
298 block = map.getBlockNoCreateNoEx(p);
300 // If block doesn't exist, don't try doing anything with it
301 // This happens if the block is not in generation boundaries
306 Do some post-generate stuff
309 v3s16 minp = data.blockpos_min*MAP_BLOCKSIZE;
310 v3s16 maxp = data.blockpos_max*MAP_BLOCKSIZE +
311 v3s16(1,1,1)*(MAP_BLOCKSIZE-1);
314 Ignore map edit events, they will not need to be
315 sent to anybody because the block hasn't been sent
318 //MapEditEventIgnorer ign(&m_server->m_ignore_map_edit_events);
319 MapEditEventAreaIgnorer ign(
320 &m_server->m_ignore_map_edit_events_area,
321 VoxelArea(minp, maxp));
323 TimeTaker timer("on_generated");
324 scriptapi_environment_on_generated(m_server->m_lua,
325 minp, maxp, mapgen::get_blockseed(data.seed, minp));
326 /*int t = timer.stop(true);
327 dstream<<"on_generated took "<<t<<"ms"<<std::endl;*/
330 if(enable_mapgen_debug_info)
331 infostream<<"EmergeThread: ended up with: "
332 <<analyze_block(block)<<std::endl;
334 // Activate objects and stuff
335 m_server->m_env->activateBlock(block, 0);
343 Set sent status of modified blocks on clients
346 // NOTE: Server's clients are also behind the connection mutex
347 JMutexAutoLock lock(m_server->m_con_mutex);
350 Add the originally fetched block to the modified list
354 modified_blocks.insert(p, block);
358 Set the modified blocks unsent for all the clients
361 for(core::map<u16, RemoteClient*>::Iterator
362 i = m_server->m_clients.getIterator();
363 i.atEnd() == false; i++)
365 RemoteClient *client = i.getNode()->getValue();
367 if(modified_blocks.size() > 0)
369 // Remove block from sent history
370 client->SetBlocksNotSent(modified_blocks);
376 END_DEBUG_EXCEPTION_HANDLER(errorstream)
378 log_deregister_thread();
383 v3f ServerSoundParams::getPos(ServerEnvironment *env, bool *pos_exists) const
385 if(pos_exists) *pos_exists = false;
390 if(pos_exists) *pos_exists = true;
395 ServerActiveObject *sao = env->getActiveObject(object);
398 if(pos_exists) *pos_exists = true;
399 return sao->getBasePosition(); }
404 void RemoteClient::GetNextBlocks(Server *server, float dtime,
405 core::array<PrioritySortedBlockTransfer> &dest)
407 DSTACK(__FUNCTION_NAME);
410 TimeTaker timer("RemoteClient::GetNextBlocks", &timer_result);*/
413 m_nothing_to_send_pause_timer -= dtime;
414 m_nearest_unsent_reset_timer += dtime;
416 if(m_nothing_to_send_pause_timer >= 0)
421 // Won't send anything if already sending
422 if(m_blocks_sending.size() >= g_settings->getU16
423 ("max_simultaneous_block_sends_per_client"))
425 //infostream<<"Not sending any blocks, Queue full."<<std::endl;
429 //TimeTaker timer("RemoteClient::GetNextBlocks");
431 Player *player = server->m_env->getPlayer(peer_id);
433 assert(player != NULL);
435 v3f playerpos = player->getPosition();
436 v3f playerspeed = player->getSpeed();
437 v3f playerspeeddir(0,0,0);
438 if(playerspeed.getLength() > 1.0*BS)
439 playerspeeddir = playerspeed / playerspeed.getLength();
440 // Predict to next block
441 v3f playerpos_predicted = playerpos + playerspeeddir*MAP_BLOCKSIZE*BS;
443 v3s16 center_nodepos = floatToInt(playerpos_predicted, BS);
445 v3s16 center = getNodeBlockPos(center_nodepos);
447 // Camera position and direction
448 v3f camera_pos = player->getEyePosition();
449 v3f camera_dir = v3f(0,0,1);
450 camera_dir.rotateYZBy(player->getPitch());
451 camera_dir.rotateXZBy(player->getYaw());
453 /*infostream<<"camera_dir=("<<camera_dir.X<<","<<camera_dir.Y<<","
454 <<camera_dir.Z<<")"<<std::endl;*/
457 Get the starting value of the block finder radius.
460 if(m_last_center != center)
462 m_nearest_unsent_d = 0;
463 m_last_center = center;
466 /*infostream<<"m_nearest_unsent_reset_timer="
467 <<m_nearest_unsent_reset_timer<<std::endl;*/
469 // Reset periodically to workaround for some bugs or stuff
470 if(m_nearest_unsent_reset_timer > 20.0)
472 m_nearest_unsent_reset_timer = 0;
473 m_nearest_unsent_d = 0;
474 //infostream<<"Resetting m_nearest_unsent_d for "
475 // <<server->getPlayerName(peer_id)<<std::endl;
478 //s16 last_nearest_unsent_d = m_nearest_unsent_d;
479 s16 d_start = m_nearest_unsent_d;
481 //infostream<<"d_start="<<d_start<<std::endl;
483 u16 max_simul_sends_setting = g_settings->getU16
484 ("max_simultaneous_block_sends_per_client");
485 u16 max_simul_sends_usually = max_simul_sends_setting;
488 Check the time from last addNode/removeNode.
490 Decrease send rate if player is building stuff.
492 m_time_from_building += dtime;
493 if(m_time_from_building < g_settings->getFloat(
494 "full_block_send_enable_min_time_from_building"))
496 max_simul_sends_usually
497 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
501 Number of blocks sending + number of blocks selected for sending
503 u32 num_blocks_selected = m_blocks_sending.size();
506 next time d will be continued from the d from which the nearest
507 unsent block was found this time.
509 This is because not necessarily any of the blocks found this
510 time are actually sent.
512 s32 new_nearest_unsent_d = -1;
514 s16 d_max = g_settings->getS16("max_block_send_distance");
515 s16 d_max_gen = g_settings->getS16("max_block_generate_distance");
517 // Don't loop very much at a time
518 s16 max_d_increment_at_time = 2;
519 if(d_max > d_start + max_d_increment_at_time)
520 d_max = d_start + max_d_increment_at_time;
521 /*if(d_max_gen > d_start+2)
522 d_max_gen = d_start+2;*/
524 //infostream<<"Starting from "<<d_start<<std::endl;
526 s32 nearest_emerged_d = -1;
527 s32 nearest_emergefull_d = -1;
528 s32 nearest_sent_d = -1;
529 bool queue_is_full = false;
532 for(d = d_start; d <= d_max; d++)
534 /*errorstream<<"checking d="<<d<<" for "
535 <<server->getPlayerName(peer_id)<<std::endl;*/
536 //infostream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
539 If m_nearest_unsent_d was changed by the EmergeThread
540 (it can change it to 0 through SetBlockNotSent),
542 Else update m_nearest_unsent_d
544 /*if(m_nearest_unsent_d != last_nearest_unsent_d)
546 d = m_nearest_unsent_d;
547 last_nearest_unsent_d = m_nearest_unsent_d;
551 Get the border/face dot coordinates of a "d-radiused"
554 core::list<v3s16> list;
555 getFacePositions(list, d);
557 core::list<v3s16>::Iterator li;
558 for(li=list.begin(); li!=list.end(); li++)
560 v3s16 p = *li + center;
564 - Don't allow too many simultaneous transfers
565 - EXCEPT when the blocks are very close
567 Also, don't send blocks that are already flying.
570 // Start with the usual maximum
571 u16 max_simul_dynamic = max_simul_sends_usually;
573 // If block is very close, allow full maximum
574 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
575 max_simul_dynamic = max_simul_sends_setting;
577 // Don't select too many blocks for sending
578 if(num_blocks_selected >= max_simul_dynamic)
580 queue_is_full = true;
581 goto queue_full_break;
584 // Don't send blocks that are currently being transferred
585 if(m_blocks_sending.find(p) != NULL)
591 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
592 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
593 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
594 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
595 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
596 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
599 // If this is true, inexistent block will be made from scratch
600 bool generate = d <= d_max_gen;
603 /*// Limit the generating area vertically to 2/3
604 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
607 // Limit the send area vertically to 1/2
608 if(abs(p.Y - center.Y) > d_max / 2)
614 If block is far away, don't generate it unless it is
620 // Block center y in nodes
621 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
622 // Don't generate if it's very high or very low
623 if(y < -64 || y > 64)
627 v2s16 p2d_nodes_center(
631 // Get ground height in nodes
632 s16 gh = server->m_env->getServerMap().findGroundLevel(
635 // If differs a lot, don't generate
636 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
638 // Actually, don't even send it
644 //infostream<<"d="<<d<<std::endl;
647 Don't generate or send if not in sight
648 FIXME This only works if the client uses a small enough
649 FOV setting. The default of 72 degrees is fine.
652 float camera_fov = (72.0*PI/180) * 4./3.;
653 if(isBlockInSight(p, camera_pos, camera_dir, camera_fov, 10000*BS) == false)
659 Don't send already sent blocks
662 if(m_blocks_sent.find(p) != NULL)
669 Check if map has this block
671 MapBlock *block = server->m_env->getMap().getBlockNoCreateNoEx(p);
673 bool surely_not_found_on_disk = false;
674 bool block_is_invalid = false;
677 // Reset usage timer, this block will be of use in the future.
678 block->resetUsageTimer();
680 // Block is dummy if data doesn't exist.
681 // It means it has been not found from disk and not generated
684 surely_not_found_on_disk = true;
687 // Block is valid if lighting is up-to-date and data exists
688 if(block->isValid() == false)
690 block_is_invalid = true;
693 /*if(block->isFullyGenerated() == false)
695 block_is_invalid = true;
700 ServerMap *map = (ServerMap*)(&server->m_env->getMap());
701 v2s16 chunkpos = map->sector_to_chunk(p2d);
702 if(map->chunkNonVolatile(chunkpos) == false)
703 block_is_invalid = true;
705 if(block->isGenerated() == false)
706 block_is_invalid = true;
709 If block is not close, don't send it unless it is near
712 Block is near ground level if night-time mesh
713 differs from day-time mesh.
717 if(block->getDayNightDiff() == false)
724 If block has been marked to not exist on disk (dummy)
725 and generating new ones is not wanted, skip block.
727 if(generate == false && surely_not_found_on_disk == true)
734 Add inexistent block to emerge queue.
736 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
738 //TODO: Get value from somewhere
739 // Allow only one block in emerge queue
740 //if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
741 // Allow two blocks in queue per client
742 //if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
744 // Make it more responsive when needing to generate stuff
745 if(surely_not_found_on_disk)
747 if(server->m_emerge_queue.peerItemCount(peer_id) < max_emerge)
749 //infostream<<"Adding block to emerge queue"<<std::endl;
751 // Add it to the emerge queue and trigger the thread
754 if(generate == false)
755 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
757 server->m_emerge_queue.addBlock(peer_id, p, flags);
758 server->m_emergethread.trigger();
760 if(nearest_emerged_d == -1)
761 nearest_emerged_d = d;
763 if(nearest_emergefull_d == -1)
764 nearest_emergefull_d = d;
771 if(nearest_sent_d == -1)
775 Add block to send queue
778 /*errorstream<<"sending from d="<<d<<" to "
779 <<server->getPlayerName(peer_id)<<std::endl;*/
781 PrioritySortedBlockTransfer q((float)d, p, peer_id);
785 num_blocks_selected += 1;
790 //infostream<<"Stopped at "<<d<<std::endl;
792 // If nothing was found for sending and nothing was queued for
793 // emerging, continue next time browsing from here
794 if(nearest_emerged_d != -1){
795 new_nearest_unsent_d = nearest_emerged_d;
796 } else if(nearest_emergefull_d != -1){
797 new_nearest_unsent_d = nearest_emergefull_d;
799 if(d > g_settings->getS16("max_block_send_distance")){
800 new_nearest_unsent_d = 0;
801 m_nothing_to_send_pause_timer = 2.0;
802 /*infostream<<"GetNextBlocks(): d wrapped around for "
803 <<server->getPlayerName(peer_id)
804 <<"; setting to 0 and pausing"<<std::endl;*/
806 if(nearest_sent_d != -1)
807 new_nearest_unsent_d = nearest_sent_d;
809 new_nearest_unsent_d = d;
813 if(new_nearest_unsent_d != -1)
814 m_nearest_unsent_d = new_nearest_unsent_d;
816 /*timer_result = timer.stop(true);
817 if(timer_result != 0)
818 infostream<<"GetNextBlocks duration: "<<timer_result<<" (!=0)"<<std::endl;*/
821 void RemoteClient::GotBlock(v3s16 p)
823 if(m_blocks_sending.find(p) != NULL)
824 m_blocks_sending.remove(p);
827 /*infostream<<"RemoteClient::GotBlock(): Didn't find in"
828 " m_blocks_sending"<<std::endl;*/
829 m_excess_gotblocks++;
831 m_blocks_sent.insert(p, true);
834 void RemoteClient::SentBlock(v3s16 p)
836 if(m_blocks_sending.find(p) == NULL)
837 m_blocks_sending.insert(p, 0.0);
839 infostream<<"RemoteClient::SentBlock(): Sent block"
840 " already in m_blocks_sending"<<std::endl;
843 void RemoteClient::SetBlockNotSent(v3s16 p)
845 m_nearest_unsent_d = 0;
847 if(m_blocks_sending.find(p) != NULL)
848 m_blocks_sending.remove(p);
849 if(m_blocks_sent.find(p) != NULL)
850 m_blocks_sent.remove(p);
853 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
855 m_nearest_unsent_d = 0;
857 for(core::map<v3s16, MapBlock*>::Iterator
858 i = blocks.getIterator();
859 i.atEnd()==false; i++)
861 v3s16 p = i.getNode()->getKey();
863 if(m_blocks_sending.find(p) != NULL)
864 m_blocks_sending.remove(p);
865 if(m_blocks_sent.find(p) != NULL)
866 m_blocks_sent.remove(p);
874 PlayerInfo::PlayerInfo()
880 void PlayerInfo::PrintLine(std::ostream *s)
883 (*s)<<"\""<<name<<"\" ("
884 <<(position.X/10)<<","<<(position.Y/10)
885 <<","<<(position.Z/10)<<") ";
887 (*s)<<" avg_rtt="<<avg_rtt;
896 const std::string &path_world,
897 const std::string &path_config,
898 const SubgameSpec &gamespec,
899 bool simple_singleplayer_mode
901 m_path_world(path_world),
902 m_path_config(path_config),
903 m_gamespec(gamespec),
904 m_simple_singleplayer_mode(simple_singleplayer_mode),
905 m_async_fatal_error(""),
907 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
908 m_authmanager(path_world+DIR_DELIM+"auth.txt"),
909 m_banmanager(path_world+DIR_DELIM+"ipban.txt"),
911 m_itemdef(createItemDefManager()),
912 m_nodedef(createNodeDefManager()),
913 m_craftdef(createCraftDefManager()),
914 m_event(new EventManager()),
916 m_emergethread(this),
917 m_time_of_day_send_timer(0),
919 m_shutdown_requested(false),
920 m_ignore_map_edit_events(false),
921 m_ignore_map_edit_events_peer_id(0)
923 m_liquid_transform_timer = 0.0;
924 m_print_info_timer = 0.0;
925 m_objectdata_timer = 0.0;
926 m_emergethread_trigger_timer = 0.0;
927 m_savemap_timer = 0.0;
931 m_step_dtime_mutex.Init();
935 throw ServerError("Supplied empty world path");
937 if(!gamespec.isValid())
938 throw ServerError("Supplied invalid gamespec");
940 infostream<<"Server created for gameid \""<<m_gamespec.id<<"\"";
941 if(m_simple_singleplayer_mode)
942 infostream<<" in simple singleplayer mode"<<std::endl;
944 infostream<<std::endl;
945 infostream<<"- world: "<<m_path_world<<std::endl;
946 infostream<<"- config: "<<m_path_config<<std::endl;
947 infostream<<"- game: "<<m_gamespec.path<<std::endl;
949 // Add world mod search path
950 m_modspaths.push_front(m_path_world + DIR_DELIM + "worldmods");
951 // Add addon mod search path
952 for(std::set<std::string>::const_iterator i = m_gamespec.mods_paths.begin();
953 i != m_gamespec.mods_paths.end(); i++)
954 m_modspaths.push_front((*i));
956 // Print out mod search paths
957 for(core::list<std::string>::Iterator i = m_modspaths.begin();
958 i != m_modspaths.end(); i++){
959 std::string modspath = *i;
960 infostream<<"- mods: "<<modspath<<std::endl;
963 // Path to builtin.lua
964 std::string builtinpath = porting::path_share + DIR_DELIM + "builtin"
965 + DIR_DELIM + "builtin.lua";
967 // Create world if it doesn't exist
968 if(!initializeWorld(m_path_world, m_gamespec.id))
969 throw ServerError("Failed to initialize world");
972 JMutexAutoLock envlock(m_env_mutex);
973 JMutexAutoLock conlock(m_con_mutex);
975 // Initialize scripting
977 infostream<<"Server: Initializing Lua"<<std::endl;
978 m_lua = script_init();
981 scriptapi_export(m_lua, this);
982 // Load and run builtin.lua
983 infostream<<"Server: Loading builtin.lua [\""
984 <<builtinpath<<"\"]"<<std::endl;
985 bool success = scriptapi_loadmod(m_lua, builtinpath, "__builtin");
987 errorstream<<"Server: Failed to load and run "
988 <<builtinpath<<std::endl;
989 throw ModError("Failed to load and run "+builtinpath);
991 // Find mods in mod search paths
992 m_mods = getMods(m_modspaths);
994 infostream<<"Server: Loading mods: ";
995 for(core::list<ModSpec>::Iterator i = m_mods.begin();
996 i != m_mods.end(); i++){
997 const ModSpec &mod = *i;
998 infostream<<mod.name<<" ";
1000 infostream<<std::endl;
1001 // Load and run "mod" scripts
1002 for(core::list<ModSpec>::Iterator i = m_mods.begin();
1003 i != m_mods.end(); i++){
1004 const ModSpec &mod = *i;
1005 std::string scriptpath = mod.path + DIR_DELIM + "init.lua";
1006 infostream<<" ["<<padStringRight(mod.name, 12)<<"] [\""
1007 <<scriptpath<<"\"]"<<std::endl;
1008 bool success = scriptapi_loadmod(m_lua, scriptpath, mod.name);
1010 errorstream<<"Server: Failed to load and run "
1011 <<scriptpath<<std::endl;
1012 throw ModError("Failed to load and run "+scriptpath);
1016 // Read Textures and calculate sha1 sums
1019 // Apply item aliases in the node definition manager
1020 m_nodedef->updateAliases(m_itemdef);
1022 // Initialize Environment
1024 m_env = new ServerEnvironment(new ServerMap(path_world, this), m_lua,
1027 // Give environment reference to scripting api
1028 scriptapi_add_environment(m_lua, m_env);
1030 // Register us to receive map edit events
1031 m_env->getMap().addEventReceiver(this);
1033 // If file exists, load environment metadata
1034 if(fs::PathExists(m_path_world+DIR_DELIM+"env_meta.txt"))
1036 infostream<<"Server: Loading environment metadata"<<std::endl;
1037 m_env->loadMeta(m_path_world);
1041 infostream<<"Server: Loading players"<<std::endl;
1042 m_env->deSerializePlayers(m_path_world);
1045 Add some test ActiveBlockModifiers to environment
1047 add_legacy_abms(m_env, m_nodedef);
1052 infostream<<"Server destructing"<<std::endl;
1055 Send shutdown message
1058 JMutexAutoLock conlock(m_con_mutex);
1060 std::wstring line = L"*** Server shutting down";
1063 Send the message to clients
1065 for(core::map<u16, RemoteClient*>::Iterator
1066 i = m_clients.getIterator();
1067 i.atEnd() == false; i++)
1069 // Get client and check that it is valid
1070 RemoteClient *client = i.getNode()->getValue();
1071 assert(client->peer_id == i.getNode()->getKey());
1072 if(client->serialization_version == SER_FMT_VER_INVALID)
1076 SendChatMessage(client->peer_id, line);
1078 catch(con::PeerNotFoundException &e)
1084 JMutexAutoLock envlock(m_env_mutex);
1089 infostream<<"Server: Saving players"<<std::endl;
1090 m_env->serializePlayers(m_path_world);
1093 Save environment metadata
1095 infostream<<"Server: Saving environment metadata"<<std::endl;
1096 m_env->saveMeta(m_path_world);
1108 JMutexAutoLock clientslock(m_con_mutex);
1110 for(core::map<u16, RemoteClient*>::Iterator
1111 i = m_clients.getIterator();
1112 i.atEnd() == false; i++)
1115 // NOTE: These are removed by env destructor
1117 u16 peer_id = i.getNode()->getKey();
1118 JMutexAutoLock envlock(m_env_mutex);
1119 m_env->removePlayer(peer_id);
1123 delete i.getNode()->getValue();
1127 // Delete things in the reverse order of creation
1134 // Deinitialize scripting
1135 infostream<<"Server: Deinitializing scripting"<<std::endl;
1136 script_deinit(m_lua);
1139 void Server::start(unsigned short port)
1141 DSTACK(__FUNCTION_NAME);
1142 infostream<<"Starting server on port "<<port<<"..."<<std::endl;
1144 // Stop thread if already running
1147 // Initialize connection
1148 m_con.SetTimeoutMs(30);
1152 m_thread.setRun(true);
1155 // ASCII art for the win!
1157 <<" .__ __ __ "<<std::endl
1158 <<" _____ |__| ____ _____/ |_ ____ _______/ |_ "<<std::endl
1159 <<" / \\| |/ \\_/ __ \\ __\\/ __ \\ / ___/\\ __\\"<<std::endl
1160 <<"| Y Y \\ | | \\ ___/| | \\ ___/ \\___ \\ | | "<<std::endl
1161 <<"|__|_| /__|___| /\\___ >__| \\___ >____ > |__| "<<std::endl
1162 <<" \\/ \\/ \\/ \\/ \\/ "<<std::endl;
1163 actionstream<<"World at ["<<m_path_world<<"]"<<std::endl;
1164 actionstream<<"Server for gameid=\""<<m_gamespec.id
1165 <<"\" listening on port "<<port<<"."<<std::endl;
1170 DSTACK(__FUNCTION_NAME);
1172 infostream<<"Server: Stopping and waiting threads"<<std::endl;
1174 // Stop threads (set run=false first so both start stopping)
1175 m_thread.setRun(false);
1176 m_emergethread.setRun(false);
1178 m_emergethread.stop();
1180 infostream<<"Server: Threads stopped"<<std::endl;
1183 void Server::step(float dtime)
1185 DSTACK(__FUNCTION_NAME);
1190 JMutexAutoLock lock(m_step_dtime_mutex);
1191 m_step_dtime += dtime;
1193 // Throw if fatal error occurred in thread
1194 std::string async_err = m_async_fatal_error.get();
1195 if(async_err != ""){
1196 throw ServerError(async_err);
1200 void Server::AsyncRunStep()
1202 DSTACK(__FUNCTION_NAME);
1204 g_profiler->add("Server::AsyncRunStep (num)", 1);
1208 JMutexAutoLock lock1(m_step_dtime_mutex);
1209 dtime = m_step_dtime;
1213 // Send blocks to clients
1220 g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
1222 //infostream<<"Server steps "<<dtime<<std::endl;
1223 //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1226 JMutexAutoLock lock1(m_step_dtime_mutex);
1227 m_step_dtime -= dtime;
1234 m_uptime.set(m_uptime.get() + dtime);
1238 // Process connection's timeouts
1239 JMutexAutoLock lock2(m_con_mutex);
1240 ScopeProfiler sp(g_profiler, "Server: connection timeout processing");
1241 m_con.RunTimeouts(dtime);
1245 // This has to be called so that the client list gets synced
1246 // with the peer list of the connection
1247 handlePeerChanges();
1251 Update time of day and overall game time
1254 JMutexAutoLock envlock(m_env_mutex);
1256 m_env->setTimeOfDaySpeed(g_settings->getFloat("time_speed"));
1259 Send to clients at constant intervals
1262 m_time_of_day_send_timer -= dtime;
1263 if(m_time_of_day_send_timer < 0.0)
1265 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
1267 //JMutexAutoLock envlock(m_env_mutex);
1268 JMutexAutoLock conlock(m_con_mutex);
1270 for(core::map<u16, RemoteClient*>::Iterator
1271 i = m_clients.getIterator();
1272 i.atEnd() == false; i++)
1274 RemoteClient *client = i.getNode()->getValue();
1275 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1276 m_env->getTimeOfDay(), g_settings->getFloat("time_speed"));
1278 m_con.Send(client->peer_id, 0, data, true);
1284 JMutexAutoLock lock(m_env_mutex);
1286 ScopeProfiler sp(g_profiler, "SEnv step");
1287 ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
1291 const float map_timer_and_unload_dtime = 2.92;
1292 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
1294 JMutexAutoLock lock(m_env_mutex);
1295 // Run Map's timers and unload unused data
1296 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
1297 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
1298 g_settings->getFloat("server_unload_unused_data_timeout"));
1309 JMutexAutoLock lock(m_env_mutex);
1310 JMutexAutoLock lock2(m_con_mutex);
1312 ScopeProfiler sp(g_profiler, "Server: handle players");
1314 for(core::map<u16, RemoteClient*>::Iterator
1315 i = m_clients.getIterator();
1316 i.atEnd() == false; i++)
1318 RemoteClient *client = i.getNode()->getValue();
1319 PlayerSAO *playersao = getPlayerSAO(client->peer_id);
1320 if(playersao == NULL)
1324 Handle player HPs (die if hp=0)
1326 if(playersao->getHP() == 0 && playersao->m_hp_not_sent)
1327 DiePlayer(client->peer_id);
1330 Send player inventories and HPs if necessary
1332 if(playersao->m_teleported){
1333 SendMovePlayer(client->peer_id);
1334 playersao->m_teleported = false;
1336 if(playersao->m_inventory_not_sent){
1337 UpdateCrafting(client->peer_id);
1338 SendInventory(client->peer_id);
1340 if(playersao->m_hp_not_sent){
1341 SendPlayerHP(client->peer_id);
1346 /* Transform liquids */
1347 m_liquid_transform_timer += dtime;
1348 if(m_liquid_transform_timer >= 1.00)
1350 m_liquid_transform_timer -= 1.00;
1352 JMutexAutoLock lock(m_env_mutex);
1354 ScopeProfiler sp(g_profiler, "Server: liquid transform");
1356 core::map<v3s16, MapBlock*> modified_blocks;
1357 m_env->getMap().transformLiquids(modified_blocks);
1362 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1363 ServerMap &map = ((ServerMap&)m_env->getMap());
1364 map.updateLighting(modified_blocks, lighting_modified_blocks);
1366 // Add blocks modified by lighting to modified_blocks
1367 for(core::map<v3s16, MapBlock*>::Iterator
1368 i = lighting_modified_blocks.getIterator();
1369 i.atEnd() == false; i++)
1371 MapBlock *block = i.getNode()->getValue();
1372 modified_blocks.insert(block->getPos(), block);
1376 Set the modified blocks unsent for all the clients
1379 JMutexAutoLock lock2(m_con_mutex);
1381 for(core::map<u16, RemoteClient*>::Iterator
1382 i = m_clients.getIterator();
1383 i.atEnd() == false; i++)
1385 RemoteClient *client = i.getNode()->getValue();
1387 if(modified_blocks.size() > 0)
1389 // Remove block from sent history
1390 client->SetBlocksNotSent(modified_blocks);
1395 // Periodically print some info
1397 float &counter = m_print_info_timer;
1403 JMutexAutoLock lock2(m_con_mutex);
1405 if(m_clients.size() != 0)
1406 infostream<<"Players:"<<std::endl;
1407 for(core::map<u16, RemoteClient*>::Iterator
1408 i = m_clients.getIterator();
1409 i.atEnd() == false; i++)
1411 //u16 peer_id = i.getNode()->getKey();
1412 RemoteClient *client = i.getNode()->getValue();
1413 Player *player = m_env->getPlayer(client->peer_id);
1416 infostream<<"* "<<player->getName()<<"\t";
1417 client->PrintInfo(infostream);
1422 //if(g_settings->getBool("enable_experimental"))
1426 Check added and deleted active objects
1429 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
1430 JMutexAutoLock envlock(m_env_mutex);
1431 JMutexAutoLock conlock(m_con_mutex);
1433 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
1435 // Radius inside which objects are active
1436 s16 radius = g_settings->getS16("active_object_send_range_blocks");
1437 radius *= MAP_BLOCKSIZE;
1439 for(core::map<u16, RemoteClient*>::Iterator
1440 i = m_clients.getIterator();
1441 i.atEnd() == false; i++)
1443 RemoteClient *client = i.getNode()->getValue();
1445 // If definitions and textures have not been sent, don't
1446 // send objects either
1447 if(!client->definitions_sent)
1450 Player *player = m_env->getPlayer(client->peer_id);
1453 // This can happen if the client timeouts somehow
1454 /*infostream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
1456 <<" has no associated player"<<std::endl;*/
1459 v3s16 pos = floatToInt(player->getPosition(), BS);
1461 core::map<u16, bool> removed_objects;
1462 core::map<u16, bool> added_objects;
1463 m_env->getRemovedActiveObjects(pos, radius,
1464 client->m_known_objects, removed_objects);
1465 m_env->getAddedActiveObjects(pos, radius,
1466 client->m_known_objects, added_objects);
1468 // Ignore if nothing happened
1469 if(removed_objects.size() == 0 && added_objects.size() == 0)
1471 //infostream<<"active objects: none changed"<<std::endl;
1475 std::string data_buffer;
1479 // Handle removed objects
1480 writeU16((u8*)buf, removed_objects.size());
1481 data_buffer.append(buf, 2);
1482 for(core::map<u16, bool>::Iterator
1483 i = removed_objects.getIterator();
1484 i.atEnd()==false; i++)
1487 u16 id = i.getNode()->getKey();
1488 ServerActiveObject* obj = m_env->getActiveObject(id);
1490 // Add to data buffer for sending
1491 writeU16((u8*)buf, i.getNode()->getKey());
1492 data_buffer.append(buf, 2);
1494 // Remove from known objects
1495 client->m_known_objects.remove(i.getNode()->getKey());
1497 if(obj && obj->m_known_by_count > 0)
1498 obj->m_known_by_count--;
1501 // Handle added objects
1502 writeU16((u8*)buf, added_objects.size());
1503 data_buffer.append(buf, 2);
1504 for(core::map<u16, bool>::Iterator
1505 i = added_objects.getIterator();
1506 i.atEnd()==false; i++)
1509 u16 id = i.getNode()->getKey();
1510 ServerActiveObject* obj = m_env->getActiveObject(id);
1513 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1515 infostream<<"WARNING: "<<__FUNCTION_NAME
1516 <<": NULL object"<<std::endl;
1518 type = obj->getSendType();
1520 // Add to data buffer for sending
1521 writeU16((u8*)buf, id);
1522 data_buffer.append(buf, 2);
1523 writeU8((u8*)buf, type);
1524 data_buffer.append(buf, 1);
1527 data_buffer.append(serializeLongString(
1528 obj->getClientInitializationData()));
1530 data_buffer.append(serializeLongString(""));
1532 // Add to known objects
1533 client->m_known_objects.insert(i.getNode()->getKey(), false);
1536 obj->m_known_by_count++;
1540 SharedBuffer<u8> reply(2 + data_buffer.size());
1541 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1542 memcpy((char*)&reply[2], data_buffer.c_str(),
1543 data_buffer.size());
1545 m_con.Send(client->peer_id, 0, reply, true);
1547 verbosestream<<"Server: Sent object remove/add: "
1548 <<removed_objects.size()<<" removed, "
1549 <<added_objects.size()<<" added, "
1550 <<"packet size is "<<reply.getSize()<<std::endl;
1555 Collect a list of all the objects known by the clients
1556 and report it back to the environment.
1559 core::map<u16, bool> all_known_objects;
1561 for(core::map<u16, RemoteClient*>::Iterator
1562 i = m_clients.getIterator();
1563 i.atEnd() == false; i++)
1565 RemoteClient *client = i.getNode()->getValue();
1566 // Go through all known objects of client
1567 for(core::map<u16, bool>::Iterator
1568 i = client->m_known_objects.getIterator();
1569 i.atEnd()==false; i++)
1571 u16 id = i.getNode()->getKey();
1572 all_known_objects[id] = true;
1576 m_env->setKnownActiveObjects(whatever);
1582 Send object messages
1585 JMutexAutoLock envlock(m_env_mutex);
1586 JMutexAutoLock conlock(m_con_mutex);
1588 ScopeProfiler sp(g_profiler, "Server: sending object messages");
1591 // Value = data sent by object
1592 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1594 // Get active object messages from environment
1597 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
1601 core::list<ActiveObjectMessage>* message_list = NULL;
1602 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1603 n = buffered_messages.find(aom.id);
1606 message_list = new core::list<ActiveObjectMessage>;
1607 buffered_messages.insert(aom.id, message_list);
1611 message_list = n->getValue();
1613 message_list->push_back(aom);
1616 // Route data to every client
1617 for(core::map<u16, RemoteClient*>::Iterator
1618 i = m_clients.getIterator();
1619 i.atEnd()==false; i++)
1621 RemoteClient *client = i.getNode()->getValue();
1622 std::string reliable_data;
1623 std::string unreliable_data;
1624 // Go through all objects in message buffer
1625 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1626 j = buffered_messages.getIterator();
1627 j.atEnd()==false; j++)
1629 // If object is not known by client, skip it
1630 u16 id = j.getNode()->getKey();
1631 if(client->m_known_objects.find(id) == NULL)
1633 // Get message list of object
1634 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1635 // Go through every message
1636 for(core::list<ActiveObjectMessage>::Iterator
1637 k = list->begin(); k != list->end(); k++)
1639 // Compose the full new data with header
1640 ActiveObjectMessage aom = *k;
1641 std::string new_data;
1644 writeU16((u8*)&buf[0], aom.id);
1645 new_data.append(buf, 2);
1647 new_data += serializeString(aom.datastring);
1648 // Add data to buffer
1650 reliable_data += new_data;
1652 unreliable_data += new_data;
1656 reliable_data and unreliable_data are now ready.
1659 if(reliable_data.size() > 0)
1661 SharedBuffer<u8> reply(2 + reliable_data.size());
1662 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1663 memcpy((char*)&reply[2], reliable_data.c_str(),
1664 reliable_data.size());
1666 m_con.Send(client->peer_id, 0, reply, true);
1668 if(unreliable_data.size() > 0)
1670 SharedBuffer<u8> reply(2 + unreliable_data.size());
1671 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1672 memcpy((char*)&reply[2], unreliable_data.c_str(),
1673 unreliable_data.size());
1674 // Send as unreliable
1675 m_con.Send(client->peer_id, 0, reply, false);
1678 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1680 infostream<<"Server: Size of object message data: "
1681 <<"reliable: "<<reliable_data.size()
1682 <<", unreliable: "<<unreliable_data.size()
1687 // Clear buffered_messages
1688 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1689 i = buffered_messages.getIterator();
1690 i.atEnd()==false; i++)
1692 delete i.getNode()->getValue();
1696 } // enable_experimental
1699 Send queued-for-sending map edit events.
1702 // We will be accessing the environment and the connection
1703 JMutexAutoLock lock(m_env_mutex);
1704 JMutexAutoLock conlock(m_con_mutex);
1706 // Don't send too many at a time
1709 // Single change sending is disabled if queue size is not small
1710 bool disable_single_change_sending = false;
1711 if(m_unsent_map_edit_queue.size() >= 4)
1712 disable_single_change_sending = true;
1714 int event_count = m_unsent_map_edit_queue.size();
1716 // We'll log the amount of each
1719 while(m_unsent_map_edit_queue.size() != 0)
1721 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1723 // Players far away from the change are stored here.
1724 // Instead of sending the changes, MapBlocks are set not sent
1726 core::list<u16> far_players;
1728 if(event->type == MEET_ADDNODE)
1730 //infostream<<"Server: MEET_ADDNODE"<<std::endl;
1731 prof.add("MEET_ADDNODE", 1);
1732 if(disable_single_change_sending)
1733 sendAddNode(event->p, event->n, event->already_known_by_peer,
1736 sendAddNode(event->p, event->n, event->already_known_by_peer,
1739 else if(event->type == MEET_REMOVENODE)
1741 //infostream<<"Server: MEET_REMOVENODE"<<std::endl;
1742 prof.add("MEET_REMOVENODE", 1);
1743 if(disable_single_change_sending)
1744 sendRemoveNode(event->p, event->already_known_by_peer,
1747 sendRemoveNode(event->p, event->already_known_by_peer,
1750 else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
1752 infostream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
1753 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
1754 setBlockNotSent(event->p);
1756 else if(event->type == MEET_OTHER)
1758 infostream<<"Server: MEET_OTHER"<<std::endl;
1759 prof.add("MEET_OTHER", 1);
1760 for(core::map<v3s16, bool>::Iterator
1761 i = event->modified_blocks.getIterator();
1762 i.atEnd()==false; i++)
1764 v3s16 p = i.getNode()->getKey();
1770 prof.add("unknown", 1);
1771 infostream<<"WARNING: Server: Unknown MapEditEvent "
1772 <<((u32)event->type)<<std::endl;
1776 Set blocks not sent to far players
1778 if(far_players.size() > 0)
1780 // Convert list format to that wanted by SetBlocksNotSent
1781 core::map<v3s16, MapBlock*> modified_blocks2;
1782 for(core::map<v3s16, bool>::Iterator
1783 i = event->modified_blocks.getIterator();
1784 i.atEnd()==false; i++)
1786 v3s16 p = i.getNode()->getKey();
1787 modified_blocks2.insert(p,
1788 m_env->getMap().getBlockNoCreateNoEx(p));
1790 // Set blocks not sent
1791 for(core::list<u16>::Iterator
1792 i = far_players.begin();
1793 i != far_players.end(); i++)
1796 RemoteClient *client = getClient(peer_id);
1799 client->SetBlocksNotSent(modified_blocks2);
1805 /*// Don't send too many at a time
1807 if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
1811 if(event_count >= 5){
1812 infostream<<"Server: MapEditEvents:"<<std::endl;
1813 prof.print(infostream);
1814 } else if(event_count != 0){
1815 verbosestream<<"Server: MapEditEvents:"<<std::endl;
1816 prof.print(verbosestream);
1822 Trigger emergethread (it somehow gets to a non-triggered but
1823 bysy state sometimes)
1826 float &counter = m_emergethread_trigger_timer;
1832 m_emergethread.trigger();
1836 // Save map, players and auth stuff
1838 float &counter = m_savemap_timer;
1840 if(counter >= g_settings->getFloat("server_map_save_interval"))
1843 JMutexAutoLock lock(m_env_mutex);
1845 ScopeProfiler sp(g_profiler, "Server: saving stuff");
1848 if(m_authmanager.isModified())
1849 m_authmanager.save();
1852 if(m_banmanager.isModified())
1853 m_banmanager.save();
1855 // Save changed parts of map
1856 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
1859 m_env->serializePlayers(m_path_world);
1861 // Save environment metadata
1862 m_env->saveMeta(m_path_world);
1867 void Server::Receive()
1869 DSTACK(__FUNCTION_NAME);
1870 SharedBuffer<u8> data;
1875 JMutexAutoLock conlock(m_con_mutex);
1876 datasize = m_con.Receive(peer_id, data);
1879 // This has to be called so that the client list gets synced
1880 // with the peer list of the connection
1881 handlePeerChanges();
1883 ProcessData(*data, datasize, peer_id);
1885 catch(con::InvalidIncomingDataException &e)
1887 infostream<<"Server::Receive(): "
1888 "InvalidIncomingDataException: what()="
1889 <<e.what()<<std::endl;
1891 catch(con::PeerNotFoundException &e)
1893 //NOTE: This is not needed anymore
1895 // The peer has been disconnected.
1896 // Find the associated player and remove it.
1898 /*JMutexAutoLock envlock(m_env_mutex);
1900 infostream<<"ServerThread: peer_id="<<peer_id
1901 <<" has apparently closed connection. "
1902 <<"Removing player."<<std::endl;
1904 m_env->removePlayer(peer_id);*/
1908 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1910 DSTACK(__FUNCTION_NAME);
1911 // Environment is locked first.
1912 JMutexAutoLock envlock(m_env_mutex);
1913 JMutexAutoLock conlock(m_con_mutex);
1915 ScopeProfiler sp(g_profiler, "Server::ProcessData");
1918 Address address = m_con.GetPeerAddress(peer_id);
1919 std::string addr_s = address.serializeString();
1921 // drop player if is ip is banned
1922 if(m_banmanager.isIpBanned(addr_s)){
1923 infostream<<"Server: A banned client tried to connect from "
1924 <<addr_s<<"; banned name was "
1925 <<m_banmanager.getBanName(addr_s)<<std::endl;
1926 // This actually doesn't seem to transfer to the client
1927 SendAccessDenied(m_con, peer_id,
1928 L"Your ip is banned. Banned name was "
1929 +narrow_to_wide(m_banmanager.getBanName(addr_s)));
1930 m_con.DeletePeer(peer_id);
1934 catch(con::PeerNotFoundException &e)
1936 infostream<<"Server::ProcessData(): Cancelling: peer "
1937 <<peer_id<<" not found"<<std::endl;
1941 std::string addr_s = m_con.GetPeerAddress(peer_id).serializeString();
1943 u8 peer_ser_ver = getClient(peer_id)->serialization_version;
1951 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1953 if(command == TOSERVER_INIT)
1955 // [0] u16 TOSERVER_INIT
1956 // [2] u8 SER_FMT_VER_HIGHEST
1957 // [3] u8[20] player_name
1958 // [23] u8[28] password <--- can be sent without this, from old versions
1960 if(datasize < 2+1+PLAYERNAME_SIZE)
1963 verbosestream<<"Server: Got TOSERVER_INIT from "
1964 <<peer_id<<std::endl;
1966 // First byte after command is maximum supported
1967 // serialization version
1968 u8 client_max = data[2];
1969 u8 our_max = SER_FMT_VER_HIGHEST;
1970 // Use the highest version supported by both
1971 u8 deployed = core::min_(client_max, our_max);
1972 // If it's lower than the lowest supported, give up.
1973 if(deployed < SER_FMT_VER_LOWEST)
1974 deployed = SER_FMT_VER_INVALID;
1976 //peer->serialization_version = deployed;
1977 getClient(peer_id)->pending_serialization_version = deployed;
1979 if(deployed == SER_FMT_VER_INVALID)
1981 actionstream<<"Server: A mismatched client tried to connect from "
1982 <<addr_s<<std::endl;
1983 infostream<<"Server: Cannot negotiate "
1984 "serialization version with peer "
1985 <<peer_id<<std::endl;
1986 SendAccessDenied(m_con, peer_id, std::wstring(
1987 L"Your client's version is not supported.\n"
1988 L"Server version is ")
1989 + narrow_to_wide(VERSION_STRING) + L"."
1995 Read and check network protocol version
1998 u16 net_proto_version = 0;
1999 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
2001 net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
2004 getClient(peer_id)->net_proto_version = net_proto_version;
2006 if(net_proto_version == 0)
2008 actionstream<<"Server: An old tried to connect from "<<addr_s
2010 SendAccessDenied(m_con, peer_id, std::wstring(
2011 L"Your client's version is not supported.\n"
2012 L"Server version is ")
2013 + narrow_to_wide(VERSION_STRING) + L"."
2018 if(g_settings->getBool("strict_protocol_version_checking"))
2020 if(net_proto_version != PROTOCOL_VERSION)
2022 actionstream<<"Server: A mismatched client tried to connect"
2023 <<" from "<<addr_s<<std::endl;
2024 SendAccessDenied(m_con, peer_id, std::wstring(
2025 L"Your client's version is not supported.\n"
2026 L"Server version is ")
2027 + narrow_to_wide(VERSION_STRING) + L",\n"
2028 + L"server's PROTOCOL_VERSION is "
2029 + narrow_to_wide(itos(PROTOCOL_VERSION))
2030 + L", client's PROTOCOL_VERSION is "
2031 + narrow_to_wide(itos(net_proto_version))
2042 char playername[PLAYERNAME_SIZE];
2043 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
2045 playername[i] = data[3+i];
2047 playername[PLAYERNAME_SIZE-1] = 0;
2049 if(playername[0]=='\0')
2051 actionstream<<"Server: Player with an empty name "
2052 <<"tried to connect from "<<addr_s<<std::endl;
2053 SendAccessDenied(m_con, peer_id,
2058 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
2060 actionstream<<"Server: Player with an invalid name "
2061 <<"tried to connect from "<<addr_s<<std::endl;
2062 SendAccessDenied(m_con, peer_id,
2063 L"Name contains unallowed characters");
2067 infostream<<"Server: New connection: \""<<playername<<"\" from "
2068 <<m_con.GetPeerAddress(peer_id).serializeString()<<std::endl;
2071 char password[PASSWORD_SIZE];
2072 if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
2074 // old version - assume blank password
2079 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2081 password[i] = data[23+i];
2083 password[PASSWORD_SIZE-1] = 0;
2086 // Add player to auth manager
2087 if(m_authmanager.exists(playername) == false)
2089 std::wstring default_password =
2090 narrow_to_wide(g_settings->get("default_password"));
2091 std::string translated_default_password =
2092 translatePassword(playername, default_password);
2094 // If default_password is empty, allow any initial password
2095 if (default_password.length() == 0)
2096 translated_default_password = password;
2098 infostream<<"Server: adding player "<<playername
2099 <<" to auth manager"<<std::endl;
2100 m_authmanager.add(playername);
2101 m_authmanager.setPassword(playername, translated_default_password);
2102 m_authmanager.setPrivs(playername,
2103 stringToPrivs(g_settings->get("default_privs")));
2104 m_authmanager.save();
2107 std::string checkpwd = m_authmanager.getPassword(playername);
2109 /*infostream<<"Server: Client gave password '"<<password
2110 <<"', the correct one is '"<<checkpwd<<"'"<<std::endl;*/
2112 if(password != checkpwd)
2114 infostream<<"Server: peer_id="<<peer_id
2115 <<": supplied invalid password for "
2116 <<playername<<std::endl;
2117 SendAccessDenied(m_con, peer_id, L"Invalid password");
2121 // Do not allow multiple players in simple singleplayer mode.
2122 // This isn't a perfect way to do it, but will suffice for now.
2123 if(m_simple_singleplayer_mode && m_clients.size() > 1){
2124 infostream<<"Server: Not allowing another client to connect in"
2125 <<" simple singleplayer mode"<<std::endl;
2126 SendAccessDenied(m_con, peer_id,
2127 L"Running in simple singleplayer mode.");
2131 // Enforce user limit.
2132 // Don't enforce for users that have some admin right
2133 if(m_clients.size() >= g_settings->getU16("max_users") &&
2134 (m_authmanager.getPrivs(playername)
2135 & (PRIV_SERVER|PRIV_BAN|PRIV_PRIVS|PRIV_PASSWORD)) == 0 &&
2136 playername != g_settings->get("name"))
2138 actionstream<<"Server: "<<playername<<" tried to join, but there"
2139 <<" are already max_users="
2140 <<g_settings->getU16("max_users")<<" players."<<std::endl;
2141 SendAccessDenied(m_con, peer_id, L"Too many users.");
2146 PlayerSAO *playersao = emergePlayer(playername, peer_id);
2148 // If failed, cancel
2149 if(playersao == NULL)
2151 errorstream<<"Server: peer_id="<<peer_id
2152 <<": failed to emerge player"<<std::endl;
2157 Answer with a TOCLIENT_INIT
2160 SharedBuffer<u8> reply(2+1+6+8);
2161 writeU16(&reply[0], TOCLIENT_INIT);
2162 writeU8(&reply[2], deployed);
2163 writeV3S16(&reply[2+1], floatToInt(playersao->getPlayer()->getPosition()+v3f(0,BS/2,0), BS));
2164 writeU64(&reply[2+1+6], m_env->getServerMap().getSeed());
2167 m_con.Send(peer_id, 0, reply, true);
2171 Send complete position information
2173 SendMovePlayer(peer_id);
2178 if(command == TOSERVER_INIT2)
2180 verbosestream<<"Server: Got TOSERVER_INIT2 from "
2181 <<peer_id<<std::endl;
2184 getClient(peer_id)->serialization_version
2185 = getClient(peer_id)->pending_serialization_version;
2188 Send some initialization data
2191 infostream<<"Server: Sending content to "
2192 <<getPlayerName(peer_id)<<std::endl;
2194 // Send item definitions
2195 SendItemDef(m_con, peer_id, m_itemdef);
2197 // Send node definitions
2198 SendNodeDef(m_con, peer_id, m_nodedef);
2200 // Send texture announcement
2201 sendMediaAnnouncement(peer_id);
2203 // Send player info to all players
2204 //SendPlayerInfos();
2206 // Send inventory to player
2207 UpdateCrafting(peer_id);
2208 SendInventory(peer_id);
2210 Player *player = m_env->getPlayer(peer_id);
2213 SendPlayerHP(peer_id);
2215 // Show death screen if necessary
2217 SendDeathscreen(m_con, peer_id, false, v3f(0,0,0));
2221 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2222 m_env->getTimeOfDay(), g_settings->getFloat("time_speed"));
2223 m_con.Send(peer_id, 0, data, true);
2226 // Note things in chat if not in simple singleplayer mode
2227 if(!m_simple_singleplayer_mode)
2229 // Send information about server to player in chat
2230 SendChatMessage(peer_id, getStatusString());
2232 // Send information about joining in chat
2234 std::wstring name = L"unknown";
2235 Player *player = m_env->getPlayer(peer_id);
2237 name = narrow_to_wide(player->getName());
2239 std::wstring message;
2242 message += L" joined game";
2243 BroadcastChatMessage(message);
2247 // Warnings about protocol version can be issued here
2248 if(getClient(peer_id)->net_proto_version < PROTOCOL_VERSION)
2250 SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT IS OLD AND MAY WORK PROPERLY WITH THIS SERVER");
2257 std::ostringstream os(std::ios_base::binary);
2258 for(core::map<u16, RemoteClient*>::Iterator
2259 i = m_clients.getIterator();
2260 i.atEnd() == false; i++)
2262 RemoteClient *client = i.getNode()->getValue();
2263 assert(client->peer_id == i.getNode()->getKey());
2264 if(client->serialization_version == SER_FMT_VER_INVALID)
2267 Player *player = m_env->getPlayer(client->peer_id);
2270 // Get name of player
2271 os<<player->getName()<<" ";
2274 actionstream<<player->getName()<<" joins game. List of players: "
2275 <<os.str()<<std::endl;
2281 if(peer_ser_ver == SER_FMT_VER_INVALID)
2283 infostream<<"Server::ProcessData(): Cancelling: Peer"
2284 " serialization format invalid or not initialized."
2285 " Skipping incoming command="<<command<<std::endl;
2289 Player *player = m_env->getPlayer(peer_id);
2291 infostream<<"Server::ProcessData(): Cancelling: "
2292 "No player for peer_id="<<peer_id
2297 PlayerSAO *playersao = player->getPlayerSAO();
2298 if(playersao == NULL){
2299 infostream<<"Server::ProcessData(): Cancelling: "
2300 "No player object for peer_id="<<peer_id
2305 if(command == TOSERVER_PLAYERPOS)
2307 if(datasize < 2+12+12+4+4)
2311 v3s32 ps = readV3S32(&data[start+2]);
2312 v3s32 ss = readV3S32(&data[start+2+12]);
2313 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2314 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2315 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2316 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2317 pitch = wrapDegrees(pitch);
2318 yaw = wrapDegrees(yaw);
2320 player->setPosition(position);
2321 player->setSpeed(speed);
2322 player->setPitch(pitch);
2323 player->setYaw(yaw);
2325 /*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2326 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2327 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2329 else if(command == TOSERVER_GOTBLOCKS)
2342 u16 count = data[2];
2343 for(u16 i=0; i<count; i++)
2345 if((s16)datasize < 2+1+(i+1)*6)
2346 throw con::InvalidIncomingDataException
2347 ("GOTBLOCKS length is too short");
2348 v3s16 p = readV3S16(&data[2+1+i*6]);
2349 /*infostream<<"Server: GOTBLOCKS ("
2350 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2351 RemoteClient *client = getClient(peer_id);
2352 client->GotBlock(p);
2355 else if(command == TOSERVER_DELETEDBLOCKS)
2368 u16 count = data[2];
2369 for(u16 i=0; i<count; i++)
2371 if((s16)datasize < 2+1+(i+1)*6)
2372 throw con::InvalidIncomingDataException
2373 ("DELETEDBLOCKS length is too short");
2374 v3s16 p = readV3S16(&data[2+1+i*6]);
2375 /*infostream<<"Server: DELETEDBLOCKS ("
2376 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2377 RemoteClient *client = getClient(peer_id);
2378 client->SetBlockNotSent(p);
2381 else if(command == TOSERVER_CLICK_OBJECT)
2383 infostream<<"Server: CLICK_OBJECT not supported anymore"<<std::endl;
2386 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2388 infostream<<"Server: CLICK_ACTIVEOBJECT not supported anymore"<<std::endl;
2391 else if(command == TOSERVER_GROUND_ACTION)
2393 infostream<<"Server: GROUND_ACTION not supported anymore"<<std::endl;
2397 else if(command == TOSERVER_RELEASE)
2399 infostream<<"Server: RELEASE not supported anymore"<<std::endl;
2402 else if(command == TOSERVER_SIGNTEXT)
2404 infostream<<"Server: SIGNTEXT not supported anymore"
2408 else if(command == TOSERVER_SIGNNODETEXT)
2410 if((getPlayerPrivs(player) & PRIV_INTERACT) == 0)
2418 std::string datastring((char*)&data[2], datasize-2);
2419 std::istringstream is(datastring, std::ios_base::binary);
2422 is.read((char*)buf, 6);
2423 v3s16 p = readV3S16(buf);
2424 is.read((char*)buf, 2);
2425 u16 textlen = readU16(buf);
2427 for(u16 i=0; i<textlen; i++)
2429 is.read((char*)buf, 1);
2430 text += (char)buf[0];
2433 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
2437 meta->setText(text);
2439 actionstream<<player->getName()<<" writes \""<<text<<"\" to sign"
2440 <<" at "<<PP(p)<<std::endl;
2442 v3s16 blockpos = getNodeBlockPos(p);
2443 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
2446 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2450 setBlockNotSent(blockpos);
2452 else if(command == TOSERVER_INVENTORY_ACTION)
2454 // Strip command and create a stream
2455 std::string datastring((char*)&data[2], datasize-2);
2456 verbosestream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2457 std::istringstream is(datastring, std::ios_base::binary);
2459 InventoryAction *a = InventoryAction::deSerialize(is);
2462 infostream<<"TOSERVER_INVENTORY_ACTION: "
2463 <<"InventoryAction::deSerialize() returned NULL"
2469 Note: Always set inventory not sent, to repair cases
2470 where the client made a bad prediction.
2474 Handle restrictions and special cases of the move action
2476 if(a->getType() == IACTION_MOVE)
2478 IMoveAction *ma = (IMoveAction*)a;
2480 ma->from_inv.applyCurrentPlayer(player->getName());
2481 ma->to_inv.applyCurrentPlayer(player->getName());
2483 setInventoryModified(ma->from_inv);
2484 setInventoryModified(ma->to_inv);
2486 bool from_inv_is_current_player =
2487 (ma->from_inv.type == InventoryLocation::PLAYER) &&
2488 (ma->from_inv.name == player->getName());
2490 bool to_inv_is_current_player =
2491 (ma->to_inv.type == InventoryLocation::PLAYER) &&
2492 (ma->to_inv.name == player->getName());
2495 Disable moving items out of craftpreview
2497 if(ma->from_list == "craftpreview")
2499 infostream<<"Ignoring IMoveAction from "
2500 <<(ma->from_inv.dump())<<":"<<ma->from_list
2501 <<" to "<<(ma->to_inv.dump())<<":"<<ma->to_list
2502 <<" because src is "<<ma->from_list<<std::endl;
2508 Disable moving items into craftresult and craftpreview
2510 if(ma->to_list == "craftpreview" || ma->to_list == "craftresult")
2512 infostream<<"Ignoring IMoveAction from "
2513 <<(ma->from_inv.dump())<<":"<<ma->from_list
2514 <<" to "<<(ma->to_inv.dump())<<":"<<ma->to_list
2515 <<" because dst is "<<ma->to_list<<std::endl;
2520 // Disallow moving items in elsewhere than player's inventory
2521 // if not allowed to interact
2522 if((getPlayerPrivs(player) & PRIV_INTERACT) == 0
2523 && (!from_inv_is_current_player
2524 || !to_inv_is_current_player))
2526 infostream<<"Cannot move outside of player's inventory: "
2527 <<"No interact privilege"<<std::endl;
2532 // If player is not an admin, check for ownership of src and dst
2533 if((getPlayerPrivs(player) & PRIV_SERVER) == 0)
2535 std::string owner_from = getInventoryOwner(ma->from_inv);
2536 if(owner_from != "" && owner_from != player->getName())
2538 infostream<<"WARNING: "<<player->getName()
2539 <<" tried to access an inventory that"
2540 <<" belongs to "<<owner_from<<std::endl;
2545 std::string owner_to = getInventoryOwner(ma->to_inv);
2546 if(owner_to != "" && owner_to != player->getName())
2548 infostream<<"WARNING: "<<player->getName()
2549 <<" tried to access an inventory that"
2550 <<" belongs to "<<owner_to<<std::endl;
2557 Handle restrictions and special cases of the drop action
2559 else if(a->getType() == IACTION_DROP)
2561 IDropAction *da = (IDropAction*)a;
2563 da->from_inv.applyCurrentPlayer(player->getName());
2565 setInventoryModified(da->from_inv);
2567 // Disallow dropping items if not allowed to interact
2568 if((getPlayerPrivs(player) & PRIV_INTERACT) == 0)
2573 // If player is not an admin, check for ownership
2574 else if((getPlayerPrivs(player) & PRIV_SERVER) == 0)
2576 std::string owner_from = getInventoryOwner(da->from_inv);
2577 if(owner_from != "" && owner_from != player->getName())
2579 infostream<<"WARNING: "<<player->getName()
2580 <<" tried to access an inventory that"
2581 <<" belongs to "<<owner_from<<std::endl;
2588 Handle restrictions and special cases of the craft action
2590 else if(a->getType() == IACTION_CRAFT)
2592 ICraftAction *ca = (ICraftAction*)a;
2594 ca->craft_inv.applyCurrentPlayer(player->getName());
2596 setInventoryModified(ca->craft_inv);
2598 //bool craft_inv_is_current_player =
2599 // (ca->craft_inv.type == InventoryLocation::PLAYER) &&
2600 // (ca->craft_inv.name == player->getName());
2602 // Disallow crafting if not allowed to interact
2603 if((getPlayerPrivs(player) & PRIV_INTERACT) == 0)
2605 infostream<<"Cannot craft: "
2606 <<"No interact privilege"<<std::endl;
2611 // If player is not an admin, check for ownership of inventory
2612 if((getPlayerPrivs(player) & PRIV_SERVER) == 0)
2614 std::string owner_craft = getInventoryOwner(ca->craft_inv);
2615 if(owner_craft != "" && owner_craft != player->getName())
2617 infostream<<"WARNING: "<<player->getName()
2618 <<" tried to access an inventory that"
2619 <<" belongs to "<<owner_craft<<std::endl;
2627 a->apply(this, playersao, this);
2631 else if(command == TOSERVER_CHAT_MESSAGE)
2639 std::string datastring((char*)&data[2], datasize-2);
2640 std::istringstream is(datastring, std::ios_base::binary);
2643 is.read((char*)buf, 2);
2644 u16 len = readU16(buf);
2646 std::wstring message;
2647 for(u16 i=0; i<len; i++)
2649 is.read((char*)buf, 2);
2650 message += (wchar_t)readU16(buf);
2653 // Get player name of this client
2654 std::wstring name = narrow_to_wide(player->getName());
2657 bool ate = scriptapi_on_chat_message(m_lua, player->getName(),
2658 wide_to_narrow(message));
2659 // If script ate the message, don't proceed
2663 // Line to send to players
2665 // Whether to send to the player that sent the line
2666 bool send_to_sender = false;
2667 // Whether to send to other players
2668 bool send_to_others = false;
2670 // Local player gets all privileges regardless of
2671 // what's set on their account.
2672 u64 privs = getPlayerPrivs(player);
2675 if(message[0] == L'/')
2677 size_t strip_size = 1;
2678 if (message[1] == L'#') // support old-style commans
2680 message = message.substr(strip_size);
2682 WStrfnd f1(message);
2683 f1.next(L" "); // Skip over /#whatever
2684 std::wstring paramstring = f1.next(L"");
2686 ServerCommandContext *ctx = new ServerCommandContext(
2687 str_split(message, L' '),
2694 std::wstring reply(processServerCommand(ctx));
2695 send_to_sender = ctx->flags & SEND_TO_SENDER;
2696 send_to_others = ctx->flags & SEND_TO_OTHERS;
2698 if (ctx->flags & SEND_NO_PREFIX)
2701 line += L"Server: " + reply;
2708 if(privs & PRIV_SHOUT)
2714 send_to_others = true;
2718 line += L"Server: You are not allowed to shout";
2719 send_to_sender = true;
2726 actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2729 Send the message to clients
2731 for(core::map<u16, RemoteClient*>::Iterator
2732 i = m_clients.getIterator();
2733 i.atEnd() == false; i++)
2735 // Get client and check that it is valid
2736 RemoteClient *client = i.getNode()->getValue();
2737 assert(client->peer_id == i.getNode()->getKey());
2738 if(client->serialization_version == SER_FMT_VER_INVALID)
2742 bool sender_selected = (peer_id == client->peer_id);
2743 if(sender_selected == true && send_to_sender == false)
2745 if(sender_selected == false && send_to_others == false)
2748 SendChatMessage(client->peer_id, line);
2752 else if(command == TOSERVER_DAMAGE)
2754 std::string datastring((char*)&data[2], datasize-2);
2755 std::istringstream is(datastring, std::ios_base::binary);
2756 u8 damage = readU8(is);
2758 actionstream<<player->getName()<<" damaged by "
2759 <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
2762 playersao->setHP(playersao->getHP() - damage);
2764 if(playersao->getHP() == 0 && playersao->m_hp_not_sent)
2767 if(playersao->m_hp_not_sent)
2768 SendPlayerHP(peer_id);
2770 else if(command == TOSERVER_PASSWORD)
2773 [0] u16 TOSERVER_PASSWORD
2774 [2] u8[28] old password
2775 [30] u8[28] new password
2778 if(datasize != 2+PASSWORD_SIZE*2)
2780 /*char password[PASSWORD_SIZE];
2781 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2782 password[i] = data[2+i];
2783 password[PASSWORD_SIZE-1] = 0;*/
2785 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2793 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2795 char c = data[2+PASSWORD_SIZE+i];
2801 infostream<<"Server: Client requests a password change from "
2802 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
2804 std::string playername = player->getName();
2806 if(m_authmanager.exists(playername) == false)
2808 infostream<<"Server: playername not found in authmanager"<<std::endl;
2809 // Wrong old password supplied!!
2810 SendChatMessage(peer_id, L"playername not found in authmanager");
2814 std::string checkpwd = m_authmanager.getPassword(playername);
2816 if(oldpwd != checkpwd)
2818 infostream<<"Server: invalid old password"<<std::endl;
2819 // Wrong old password supplied!!
2820 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
2824 actionstream<<player->getName()<<" changes password"<<std::endl;
2826 m_authmanager.setPassword(playername, newpwd);
2828 infostream<<"Server: password change successful for "<<playername
2830 SendChatMessage(peer_id, L"Password change successful");
2832 else if(command == TOSERVER_PLAYERITEM)
2837 u16 item = readU16(&data[2]);
2838 playersao->setWieldIndex(item);
2840 else if(command == TOSERVER_RESPAWN)
2845 RespawnPlayer(peer_id);
2847 actionstream<<player->getName()<<" respawns at "
2848 <<PP(player->getPosition()/BS)<<std::endl;
2850 // ActiveObject is added to environment in AsyncRunStep after
2851 // the previous addition has been succesfully removed
2853 else if(command == TOSERVER_REQUEST_MEDIA) {
2854 std::string datastring((char*)&data[2], datasize-2);
2855 std::istringstream is(datastring, std::ios_base::binary);
2857 core::list<MediaRequest> tosend;
2858 u16 numfiles = readU16(is);
2860 infostream<<"Sending "<<numfiles<<" files to "
2861 <<getPlayerName(peer_id)<<std::endl;
2862 verbosestream<<"TOSERVER_REQUEST_MEDIA: "<<std::endl;
2864 for(int i = 0; i < numfiles; i++) {
2865 std::string name = deSerializeString(is);
2866 tosend.push_back(MediaRequest(name));
2867 verbosestream<<"TOSERVER_REQUEST_MEDIA: requested file "
2871 sendRequestedMedia(peer_id, tosend);
2873 // Now the client should know about everything
2874 // (definitions and files)
2875 getClient(peer_id)->definitions_sent = true;
2877 else if(command == TOSERVER_INTERACT)
2879 std::string datastring((char*)&data[2], datasize-2);
2880 std::istringstream is(datastring, std::ios_base::binary);
2886 [5] u32 length of the next item
2887 [9] serialized PointedThing
2889 0: start digging (from undersurface) or use
2890 1: stop digging (all parameters ignored)
2891 2: digging completed
2892 3: place block or item (to abovesurface)
2895 u8 action = readU8(is);
2896 u16 item_i = readU16(is);
2897 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
2898 PointedThing pointed;
2899 pointed.deSerialize(tmp_is);
2901 verbosestream<<"TOSERVER_INTERACT: action="<<(int)action<<", item="
2902 <<item_i<<", pointed="<<pointed.dump()<<std::endl;
2906 verbosestream<<"TOSERVER_INTERACT: "<<player->getName()
2907 <<" tried to interact, but is dead!"<<std::endl;
2911 v3f player_pos = playersao->getLastGoodPosition();
2913 // Update wielded item
2914 playersao->setWieldIndex(item_i);
2916 // Get pointed to node (undefined if not POINTEDTYPE_NODE)
2917 v3s16 p_under = pointed.node_undersurface;
2918 v3s16 p_above = pointed.node_abovesurface;
2920 // Get pointed to object (NULL if not POINTEDTYPE_OBJECT)
2921 ServerActiveObject *pointed_object = NULL;
2922 if(pointed.type == POINTEDTHING_OBJECT)
2924 pointed_object = m_env->getActiveObject(pointed.object_id);
2925 if(pointed_object == NULL)
2927 verbosestream<<"TOSERVER_INTERACT: "
2928 "pointed object is NULL"<<std::endl;
2934 v3f pointed_pos_under = player_pos;
2935 v3f pointed_pos_above = player_pos;
2936 if(pointed.type == POINTEDTHING_NODE)
2938 pointed_pos_under = intToFloat(p_under, BS);
2939 pointed_pos_above = intToFloat(p_above, BS);
2941 else if(pointed.type == POINTEDTHING_OBJECT)
2943 pointed_pos_under = pointed_object->getBasePosition();
2944 pointed_pos_above = pointed_pos_under;
2948 Check that target is reasonably close
2949 (only when digging or placing things)
2951 if(action == 0 || action == 2 || action == 3)
2953 float d = player_pos.getDistanceFrom(pointed_pos_under);
2954 float max_d = BS * 14; // Just some large enough value
2956 actionstream<<"Player "<<player->getName()
2957 <<" tried to access "<<pointed.dump()
2959 <<"d="<<d<<", max_d="<<max_d
2960 <<". ignoring."<<std::endl;
2961 // Re-send block to revert change on client-side
2962 RemoteClient *client = getClient(peer_id);
2963 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2964 client->SetBlockNotSent(blockpos);
2971 Make sure the player is allowed to do it
2973 if((getPlayerPrivs(player) & PRIV_INTERACT) == 0)
2975 infostream<<"Ignoring interaction from player "<<player->getName()
2976 <<" because privileges are "<<getPlayerPrivs(player)
2982 0: start digging or punch object
2986 if(pointed.type == POINTEDTHING_NODE)
2989 NOTE: This can be used in the future to check if
2990 somebody is cheating, by checking the timing.
2992 MapNode n(CONTENT_IGNORE);
2995 n = m_env->getMap().getNode(p_under);
2997 catch(InvalidPositionException &e)
2999 infostream<<"Server: Not punching: Node not found."
3000 <<" Adding block to emerge queue."
3002 m_emerge_queue.addBlock(peer_id,
3003 getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
3005 if(n.getContent() != CONTENT_IGNORE)
3006 scriptapi_node_on_punch(m_lua, p_under, n, playersao);
3008 else if(pointed.type == POINTEDTHING_OBJECT)
3010 // Skip if object has been removed
3011 if(pointed_object->m_removed)
3014 actionstream<<player->getName()<<" punches object "
3015 <<pointed.object_id<<": "
3016 <<pointed_object->getDescription()<<std::endl;
3018 ItemStack punchitem = playersao->getWieldedItem();
3019 ToolCapabilities toolcap =
3020 punchitem.getToolCapabilities(m_itemdef);
3021 v3f dir = (pointed_object->getBasePosition() -
3022 (player->getPosition() + player->getEyeOffset())
3024 float time_from_last_punch =
3025 playersao->resetTimeFromLastPunch();
3026 pointed_object->punch(dir, &toolcap, playersao,
3027 time_from_last_punch);
3035 else if(action == 1)
3040 2: Digging completed
3042 else if(action == 2)
3044 // Only complete digging of nodes
3045 if(pointed.type == POINTEDTHING_NODE)
3047 MapNode n(CONTENT_IGNORE);
3050 n = m_env->getMap().getNode(p_under);
3052 catch(InvalidPositionException &e)
3054 infostream<<"Server: Not finishing digging: Node not found."
3055 <<" Adding block to emerge queue."
3057 m_emerge_queue.addBlock(peer_id,
3058 getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
3060 if(n.getContent() != CONTENT_IGNORE)
3061 scriptapi_node_on_dig(m_lua, p_under, n, playersao);
3066 3: place block or right-click object
3068 else if(action == 3)
3070 ItemStack item = playersao->getWieldedItem();
3072 // Reset build time counter
3073 if(pointed.type == POINTEDTHING_NODE &&
3074 item.getDefinition(m_itemdef).type == ITEM_NODE)
3075 getClient(peer_id)->m_time_from_building = 0.0;
3077 if(pointed.type == POINTEDTHING_OBJECT)
3079 // Right click object
3081 // Skip if object has been removed
3082 if(pointed_object->m_removed)
3085 actionstream<<player->getName()<<" right-clicks object "
3086 <<pointed.object_id<<": "
3087 <<pointed_object->getDescription()<<std::endl;
3090 pointed_object->rightClick(playersao);
3092 else if(scriptapi_item_on_place(m_lua,
3093 item, playersao, pointed))
3095 // Placement was handled in lua
3097 // Apply returned ItemStack
3098 if(g_settings->getBool("creative_mode") == false)
3099 playersao->setWieldedItem(item);
3107 else if(action == 4)
3109 ItemStack item = playersao->getWieldedItem();
3111 actionstream<<player->getName()<<" uses "<<item.name
3112 <<", pointing at "<<pointed.dump()<<std::endl;
3114 if(scriptapi_item_on_use(m_lua,
3115 item, playersao, pointed))
3117 // Apply returned ItemStack
3118 if(g_settings->getBool("creative_mode") == false)
3119 playersao->setWieldedItem(item);
3125 Catch invalid actions
3129 infostream<<"WARNING: Server: Invalid action "
3130 <<action<<std::endl;
3133 else if(command == TOSERVER_REMOVED_SOUNDS)
3135 std::string datastring((char*)&data[2], datasize-2);
3136 std::istringstream is(datastring, std::ios_base::binary);
3138 int num = readU16(is);
3139 for(int k=0; k<num; k++){
3140 s32 id = readS32(is);
3141 std::map<s32, ServerPlayingSound>::iterator i =
3142 m_playing_sounds.find(id);
3143 if(i == m_playing_sounds.end())
3145 ServerPlayingSound &psound = i->second;
3146 psound.clients.erase(peer_id);
3147 if(psound.clients.size() == 0)
3148 m_playing_sounds.erase(i++);
3153 infostream<<"Server::ProcessData(): Ignoring "
3154 "unknown command "<<command<<std::endl;
3158 catch(SendFailedException &e)
3160 errorstream<<"Server::ProcessData(): SendFailedException: "
3166 void Server::onMapEditEvent(MapEditEvent *event)
3168 //infostream<<"Server::onMapEditEvent()"<<std::endl;
3169 if(m_ignore_map_edit_events)
3171 if(m_ignore_map_edit_events_area.contains(event->getArea()))
3173 MapEditEvent *e = event->clone();
3174 m_unsent_map_edit_queue.push_back(e);
3177 Inventory* Server::getInventory(const InventoryLocation &loc)
3180 case InventoryLocation::UNDEFINED:
3183 case InventoryLocation::CURRENT_PLAYER:
3186 case InventoryLocation::PLAYER:
3188 Player *player = m_env->getPlayer(loc.name.c_str());
3191 PlayerSAO *playersao = player->getPlayerSAO();
3194 return playersao->getInventory();
3197 case InventoryLocation::NODEMETA:
3199 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
3202 return meta->getInventory();
3210 std::string Server::getInventoryOwner(const InventoryLocation &loc)
3213 case InventoryLocation::UNDEFINED:
3216 case InventoryLocation::CURRENT_PLAYER:
3219 case InventoryLocation::PLAYER:
3224 case InventoryLocation::NODEMETA:
3226 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
3229 return meta->getOwner();
3237 void Server::setInventoryModified(const InventoryLocation &loc)
3240 case InventoryLocation::UNDEFINED:
3243 case InventoryLocation::PLAYER:
3245 Player *player = m_env->getPlayer(loc.name.c_str());
3248 PlayerSAO *playersao = player->getPlayerSAO();
3251 playersao->m_inventory_not_sent = true;
3252 playersao->m_wielded_item_not_sent = true;
3255 case InventoryLocation::NODEMETA:
3257 v3s16 blockpos = getNodeBlockPos(loc.p);
3259 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
3261 meta->inventoryModified();
3263 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3265 block->raiseModified(MOD_STATE_WRITE_NEEDED);
3267 setBlockNotSent(blockpos);
3275 core::list<PlayerInfo> Server::getPlayerInfo()
3277 DSTACK(__FUNCTION_NAME);
3278 JMutexAutoLock envlock(m_env_mutex);
3279 JMutexAutoLock conlock(m_con_mutex);
3281 core::list<PlayerInfo> list;
3283 core::list<Player*> players = m_env->getPlayers();
3285 core::list<Player*>::Iterator i;
3286 for(i = players.begin();
3287 i != players.end(); i++)
3291 Player *player = *i;
3294 // Copy info from connection to info struct
3295 info.id = player->peer_id;
3296 info.address = m_con.GetPeerAddress(player->peer_id);
3297 info.avg_rtt = m_con.GetPeerAvgRTT(player->peer_id);
3299 catch(con::PeerNotFoundException &e)
3301 // Set dummy peer info
3303 info.address = Address(0,0,0,0,0);
3307 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3308 info.position = player->getPosition();
3310 list.push_back(info);
3317 void Server::peerAdded(con::Peer *peer)
3319 DSTACK(__FUNCTION_NAME);
3320 verbosestream<<"Server::peerAdded(): peer->id="
3321 <<peer->id<<std::endl;
3324 c.type = PEER_ADDED;
3325 c.peer_id = peer->id;
3327 m_peer_change_queue.push_back(c);
3330 void Server::deletingPeer(con::Peer *peer, bool timeout)
3332 DSTACK(__FUNCTION_NAME);
3333 verbosestream<<"Server::deletingPeer(): peer->id="
3334 <<peer->id<<", timeout="<<timeout<<std::endl;
3337 c.type = PEER_REMOVED;
3338 c.peer_id = peer->id;
3339 c.timeout = timeout;
3340 m_peer_change_queue.push_back(c);
3347 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3349 DSTACK(__FUNCTION_NAME);
3350 std::ostringstream os(std::ios_base::binary);
3352 writeU16(os, TOCLIENT_HP);
3356 std::string s = os.str();
3357 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3359 con.Send(peer_id, 0, data, true);
3362 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3363 const std::wstring &reason)
3365 DSTACK(__FUNCTION_NAME);
3366 std::ostringstream os(std::ios_base::binary);
3368 writeU16(os, TOCLIENT_ACCESS_DENIED);
3369 os<<serializeWideString(reason);
3372 std::string s = os.str();
3373 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3375 con.Send(peer_id, 0, data, true);
3378 void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
3379 bool set_camera_point_target, v3f camera_point_target)
3381 DSTACK(__FUNCTION_NAME);
3382 std::ostringstream os(std::ios_base::binary);
3384 writeU16(os, TOCLIENT_DEATHSCREEN);
3385 writeU8(os, set_camera_point_target);
3386 writeV3F1000(os, camera_point_target);
3389 std::string s = os.str();
3390 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3392 con.Send(peer_id, 0, data, true);
3395 void Server::SendItemDef(con::Connection &con, u16 peer_id,
3396 IItemDefManager *itemdef)
3398 DSTACK(__FUNCTION_NAME);
3399 std::ostringstream os(std::ios_base::binary);
3403 u32 length of the next item
3404 zlib-compressed serialized ItemDefManager
3406 writeU16(os, TOCLIENT_ITEMDEF);
3407 std::ostringstream tmp_os(std::ios::binary);
3408 itemdef->serialize(tmp_os);
3409 std::ostringstream tmp_os2(std::ios::binary);
3410 compressZlib(tmp_os.str(), tmp_os2);
3411 os<<serializeLongString(tmp_os2.str());
3414 std::string s = os.str();
3415 verbosestream<<"Server: Sending item definitions to id("<<peer_id
3416 <<"): size="<<s.size()<<std::endl;
3417 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3419 con.Send(peer_id, 0, data, true);
3422 void Server::SendNodeDef(con::Connection &con, u16 peer_id,
3423 INodeDefManager *nodedef)
3425 DSTACK(__FUNCTION_NAME);
3426 std::ostringstream os(std::ios_base::binary);
3430 u32 length of the next item
3431 zlib-compressed serialized NodeDefManager
3433 writeU16(os, TOCLIENT_NODEDEF);
3434 std::ostringstream tmp_os(std::ios::binary);
3435 nodedef->serialize(tmp_os);
3436 std::ostringstream tmp_os2(std::ios::binary);
3437 compressZlib(tmp_os.str(), tmp_os2);
3438 os<<serializeLongString(tmp_os2.str());
3441 std::string s = os.str();
3442 verbosestream<<"Server: Sending node definitions to id("<<peer_id
3443 <<"): size="<<s.size()<<std::endl;
3444 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3446 con.Send(peer_id, 0, data, true);
3450 Non-static send methods
3453 void Server::SendInventory(u16 peer_id)
3455 DSTACK(__FUNCTION_NAME);
3457 PlayerSAO *playersao = getPlayerSAO(peer_id);
3460 playersao->m_inventory_not_sent = false;
3466 std::ostringstream os;
3467 playersao->getInventory()->serialize(os);
3469 std::string s = os.str();
3471 SharedBuffer<u8> data(s.size()+2);
3472 writeU16(&data[0], TOCLIENT_INVENTORY);
3473 memcpy(&data[2], s.c_str(), s.size());
3476 m_con.Send(peer_id, 0, data, true);
3479 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3481 DSTACK(__FUNCTION_NAME);
3483 std::ostringstream os(std::ios_base::binary);
3487 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3488 os.write((char*)buf, 2);
3491 writeU16(buf, message.size());
3492 os.write((char*)buf, 2);
3495 for(u32 i=0; i<message.size(); i++)
3499 os.write((char*)buf, 2);
3503 std::string s = os.str();
3504 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3506 m_con.Send(peer_id, 0, data, true);
3509 void Server::BroadcastChatMessage(const std::wstring &message)
3511 for(core::map<u16, RemoteClient*>::Iterator
3512 i = m_clients.getIterator();
3513 i.atEnd() == false; i++)
3515 // Get client and check that it is valid
3516 RemoteClient *client = i.getNode()->getValue();
3517 assert(client->peer_id == i.getNode()->getKey());
3518 if(client->serialization_version == SER_FMT_VER_INVALID)
3521 SendChatMessage(client->peer_id, message);
3525 void Server::SendPlayerHP(u16 peer_id)
3527 DSTACK(__FUNCTION_NAME);
3528 PlayerSAO *playersao = getPlayerSAO(peer_id);
3530 playersao->m_hp_not_sent = false;
3531 SendHP(m_con, peer_id, playersao->getHP());
3534 void Server::SendMovePlayer(u16 peer_id)
3536 DSTACK(__FUNCTION_NAME);
3537 Player *player = m_env->getPlayer(peer_id);
3540 std::ostringstream os(std::ios_base::binary);
3541 writeU16(os, TOCLIENT_MOVE_PLAYER);
3542 writeV3F1000(os, player->getPosition());
3543 writeF1000(os, player->getPitch());
3544 writeF1000(os, player->getYaw());
3547 v3f pos = player->getPosition();
3548 f32 pitch = player->getPitch();
3549 f32 yaw = player->getYaw();
3550 verbosestream<<"Server: Sending TOCLIENT_MOVE_PLAYER"
3551 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3558 std::string s = os.str();
3559 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3561 m_con.Send(peer_id, 0, data, true);
3564 s32 Server::playSound(const SimpleSoundSpec &spec,
3565 const ServerSoundParams ¶ms)
3567 // Find out initial position of sound
3568 bool pos_exists = false;
3569 v3f pos = params.getPos(m_env, &pos_exists);
3570 // If position is not found while it should be, cancel sound
3571 if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
3573 // Filter destination clients
3574 std::set<RemoteClient*> dst_clients;
3575 if(params.to_player != "")
3577 Player *player = m_env->getPlayer(params.to_player.c_str());
3579 infostream<<"Server::playSound: Player \""<<params.to_player
3580 <<"\" not found"<<std::endl;
3583 if(player->peer_id == PEER_ID_INEXISTENT){
3584 infostream<<"Server::playSound: Player \""<<params.to_player
3585 <<"\" not connected"<<std::endl;
3588 RemoteClient *client = getClient(player->peer_id);
3589 dst_clients.insert(client);
3593 for(core::map<u16, RemoteClient*>::Iterator
3594 i = m_clients.getIterator(); i.atEnd() == false; i++)
3596 RemoteClient *client = i.getNode()->getValue();
3597 Player *player = m_env->getPlayer(client->peer_id);
3601 if(player->getPosition().getDistanceFrom(pos) >
3602 params.max_hear_distance)
3605 dst_clients.insert(client);
3608 if(dst_clients.size() == 0)
3611 s32 id = m_next_sound_id++;
3612 // The sound will exist as a reference in m_playing_sounds
3613 m_playing_sounds[id] = ServerPlayingSound();
3614 ServerPlayingSound &psound = m_playing_sounds[id];
3615 psound.params = params;
3616 for(std::set<RemoteClient*>::iterator i = dst_clients.begin();
3617 i != dst_clients.end(); i++)
3618 psound.clients.insert((*i)->peer_id);
3620 std::ostringstream os(std::ios_base::binary);
3621 writeU16(os, TOCLIENT_PLAY_SOUND);
3623 os<<serializeString(spec.name);
3624 writeF1000(os, spec.gain * params.gain);
3625 writeU8(os, params.type);
3626 writeV3F1000(os, pos);
3627 writeU16(os, params.object);
3628 writeU8(os, params.loop);
3630 std::string s = os.str();
3631 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3633 for(std::set<RemoteClient*>::iterator i = dst_clients.begin();
3634 i != dst_clients.end(); i++){
3636 m_con.Send((*i)->peer_id, 0, data, true);
3640 void Server::stopSound(s32 handle)
3642 // Get sound reference
3643 std::map<s32, ServerPlayingSound>::iterator i =
3644 m_playing_sounds.find(handle);
3645 if(i == m_playing_sounds.end())
3647 ServerPlayingSound &psound = i->second;
3649 std::ostringstream os(std::ios_base::binary);
3650 writeU16(os, TOCLIENT_STOP_SOUND);
3651 writeS32(os, handle);
3653 std::string s = os.str();
3654 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3656 for(std::set<u16>::iterator i = psound.clients.begin();
3657 i != psound.clients.end(); i++){
3659 m_con.Send(*i, 0, data, true);
3661 // Remove sound reference
3662 m_playing_sounds.erase(i);
3665 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3666 core::list<u16> *far_players, float far_d_nodes)
3668 float maxd = far_d_nodes*BS;
3669 v3f p_f = intToFloat(p, BS);
3673 SharedBuffer<u8> reply(replysize);
3674 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3675 writeS16(&reply[2], p.X);
3676 writeS16(&reply[4], p.Y);
3677 writeS16(&reply[6], p.Z);
3679 for(core::map<u16, RemoteClient*>::Iterator
3680 i = m_clients.getIterator();
3681 i.atEnd() == false; i++)
3683 // Get client and check that it is valid
3684 RemoteClient *client = i.getNode()->getValue();
3685 assert(client->peer_id == i.getNode()->getKey());
3686 if(client->serialization_version == SER_FMT_VER_INVALID)
3689 // Don't send if it's the same one
3690 if(client->peer_id == ignore_id)
3696 Player *player = m_env->getPlayer(client->peer_id);
3699 // If player is far away, only set modified blocks not sent
3700 v3f player_pos = player->getPosition();
3701 if(player_pos.getDistanceFrom(p_f) > maxd)
3703 far_players->push_back(client->peer_id);
3710 m_con.Send(client->peer_id, 0, reply, true);
3714 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3715 core::list<u16> *far_players, float far_d_nodes)
3717 float maxd = far_d_nodes*BS;
3718 v3f p_f = intToFloat(p, BS);
3720 for(core::map<u16, RemoteClient*>::Iterator
3721 i = m_clients.getIterator();
3722 i.atEnd() == false; i++)
3724 // Get client and check that it is valid
3725 RemoteClient *client = i.getNode()->getValue();
3726 assert(client->peer_id == i.getNode()->getKey());
3727 if(client->serialization_version == SER_FMT_VER_INVALID)
3730 // Don't send if it's the same one
3731 if(client->peer_id == ignore_id)
3737 Player *player = m_env->getPlayer(client->peer_id);
3740 // If player is far away, only set modified blocks not sent
3741 v3f player_pos = player->getPosition();
3742 if(player_pos.getDistanceFrom(p_f) > maxd)
3744 far_players->push_back(client->peer_id);
3751 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3752 SharedBuffer<u8> reply(replysize);
3753 writeU16(&reply[0], TOCLIENT_ADDNODE);
3754 writeS16(&reply[2], p.X);
3755 writeS16(&reply[4], p.Y);
3756 writeS16(&reply[6], p.Z);
3757 n.serialize(&reply[8], client->serialization_version);
3760 m_con.Send(client->peer_id, 0, reply, true);
3764 void Server::setBlockNotSent(v3s16 p)
3766 for(core::map<u16, RemoteClient*>::Iterator
3767 i = m_clients.getIterator();
3768 i.atEnd()==false; i++)
3770 RemoteClient *client = i.getNode()->getValue();
3771 client->SetBlockNotSent(p);
3775 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3777 DSTACK(__FUNCTION_NAME);
3779 v3s16 p = block->getPos();
3783 bool completely_air = true;
3784 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3785 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3786 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3788 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
3790 completely_air = false;
3791 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
3796 infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
3798 infostream<<"[completely air] ";
3799 infostream<<std::endl;
3803 Create a packet with the block in the right format
3806 std::ostringstream os(std::ios_base::binary);
3807 block->serialize(os, ver, false);
3808 std::string s = os.str();
3809 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3811 u32 replysize = 8 + blockdata.getSize();
3812 SharedBuffer<u8> reply(replysize);
3813 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3814 writeS16(&reply[2], p.X);
3815 writeS16(&reply[4], p.Y);
3816 writeS16(&reply[6], p.Z);
3817 memcpy(&reply[8], *blockdata, blockdata.getSize());
3819 /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3820 <<": \tpacket size: "<<replysize<<std::endl;*/
3825 m_con.Send(peer_id, 1, reply, true);
3828 void Server::SendBlocks(float dtime)
3830 DSTACK(__FUNCTION_NAME);
3832 JMutexAutoLock envlock(m_env_mutex);
3833 JMutexAutoLock conlock(m_con_mutex);
3835 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
3837 core::array<PrioritySortedBlockTransfer> queue;
3839 s32 total_sending = 0;
3842 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
3844 for(core::map<u16, RemoteClient*>::Iterator
3845 i = m_clients.getIterator();
3846 i.atEnd() == false; i++)
3848 RemoteClient *client = i.getNode()->getValue();
3849 assert(client->peer_id == i.getNode()->getKey());
3851 // If definitions and textures have not been sent, don't
3852 // send MapBlocks either
3853 if(!client->definitions_sent)
3856 total_sending += client->SendingCount();
3858 if(client->serialization_version == SER_FMT_VER_INVALID)
3861 client->GetNextBlocks(this, dtime, queue);
3866 // Lowest priority number comes first.
3867 // Lowest is most important.
3870 for(u32 i=0; i<queue.size(); i++)
3872 //TODO: Calculate limit dynamically
3873 if(total_sending >= g_settings->getS32
3874 ("max_simultaneous_block_sends_server_total"))
3877 PrioritySortedBlockTransfer q = queue[i];
3879 MapBlock *block = NULL;
3882 block = m_env->getMap().getBlockNoCreate(q.pos);
3884 catch(InvalidPositionException &e)
3889 RemoteClient *client = getClient(q.peer_id);
3891 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3893 client->SentBlock(q.pos);
3899 void Server::fillMediaCache()
3901 DSTACK(__FUNCTION_NAME);
3903 infostream<<"Server: Calculating media file checksums"<<std::endl;
3905 // Collect all media file paths
3906 std::list<std::string> paths;
3907 for(core::list<ModSpec>::Iterator i = m_mods.begin();
3908 i != m_mods.end(); i++){
3909 const ModSpec &mod = *i;
3910 paths.push_back(mod.path + DIR_DELIM + "textures");
3911 paths.push_back(mod.path + DIR_DELIM + "sounds");
3912 paths.push_back(mod.path + DIR_DELIM + "media");
3915 // Collect media file information from paths into cache
3916 for(std::list<std::string>::iterator i = paths.begin();
3917 i != paths.end(); i++)
3919 std::string mediapath = *i;
3920 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
3921 for(u32 j=0; j<dirlist.size(); j++){
3922 if(dirlist[j].dir) // Ignode dirs
3924 std::string filename = dirlist[j].name;
3925 // if name contains illegal characters, ignore the file
3926 if(!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)){
3927 errorstream<<"Server: ignoring illegal file name: \""
3928 <<filename<<"\""<<std::endl;
3931 std::string filepath = mediapath + DIR_DELIM + filename;
3933 std::ifstream fis(filepath.c_str(), std::ios_base::binary);
3934 if(fis.good() == false){
3935 errorstream<<"Server::fillMediaCache(): Could not open \""
3936 <<filename<<"\" for reading"<<std::endl;
3939 std::ostringstream tmp_os(std::ios_base::binary);
3943 fis.read(buf, 1024);
3944 std::streamsize len = fis.gcount();
3945 tmp_os.write(buf, len);
3954 errorstream<<"Server::fillMediaCache(): Failed to read \""
3955 <<filename<<"\""<<std::endl;
3958 if(tmp_os.str().length() == 0){
3959 errorstream<<"Server::fillMediaCache(): Empty file \""
3960 <<filepath<<"\""<<std::endl;
3965 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
3967 unsigned char *digest = sha1.getDigest();
3968 std::string sha1_base64 = base64_encode(digest, 20);
3969 std::string sha1_hex = hex_encode((char*)digest, 20);
3973 this->m_media[filename] = MediaInfo(filepath, sha1_base64);
3974 verbosestream<<"Server: "<<sha1_hex<<" is "<<filename<<std::endl;
3979 struct SendableMediaAnnouncement
3982 std::string sha1_digest;
3984 SendableMediaAnnouncement(const std::string name_="",
3985 const std::string sha1_digest_=""):
3987 sha1_digest(sha1_digest_)
3991 void Server::sendMediaAnnouncement(u16 peer_id)
3993 DSTACK(__FUNCTION_NAME);
3995 verbosestream<<"Server: Announcing files to id("<<peer_id<<")"
3998 core::list<SendableMediaAnnouncement> file_announcements;
4000 for(std::map<std::string, MediaInfo>::iterator i = m_media.begin();
4001 i != m_media.end(); i++){
4003 file_announcements.push_back(
4004 SendableMediaAnnouncement(i->first, i->second.sha1_digest));
4008 std::ostringstream os(std::ios_base::binary);
4016 u16 length of sha1_digest
4021 writeU16(os, TOCLIENT_ANNOUNCE_MEDIA);
4022 writeU16(os, file_announcements.size());
4024 for(core::list<SendableMediaAnnouncement>::Iterator
4025 j = file_announcements.begin();
4026 j != file_announcements.end(); j++){
4027 os<<serializeString(j->name);
4028 os<<serializeString(j->sha1_digest);
4032 std::string s = os.str();
4033 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4036 m_con.Send(peer_id, 0, data, true);
4040 struct SendableMedia
4046 SendableMedia(const std::string &name_="", const std::string path_="",
4047 const std::string &data_=""):
4054 void Server::sendRequestedMedia(u16 peer_id,
4055 const core::list<MediaRequest> &tosend)
4057 DSTACK(__FUNCTION_NAME);
4059 verbosestream<<"Server::sendRequestedMedia(): "
4060 <<"Sending files to client"<<std::endl;
4064 // Put 5kB in one bunch (this is not accurate)
4065 u32 bytes_per_bunch = 5000;
4067 core::array< core::list<SendableMedia> > file_bunches;
4068 file_bunches.push_back(core::list<SendableMedia>());
4070 u32 file_size_bunch_total = 0;
4072 for(core::list<MediaRequest>::ConstIterator i = tosend.begin();
4073 i != tosend.end(); i++)
4075 if(m_media.find(i->name) == m_media.end()){
4076 errorstream<<"Server::sendRequestedMedia(): Client asked for "
4077 <<"unknown file \""<<(i->name)<<"\""<<std::endl;
4081 //TODO get path + name
4082 std::string tpath = m_media[(*i).name].path;
4085 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
4086 if(fis.good() == false){
4087 errorstream<<"Server::sendRequestedMedia(): Could not open \""
4088 <<tpath<<"\" for reading"<<std::endl;
4091 std::ostringstream tmp_os(std::ios_base::binary);
4095 fis.read(buf, 1024);
4096 std::streamsize len = fis.gcount();
4097 tmp_os.write(buf, len);
4098 file_size_bunch_total += len;
4107 errorstream<<"Server::sendRequestedMedia(): Failed to read \""
4108 <<(*i).name<<"\""<<std::endl;
4111 /*infostream<<"Server::sendRequestedMedia(): Loaded \""
4112 <<tname<<"\""<<std::endl;*/
4114 file_bunches[file_bunches.size()-1].push_back(
4115 SendableMedia((*i).name, tpath, tmp_os.str()));
4117 // Start next bunch if got enough data
4118 if(file_size_bunch_total >= bytes_per_bunch){
4119 file_bunches.push_back(core::list<SendableMedia>());
4120 file_size_bunch_total = 0;
4125 /* Create and send packets */
4127 u32 num_bunches = file_bunches.size();
4128 for(u32 i=0; i<num_bunches; i++)
4130 std::ostringstream os(std::ios_base::binary);
4134 u16 total number of texture bunches
4135 u16 index of this bunch
4136 u32 number of files in this bunch
4145 writeU16(os, TOCLIENT_MEDIA);
4146 writeU16(os, num_bunches);
4148 writeU32(os, file_bunches[i].size());
4150 for(core::list<SendableMedia>::Iterator
4151 j = file_bunches[i].begin();
4152 j != file_bunches[i].end(); j++){
4153 os<<serializeString(j->name);
4154 os<<serializeLongString(j->data);
4158 std::string s = os.str();
4159 verbosestream<<"Server::sendRequestedMedia(): bunch "
4160 <<i<<"/"<<num_bunches
4161 <<" files="<<file_bunches[i].size()
4162 <<" size=" <<s.size()<<std::endl;
4163 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4165 m_con.Send(peer_id, 0, data, true);
4173 void Server::DiePlayer(u16 peer_id)
4175 DSTACK(__FUNCTION_NAME);
4177 PlayerSAO *playersao = getPlayerSAO(peer_id);
4180 infostream<<"Server::DiePlayer(): Player "
4181 <<playersao->getPlayer()->getName()
4182 <<" dies"<<std::endl;
4184 playersao->setHP(0);
4186 // Trigger scripted stuff
4187 scriptapi_on_dieplayer(m_lua, playersao);
4189 SendPlayerHP(peer_id);
4190 SendDeathscreen(m_con, peer_id, false, v3f(0,0,0));
4193 void Server::RespawnPlayer(u16 peer_id)
4195 DSTACK(__FUNCTION_NAME);
4197 PlayerSAO *playersao = getPlayerSAO(peer_id);
4200 infostream<<"Server::RespawnPlayer(): Player "
4201 <<playersao->getPlayer()->getName()
4202 <<" respawns"<<std::endl;
4204 playersao->setHP(PLAYER_MAX_HP);
4206 bool repositioned = scriptapi_on_respawnplayer(m_lua, playersao);
4208 v3f pos = findSpawnPos(m_env->getServerMap());
4209 playersao->setPos(pos);
4213 void Server::UpdateCrafting(u16 peer_id)
4215 DSTACK(__FUNCTION_NAME);
4217 Player* player = m_env->getPlayer(peer_id);
4220 // Get a preview for crafting
4222 // No crafting in creative mode
4223 if(g_settings->getBool("creative_mode") == false)
4224 getCraftingResult(&player->inventory, preview, false, this);
4226 // Put the new preview in
4227 InventoryList *plist = player->inventory.getList("craftpreview");
4229 assert(plist->getSize() >= 1);
4230 plist->changeItem(0, preview);
4233 RemoteClient* Server::getClient(u16 peer_id)
4235 DSTACK(__FUNCTION_NAME);
4236 //JMutexAutoLock lock(m_con_mutex);
4237 core::map<u16, RemoteClient*>::Node *n;
4238 n = m_clients.find(peer_id);
4239 // A client should exist for all peers
4241 return n->getValue();
4244 std::wstring Server::getStatusString()
4246 std::wostringstream os(std::ios_base::binary);
4249 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4251 os<<L", uptime="<<m_uptime.get();
4252 // Information about clients
4254 for(core::map<u16, RemoteClient*>::Iterator
4255 i = m_clients.getIterator();
4256 i.atEnd() == false; i++)
4258 // Get client and check that it is valid
4259 RemoteClient *client = i.getNode()->getValue();
4260 assert(client->peer_id == i.getNode()->getKey());
4261 if(client->serialization_version == SER_FMT_VER_INVALID)
4264 Player *player = m_env->getPlayer(client->peer_id);
4265 // Get name of player
4266 std::wstring name = L"unknown";
4268 name = narrow_to_wide(player->getName());
4269 // Add name to information string
4273 if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
4274 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4275 if(g_settings->get("motd") != "")
4276 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4280 u64 Server::getPlayerAuthPrivs(const std::string &name)
4283 return m_authmanager.getPrivs(name);
4285 catch(AuthNotFoundException &e)
4287 dstream<<"WARNING: Auth not found for "<<name<<std::endl;
4292 void Server::setPlayerAuthPrivs(const std::string &name, u64 privs)
4295 return m_authmanager.setPrivs(name, privs);
4297 catch(AuthNotFoundException &e)
4299 dstream<<"WARNING: Auth not found for "<<name<<std::endl;
4303 u64 Server::getPlayerEffectivePrivs(const std::string &name)
4305 // Local player gets all privileges regardless of
4306 // what's set on their account.
4307 if(m_simple_singleplayer_mode)
4309 if(name == g_settings->get("name"))
4311 return getPlayerAuthPrivs(name);
4314 void Server::setPlayerPassword(const std::string &name, const std::wstring &password)
4316 // Add player to auth manager
4317 if(m_authmanager.exists(name) == false)
4319 infostream<<"Server: adding player "<<name
4320 <<" to auth manager"<<std::endl;
4321 m_authmanager.add(name);
4322 m_authmanager.setPrivs(name,
4323 stringToPrivs(g_settings->get("default_privs")));
4325 // Change password and save
4326 m_authmanager.setPassword(name, translatePassword(name, password));
4327 m_authmanager.save();
4330 // Saves g_settings to configpath given at initialization
4331 void Server::saveConfig()
4333 if(m_path_config != "")
4334 g_settings->updateConfigFile(m_path_config.c_str());
4337 void Server::notifyPlayer(const char *name, const std::wstring msg)
4339 Player *player = m_env->getPlayer(name);
4342 SendChatMessage(player->peer_id, std::wstring(L"Server: -!- ")+msg);
4345 void Server::notifyPlayers(const std::wstring msg)
4347 BroadcastChatMessage(msg);
4350 void Server::queueBlockEmerge(v3s16 blockpos, bool allow_generate)
4354 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
4355 m_emerge_queue.addBlock(PEER_ID_INEXISTENT, blockpos, flags);
4358 // IGameDef interface
4360 IItemDefManager* Server::getItemDefManager()
4364 INodeDefManager* Server::getNodeDefManager()
4368 ICraftDefManager* Server::getCraftDefManager()
4372 ITextureSource* Server::getTextureSource()
4376 u16 Server::allocateUnknownNodeId(const std::string &name)
4378 return m_nodedef->allocateDummy(name);
4380 ISoundManager* Server::getSoundManager()
4382 return &dummySoundManager;
4384 MtEventManager* Server::getEventManager()
4389 IWritableItemDefManager* Server::getWritableItemDefManager()
4393 IWritableNodeDefManager* Server::getWritableNodeDefManager()
4397 IWritableCraftDefManager* Server::getWritableCraftDefManager()
4402 const ModSpec* Server::getModSpec(const std::string &modname)
4404 for(core::list<ModSpec>::Iterator i = m_mods.begin();
4405 i != m_mods.end(); i++){
4406 const ModSpec &mod = *i;
4407 if(mod.name == modname)
4413 v3f findSpawnPos(ServerMap &map)
4415 //return v3f(50,50,50)*BS;
4420 nodepos = v2s16(0,0);
4425 // Try to find a good place a few times
4426 for(s32 i=0; i<1000; i++)
4429 // We're going to try to throw the player to this position
4430 v2s16 nodepos2d = v2s16(-range + (myrand()%(range*2)),
4431 -range + (myrand()%(range*2)));
4432 //v2s16 sectorpos = getNodeSectorPos(nodepos2d);
4433 // Get ground height at point (fallbacks to heightmap function)
4434 s16 groundheight = map.findGroundLevel(nodepos2d);
4435 // Don't go underwater
4436 if(groundheight < WATER_LEVEL)
4438 //infostream<<"-> Underwater"<<std::endl;
4441 // Don't go to high places
4442 if(groundheight > WATER_LEVEL + 4)
4444 //infostream<<"-> Underwater"<<std::endl;
4448 nodepos = v3s16(nodepos2d.X, groundheight-2, nodepos2d.Y);
4449 bool is_good = false;
4451 for(s32 i=0; i<10; i++){
4452 v3s16 blockpos = getNodeBlockPos(nodepos);
4453 map.emergeBlock(blockpos, true);
4454 MapNode n = map.getNodeNoEx(nodepos);
4455 if(n.getContent() == CONTENT_AIR){
4466 // Found a good place
4467 //infostream<<"Searched through "<<i<<" places."<<std::endl;
4473 return intToFloat(nodepos, BS);
4476 PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id)
4478 RemotePlayer *player = NULL;
4479 bool newplayer = false;
4482 Try to get an existing player
4484 player = static_cast<RemotePlayer*>(m_env->getPlayer(name));
4486 // If player is already connected, cancel
4487 if(player != NULL && player->peer_id != 0)
4489 infostream<<"emergePlayer(): Player already connected"<<std::endl;
4494 If player with the wanted peer_id already exists, cancel.
4496 if(m_env->getPlayer(peer_id) != NULL)
4498 infostream<<"emergePlayer(): Player with wrong name but same"
4499 " peer_id already exists"<<std::endl;
4504 Create a new player if it doesn't exist yet
4509 player = new RemotePlayer(this);
4510 player->updateName(name);
4512 /* Set player position */
4513 infostream<<"Server: Finding spawn place for player \""
4514 <<name<<"\""<<std::endl;
4515 v3f pos = findSpawnPos(m_env->getServerMap());
4516 player->setPosition(pos);
4518 /* Add player to environment */
4519 m_env->addPlayer(player);
4523 Create a new player active object
4525 PlayerSAO *playersao = new PlayerSAO(m_env, player, peer_id);
4527 /* Add object to environment */
4528 m_env->addActiveObject(playersao);
4532 scriptapi_on_newplayer(m_lua, playersao);
4534 scriptapi_on_joinplayer(m_lua, playersao);
4537 if(g_settings->getBool("creative_mode"))
4538 playersao->createCreativeInventory();
4543 void Server::handlePeerChange(PeerChange &c)
4545 JMutexAutoLock envlock(m_env_mutex);
4546 JMutexAutoLock conlock(m_con_mutex);
4548 if(c.type == PEER_ADDED)
4555 core::map<u16, RemoteClient*>::Node *n;
4556 n = m_clients.find(c.peer_id);
4557 // The client shouldn't already exist
4561 RemoteClient *client = new RemoteClient();
4562 client->peer_id = c.peer_id;
4563 m_clients.insert(client->peer_id, client);
4566 else if(c.type == PEER_REMOVED)
4573 core::map<u16, RemoteClient*>::Node *n;
4574 n = m_clients.find(c.peer_id);
4575 // The client should exist
4579 Mark objects to be not known by the client
4581 RemoteClient *client = n->getValue();
4583 for(core::map<u16, bool>::Iterator
4584 i = client->m_known_objects.getIterator();
4585 i.atEnd()==false; i++)
4588 u16 id = i.getNode()->getKey();
4589 ServerActiveObject* obj = m_env->getActiveObject(id);
4591 if(obj && obj->m_known_by_count > 0)
4592 obj->m_known_by_count--;
4596 Clear references to playing sounds
4598 for(std::map<s32, ServerPlayingSound>::iterator
4599 i = m_playing_sounds.begin();
4600 i != m_playing_sounds.end();)
4602 ServerPlayingSound &psound = i->second;
4603 psound.clients.erase(c.peer_id);
4604 if(psound.clients.size() == 0)
4605 m_playing_sounds.erase(i++);
4610 Player *player = m_env->getPlayer(c.peer_id);
4612 // Collect information about leaving in chat
4613 std::wstring message;
4617 std::wstring name = narrow_to_wide(player->getName());
4620 message += L" left game";
4622 message += L" (timed out)";
4626 /* Run scripts and remove from environment */
4630 PlayerSAO *playersao = player->getPlayerSAO();
4633 scriptapi_on_leaveplayer(m_lua, playersao);
4635 playersao->disconnected();
4645 std::ostringstream os(std::ios_base::binary);
4646 for(core::map<u16, RemoteClient*>::Iterator
4647 i = m_clients.getIterator();
4648 i.atEnd() == false; i++)
4650 RemoteClient *client = i.getNode()->getValue();
4651 assert(client->peer_id == i.getNode()->getKey());
4652 if(client->serialization_version == SER_FMT_VER_INVALID)
4655 Player *player = m_env->getPlayer(client->peer_id);
4658 // Get name of player
4659 os<<player->getName()<<" ";
4662 actionstream<<player->getName()<<" "
4663 <<(c.timeout?"times out.":"leaves game.")
4664 <<" List of players: "
4665 <<os.str()<<std::endl;
4670 delete m_clients[c.peer_id];
4671 m_clients.remove(c.peer_id);
4673 // Send player info to all remaining clients
4674 //SendPlayerInfos();
4676 // Send leave chat message to all remaining clients
4677 if(message.length() != 0)
4678 BroadcastChatMessage(message);
4687 void Server::handlePeerChanges()
4689 while(m_peer_change_queue.size() > 0)
4691 PeerChange c = m_peer_change_queue.pop_front();
4693 verbosestream<<"Server: Handling peer change: "
4694 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4697 handlePeerChange(c);
4701 u64 Server::getPlayerPrivs(Player *player)
4705 std::string playername = player->getName();
4706 return getPlayerEffectivePrivs(playername);
4709 void dedicated_server_loop(Server &server, bool &kill)
4711 DSTACK(__FUNCTION_NAME);
4713 verbosestream<<"dedicated_server_loop()"<<std::endl;
4715 IntervalLimiter m_profiler_interval;
4719 float steplen = g_settings->getFloat("dedicated_server_step");
4720 // This is kind of a hack but can be done like this
4721 // because server.step() is very light
4723 ScopeProfiler sp(g_profiler, "dedicated server sleep");
4724 sleep_ms((int)(steplen*1000.0));
4726 server.step(steplen);
4728 if(server.getShutdownRequested() || kill)
4730 infostream<<"Dedicated server quitting"<<std::endl;
4737 float profiler_print_interval =
4738 g_settings->getFloat("profiler_print_interval");
4739 if(profiler_print_interval != 0)
4741 if(m_profiler_interval.step(steplen, profiler_print_interval))
4743 infostream<<"Profiler:"<<std::endl;
4744 g_profiler->print(infostream);
4745 g_profiler->clear();