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 m_server->setAsyncFatalError(e.what());
151 END_DEBUG_EXCEPTION_HANDLER(errorstream)
156 void * EmergeThread::Thread()
160 log_register_thread("EmergeThread");
162 DSTACK(__FUNCTION_NAME);
164 BEGIN_DEBUG_EXCEPTION_HANDLER
166 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
169 Get block info from queue, emerge them and send them
172 After queue is empty, exit.
176 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
180 SharedPtr<QueuedBlockEmerge> q(qptr);
186 Do not generate over-limit
188 if(blockpos_over_limit(p))
191 //infostream<<"EmergeThread::Thread(): running"<<std::endl;
193 //TimeTaker timer("block emerge");
196 Try to emerge it from somewhere.
198 If it is only wanted as optional, only loading from disk
203 Check if any peer wants it as non-optional. In that case it
206 Also decrement the emerge queue count in clients.
209 bool only_from_disk = true;
212 core::map<u16, u8>::Iterator i;
213 for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
215 //u16 peer_id = i.getNode()->getKey();
218 u8 flags = i.getNode()->getValue();
219 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
220 only_from_disk = false;
225 if(enable_mapgen_debug_info)
226 infostream<<"EmergeThread: p="
227 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
228 <<"only_from_disk="<<only_from_disk<<std::endl;
230 ServerMap &map = ((ServerMap&)m_server->m_env->getMap());
232 MapBlock *block = NULL;
233 bool got_block = true;
234 core::map<v3s16, MapBlock*> modified_blocks;
237 Try to fetch block from memory or disk.
238 If not found and asked to generate, initialize generator.
241 bool started_generate = false;
242 mapgen::BlockMakeData data;
245 JMutexAutoLock envlock(m_server->m_env_mutex);
247 // Load sector if it isn't loaded
248 if(map.getSectorNoGenerateNoEx(p2d) == NULL)
249 map.loadSectorMeta(p2d);
251 // Attempt to load block
252 block = map.getBlockNoCreateNoEx(p);
253 if(!block || block->isDummy() || !block->isGenerated())
255 if(enable_mapgen_debug_info)
256 infostream<<"EmergeThread: not in memory, "
257 <<"attempting to load from disk"<<std::endl;
259 block = map.loadBlock(p);
262 // If could not load and allowed to generate, start generation
263 // inside this same envlock
264 if(only_from_disk == false &&
265 (block == NULL || block->isGenerated() == false)){
266 if(enable_mapgen_debug_info)
267 infostream<<"EmergeThread: generating"<<std::endl;
268 started_generate = true;
270 map.initBlockMake(&data, p);
275 If generator was initialized, generate now when envlock is free.
280 ScopeProfiler sp(g_profiler, "EmergeThread: mapgen::make_block",
282 TimeTaker t("mapgen::make_block()");
284 mapgen::make_block(&data);
286 if(enable_mapgen_debug_info == false)
287 t.stop(true); // Hide output
291 // Lock environment again to access the map
292 JMutexAutoLock envlock(m_server->m_env_mutex);
294 ScopeProfiler sp(g_profiler, "EmergeThread: after "
295 "mapgen::make_block (envlock)", SPT_AVG);
297 // Blit data back on map, update lighting, add mobs and
298 // whatever this does
299 map.finishBlockMake(&data, modified_blocks);
302 block = map.getBlockNoCreateNoEx(p);
304 // If block doesn't exist, don't try doing anything with it
305 // This happens if the block is not in generation boundaries
310 Do some post-generate stuff
313 v3s16 minp = data.blockpos_min*MAP_BLOCKSIZE;
314 v3s16 maxp = data.blockpos_max*MAP_BLOCKSIZE +
315 v3s16(1,1,1)*(MAP_BLOCKSIZE-1);
318 Ignore map edit events, they will not need to be
319 sent to anybody because the block hasn't been sent
322 //MapEditEventIgnorer ign(&m_server->m_ignore_map_edit_events);
323 MapEditEventAreaIgnorer ign(
324 &m_server->m_ignore_map_edit_events_area,
325 VoxelArea(minp, maxp));
327 TimeTaker timer("on_generated");
328 scriptapi_environment_on_generated(m_server->m_lua,
329 minp, maxp, mapgen::get_blockseed(data.seed, minp));
330 /*int t = timer.stop(true);
331 dstream<<"on_generated took "<<t<<"ms"<<std::endl;*/
334 if(enable_mapgen_debug_info)
335 infostream<<"EmergeThread: ended up with: "
336 <<analyze_block(block)<<std::endl;
338 // Activate objects and stuff
339 m_server->m_env->activateBlock(block, 0);
347 Set sent status of modified blocks on clients
350 // NOTE: Server's clients are also behind the connection mutex
351 JMutexAutoLock lock(m_server->m_con_mutex);
354 Add the originally fetched block to the modified list
358 modified_blocks.insert(p, block);
362 Set the modified blocks unsent for all the clients
365 for(core::map<u16, RemoteClient*>::Iterator
366 i = m_server->m_clients.getIterator();
367 i.atEnd() == false; i++)
369 RemoteClient *client = i.getNode()->getValue();
371 if(modified_blocks.size() > 0)
373 // Remove block from sent history
374 client->SetBlocksNotSent(modified_blocks);
380 END_DEBUG_EXCEPTION_HANDLER(errorstream)
382 log_deregister_thread();
387 v3f ServerSoundParams::getPos(ServerEnvironment *env, bool *pos_exists) const
389 if(pos_exists) *pos_exists = false;
394 if(pos_exists) *pos_exists = true;
399 ServerActiveObject *sao = env->getActiveObject(object);
402 if(pos_exists) *pos_exists = true;
403 return sao->getBasePosition(); }
408 void RemoteClient::GetNextBlocks(Server *server, float dtime,
409 core::array<PrioritySortedBlockTransfer> &dest)
411 DSTACK(__FUNCTION_NAME);
414 TimeTaker timer("RemoteClient::GetNextBlocks", &timer_result);*/
417 m_nothing_to_send_pause_timer -= dtime;
418 m_nearest_unsent_reset_timer += dtime;
420 if(m_nothing_to_send_pause_timer >= 0)
425 // Won't send anything if already sending
426 if(m_blocks_sending.size() >= g_settings->getU16
427 ("max_simultaneous_block_sends_per_client"))
429 //infostream<<"Not sending any blocks, Queue full."<<std::endl;
433 //TimeTaker timer("RemoteClient::GetNextBlocks");
435 Player *player = server->m_env->getPlayer(peer_id);
437 assert(player != NULL);
439 v3f playerpos = player->getPosition();
440 v3f playerspeed = player->getSpeed();
441 v3f playerspeeddir(0,0,0);
442 if(playerspeed.getLength() > 1.0*BS)
443 playerspeeddir = playerspeed / playerspeed.getLength();
444 // Predict to next block
445 v3f playerpos_predicted = playerpos + playerspeeddir*MAP_BLOCKSIZE*BS;
447 v3s16 center_nodepos = floatToInt(playerpos_predicted, BS);
449 v3s16 center = getNodeBlockPos(center_nodepos);
451 // Camera position and direction
452 v3f camera_pos = player->getEyePosition();
453 v3f camera_dir = v3f(0,0,1);
454 camera_dir.rotateYZBy(player->getPitch());
455 camera_dir.rotateXZBy(player->getYaw());
457 /*infostream<<"camera_dir=("<<camera_dir.X<<","<<camera_dir.Y<<","
458 <<camera_dir.Z<<")"<<std::endl;*/
461 Get the starting value of the block finder radius.
464 if(m_last_center != center)
466 m_nearest_unsent_d = 0;
467 m_last_center = center;
470 /*infostream<<"m_nearest_unsent_reset_timer="
471 <<m_nearest_unsent_reset_timer<<std::endl;*/
473 // Reset periodically to workaround for some bugs or stuff
474 if(m_nearest_unsent_reset_timer > 20.0)
476 m_nearest_unsent_reset_timer = 0;
477 m_nearest_unsent_d = 0;
478 //infostream<<"Resetting m_nearest_unsent_d for "
479 // <<server->getPlayerName(peer_id)<<std::endl;
482 //s16 last_nearest_unsent_d = m_nearest_unsent_d;
483 s16 d_start = m_nearest_unsent_d;
485 //infostream<<"d_start="<<d_start<<std::endl;
487 u16 max_simul_sends_setting = g_settings->getU16
488 ("max_simultaneous_block_sends_per_client");
489 u16 max_simul_sends_usually = max_simul_sends_setting;
492 Check the time from last addNode/removeNode.
494 Decrease send rate if player is building stuff.
496 m_time_from_building += dtime;
497 if(m_time_from_building < g_settings->getFloat(
498 "full_block_send_enable_min_time_from_building"))
500 max_simul_sends_usually
501 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
505 Number of blocks sending + number of blocks selected for sending
507 u32 num_blocks_selected = m_blocks_sending.size();
510 next time d will be continued from the d from which the nearest
511 unsent block was found this time.
513 This is because not necessarily any of the blocks found this
514 time are actually sent.
516 s32 new_nearest_unsent_d = -1;
518 s16 d_max = g_settings->getS16("max_block_send_distance");
519 s16 d_max_gen = g_settings->getS16("max_block_generate_distance");
521 // Don't loop very much at a time
522 s16 max_d_increment_at_time = 2;
523 if(d_max > d_start + max_d_increment_at_time)
524 d_max = d_start + max_d_increment_at_time;
525 /*if(d_max_gen > d_start+2)
526 d_max_gen = d_start+2;*/
528 //infostream<<"Starting from "<<d_start<<std::endl;
530 s32 nearest_emerged_d = -1;
531 s32 nearest_emergefull_d = -1;
532 s32 nearest_sent_d = -1;
533 bool queue_is_full = false;
536 for(d = d_start; d <= d_max; d++)
538 /*errorstream<<"checking d="<<d<<" for "
539 <<server->getPlayerName(peer_id)<<std::endl;*/
540 //infostream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
543 If m_nearest_unsent_d was changed by the EmergeThread
544 (it can change it to 0 through SetBlockNotSent),
546 Else update m_nearest_unsent_d
548 /*if(m_nearest_unsent_d != last_nearest_unsent_d)
550 d = m_nearest_unsent_d;
551 last_nearest_unsent_d = m_nearest_unsent_d;
555 Get the border/face dot coordinates of a "d-radiused"
558 core::list<v3s16> list;
559 getFacePositions(list, d);
561 core::list<v3s16>::Iterator li;
562 for(li=list.begin(); li!=list.end(); li++)
564 v3s16 p = *li + center;
568 - Don't allow too many simultaneous transfers
569 - EXCEPT when the blocks are very close
571 Also, don't send blocks that are already flying.
574 // Start with the usual maximum
575 u16 max_simul_dynamic = max_simul_sends_usually;
577 // If block is very close, allow full maximum
578 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
579 max_simul_dynamic = max_simul_sends_setting;
581 // Don't select too many blocks for sending
582 if(num_blocks_selected >= max_simul_dynamic)
584 queue_is_full = true;
585 goto queue_full_break;
588 // Don't send blocks that are currently being transferred
589 if(m_blocks_sending.find(p) != NULL)
595 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
596 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
597 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
598 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
599 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
600 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
603 // If this is true, inexistent block will be made from scratch
604 bool generate = d <= d_max_gen;
607 /*// Limit the generating area vertically to 2/3
608 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
611 // Limit the send area vertically to 1/2
612 if(abs(p.Y - center.Y) > d_max / 2)
618 If block is far away, don't generate it unless it is
624 // Block center y in nodes
625 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
626 // Don't generate if it's very high or very low
627 if(y < -64 || y > 64)
631 v2s16 p2d_nodes_center(
635 // Get ground height in nodes
636 s16 gh = server->m_env->getServerMap().findGroundLevel(
639 // If differs a lot, don't generate
640 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
642 // Actually, don't even send it
648 //infostream<<"d="<<d<<std::endl;
651 Don't generate or send if not in sight
652 FIXME This only works if the client uses a small enough
653 FOV setting. The default of 72 degrees is fine.
656 float camera_fov = (72.0*PI/180) * 4./3.;
657 if(isBlockInSight(p, camera_pos, camera_dir, camera_fov, 10000*BS) == false)
663 Don't send already sent blocks
666 if(m_blocks_sent.find(p) != NULL)
673 Check if map has this block
675 MapBlock *block = server->m_env->getMap().getBlockNoCreateNoEx(p);
677 bool surely_not_found_on_disk = false;
678 bool block_is_invalid = false;
681 // Reset usage timer, this block will be of use in the future.
682 block->resetUsageTimer();
684 // Block is dummy if data doesn't exist.
685 // It means it has been not found from disk and not generated
688 surely_not_found_on_disk = true;
691 // Block is valid if lighting is up-to-date and data exists
692 if(block->isValid() == false)
694 block_is_invalid = true;
697 /*if(block->isFullyGenerated() == false)
699 block_is_invalid = true;
704 ServerMap *map = (ServerMap*)(&server->m_env->getMap());
705 v2s16 chunkpos = map->sector_to_chunk(p2d);
706 if(map->chunkNonVolatile(chunkpos) == false)
707 block_is_invalid = true;
709 if(block->isGenerated() == false)
710 block_is_invalid = true;
713 If block is not close, don't send it unless it is near
716 Block is near ground level if night-time mesh
717 differs from day-time mesh.
721 if(block->getDayNightDiff() == false)
728 If block has been marked to not exist on disk (dummy)
729 and generating new ones is not wanted, skip block.
731 if(generate == false && surely_not_found_on_disk == true)
738 Add inexistent block to emerge queue.
740 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
742 //TODO: Get value from somewhere
743 // Allow only one block in emerge queue
744 //if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
745 // Allow two blocks in queue per client
746 //if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
748 // Make it more responsive when needing to generate stuff
749 if(surely_not_found_on_disk)
751 if(server->m_emerge_queue.peerItemCount(peer_id) < max_emerge)
753 //infostream<<"Adding block to emerge queue"<<std::endl;
755 // Add it to the emerge queue and trigger the thread
758 if(generate == false)
759 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
761 server->m_emerge_queue.addBlock(peer_id, p, flags);
762 server->m_emergethread.trigger();
764 if(nearest_emerged_d == -1)
765 nearest_emerged_d = d;
767 if(nearest_emergefull_d == -1)
768 nearest_emergefull_d = d;
775 if(nearest_sent_d == -1)
779 Add block to send queue
782 /*errorstream<<"sending from d="<<d<<" to "
783 <<server->getPlayerName(peer_id)<<std::endl;*/
785 PrioritySortedBlockTransfer q((float)d, p, peer_id);
789 num_blocks_selected += 1;
794 //infostream<<"Stopped at "<<d<<std::endl;
796 // If nothing was found for sending and nothing was queued for
797 // emerging, continue next time browsing from here
798 if(nearest_emerged_d != -1){
799 new_nearest_unsent_d = nearest_emerged_d;
800 } else if(nearest_emergefull_d != -1){
801 new_nearest_unsent_d = nearest_emergefull_d;
803 if(d > g_settings->getS16("max_block_send_distance")){
804 new_nearest_unsent_d = 0;
805 m_nothing_to_send_pause_timer = 2.0;
806 /*infostream<<"GetNextBlocks(): d wrapped around for "
807 <<server->getPlayerName(peer_id)
808 <<"; setting to 0 and pausing"<<std::endl;*/
810 if(nearest_sent_d != -1)
811 new_nearest_unsent_d = nearest_sent_d;
813 new_nearest_unsent_d = d;
817 if(new_nearest_unsent_d != -1)
818 m_nearest_unsent_d = new_nearest_unsent_d;
820 /*timer_result = timer.stop(true);
821 if(timer_result != 0)
822 infostream<<"GetNextBlocks duration: "<<timer_result<<" (!=0)"<<std::endl;*/
825 void RemoteClient::GotBlock(v3s16 p)
827 if(m_blocks_sending.find(p) != NULL)
828 m_blocks_sending.remove(p);
831 /*infostream<<"RemoteClient::GotBlock(): Didn't find in"
832 " m_blocks_sending"<<std::endl;*/
833 m_excess_gotblocks++;
835 m_blocks_sent.insert(p, true);
838 void RemoteClient::SentBlock(v3s16 p)
840 if(m_blocks_sending.find(p) == NULL)
841 m_blocks_sending.insert(p, 0.0);
843 infostream<<"RemoteClient::SentBlock(): Sent block"
844 " already in m_blocks_sending"<<std::endl;
847 void RemoteClient::SetBlockNotSent(v3s16 p)
849 m_nearest_unsent_d = 0;
851 if(m_blocks_sending.find(p) != NULL)
852 m_blocks_sending.remove(p);
853 if(m_blocks_sent.find(p) != NULL)
854 m_blocks_sent.remove(p);
857 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
859 m_nearest_unsent_d = 0;
861 for(core::map<v3s16, MapBlock*>::Iterator
862 i = blocks.getIterator();
863 i.atEnd()==false; i++)
865 v3s16 p = i.getNode()->getKey();
867 if(m_blocks_sending.find(p) != NULL)
868 m_blocks_sending.remove(p);
869 if(m_blocks_sent.find(p) != NULL)
870 m_blocks_sent.remove(p);
878 PlayerInfo::PlayerInfo()
884 void PlayerInfo::PrintLine(std::ostream *s)
887 (*s)<<"\""<<name<<"\" ("
888 <<(position.X/10)<<","<<(position.Y/10)
889 <<","<<(position.Z/10)<<") ";
891 (*s)<<" avg_rtt="<<avg_rtt;
900 const std::string &path_world,
901 const std::string &path_config,
902 const SubgameSpec &gamespec,
903 bool simple_singleplayer_mode
905 m_path_world(path_world),
906 m_path_config(path_config),
907 m_gamespec(gamespec),
908 m_simple_singleplayer_mode(simple_singleplayer_mode),
909 m_async_fatal_error(""),
911 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
912 m_banmanager(path_world+DIR_DELIM+"ipban.txt"),
914 m_itemdef(createItemDefManager()),
915 m_nodedef(createNodeDefManager()),
916 m_craftdef(createCraftDefManager()),
917 m_event(new EventManager()),
919 m_emergethread(this),
920 m_time_of_day_send_timer(0),
922 m_shutdown_requested(false),
923 m_ignore_map_edit_events(false),
924 m_ignore_map_edit_events_peer_id(0)
926 m_liquid_transform_timer = 0.0;
927 m_print_info_timer = 0.0;
928 m_objectdata_timer = 0.0;
929 m_emergethread_trigger_timer = 0.0;
930 m_savemap_timer = 0.0;
934 m_step_dtime_mutex.Init();
938 throw ServerError("Supplied empty world path");
940 if(!gamespec.isValid())
941 throw ServerError("Supplied invalid gamespec");
943 infostream<<"Server created for gameid \""<<m_gamespec.id<<"\"";
944 if(m_simple_singleplayer_mode)
945 infostream<<" in simple singleplayer mode"<<std::endl;
947 infostream<<std::endl;
948 infostream<<"- world: "<<m_path_world<<std::endl;
949 infostream<<"- config: "<<m_path_config<<std::endl;
950 infostream<<"- game: "<<m_gamespec.path<<std::endl;
952 // Add world mod search path
953 m_modspaths.push_front(m_path_world + DIR_DELIM + "worldmods");
954 // Add addon mod search path
955 for(std::set<std::string>::const_iterator i = m_gamespec.mods_paths.begin();
956 i != m_gamespec.mods_paths.end(); i++)
957 m_modspaths.push_front((*i));
959 // Print out mod search paths
960 for(core::list<std::string>::Iterator i = m_modspaths.begin();
961 i != m_modspaths.end(); i++){
962 std::string modspath = *i;
963 infostream<<"- mods: "<<modspath<<std::endl;
966 // Path to builtin.lua
967 std::string builtinpath = getBuiltinLuaPath() + DIR_DELIM + "builtin.lua";
969 // Create world if it doesn't exist
970 if(!initializeWorld(m_path_world, m_gamespec.id))
971 throw ServerError("Failed to initialize world");
974 JMutexAutoLock envlock(m_env_mutex);
975 JMutexAutoLock conlock(m_con_mutex);
977 // Initialize scripting
979 infostream<<"Server: Initializing Lua"<<std::endl;
980 m_lua = script_init();
983 scriptapi_export(m_lua, this);
984 // Load and run builtin.lua
985 infostream<<"Server: Loading builtin.lua [\""
986 <<builtinpath<<"\"]"<<std::endl;
987 bool success = scriptapi_loadmod(m_lua, builtinpath, "__builtin");
989 errorstream<<"Server: Failed to load and run "
990 <<builtinpath<<std::endl;
991 throw ModError("Failed to load and run "+builtinpath);
993 // Find mods in mod search paths
994 m_mods = getMods(m_modspaths);
996 infostream<<"Server: Loading mods: ";
997 for(core::list<ModSpec>::Iterator i = m_mods.begin();
998 i != m_mods.end(); i++){
999 const ModSpec &mod = *i;
1000 infostream<<mod.name<<" ";
1002 infostream<<std::endl;
1003 // Load and run "mod" scripts
1004 for(core::list<ModSpec>::Iterator i = m_mods.begin();
1005 i != m_mods.end(); i++){
1006 const ModSpec &mod = *i;
1007 std::string scriptpath = mod.path + DIR_DELIM + "init.lua";
1008 infostream<<" ["<<padStringRight(mod.name, 12)<<"] [\""
1009 <<scriptpath<<"\"]"<<std::endl;
1010 bool success = scriptapi_loadmod(m_lua, scriptpath, mod.name);
1012 errorstream<<"Server: Failed to load and run "
1013 <<scriptpath<<std::endl;
1014 throw ModError("Failed to load and run "+scriptpath);
1018 // Read Textures and calculate sha1 sums
1021 // Apply item aliases in the node definition manager
1022 m_nodedef->updateAliases(m_itemdef);
1024 // Initialize Environment
1026 m_env = new ServerEnvironment(new ServerMap(path_world, this), m_lua,
1029 // Give environment reference to scripting api
1030 scriptapi_add_environment(m_lua, m_env);
1032 // Register us to receive map edit events
1033 m_env->getMap().addEventReceiver(this);
1035 // If file exists, load environment metadata
1036 if(fs::PathExists(m_path_world+DIR_DELIM+"env_meta.txt"))
1038 infostream<<"Server: Loading environment metadata"<<std::endl;
1039 m_env->loadMeta(m_path_world);
1043 infostream<<"Server: Loading players"<<std::endl;
1044 m_env->deSerializePlayers(m_path_world);
1047 Add some test ActiveBlockModifiers to environment
1049 add_legacy_abms(m_env, m_nodedef);
1054 infostream<<"Server destructing"<<std::endl;
1057 Send shutdown message
1060 JMutexAutoLock conlock(m_con_mutex);
1062 std::wstring line = L"*** Server shutting down";
1065 Send the message to clients
1067 for(core::map<u16, RemoteClient*>::Iterator
1068 i = m_clients.getIterator();
1069 i.atEnd() == false; i++)
1071 // Get client and check that it is valid
1072 RemoteClient *client = i.getNode()->getValue();
1073 assert(client->peer_id == i.getNode()->getKey());
1074 if(client->serialization_version == SER_FMT_VER_INVALID)
1078 SendChatMessage(client->peer_id, line);
1080 catch(con::PeerNotFoundException &e)
1086 JMutexAutoLock envlock(m_env_mutex);
1091 infostream<<"Server: Saving players"<<std::endl;
1092 m_env->serializePlayers(m_path_world);
1095 Save environment metadata
1097 infostream<<"Server: Saving environment metadata"<<std::endl;
1098 m_env->saveMeta(m_path_world);
1110 JMutexAutoLock clientslock(m_con_mutex);
1112 for(core::map<u16, RemoteClient*>::Iterator
1113 i = m_clients.getIterator();
1114 i.atEnd() == false; i++)
1117 // NOTE: These are removed by env destructor
1119 u16 peer_id = i.getNode()->getKey();
1120 JMutexAutoLock envlock(m_env_mutex);
1121 m_env->removePlayer(peer_id);
1125 delete i.getNode()->getValue();
1129 // Delete things in the reverse order of creation
1136 // Deinitialize scripting
1137 infostream<<"Server: Deinitializing scripting"<<std::endl;
1138 script_deinit(m_lua);
1141 void Server::start(unsigned short port)
1143 DSTACK(__FUNCTION_NAME);
1144 infostream<<"Starting server on port "<<port<<"..."<<std::endl;
1146 // Stop thread if already running
1149 // Initialize connection
1150 m_con.SetTimeoutMs(30);
1154 m_thread.setRun(true);
1157 // ASCII art for the win!
1159 <<" .__ __ __ "<<std::endl
1160 <<" _____ |__| ____ _____/ |_ ____ _______/ |_ "<<std::endl
1161 <<" / \\| |/ \\_/ __ \\ __\\/ __ \\ / ___/\\ __\\"<<std::endl
1162 <<"| Y Y \\ | | \\ ___/| | \\ ___/ \\___ \\ | | "<<std::endl
1163 <<"|__|_| /__|___| /\\___ >__| \\___ >____ > |__| "<<std::endl
1164 <<" \\/ \\/ \\/ \\/ \\/ "<<std::endl;
1165 actionstream<<"World at ["<<m_path_world<<"]"<<std::endl;
1166 actionstream<<"Server for gameid=\""<<m_gamespec.id
1167 <<"\" listening on port "<<port<<"."<<std::endl;
1172 DSTACK(__FUNCTION_NAME);
1174 infostream<<"Server: Stopping and waiting threads"<<std::endl;
1176 // Stop threads (set run=false first so both start stopping)
1177 m_thread.setRun(false);
1178 m_emergethread.setRun(false);
1180 m_emergethread.stop();
1182 infostream<<"Server: Threads stopped"<<std::endl;
1185 void Server::step(float dtime)
1187 DSTACK(__FUNCTION_NAME);
1192 JMutexAutoLock lock(m_step_dtime_mutex);
1193 m_step_dtime += dtime;
1195 // Throw if fatal error occurred in thread
1196 std::string async_err = m_async_fatal_error.get();
1197 if(async_err != ""){
1198 throw ServerError(async_err);
1202 void Server::AsyncRunStep()
1204 DSTACK(__FUNCTION_NAME);
1206 g_profiler->add("Server::AsyncRunStep (num)", 1);
1210 JMutexAutoLock lock1(m_step_dtime_mutex);
1211 dtime = m_step_dtime;
1215 // Send blocks to clients
1222 g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
1224 //infostream<<"Server steps "<<dtime<<std::endl;
1225 //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1228 JMutexAutoLock lock1(m_step_dtime_mutex);
1229 m_step_dtime -= dtime;
1236 m_uptime.set(m_uptime.get() + dtime);
1240 // Process connection's timeouts
1241 JMutexAutoLock lock2(m_con_mutex);
1242 ScopeProfiler sp(g_profiler, "Server: connection timeout processing");
1243 m_con.RunTimeouts(dtime);
1247 // This has to be called so that the client list gets synced
1248 // with the peer list of the connection
1249 handlePeerChanges();
1253 Update time of day and overall game time
1256 JMutexAutoLock envlock(m_env_mutex);
1258 m_env->setTimeOfDaySpeed(g_settings->getFloat("time_speed"));
1261 Send to clients at constant intervals
1264 m_time_of_day_send_timer -= dtime;
1265 if(m_time_of_day_send_timer < 0.0)
1267 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
1269 //JMutexAutoLock envlock(m_env_mutex);
1270 JMutexAutoLock conlock(m_con_mutex);
1272 for(core::map<u16, RemoteClient*>::Iterator
1273 i = m_clients.getIterator();
1274 i.atEnd() == false; i++)
1276 RemoteClient *client = i.getNode()->getValue();
1277 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1278 m_env->getTimeOfDay(), g_settings->getFloat("time_speed"));
1280 m_con.Send(client->peer_id, 0, data, true);
1286 JMutexAutoLock lock(m_env_mutex);
1288 ScopeProfiler sp(g_profiler, "SEnv step");
1289 ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
1293 const float map_timer_and_unload_dtime = 2.92;
1294 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
1296 JMutexAutoLock lock(m_env_mutex);
1297 // Run Map's timers and unload unused data
1298 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
1299 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
1300 g_settings->getFloat("server_unload_unused_data_timeout"));
1311 JMutexAutoLock lock(m_env_mutex);
1312 JMutexAutoLock lock2(m_con_mutex);
1314 ScopeProfiler sp(g_profiler, "Server: handle players");
1316 for(core::map<u16, RemoteClient*>::Iterator
1317 i = m_clients.getIterator();
1318 i.atEnd() == false; i++)
1320 RemoteClient *client = i.getNode()->getValue();
1321 PlayerSAO *playersao = getPlayerSAO(client->peer_id);
1322 if(playersao == NULL)
1326 Handle player HPs (die if hp=0)
1328 if(playersao->getHP() == 0 && playersao->m_hp_not_sent)
1329 DiePlayer(client->peer_id);
1332 Send player inventories and HPs if necessary
1334 if(playersao->m_teleported){
1335 SendMovePlayer(client->peer_id);
1336 playersao->m_teleported = false;
1338 if(playersao->m_inventory_not_sent){
1339 UpdateCrafting(client->peer_id);
1340 SendInventory(client->peer_id);
1342 if(playersao->m_hp_not_sent){
1343 SendPlayerHP(client->peer_id);
1348 /* Transform liquids */
1349 m_liquid_transform_timer += dtime;
1350 if(m_liquid_transform_timer >= 1.00)
1352 m_liquid_transform_timer -= 1.00;
1354 JMutexAutoLock lock(m_env_mutex);
1356 ScopeProfiler sp(g_profiler, "Server: liquid transform");
1358 core::map<v3s16, MapBlock*> modified_blocks;
1359 m_env->getMap().transformLiquids(modified_blocks);
1364 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1365 ServerMap &map = ((ServerMap&)m_env->getMap());
1366 map.updateLighting(modified_blocks, lighting_modified_blocks);
1368 // Add blocks modified by lighting to modified_blocks
1369 for(core::map<v3s16, MapBlock*>::Iterator
1370 i = lighting_modified_blocks.getIterator();
1371 i.atEnd() == false; i++)
1373 MapBlock *block = i.getNode()->getValue();
1374 modified_blocks.insert(block->getPos(), block);
1378 Set the modified blocks unsent for all the clients
1381 JMutexAutoLock lock2(m_con_mutex);
1383 for(core::map<u16, RemoteClient*>::Iterator
1384 i = m_clients.getIterator();
1385 i.atEnd() == false; i++)
1387 RemoteClient *client = i.getNode()->getValue();
1389 if(modified_blocks.size() > 0)
1391 // Remove block from sent history
1392 client->SetBlocksNotSent(modified_blocks);
1397 // Periodically print some info
1399 float &counter = m_print_info_timer;
1405 JMutexAutoLock lock2(m_con_mutex);
1407 if(m_clients.size() != 0)
1408 infostream<<"Players:"<<std::endl;
1409 for(core::map<u16, RemoteClient*>::Iterator
1410 i = m_clients.getIterator();
1411 i.atEnd() == false; i++)
1413 //u16 peer_id = i.getNode()->getKey();
1414 RemoteClient *client = i.getNode()->getValue();
1415 Player *player = m_env->getPlayer(client->peer_id);
1418 infostream<<"* "<<player->getName()<<"\t";
1419 client->PrintInfo(infostream);
1424 //if(g_settings->getBool("enable_experimental"))
1428 Check added and deleted active objects
1431 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
1432 JMutexAutoLock envlock(m_env_mutex);
1433 JMutexAutoLock conlock(m_con_mutex);
1435 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
1437 // Radius inside which objects are active
1438 s16 radius = g_settings->getS16("active_object_send_range_blocks");
1439 radius *= MAP_BLOCKSIZE;
1441 for(core::map<u16, RemoteClient*>::Iterator
1442 i = m_clients.getIterator();
1443 i.atEnd() == false; i++)
1445 RemoteClient *client = i.getNode()->getValue();
1447 // If definitions and textures have not been sent, don't
1448 // send objects either
1449 if(!client->definitions_sent)
1452 Player *player = m_env->getPlayer(client->peer_id);
1455 // This can happen if the client timeouts somehow
1456 /*infostream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
1458 <<" has no associated player"<<std::endl;*/
1461 v3s16 pos = floatToInt(player->getPosition(), BS);
1463 core::map<u16, bool> removed_objects;
1464 core::map<u16, bool> added_objects;
1465 m_env->getRemovedActiveObjects(pos, radius,
1466 client->m_known_objects, removed_objects);
1467 m_env->getAddedActiveObjects(pos, radius,
1468 client->m_known_objects, added_objects);
1470 // Ignore if nothing happened
1471 if(removed_objects.size() == 0 && added_objects.size() == 0)
1473 //infostream<<"active objects: none changed"<<std::endl;
1477 std::string data_buffer;
1481 // Handle removed objects
1482 writeU16((u8*)buf, removed_objects.size());
1483 data_buffer.append(buf, 2);
1484 for(core::map<u16, bool>::Iterator
1485 i = removed_objects.getIterator();
1486 i.atEnd()==false; i++)
1489 u16 id = i.getNode()->getKey();
1490 ServerActiveObject* obj = m_env->getActiveObject(id);
1492 // Add to data buffer for sending
1493 writeU16((u8*)buf, i.getNode()->getKey());
1494 data_buffer.append(buf, 2);
1496 // Remove from known objects
1497 client->m_known_objects.remove(i.getNode()->getKey());
1499 if(obj && obj->m_known_by_count > 0)
1500 obj->m_known_by_count--;
1503 // Handle added objects
1504 writeU16((u8*)buf, added_objects.size());
1505 data_buffer.append(buf, 2);
1506 for(core::map<u16, bool>::Iterator
1507 i = added_objects.getIterator();
1508 i.atEnd()==false; i++)
1511 u16 id = i.getNode()->getKey();
1512 ServerActiveObject* obj = m_env->getActiveObject(id);
1515 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1517 infostream<<"WARNING: "<<__FUNCTION_NAME
1518 <<": NULL object"<<std::endl;
1520 type = obj->getSendType();
1522 // Add to data buffer for sending
1523 writeU16((u8*)buf, id);
1524 data_buffer.append(buf, 2);
1525 writeU8((u8*)buf, type);
1526 data_buffer.append(buf, 1);
1529 data_buffer.append(serializeLongString(
1530 obj->getClientInitializationData()));
1532 data_buffer.append(serializeLongString(""));
1534 // Add to known objects
1535 client->m_known_objects.insert(i.getNode()->getKey(), false);
1538 obj->m_known_by_count++;
1542 SharedBuffer<u8> reply(2 + data_buffer.size());
1543 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1544 memcpy((char*)&reply[2], data_buffer.c_str(),
1545 data_buffer.size());
1547 m_con.Send(client->peer_id, 0, reply, true);
1549 verbosestream<<"Server: Sent object remove/add: "
1550 <<removed_objects.size()<<" removed, "
1551 <<added_objects.size()<<" added, "
1552 <<"packet size is "<<reply.getSize()<<std::endl;
1557 Collect a list of all the objects known by the clients
1558 and report it back to the environment.
1561 core::map<u16, bool> all_known_objects;
1563 for(core::map<u16, RemoteClient*>::Iterator
1564 i = m_clients.getIterator();
1565 i.atEnd() == false; i++)
1567 RemoteClient *client = i.getNode()->getValue();
1568 // Go through all known objects of client
1569 for(core::map<u16, bool>::Iterator
1570 i = client->m_known_objects.getIterator();
1571 i.atEnd()==false; i++)
1573 u16 id = i.getNode()->getKey();
1574 all_known_objects[id] = true;
1578 m_env->setKnownActiveObjects(whatever);
1584 Send object messages
1587 JMutexAutoLock envlock(m_env_mutex);
1588 JMutexAutoLock conlock(m_con_mutex);
1590 ScopeProfiler sp(g_profiler, "Server: sending object messages");
1593 // Value = data sent by object
1594 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1596 // Get active object messages from environment
1599 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
1603 core::list<ActiveObjectMessage>* message_list = NULL;
1604 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1605 n = buffered_messages.find(aom.id);
1608 message_list = new core::list<ActiveObjectMessage>;
1609 buffered_messages.insert(aom.id, message_list);
1613 message_list = n->getValue();
1615 message_list->push_back(aom);
1618 // Route data to every client
1619 for(core::map<u16, RemoteClient*>::Iterator
1620 i = m_clients.getIterator();
1621 i.atEnd()==false; i++)
1623 RemoteClient *client = i.getNode()->getValue();
1624 std::string reliable_data;
1625 std::string unreliable_data;
1626 // Go through all objects in message buffer
1627 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1628 j = buffered_messages.getIterator();
1629 j.atEnd()==false; j++)
1631 // If object is not known by client, skip it
1632 u16 id = j.getNode()->getKey();
1633 if(client->m_known_objects.find(id) == NULL)
1635 // Get message list of object
1636 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1637 // Go through every message
1638 for(core::list<ActiveObjectMessage>::Iterator
1639 k = list->begin(); k != list->end(); k++)
1641 // Compose the full new data with header
1642 ActiveObjectMessage aom = *k;
1643 std::string new_data;
1646 writeU16((u8*)&buf[0], aom.id);
1647 new_data.append(buf, 2);
1649 new_data += serializeString(aom.datastring);
1650 // Add data to buffer
1652 reliable_data += new_data;
1654 unreliable_data += new_data;
1658 reliable_data and unreliable_data are now ready.
1661 if(reliable_data.size() > 0)
1663 SharedBuffer<u8> reply(2 + reliable_data.size());
1664 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1665 memcpy((char*)&reply[2], reliable_data.c_str(),
1666 reliable_data.size());
1668 m_con.Send(client->peer_id, 0, reply, true);
1670 if(unreliable_data.size() > 0)
1672 SharedBuffer<u8> reply(2 + unreliable_data.size());
1673 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1674 memcpy((char*)&reply[2], unreliable_data.c_str(),
1675 unreliable_data.size());
1676 // Send as unreliable
1677 m_con.Send(client->peer_id, 0, reply, false);
1680 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1682 infostream<<"Server: Size of object message data: "
1683 <<"reliable: "<<reliable_data.size()
1684 <<", unreliable: "<<unreliable_data.size()
1689 // Clear buffered_messages
1690 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1691 i = buffered_messages.getIterator();
1692 i.atEnd()==false; i++)
1694 delete i.getNode()->getValue();
1698 } // enable_experimental
1701 Send queued-for-sending map edit events.
1704 // We will be accessing the environment and the connection
1705 JMutexAutoLock lock(m_env_mutex);
1706 JMutexAutoLock conlock(m_con_mutex);
1708 // Don't send too many at a time
1711 // Single change sending is disabled if queue size is not small
1712 bool disable_single_change_sending = false;
1713 if(m_unsent_map_edit_queue.size() >= 4)
1714 disable_single_change_sending = true;
1716 int event_count = m_unsent_map_edit_queue.size();
1718 // We'll log the amount of each
1721 while(m_unsent_map_edit_queue.size() != 0)
1723 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1725 // Players far away from the change are stored here.
1726 // Instead of sending the changes, MapBlocks are set not sent
1728 core::list<u16> far_players;
1730 if(event->type == MEET_ADDNODE)
1732 //infostream<<"Server: MEET_ADDNODE"<<std::endl;
1733 prof.add("MEET_ADDNODE", 1);
1734 if(disable_single_change_sending)
1735 sendAddNode(event->p, event->n, event->already_known_by_peer,
1738 sendAddNode(event->p, event->n, event->already_known_by_peer,
1741 else if(event->type == MEET_REMOVENODE)
1743 //infostream<<"Server: MEET_REMOVENODE"<<std::endl;
1744 prof.add("MEET_REMOVENODE", 1);
1745 if(disable_single_change_sending)
1746 sendRemoveNode(event->p, event->already_known_by_peer,
1749 sendRemoveNode(event->p, event->already_known_by_peer,
1752 else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
1754 infostream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
1755 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
1756 setBlockNotSent(event->p);
1758 else if(event->type == MEET_OTHER)
1760 infostream<<"Server: MEET_OTHER"<<std::endl;
1761 prof.add("MEET_OTHER", 1);
1762 for(core::map<v3s16, bool>::Iterator
1763 i = event->modified_blocks.getIterator();
1764 i.atEnd()==false; i++)
1766 v3s16 p = i.getNode()->getKey();
1772 prof.add("unknown", 1);
1773 infostream<<"WARNING: Server: Unknown MapEditEvent "
1774 <<((u32)event->type)<<std::endl;
1778 Set blocks not sent to far players
1780 if(far_players.size() > 0)
1782 // Convert list format to that wanted by SetBlocksNotSent
1783 core::map<v3s16, MapBlock*> modified_blocks2;
1784 for(core::map<v3s16, bool>::Iterator
1785 i = event->modified_blocks.getIterator();
1786 i.atEnd()==false; i++)
1788 v3s16 p = i.getNode()->getKey();
1789 modified_blocks2.insert(p,
1790 m_env->getMap().getBlockNoCreateNoEx(p));
1792 // Set blocks not sent
1793 for(core::list<u16>::Iterator
1794 i = far_players.begin();
1795 i != far_players.end(); i++)
1798 RemoteClient *client = getClient(peer_id);
1801 client->SetBlocksNotSent(modified_blocks2);
1807 /*// Don't send too many at a time
1809 if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
1813 if(event_count >= 5){
1814 infostream<<"Server: MapEditEvents:"<<std::endl;
1815 prof.print(infostream);
1816 } else if(event_count != 0){
1817 verbosestream<<"Server: MapEditEvents:"<<std::endl;
1818 prof.print(verbosestream);
1824 Trigger emergethread (it somehow gets to a non-triggered but
1825 bysy state sometimes)
1828 float &counter = m_emergethread_trigger_timer;
1834 m_emergethread.trigger();
1838 // Save map, players and auth stuff
1840 float &counter = m_savemap_timer;
1842 if(counter >= g_settings->getFloat("server_map_save_interval"))
1845 JMutexAutoLock lock(m_env_mutex);
1847 ScopeProfiler sp(g_profiler, "Server: saving stuff");
1850 if(m_banmanager.isModified())
1851 m_banmanager.save();
1853 // Save changed parts of map
1854 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
1857 m_env->serializePlayers(m_path_world);
1859 // Save environment metadata
1860 m_env->saveMeta(m_path_world);
1865 void Server::Receive()
1867 DSTACK(__FUNCTION_NAME);
1868 SharedBuffer<u8> data;
1873 JMutexAutoLock conlock(m_con_mutex);
1874 datasize = m_con.Receive(peer_id, data);
1877 // This has to be called so that the client list gets synced
1878 // with the peer list of the connection
1879 handlePeerChanges();
1881 ProcessData(*data, datasize, peer_id);
1883 catch(con::InvalidIncomingDataException &e)
1885 infostream<<"Server::Receive(): "
1886 "InvalidIncomingDataException: what()="
1887 <<e.what()<<std::endl;
1889 catch(con::PeerNotFoundException &e)
1891 //NOTE: This is not needed anymore
1893 // The peer has been disconnected.
1894 // Find the associated player and remove it.
1896 /*JMutexAutoLock envlock(m_env_mutex);
1898 infostream<<"ServerThread: peer_id="<<peer_id
1899 <<" has apparently closed connection. "
1900 <<"Removing player."<<std::endl;
1902 m_env->removePlayer(peer_id);*/
1906 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1908 DSTACK(__FUNCTION_NAME);
1909 // Environment is locked first.
1910 JMutexAutoLock envlock(m_env_mutex);
1911 JMutexAutoLock conlock(m_con_mutex);
1913 ScopeProfiler sp(g_profiler, "Server::ProcessData");
1916 Address address = m_con.GetPeerAddress(peer_id);
1917 std::string addr_s = address.serializeString();
1919 // drop player if is ip is banned
1920 if(m_banmanager.isIpBanned(addr_s)){
1921 infostream<<"Server: A banned client tried to connect from "
1922 <<addr_s<<"; banned name was "
1923 <<m_banmanager.getBanName(addr_s)<<std::endl;
1924 // This actually doesn't seem to transfer to the client
1925 SendAccessDenied(m_con, peer_id,
1926 L"Your ip is banned. Banned name was "
1927 +narrow_to_wide(m_banmanager.getBanName(addr_s)));
1928 m_con.DeletePeer(peer_id);
1932 catch(con::PeerNotFoundException &e)
1934 infostream<<"Server::ProcessData(): Cancelling: peer "
1935 <<peer_id<<" not found"<<std::endl;
1939 std::string addr_s = m_con.GetPeerAddress(peer_id).serializeString();
1941 u8 peer_ser_ver = getClient(peer_id)->serialization_version;
1949 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1951 if(command == TOSERVER_INIT)
1953 // [0] u16 TOSERVER_INIT
1954 // [2] u8 SER_FMT_VER_HIGHEST
1955 // [3] u8[20] player_name
1956 // [23] u8[28] password <--- can be sent without this, from old versions
1958 if(datasize < 2+1+PLAYERNAME_SIZE)
1961 verbosestream<<"Server: Got TOSERVER_INIT from "
1962 <<peer_id<<std::endl;
1964 // First byte after command is maximum supported
1965 // serialization version
1966 u8 client_max = data[2];
1967 u8 our_max = SER_FMT_VER_HIGHEST;
1968 // Use the highest version supported by both
1969 u8 deployed = core::min_(client_max, our_max);
1970 // If it's lower than the lowest supported, give up.
1971 if(deployed < SER_FMT_VER_LOWEST)
1972 deployed = SER_FMT_VER_INVALID;
1974 //peer->serialization_version = deployed;
1975 getClient(peer_id)->pending_serialization_version = deployed;
1977 if(deployed == SER_FMT_VER_INVALID)
1979 actionstream<<"Server: A mismatched client tried to connect from "
1980 <<addr_s<<std::endl;
1981 infostream<<"Server: Cannot negotiate "
1982 "serialization version with peer "
1983 <<peer_id<<std::endl;
1984 SendAccessDenied(m_con, peer_id, std::wstring(
1985 L"Your client's version is not supported.\n"
1986 L"Server version is ")
1987 + narrow_to_wide(VERSION_STRING) + L"."
1993 Read and check network protocol version
1996 u16 net_proto_version = 0;
1997 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
1999 net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
2002 getClient(peer_id)->net_proto_version = net_proto_version;
2004 if(net_proto_version == 0)
2006 actionstream<<"Server: An old tried to connect from "<<addr_s
2008 SendAccessDenied(m_con, peer_id, std::wstring(
2009 L"Your client's version is not supported.\n"
2010 L"Server version is ")
2011 + narrow_to_wide(VERSION_STRING) + L"."
2016 if(g_settings->getBool("strict_protocol_version_checking"))
2018 if(net_proto_version != PROTOCOL_VERSION)
2020 actionstream<<"Server: A mismatched client tried to connect"
2021 <<" from "<<addr_s<<std::endl;
2022 SendAccessDenied(m_con, peer_id, std::wstring(
2023 L"Your client's version is not supported.\n"
2024 L"Server version is ")
2025 + narrow_to_wide(VERSION_STRING) + L",\n"
2026 + L"server's PROTOCOL_VERSION is "
2027 + narrow_to_wide(itos(PROTOCOL_VERSION))
2028 + L", client's PROTOCOL_VERSION is "
2029 + narrow_to_wide(itos(net_proto_version))
2040 char playername[PLAYERNAME_SIZE];
2041 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
2043 playername[i] = data[3+i];
2045 playername[PLAYERNAME_SIZE-1] = 0;
2047 if(playername[0]=='\0')
2049 actionstream<<"Server: Player with an empty name "
2050 <<"tried to connect from "<<addr_s<<std::endl;
2051 SendAccessDenied(m_con, peer_id,
2056 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
2058 actionstream<<"Server: Player with an invalid name "
2059 <<"tried to connect from "<<addr_s<<std::endl;
2060 SendAccessDenied(m_con, peer_id,
2061 L"Name contains unallowed characters");
2065 infostream<<"Server: New connection: \""<<playername<<"\" from "
2066 <<m_con.GetPeerAddress(peer_id).serializeString()<<std::endl;
2069 char password[PASSWORD_SIZE];
2070 if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
2072 // old version - assume blank password
2077 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2079 password[i] = data[23+i];
2081 password[PASSWORD_SIZE-1] = 0;
2084 std::string checkpwd;
2085 bool has_auth = scriptapi_get_auth(m_lua, playername, &checkpwd, NULL);
2088 std::wstring raw_default_password =
2089 narrow_to_wide(g_settings->get("default_password"));
2090 std::string use_password =
2091 translatePassword(playername, raw_default_password);
2093 // If default_password is empty, allow any initial password
2094 if (raw_default_password.length() == 0)
2095 use_password = password;
2097 scriptapi_create_auth(m_lua, playername, use_password);
2100 has_auth = scriptapi_get_auth(m_lua, playername, &checkpwd, NULL);
2103 SendAccessDenied(m_con, peer_id, L"Not allowed to login");
2107 if(password != checkpwd){
2108 infostream<<"Server: peer_id="<<peer_id
2109 <<": supplied invalid password for "
2110 <<playername<<std::endl;
2111 SendAccessDenied(m_con, peer_id, L"Invalid password");
2115 // Do not allow multiple players in simple singleplayer mode.
2116 // This isn't a perfect way to do it, but will suffice for now.
2117 if(m_simple_singleplayer_mode && m_clients.size() > 1){
2118 infostream<<"Server: Not allowing another client to connect in"
2119 <<" simple singleplayer mode"<<std::endl;
2120 SendAccessDenied(m_con, peer_id,
2121 L"Running in simple singleplayer mode.");
2125 // Enforce user limit.
2126 // Don't enforce for users that have some admin right
2127 if(m_clients.size() >= g_settings->getU16("max_users") &&
2128 !checkPriv(playername, "server") &&
2129 !checkPriv(playername, "ban") &&
2130 !checkPriv(playername, "privs") &&
2131 !checkPriv(playername, "password") &&
2132 playername != g_settings->get("name"))
2134 actionstream<<"Server: "<<playername<<" tried to join, but there"
2135 <<" are already max_users="
2136 <<g_settings->getU16("max_users")<<" players."<<std::endl;
2137 SendAccessDenied(m_con, peer_id, L"Too many users.");
2142 PlayerSAO *playersao = emergePlayer(playername, peer_id);
2144 // If failed, cancel
2145 if(playersao == NULL)
2147 errorstream<<"Server: peer_id="<<peer_id
2148 <<": failed to emerge player"<<std::endl;
2153 Answer with a TOCLIENT_INIT
2156 SharedBuffer<u8> reply(2+1+6+8);
2157 writeU16(&reply[0], TOCLIENT_INIT);
2158 writeU8(&reply[2], deployed);
2159 writeV3S16(&reply[2+1], floatToInt(playersao->getPlayer()->getPosition()+v3f(0,BS/2,0), BS));
2160 writeU64(&reply[2+1+6], m_env->getServerMap().getSeed());
2163 m_con.Send(peer_id, 0, reply, true);
2167 Send complete position information
2169 SendMovePlayer(peer_id);
2174 if(command == TOSERVER_INIT2)
2176 verbosestream<<"Server: Got TOSERVER_INIT2 from "
2177 <<peer_id<<std::endl;
2180 getClient(peer_id)->serialization_version
2181 = getClient(peer_id)->pending_serialization_version;
2184 Send some initialization data
2187 infostream<<"Server: Sending content to "
2188 <<getPlayerName(peer_id)<<std::endl;
2190 // Send item definitions
2191 SendItemDef(m_con, peer_id, m_itemdef);
2193 // Send node definitions
2194 SendNodeDef(m_con, peer_id, m_nodedef);
2196 // Send media announcement
2197 sendMediaAnnouncement(peer_id);
2200 SendPlayerPrivileges(peer_id);
2203 UpdateCrafting(peer_id);
2204 SendInventory(peer_id);
2206 Player *player = m_env->getPlayer(peer_id);
2209 SendPlayerHP(peer_id);
2211 // Show death screen if necessary
2213 SendDeathscreen(m_con, peer_id, false, v3f(0,0,0));
2217 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2218 m_env->getTimeOfDay(), g_settings->getFloat("time_speed"));
2219 m_con.Send(peer_id, 0, data, true);
2222 // Note things in chat if not in simple singleplayer mode
2223 if(!m_simple_singleplayer_mode)
2225 // Send information about server to player in chat
2226 SendChatMessage(peer_id, getStatusString());
2228 // Send information about joining in chat
2230 std::wstring name = L"unknown";
2231 Player *player = m_env->getPlayer(peer_id);
2233 name = narrow_to_wide(player->getName());
2235 std::wstring message;
2238 message += L" joined game";
2239 BroadcastChatMessage(message);
2243 // Warnings about protocol version can be issued here
2244 if(getClient(peer_id)->net_proto_version < PROTOCOL_VERSION)
2246 SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT IS OLD AND MAY WORK PROPERLY WITH THIS SERVER");
2253 std::ostringstream os(std::ios_base::binary);
2254 for(core::map<u16, RemoteClient*>::Iterator
2255 i = m_clients.getIterator();
2256 i.atEnd() == false; i++)
2258 RemoteClient *client = i.getNode()->getValue();
2259 assert(client->peer_id == i.getNode()->getKey());
2260 if(client->serialization_version == SER_FMT_VER_INVALID)
2263 Player *player = m_env->getPlayer(client->peer_id);
2266 // Get name of player
2267 os<<player->getName()<<" ";
2270 actionstream<<player->getName()<<" joins game. List of players: "
2271 <<os.str()<<std::endl;
2277 if(peer_ser_ver == SER_FMT_VER_INVALID)
2279 infostream<<"Server::ProcessData(): Cancelling: Peer"
2280 " serialization format invalid or not initialized."
2281 " Skipping incoming command="<<command<<std::endl;
2285 Player *player = m_env->getPlayer(peer_id);
2287 infostream<<"Server::ProcessData(): Cancelling: "
2288 "No player for peer_id="<<peer_id
2293 PlayerSAO *playersao = player->getPlayerSAO();
2294 if(playersao == NULL){
2295 infostream<<"Server::ProcessData(): Cancelling: "
2296 "No player object for peer_id="<<peer_id
2301 if(command == TOSERVER_PLAYERPOS)
2303 if(datasize < 2+12+12+4+4)
2307 v3s32 ps = readV3S32(&data[start+2]);
2308 v3s32 ss = readV3S32(&data[start+2+12]);
2309 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2310 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2311 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2312 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2313 pitch = wrapDegrees(pitch);
2314 yaw = wrapDegrees(yaw);
2316 player->setPosition(position);
2317 player->setSpeed(speed);
2318 player->setPitch(pitch);
2319 player->setYaw(yaw);
2321 /*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2322 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2323 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2325 else if(command == TOSERVER_GOTBLOCKS)
2338 u16 count = data[2];
2339 for(u16 i=0; i<count; i++)
2341 if((s16)datasize < 2+1+(i+1)*6)
2342 throw con::InvalidIncomingDataException
2343 ("GOTBLOCKS length is too short");
2344 v3s16 p = readV3S16(&data[2+1+i*6]);
2345 /*infostream<<"Server: GOTBLOCKS ("
2346 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2347 RemoteClient *client = getClient(peer_id);
2348 client->GotBlock(p);
2351 else if(command == TOSERVER_DELETEDBLOCKS)
2364 u16 count = data[2];
2365 for(u16 i=0; i<count; i++)
2367 if((s16)datasize < 2+1+(i+1)*6)
2368 throw con::InvalidIncomingDataException
2369 ("DELETEDBLOCKS length is too short");
2370 v3s16 p = readV3S16(&data[2+1+i*6]);
2371 /*infostream<<"Server: DELETEDBLOCKS ("
2372 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2373 RemoteClient *client = getClient(peer_id);
2374 client->SetBlockNotSent(p);
2377 else if(command == TOSERVER_CLICK_OBJECT)
2379 infostream<<"Server: CLICK_OBJECT not supported anymore"<<std::endl;
2382 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2384 infostream<<"Server: CLICK_ACTIVEOBJECT not supported anymore"<<std::endl;
2387 else if(command == TOSERVER_GROUND_ACTION)
2389 infostream<<"Server: GROUND_ACTION not supported anymore"<<std::endl;
2393 else if(command == TOSERVER_RELEASE)
2395 infostream<<"Server: RELEASE not supported anymore"<<std::endl;
2398 else if(command == TOSERVER_SIGNTEXT)
2400 infostream<<"Server: SIGNTEXT not supported anymore"
2404 else if(command == TOSERVER_SIGNNODETEXT)
2406 if(!checkPriv(player->getName(), "interact"))
2414 std::string datastring((char*)&data[2], datasize-2);
2415 std::istringstream is(datastring, std::ios_base::binary);
2418 is.read((char*)buf, 6);
2419 v3s16 p = readV3S16(buf);
2420 is.read((char*)buf, 2);
2421 u16 textlen = readU16(buf);
2423 for(u16 i=0; i<textlen; i++)
2425 is.read((char*)buf, 1);
2426 text += (char)buf[0];
2429 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
2433 meta->setText(text);
2435 actionstream<<player->getName()<<" writes \""<<text<<"\" to sign"
2436 <<" at "<<PP(p)<<std::endl;
2438 v3s16 blockpos = getNodeBlockPos(p);
2439 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
2442 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2446 setBlockNotSent(blockpos);
2448 else if(command == TOSERVER_INVENTORY_ACTION)
2450 // Strip command and create a stream
2451 std::string datastring((char*)&data[2], datasize-2);
2452 verbosestream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2453 std::istringstream is(datastring, std::ios_base::binary);
2455 InventoryAction *a = InventoryAction::deSerialize(is);
2458 infostream<<"TOSERVER_INVENTORY_ACTION: "
2459 <<"InventoryAction::deSerialize() returned NULL"
2465 Note: Always set inventory not sent, to repair cases
2466 where the client made a bad prediction.
2470 Handle restrictions and special cases of the move action
2472 if(a->getType() == IACTION_MOVE)
2474 IMoveAction *ma = (IMoveAction*)a;
2476 ma->from_inv.applyCurrentPlayer(player->getName());
2477 ma->to_inv.applyCurrentPlayer(player->getName());
2479 setInventoryModified(ma->from_inv);
2480 setInventoryModified(ma->to_inv);
2482 bool from_inv_is_current_player =
2483 (ma->from_inv.type == InventoryLocation::PLAYER) &&
2484 (ma->from_inv.name == player->getName());
2486 bool to_inv_is_current_player =
2487 (ma->to_inv.type == InventoryLocation::PLAYER) &&
2488 (ma->to_inv.name == player->getName());
2491 Disable moving items out of craftpreview
2493 if(ma->from_list == "craftpreview")
2495 infostream<<"Ignoring IMoveAction from "
2496 <<(ma->from_inv.dump())<<":"<<ma->from_list
2497 <<" to "<<(ma->to_inv.dump())<<":"<<ma->to_list
2498 <<" because src is "<<ma->from_list<<std::endl;
2504 Disable moving items into craftresult and craftpreview
2506 if(ma->to_list == "craftpreview" || ma->to_list == "craftresult")
2508 infostream<<"Ignoring IMoveAction from "
2509 <<(ma->from_inv.dump())<<":"<<ma->from_list
2510 <<" to "<<(ma->to_inv.dump())<<":"<<ma->to_list
2511 <<" because dst is "<<ma->to_list<<std::endl;
2516 // Disallow moving items in elsewhere than player's inventory
2517 // if not allowed to interact
2518 if(!checkPriv(player->getName(), "interact") &&
2519 (!from_inv_is_current_player ||
2520 !to_inv_is_current_player))
2522 infostream<<"Cannot move outside of player's inventory: "
2523 <<"No interact privilege"<<std::endl;
2528 // If player is not an admin, check for ownership of src and dst
2529 if(!checkPriv(player->getName(), "server"))
2531 std::string owner_from = getInventoryOwner(ma->from_inv);
2532 if(owner_from != "" && owner_from != player->getName())
2534 infostream<<"WARNING: "<<player->getName()
2535 <<" tried to access an inventory that"
2536 <<" belongs to "<<owner_from<<std::endl;
2541 std::string owner_to = getInventoryOwner(ma->to_inv);
2542 if(owner_to != "" && owner_to != player->getName())
2544 infostream<<"WARNING: "<<player->getName()
2545 <<" tried to access an inventory that"
2546 <<" belongs to "<<owner_to<<std::endl;
2553 Handle restrictions and special cases of the drop action
2555 else if(a->getType() == IACTION_DROP)
2557 IDropAction *da = (IDropAction*)a;
2559 da->from_inv.applyCurrentPlayer(player->getName());
2561 setInventoryModified(da->from_inv);
2563 // Disallow dropping items if not allowed to interact
2564 if(!checkPriv(player->getName(), "interact"))
2569 // If player is not an admin, check for ownership
2570 else if(!checkPriv(player->getName(), "server"))
2572 std::string owner_from = getInventoryOwner(da->from_inv);
2573 if(owner_from != "" && owner_from != player->getName())
2575 infostream<<"WARNING: "<<player->getName()
2576 <<" tried to access an inventory that"
2577 <<" belongs to "<<owner_from<<std::endl;
2584 Handle restrictions and special cases of the craft action
2586 else if(a->getType() == IACTION_CRAFT)
2588 ICraftAction *ca = (ICraftAction*)a;
2590 ca->craft_inv.applyCurrentPlayer(player->getName());
2592 setInventoryModified(ca->craft_inv);
2594 //bool craft_inv_is_current_player =
2595 // (ca->craft_inv.type == InventoryLocation::PLAYER) &&
2596 // (ca->craft_inv.name == player->getName());
2598 // Disallow crafting if not allowed to interact
2599 if(!checkPriv(player->getName(), "interact"))
2601 infostream<<"Cannot craft: "
2602 <<"No interact privilege"<<std::endl;
2607 // If player is not an admin, check for ownership of inventory
2608 if(!checkPriv(player->getName(), "server"))
2610 std::string owner_craft = getInventoryOwner(ca->craft_inv);
2611 if(owner_craft != "" && owner_craft != player->getName())
2613 infostream<<"WARNING: "<<player->getName()
2614 <<" tried to access an inventory that"
2615 <<" belongs to "<<owner_craft<<std::endl;
2623 a->apply(this, playersao, this);
2627 else if(command == TOSERVER_CHAT_MESSAGE)
2635 std::string datastring((char*)&data[2], datasize-2);
2636 std::istringstream is(datastring, std::ios_base::binary);
2639 is.read((char*)buf, 2);
2640 u16 len = readU16(buf);
2642 std::wstring message;
2643 for(u16 i=0; i<len; i++)
2645 is.read((char*)buf, 2);
2646 message += (wchar_t)readU16(buf);
2649 // Get player name of this client
2650 std::wstring name = narrow_to_wide(player->getName());
2653 bool ate = scriptapi_on_chat_message(m_lua, player->getName(),
2654 wide_to_narrow(message));
2655 // If script ate the message, don't proceed
2659 // Line to send to players
2661 // Whether to send to the player that sent the line
2662 bool send_to_sender = false;
2663 // Whether to send to other players
2664 bool send_to_others = false;
2667 if(message[0] == L'/')
2669 size_t strip_size = 1;
2670 if (message[1] == L'#') // support old-style commans
2672 message = message.substr(strip_size);
2674 WStrfnd f1(message);
2675 f1.next(L" "); // Skip over /#whatever
2676 std::wstring paramstring = f1.next(L"");
2678 ServerCommandContext *ctx = new ServerCommandContext(
2679 str_split(message, L' '),
2685 std::wstring reply(processServerCommand(ctx));
2686 send_to_sender = ctx->flags & SEND_TO_SENDER;
2687 send_to_others = ctx->flags & SEND_TO_OTHERS;
2689 if (ctx->flags & SEND_NO_PREFIX)
2692 line += L"Server: " + reply;
2699 if(checkPriv(player->getName(), "shout")){
2704 send_to_others = true;
2706 line += L"Server: You are not allowed to shout";
2707 send_to_sender = true;
2714 actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2717 Send the message to clients
2719 for(core::map<u16, RemoteClient*>::Iterator
2720 i = m_clients.getIterator();
2721 i.atEnd() == false; i++)
2723 // Get client and check that it is valid
2724 RemoteClient *client = i.getNode()->getValue();
2725 assert(client->peer_id == i.getNode()->getKey());
2726 if(client->serialization_version == SER_FMT_VER_INVALID)
2730 bool sender_selected = (peer_id == client->peer_id);
2731 if(sender_selected == true && send_to_sender == false)
2733 if(sender_selected == false && send_to_others == false)
2736 SendChatMessage(client->peer_id, line);
2740 else if(command == TOSERVER_DAMAGE)
2742 std::string datastring((char*)&data[2], datasize-2);
2743 std::istringstream is(datastring, std::ios_base::binary);
2744 u8 damage = readU8(is);
2746 actionstream<<player->getName()<<" damaged by "
2747 <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
2750 playersao->setHP(playersao->getHP() - damage);
2752 if(playersao->getHP() == 0 && playersao->m_hp_not_sent)
2755 if(playersao->m_hp_not_sent)
2756 SendPlayerHP(peer_id);
2758 else if(command == TOSERVER_PASSWORD)
2761 [0] u16 TOSERVER_PASSWORD
2762 [2] u8[28] old password
2763 [30] u8[28] new password
2766 if(datasize != 2+PASSWORD_SIZE*2)
2768 /*char password[PASSWORD_SIZE];
2769 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2770 password[i] = data[2+i];
2771 password[PASSWORD_SIZE-1] = 0;*/
2773 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2781 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2783 char c = data[2+PASSWORD_SIZE+i];
2789 infostream<<"Server: Client requests a password change from "
2790 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
2792 std::string playername = player->getName();
2794 std::string checkpwd;
2795 scriptapi_get_auth(m_lua, playername, &checkpwd, NULL);
2797 if(oldpwd != checkpwd)
2799 infostream<<"Server: invalid old password"<<std::endl;
2800 // Wrong old password supplied!!
2801 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
2805 bool success = scriptapi_set_password(m_lua, playername, newpwd);
2807 actionstream<<player->getName()<<" changes password"<<std::endl;
2808 SendChatMessage(peer_id, L"Password change successful");
2810 actionstream<<player->getName()<<" tries to change password but "
2811 <<"it fails"<<std::endl;
2812 SendChatMessage(peer_id, L"Password change failed or inavailable");
2815 else if(command == TOSERVER_PLAYERITEM)
2820 u16 item = readU16(&data[2]);
2821 playersao->setWieldIndex(item);
2823 else if(command == TOSERVER_RESPAWN)
2828 RespawnPlayer(peer_id);
2830 actionstream<<player->getName()<<" respawns at "
2831 <<PP(player->getPosition()/BS)<<std::endl;
2833 // ActiveObject is added to environment in AsyncRunStep after
2834 // the previous addition has been succesfully removed
2836 else if(command == TOSERVER_REQUEST_MEDIA) {
2837 std::string datastring((char*)&data[2], datasize-2);
2838 std::istringstream is(datastring, std::ios_base::binary);
2840 core::list<MediaRequest> tosend;
2841 u16 numfiles = readU16(is);
2843 infostream<<"Sending "<<numfiles<<" files to "
2844 <<getPlayerName(peer_id)<<std::endl;
2845 verbosestream<<"TOSERVER_REQUEST_MEDIA: "<<std::endl;
2847 for(int i = 0; i < numfiles; i++) {
2848 std::string name = deSerializeString(is);
2849 tosend.push_back(MediaRequest(name));
2850 verbosestream<<"TOSERVER_REQUEST_MEDIA: requested file "
2854 sendRequestedMedia(peer_id, tosend);
2856 // Now the client should know about everything
2857 // (definitions and files)
2858 getClient(peer_id)->definitions_sent = true;
2860 else if(command == TOSERVER_INTERACT)
2862 std::string datastring((char*)&data[2], datasize-2);
2863 std::istringstream is(datastring, std::ios_base::binary);
2869 [5] u32 length of the next item
2870 [9] serialized PointedThing
2872 0: start digging (from undersurface) or use
2873 1: stop digging (all parameters ignored)
2874 2: digging completed
2875 3: place block or item (to abovesurface)
2878 u8 action = readU8(is);
2879 u16 item_i = readU16(is);
2880 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
2881 PointedThing pointed;
2882 pointed.deSerialize(tmp_is);
2884 verbosestream<<"TOSERVER_INTERACT: action="<<(int)action<<", item="
2885 <<item_i<<", pointed="<<pointed.dump()<<std::endl;
2889 verbosestream<<"TOSERVER_INTERACT: "<<player->getName()
2890 <<" tried to interact, but is dead!"<<std::endl;
2894 v3f player_pos = playersao->getLastGoodPosition();
2896 // Update wielded item
2897 playersao->setWieldIndex(item_i);
2899 // Get pointed to node (undefined if not POINTEDTYPE_NODE)
2900 v3s16 p_under = pointed.node_undersurface;
2901 v3s16 p_above = pointed.node_abovesurface;
2903 // Get pointed to object (NULL if not POINTEDTYPE_OBJECT)
2904 ServerActiveObject *pointed_object = NULL;
2905 if(pointed.type == POINTEDTHING_OBJECT)
2907 pointed_object = m_env->getActiveObject(pointed.object_id);
2908 if(pointed_object == NULL)
2910 verbosestream<<"TOSERVER_INTERACT: "
2911 "pointed object is NULL"<<std::endl;
2917 v3f pointed_pos_under = player_pos;
2918 v3f pointed_pos_above = player_pos;
2919 if(pointed.type == POINTEDTHING_NODE)
2921 pointed_pos_under = intToFloat(p_under, BS);
2922 pointed_pos_above = intToFloat(p_above, BS);
2924 else if(pointed.type == POINTEDTHING_OBJECT)
2926 pointed_pos_under = pointed_object->getBasePosition();
2927 pointed_pos_above = pointed_pos_under;
2931 Check that target is reasonably close
2932 (only when digging or placing things)
2934 if(action == 0 || action == 2 || action == 3)
2936 float d = player_pos.getDistanceFrom(pointed_pos_under);
2937 float max_d = BS * 14; // Just some large enough value
2939 actionstream<<"Player "<<player->getName()
2940 <<" tried to access "<<pointed.dump()
2942 <<"d="<<d<<", max_d="<<max_d
2943 <<". ignoring."<<std::endl;
2944 // Re-send block to revert change on client-side
2945 RemoteClient *client = getClient(peer_id);
2946 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2947 client->SetBlockNotSent(blockpos);
2954 Make sure the player is allowed to do it
2956 if(!checkPriv(player->getName(), "interact"))
2958 actionstream<<player->getName()<<" attempted to interact with "
2959 <<pointed.dump()<<" without 'interact' privilege"
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);
2969 0: start digging or punch object
2973 if(pointed.type == POINTEDTHING_NODE)
2976 NOTE: This can be used in the future to check if
2977 somebody is cheating, by checking the timing.
2979 MapNode n(CONTENT_IGNORE);
2982 n = m_env->getMap().getNode(p_under);
2984 catch(InvalidPositionException &e)
2986 infostream<<"Server: Not punching: Node not found."
2987 <<" Adding block to emerge queue."
2989 m_emerge_queue.addBlock(peer_id,
2990 getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
2992 if(n.getContent() != CONTENT_IGNORE)
2993 scriptapi_node_on_punch(m_lua, p_under, n, playersao);
2995 else if(pointed.type == POINTEDTHING_OBJECT)
2997 // Skip if object has been removed
2998 if(pointed_object->m_removed)
3001 actionstream<<player->getName()<<" punches object "
3002 <<pointed.object_id<<": "
3003 <<pointed_object->getDescription()<<std::endl;
3005 ItemStack punchitem = playersao->getWieldedItem();
3006 ToolCapabilities toolcap =
3007 punchitem.getToolCapabilities(m_itemdef);
3008 v3f dir = (pointed_object->getBasePosition() -
3009 (player->getPosition() + player->getEyeOffset())
3011 float time_from_last_punch =
3012 playersao->resetTimeFromLastPunch();
3013 pointed_object->punch(dir, &toolcap, playersao,
3014 time_from_last_punch);
3022 else if(action == 1)
3027 2: Digging completed
3029 else if(action == 2)
3031 // Only complete digging of nodes
3032 if(pointed.type == POINTEDTHING_NODE)
3034 MapNode n(CONTENT_IGNORE);
3037 n = m_env->getMap().getNode(p_under);
3039 catch(InvalidPositionException &e)
3041 infostream<<"Server: Not finishing digging: Node not found."
3042 <<" Adding block to emerge queue."
3044 m_emerge_queue.addBlock(peer_id,
3045 getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
3047 if(n.getContent() != CONTENT_IGNORE)
3048 scriptapi_node_on_dig(m_lua, p_under, n, playersao);
3050 if (m_env->getMap().getNodeNoEx(p_under).getContent() != CONTENT_AIR)
3052 // Re-send block to revert change on client-side
3053 RemoteClient *client = getClient(peer_id);
3054 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
3055 client->SetBlockNotSent(blockpos);
3061 3: place block or right-click object
3063 else if(action == 3)
3065 ItemStack item = playersao->getWieldedItem();
3067 // Reset build time counter
3068 if(pointed.type == POINTEDTHING_NODE &&
3069 item.getDefinition(m_itemdef).type == ITEM_NODE)
3070 getClient(peer_id)->m_time_from_building = 0.0;
3072 if(pointed.type == POINTEDTHING_OBJECT)
3074 // Right click object
3076 // Skip if object has been removed
3077 if(pointed_object->m_removed)
3080 actionstream<<player->getName()<<" right-clicks object "
3081 <<pointed.object_id<<": "
3082 <<pointed_object->getDescription()<<std::endl;
3085 pointed_object->rightClick(playersao);
3087 else if(scriptapi_item_on_place(m_lua,
3088 item, playersao, pointed))
3090 // Placement was handled in lua
3092 // Apply returned ItemStack
3093 if(g_settings->getBool("creative_mode") == false)
3094 playersao->setWieldedItem(item);
3102 else if(action == 4)
3104 ItemStack item = playersao->getWieldedItem();
3106 actionstream<<player->getName()<<" uses "<<item.name
3107 <<", pointing at "<<pointed.dump()<<std::endl;
3109 if(scriptapi_item_on_use(m_lua,
3110 item, playersao, pointed))
3112 // Apply returned ItemStack
3113 if(g_settings->getBool("creative_mode") == false)
3114 playersao->setWieldedItem(item);
3120 Catch invalid actions
3124 infostream<<"WARNING: Server: Invalid action "
3125 <<action<<std::endl;
3128 else if(command == TOSERVER_REMOVED_SOUNDS)
3130 std::string datastring((char*)&data[2], datasize-2);
3131 std::istringstream is(datastring, std::ios_base::binary);
3133 int num = readU16(is);
3134 for(int k=0; k<num; k++){
3135 s32 id = readS32(is);
3136 std::map<s32, ServerPlayingSound>::iterator i =
3137 m_playing_sounds.find(id);
3138 if(i == m_playing_sounds.end())
3140 ServerPlayingSound &psound = i->second;
3141 psound.clients.erase(peer_id);
3142 if(psound.clients.size() == 0)
3143 m_playing_sounds.erase(i++);
3148 infostream<<"Server::ProcessData(): Ignoring "
3149 "unknown command "<<command<<std::endl;
3153 catch(SendFailedException &e)
3155 errorstream<<"Server::ProcessData(): SendFailedException: "
3161 void Server::onMapEditEvent(MapEditEvent *event)
3163 //infostream<<"Server::onMapEditEvent()"<<std::endl;
3164 if(m_ignore_map_edit_events)
3166 if(m_ignore_map_edit_events_area.contains(event->getArea()))
3168 MapEditEvent *e = event->clone();
3169 m_unsent_map_edit_queue.push_back(e);
3172 Inventory* Server::getInventory(const InventoryLocation &loc)
3175 case InventoryLocation::UNDEFINED:
3178 case InventoryLocation::CURRENT_PLAYER:
3181 case InventoryLocation::PLAYER:
3183 Player *player = m_env->getPlayer(loc.name.c_str());
3186 PlayerSAO *playersao = player->getPlayerSAO();
3189 return playersao->getInventory();
3192 case InventoryLocation::NODEMETA:
3194 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
3197 return meta->getInventory();
3205 std::string Server::getInventoryOwner(const InventoryLocation &loc)
3208 case InventoryLocation::UNDEFINED:
3211 case InventoryLocation::CURRENT_PLAYER:
3214 case InventoryLocation::PLAYER:
3219 case InventoryLocation::NODEMETA:
3221 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
3224 return meta->getOwner();
3232 void Server::setInventoryModified(const InventoryLocation &loc)
3235 case InventoryLocation::UNDEFINED:
3238 case InventoryLocation::PLAYER:
3240 Player *player = m_env->getPlayer(loc.name.c_str());
3243 PlayerSAO *playersao = player->getPlayerSAO();
3246 playersao->m_inventory_not_sent = true;
3247 playersao->m_wielded_item_not_sent = true;
3250 case InventoryLocation::NODEMETA:
3252 v3s16 blockpos = getNodeBlockPos(loc.p);
3254 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
3256 meta->inventoryModified();
3258 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3260 block->raiseModified(MOD_STATE_WRITE_NEEDED);
3262 setBlockNotSent(blockpos);
3270 core::list<PlayerInfo> Server::getPlayerInfo()
3272 DSTACK(__FUNCTION_NAME);
3273 JMutexAutoLock envlock(m_env_mutex);
3274 JMutexAutoLock conlock(m_con_mutex);
3276 core::list<PlayerInfo> list;
3278 core::list<Player*> players = m_env->getPlayers();
3280 core::list<Player*>::Iterator i;
3281 for(i = players.begin();
3282 i != players.end(); i++)
3286 Player *player = *i;
3289 // Copy info from connection to info struct
3290 info.id = player->peer_id;
3291 info.address = m_con.GetPeerAddress(player->peer_id);
3292 info.avg_rtt = m_con.GetPeerAvgRTT(player->peer_id);
3294 catch(con::PeerNotFoundException &e)
3296 // Set dummy peer info
3298 info.address = Address(0,0,0,0,0);
3302 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3303 info.position = player->getPosition();
3305 list.push_back(info);
3312 void Server::peerAdded(con::Peer *peer)
3314 DSTACK(__FUNCTION_NAME);
3315 verbosestream<<"Server::peerAdded(): peer->id="
3316 <<peer->id<<std::endl;
3319 c.type = PEER_ADDED;
3320 c.peer_id = peer->id;
3322 m_peer_change_queue.push_back(c);
3325 void Server::deletingPeer(con::Peer *peer, bool timeout)
3327 DSTACK(__FUNCTION_NAME);
3328 verbosestream<<"Server::deletingPeer(): peer->id="
3329 <<peer->id<<", timeout="<<timeout<<std::endl;
3332 c.type = PEER_REMOVED;
3333 c.peer_id = peer->id;
3334 c.timeout = timeout;
3335 m_peer_change_queue.push_back(c);
3342 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3344 DSTACK(__FUNCTION_NAME);
3345 std::ostringstream os(std::ios_base::binary);
3347 writeU16(os, TOCLIENT_HP);
3351 std::string s = os.str();
3352 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3354 con.Send(peer_id, 0, data, true);
3357 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3358 const std::wstring &reason)
3360 DSTACK(__FUNCTION_NAME);
3361 std::ostringstream os(std::ios_base::binary);
3363 writeU16(os, TOCLIENT_ACCESS_DENIED);
3364 os<<serializeWideString(reason);
3367 std::string s = os.str();
3368 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3370 con.Send(peer_id, 0, data, true);
3373 void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
3374 bool set_camera_point_target, v3f camera_point_target)
3376 DSTACK(__FUNCTION_NAME);
3377 std::ostringstream os(std::ios_base::binary);
3379 writeU16(os, TOCLIENT_DEATHSCREEN);
3380 writeU8(os, set_camera_point_target);
3381 writeV3F1000(os, camera_point_target);
3384 std::string s = os.str();
3385 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3387 con.Send(peer_id, 0, data, true);
3390 void Server::SendItemDef(con::Connection &con, u16 peer_id,
3391 IItemDefManager *itemdef)
3393 DSTACK(__FUNCTION_NAME);
3394 std::ostringstream os(std::ios_base::binary);
3398 u32 length of the next item
3399 zlib-compressed serialized ItemDefManager
3401 writeU16(os, TOCLIENT_ITEMDEF);
3402 std::ostringstream tmp_os(std::ios::binary);
3403 itemdef->serialize(tmp_os);
3404 std::ostringstream tmp_os2(std::ios::binary);
3405 compressZlib(tmp_os.str(), tmp_os2);
3406 os<<serializeLongString(tmp_os2.str());
3409 std::string s = os.str();
3410 verbosestream<<"Server: Sending item definitions to id("<<peer_id
3411 <<"): size="<<s.size()<<std::endl;
3412 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3414 con.Send(peer_id, 0, data, true);
3417 void Server::SendNodeDef(con::Connection &con, u16 peer_id,
3418 INodeDefManager *nodedef)
3420 DSTACK(__FUNCTION_NAME);
3421 std::ostringstream os(std::ios_base::binary);
3425 u32 length of the next item
3426 zlib-compressed serialized NodeDefManager
3428 writeU16(os, TOCLIENT_NODEDEF);
3429 std::ostringstream tmp_os(std::ios::binary);
3430 nodedef->serialize(tmp_os);
3431 std::ostringstream tmp_os2(std::ios::binary);
3432 compressZlib(tmp_os.str(), tmp_os2);
3433 os<<serializeLongString(tmp_os2.str());
3436 std::string s = os.str();
3437 verbosestream<<"Server: Sending node definitions to id("<<peer_id
3438 <<"): size="<<s.size()<<std::endl;
3439 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3441 con.Send(peer_id, 0, data, true);
3445 Non-static send methods
3448 void Server::SendInventory(u16 peer_id)
3450 DSTACK(__FUNCTION_NAME);
3452 PlayerSAO *playersao = getPlayerSAO(peer_id);
3455 playersao->m_inventory_not_sent = false;
3461 std::ostringstream os;
3462 playersao->getInventory()->serialize(os);
3464 std::string s = os.str();
3466 SharedBuffer<u8> data(s.size()+2);
3467 writeU16(&data[0], TOCLIENT_INVENTORY);
3468 memcpy(&data[2], s.c_str(), s.size());
3471 m_con.Send(peer_id, 0, data, true);
3474 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3476 DSTACK(__FUNCTION_NAME);
3478 std::ostringstream os(std::ios_base::binary);
3482 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3483 os.write((char*)buf, 2);
3486 writeU16(buf, message.size());
3487 os.write((char*)buf, 2);
3490 for(u32 i=0; i<message.size(); i++)
3494 os.write((char*)buf, 2);
3498 std::string s = os.str();
3499 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3501 m_con.Send(peer_id, 0, data, true);
3504 void Server::BroadcastChatMessage(const std::wstring &message)
3506 for(core::map<u16, RemoteClient*>::Iterator
3507 i = m_clients.getIterator();
3508 i.atEnd() == false; i++)
3510 // Get client and check that it is valid
3511 RemoteClient *client = i.getNode()->getValue();
3512 assert(client->peer_id == i.getNode()->getKey());
3513 if(client->serialization_version == SER_FMT_VER_INVALID)
3516 SendChatMessage(client->peer_id, message);
3520 void Server::SendPlayerHP(u16 peer_id)
3522 DSTACK(__FUNCTION_NAME);
3523 PlayerSAO *playersao = getPlayerSAO(peer_id);
3525 playersao->m_hp_not_sent = false;
3526 SendHP(m_con, peer_id, playersao->getHP());
3529 void Server::SendMovePlayer(u16 peer_id)
3531 DSTACK(__FUNCTION_NAME);
3532 Player *player = m_env->getPlayer(peer_id);
3535 std::ostringstream os(std::ios_base::binary);
3536 writeU16(os, TOCLIENT_MOVE_PLAYER);
3537 writeV3F1000(os, player->getPosition());
3538 writeF1000(os, player->getPitch());
3539 writeF1000(os, player->getYaw());
3542 v3f pos = player->getPosition();
3543 f32 pitch = player->getPitch();
3544 f32 yaw = player->getYaw();
3545 verbosestream<<"Server: Sending TOCLIENT_MOVE_PLAYER"
3546 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3553 std::string s = os.str();
3554 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3556 m_con.Send(peer_id, 0, data, true);
3559 void Server::SendPlayerPrivileges(u16 peer_id)
3561 Player *player = m_env->getPlayer(peer_id);
3563 std::set<std::string> privs;
3564 scriptapi_get_auth(m_lua, player->getName(), NULL, &privs);
3566 std::ostringstream os(std::ios_base::binary);
3567 writeU16(os, TOCLIENT_PRIVILEGES);
3568 writeU16(os, privs.size());
3569 for(std::set<std::string>::const_iterator i = privs.begin();
3570 i != privs.end(); i++){
3571 os<<serializeString(*i);
3575 std::string s = os.str();
3576 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3578 m_con.Send(peer_id, 0, data, true);
3581 s32 Server::playSound(const SimpleSoundSpec &spec,
3582 const ServerSoundParams ¶ms)
3584 // Find out initial position of sound
3585 bool pos_exists = false;
3586 v3f pos = params.getPos(m_env, &pos_exists);
3587 // If position is not found while it should be, cancel sound
3588 if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
3590 // Filter destination clients
3591 std::set<RemoteClient*> dst_clients;
3592 if(params.to_player != "")
3594 Player *player = m_env->getPlayer(params.to_player.c_str());
3596 infostream<<"Server::playSound: Player \""<<params.to_player
3597 <<"\" not found"<<std::endl;
3600 if(player->peer_id == PEER_ID_INEXISTENT){
3601 infostream<<"Server::playSound: Player \""<<params.to_player
3602 <<"\" not connected"<<std::endl;
3605 RemoteClient *client = getClient(player->peer_id);
3606 dst_clients.insert(client);
3610 for(core::map<u16, RemoteClient*>::Iterator
3611 i = m_clients.getIterator(); i.atEnd() == false; i++)
3613 RemoteClient *client = i.getNode()->getValue();
3614 Player *player = m_env->getPlayer(client->peer_id);
3618 if(player->getPosition().getDistanceFrom(pos) >
3619 params.max_hear_distance)
3622 dst_clients.insert(client);
3625 if(dst_clients.size() == 0)
3628 s32 id = m_next_sound_id++;
3629 // The sound will exist as a reference in m_playing_sounds
3630 m_playing_sounds[id] = ServerPlayingSound();
3631 ServerPlayingSound &psound = m_playing_sounds[id];
3632 psound.params = params;
3633 for(std::set<RemoteClient*>::iterator i = dst_clients.begin();
3634 i != dst_clients.end(); i++)
3635 psound.clients.insert((*i)->peer_id);
3637 std::ostringstream os(std::ios_base::binary);
3638 writeU16(os, TOCLIENT_PLAY_SOUND);
3640 os<<serializeString(spec.name);
3641 writeF1000(os, spec.gain * params.gain);
3642 writeU8(os, params.type);
3643 writeV3F1000(os, pos);
3644 writeU16(os, params.object);
3645 writeU8(os, params.loop);
3647 std::string s = os.str();
3648 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3650 for(std::set<RemoteClient*>::iterator i = dst_clients.begin();
3651 i != dst_clients.end(); i++){
3653 m_con.Send((*i)->peer_id, 0, data, true);
3657 void Server::stopSound(s32 handle)
3659 // Get sound reference
3660 std::map<s32, ServerPlayingSound>::iterator i =
3661 m_playing_sounds.find(handle);
3662 if(i == m_playing_sounds.end())
3664 ServerPlayingSound &psound = i->second;
3666 std::ostringstream os(std::ios_base::binary);
3667 writeU16(os, TOCLIENT_STOP_SOUND);
3668 writeS32(os, handle);
3670 std::string s = os.str();
3671 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3673 for(std::set<u16>::iterator i = psound.clients.begin();
3674 i != psound.clients.end(); i++){
3676 m_con.Send(*i, 0, data, true);
3678 // Remove sound reference
3679 m_playing_sounds.erase(i);
3682 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3683 core::list<u16> *far_players, float far_d_nodes)
3685 float maxd = far_d_nodes*BS;
3686 v3f p_f = intToFloat(p, BS);
3690 SharedBuffer<u8> reply(replysize);
3691 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3692 writeS16(&reply[2], p.X);
3693 writeS16(&reply[4], p.Y);
3694 writeS16(&reply[6], p.Z);
3696 for(core::map<u16, RemoteClient*>::Iterator
3697 i = m_clients.getIterator();
3698 i.atEnd() == false; i++)
3700 // Get client and check that it is valid
3701 RemoteClient *client = i.getNode()->getValue();
3702 assert(client->peer_id == i.getNode()->getKey());
3703 if(client->serialization_version == SER_FMT_VER_INVALID)
3706 // Don't send if it's the same one
3707 if(client->peer_id == ignore_id)
3713 Player *player = m_env->getPlayer(client->peer_id);
3716 // If player is far away, only set modified blocks not sent
3717 v3f player_pos = player->getPosition();
3718 if(player_pos.getDistanceFrom(p_f) > maxd)
3720 far_players->push_back(client->peer_id);
3727 m_con.Send(client->peer_id, 0, reply, true);
3731 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3732 core::list<u16> *far_players, float far_d_nodes)
3734 float maxd = far_d_nodes*BS;
3735 v3f p_f = intToFloat(p, BS);
3737 for(core::map<u16, RemoteClient*>::Iterator
3738 i = m_clients.getIterator();
3739 i.atEnd() == false; i++)
3741 // Get client and check that it is valid
3742 RemoteClient *client = i.getNode()->getValue();
3743 assert(client->peer_id == i.getNode()->getKey());
3744 if(client->serialization_version == SER_FMT_VER_INVALID)
3747 // Don't send if it's the same one
3748 if(client->peer_id == ignore_id)
3754 Player *player = m_env->getPlayer(client->peer_id);
3757 // If player is far away, only set modified blocks not sent
3758 v3f player_pos = player->getPosition();
3759 if(player_pos.getDistanceFrom(p_f) > maxd)
3761 far_players->push_back(client->peer_id);
3768 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3769 SharedBuffer<u8> reply(replysize);
3770 writeU16(&reply[0], TOCLIENT_ADDNODE);
3771 writeS16(&reply[2], p.X);
3772 writeS16(&reply[4], p.Y);
3773 writeS16(&reply[6], p.Z);
3774 n.serialize(&reply[8], client->serialization_version);
3777 m_con.Send(client->peer_id, 0, reply, true);
3781 void Server::setBlockNotSent(v3s16 p)
3783 for(core::map<u16, RemoteClient*>::Iterator
3784 i = m_clients.getIterator();
3785 i.atEnd()==false; i++)
3787 RemoteClient *client = i.getNode()->getValue();
3788 client->SetBlockNotSent(p);
3792 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3794 DSTACK(__FUNCTION_NAME);
3796 v3s16 p = block->getPos();
3800 bool completely_air = true;
3801 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3802 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3803 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3805 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
3807 completely_air = false;
3808 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
3813 infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
3815 infostream<<"[completely air] ";
3816 infostream<<std::endl;
3820 Create a packet with the block in the right format
3823 std::ostringstream os(std::ios_base::binary);
3824 block->serialize(os, ver, false);
3825 std::string s = os.str();
3826 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3828 u32 replysize = 8 + blockdata.getSize();
3829 SharedBuffer<u8> reply(replysize);
3830 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3831 writeS16(&reply[2], p.X);
3832 writeS16(&reply[4], p.Y);
3833 writeS16(&reply[6], p.Z);
3834 memcpy(&reply[8], *blockdata, blockdata.getSize());
3836 /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3837 <<": \tpacket size: "<<replysize<<std::endl;*/
3842 m_con.Send(peer_id, 1, reply, true);
3845 void Server::SendBlocks(float dtime)
3847 DSTACK(__FUNCTION_NAME);
3849 JMutexAutoLock envlock(m_env_mutex);
3850 JMutexAutoLock conlock(m_con_mutex);
3852 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
3854 core::array<PrioritySortedBlockTransfer> queue;
3856 s32 total_sending = 0;
3859 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
3861 for(core::map<u16, RemoteClient*>::Iterator
3862 i = m_clients.getIterator();
3863 i.atEnd() == false; i++)
3865 RemoteClient *client = i.getNode()->getValue();
3866 assert(client->peer_id == i.getNode()->getKey());
3868 // If definitions and textures have not been sent, don't
3869 // send MapBlocks either
3870 if(!client->definitions_sent)
3873 total_sending += client->SendingCount();
3875 if(client->serialization_version == SER_FMT_VER_INVALID)
3878 client->GetNextBlocks(this, dtime, queue);
3883 // Lowest priority number comes first.
3884 // Lowest is most important.
3887 for(u32 i=0; i<queue.size(); i++)
3889 //TODO: Calculate limit dynamically
3890 if(total_sending >= g_settings->getS32
3891 ("max_simultaneous_block_sends_server_total"))
3894 PrioritySortedBlockTransfer q = queue[i];
3896 MapBlock *block = NULL;
3899 block = m_env->getMap().getBlockNoCreate(q.pos);
3901 catch(InvalidPositionException &e)
3906 RemoteClient *client = getClient(q.peer_id);
3908 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3910 client->SentBlock(q.pos);
3916 void Server::fillMediaCache()
3918 DSTACK(__FUNCTION_NAME);
3920 infostream<<"Server: Calculating media file checksums"<<std::endl;
3922 // Collect all media file paths
3923 std::list<std::string> paths;
3924 for(core::list<ModSpec>::Iterator i = m_mods.begin();
3925 i != m_mods.end(); i++){
3926 const ModSpec &mod = *i;
3927 paths.push_back(mod.path + DIR_DELIM + "textures");
3928 paths.push_back(mod.path + DIR_DELIM + "sounds");
3929 paths.push_back(mod.path + DIR_DELIM + "media");
3932 // Collect media file information from paths into cache
3933 for(std::list<std::string>::iterator i = paths.begin();
3934 i != paths.end(); i++)
3936 std::string mediapath = *i;
3937 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
3938 for(u32 j=0; j<dirlist.size(); j++){
3939 if(dirlist[j].dir) // Ignode dirs
3941 std::string filename = dirlist[j].name;
3942 // If name contains illegal characters, ignore the file
3943 if(!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)){
3944 infostream<<"Server: ignoring illegal file name: \""
3945 <<filename<<"\""<<std::endl;
3948 // If name is not in a supported format, ignore it
3949 const char *supported_ext[] = {
3950 ".png", ".jpg", ".bmp", ".tga",
3951 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
3955 if(removeStringEnd(filename, supported_ext) == ""){
3956 infostream<<"Server: ignoring unsupported file extension: \""
3957 <<filename<<"\""<<std::endl;
3960 // Ok, attempt to load the file and add to cache
3961 std::string filepath = mediapath + DIR_DELIM + filename;
3963 std::ifstream fis(filepath.c_str(), std::ios_base::binary);
3964 if(fis.good() == false){
3965 errorstream<<"Server::fillMediaCache(): Could not open \""
3966 <<filename<<"\" for reading"<<std::endl;
3969 std::ostringstream tmp_os(std::ios_base::binary);
3973 fis.read(buf, 1024);
3974 std::streamsize len = fis.gcount();
3975 tmp_os.write(buf, len);
3984 errorstream<<"Server::fillMediaCache(): Failed to read \""
3985 <<filename<<"\""<<std::endl;
3988 if(tmp_os.str().length() == 0){
3989 errorstream<<"Server::fillMediaCache(): Empty file \""
3990 <<filepath<<"\""<<std::endl;
3995 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
3997 unsigned char *digest = sha1.getDigest();
3998 std::string sha1_base64 = base64_encode(digest, 20);
3999 std::string sha1_hex = hex_encode((char*)digest, 20);
4003 this->m_media[filename] = MediaInfo(filepath, sha1_base64);
4004 verbosestream<<"Server: "<<sha1_hex<<" is "<<filename<<std::endl;
4009 struct SendableMediaAnnouncement
4012 std::string sha1_digest;
4014 SendableMediaAnnouncement(const std::string name_="",
4015 const std::string sha1_digest_=""):
4017 sha1_digest(sha1_digest_)
4021 void Server::sendMediaAnnouncement(u16 peer_id)
4023 DSTACK(__FUNCTION_NAME);
4025 verbosestream<<"Server: Announcing files to id("<<peer_id<<")"
4028 core::list<SendableMediaAnnouncement> file_announcements;
4030 for(std::map<std::string, MediaInfo>::iterator i = m_media.begin();
4031 i != m_media.end(); i++){
4033 file_announcements.push_back(
4034 SendableMediaAnnouncement(i->first, i->second.sha1_digest));
4038 std::ostringstream os(std::ios_base::binary);
4046 u16 length of sha1_digest
4051 writeU16(os, TOCLIENT_ANNOUNCE_MEDIA);
4052 writeU16(os, file_announcements.size());
4054 for(core::list<SendableMediaAnnouncement>::Iterator
4055 j = file_announcements.begin();
4056 j != file_announcements.end(); j++){
4057 os<<serializeString(j->name);
4058 os<<serializeString(j->sha1_digest);
4062 std::string s = os.str();
4063 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4066 m_con.Send(peer_id, 0, data, true);
4070 struct SendableMedia
4076 SendableMedia(const std::string &name_="", const std::string path_="",
4077 const std::string &data_=""):
4084 void Server::sendRequestedMedia(u16 peer_id,
4085 const core::list<MediaRequest> &tosend)
4087 DSTACK(__FUNCTION_NAME);
4089 verbosestream<<"Server::sendRequestedMedia(): "
4090 <<"Sending files to client"<<std::endl;
4094 // Put 5kB in one bunch (this is not accurate)
4095 u32 bytes_per_bunch = 5000;
4097 core::array< core::list<SendableMedia> > file_bunches;
4098 file_bunches.push_back(core::list<SendableMedia>());
4100 u32 file_size_bunch_total = 0;
4102 for(core::list<MediaRequest>::ConstIterator i = tosend.begin();
4103 i != tosend.end(); i++)
4105 if(m_media.find(i->name) == m_media.end()){
4106 errorstream<<"Server::sendRequestedMedia(): Client asked for "
4107 <<"unknown file \""<<(i->name)<<"\""<<std::endl;
4111 //TODO get path + name
4112 std::string tpath = m_media[(*i).name].path;
4115 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
4116 if(fis.good() == false){
4117 errorstream<<"Server::sendRequestedMedia(): Could not open \""
4118 <<tpath<<"\" for reading"<<std::endl;
4121 std::ostringstream tmp_os(std::ios_base::binary);
4125 fis.read(buf, 1024);
4126 std::streamsize len = fis.gcount();
4127 tmp_os.write(buf, len);
4128 file_size_bunch_total += len;
4137 errorstream<<"Server::sendRequestedMedia(): Failed to read \""
4138 <<(*i).name<<"\""<<std::endl;
4141 /*infostream<<"Server::sendRequestedMedia(): Loaded \""
4142 <<tname<<"\""<<std::endl;*/
4144 file_bunches[file_bunches.size()-1].push_back(
4145 SendableMedia((*i).name, tpath, tmp_os.str()));
4147 // Start next bunch if got enough data
4148 if(file_size_bunch_total >= bytes_per_bunch){
4149 file_bunches.push_back(core::list<SendableMedia>());
4150 file_size_bunch_total = 0;
4155 /* Create and send packets */
4157 u32 num_bunches = file_bunches.size();
4158 for(u32 i=0; i<num_bunches; i++)
4160 std::ostringstream os(std::ios_base::binary);
4164 u16 total number of texture bunches
4165 u16 index of this bunch
4166 u32 number of files in this bunch
4175 writeU16(os, TOCLIENT_MEDIA);
4176 writeU16(os, num_bunches);
4178 writeU32(os, file_bunches[i].size());
4180 for(core::list<SendableMedia>::Iterator
4181 j = file_bunches[i].begin();
4182 j != file_bunches[i].end(); j++){
4183 os<<serializeString(j->name);
4184 os<<serializeLongString(j->data);
4188 std::string s = os.str();
4189 verbosestream<<"Server::sendRequestedMedia(): bunch "
4190 <<i<<"/"<<num_bunches
4191 <<" files="<<file_bunches[i].size()
4192 <<" size=" <<s.size()<<std::endl;
4193 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4195 m_con.Send(peer_id, 0, data, true);
4203 void Server::DiePlayer(u16 peer_id)
4205 DSTACK(__FUNCTION_NAME);
4207 PlayerSAO *playersao = getPlayerSAO(peer_id);
4210 infostream<<"Server::DiePlayer(): Player "
4211 <<playersao->getPlayer()->getName()
4212 <<" dies"<<std::endl;
4214 playersao->setHP(0);
4216 // Trigger scripted stuff
4217 scriptapi_on_dieplayer(m_lua, playersao);
4219 SendPlayerHP(peer_id);
4220 SendDeathscreen(m_con, peer_id, false, v3f(0,0,0));
4223 void Server::RespawnPlayer(u16 peer_id)
4225 DSTACK(__FUNCTION_NAME);
4227 PlayerSAO *playersao = getPlayerSAO(peer_id);
4230 infostream<<"Server::RespawnPlayer(): Player "
4231 <<playersao->getPlayer()->getName()
4232 <<" respawns"<<std::endl;
4234 playersao->setHP(PLAYER_MAX_HP);
4236 bool repositioned = scriptapi_on_respawnplayer(m_lua, playersao);
4238 v3f pos = findSpawnPos(m_env->getServerMap());
4239 playersao->setPos(pos);
4243 void Server::UpdateCrafting(u16 peer_id)
4245 DSTACK(__FUNCTION_NAME);
4247 Player* player = m_env->getPlayer(peer_id);
4250 // Get a preview for crafting
4252 // No crafting in creative mode
4253 if(g_settings->getBool("creative_mode") == false)
4254 getCraftingResult(&player->inventory, preview, false, this);
4256 // Put the new preview in
4257 InventoryList *plist = player->inventory.getList("craftpreview");
4259 assert(plist->getSize() >= 1);
4260 plist->changeItem(0, preview);
4263 RemoteClient* Server::getClient(u16 peer_id)
4265 DSTACK(__FUNCTION_NAME);
4266 //JMutexAutoLock lock(m_con_mutex);
4267 core::map<u16, RemoteClient*>::Node *n;
4268 n = m_clients.find(peer_id);
4269 // A client should exist for all peers
4271 return n->getValue();
4274 std::wstring Server::getStatusString()
4276 std::wostringstream os(std::ios_base::binary);
4279 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4281 os<<L", uptime="<<m_uptime.get();
4282 // Information about clients
4284 for(core::map<u16, RemoteClient*>::Iterator
4285 i = m_clients.getIterator();
4286 i.atEnd() == false; i++)
4288 // Get client and check that it is valid
4289 RemoteClient *client = i.getNode()->getValue();
4290 assert(client->peer_id == i.getNode()->getKey());
4291 if(client->serialization_version == SER_FMT_VER_INVALID)
4294 Player *player = m_env->getPlayer(client->peer_id);
4295 // Get name of player
4296 std::wstring name = L"unknown";
4298 name = narrow_to_wide(player->getName());
4299 // Add name to information string
4303 if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
4304 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4305 if(g_settings->get("motd") != "")
4306 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4310 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
4312 std::set<std::string> privs;
4313 scriptapi_get_auth(m_lua, name, NULL, &privs);
4317 bool Server::checkPriv(const std::string &name, const std::string &priv)
4319 std::set<std::string> privs = getPlayerEffectivePrivs(name);
4320 return (privs.count(priv) != 0);
4323 void Server::reportPrivsModified(const std::string &name)
4326 for(core::map<u16, RemoteClient*>::Iterator
4327 i = m_clients.getIterator();
4328 i.atEnd() == false; i++){
4329 RemoteClient *client = i.getNode()->getValue();
4330 Player *player = m_env->getPlayer(client->peer_id);
4331 reportPrivsModified(player->getName());
4334 Player *player = m_env->getPlayer(name.c_str());
4337 SendPlayerPrivileges(player->peer_id);
4338 PlayerSAO *sao = player->getPlayerSAO();
4341 sao->updatePrivileges(
4342 getPlayerEffectivePrivs(name),
4347 // Saves g_settings to configpath given at initialization
4348 void Server::saveConfig()
4350 if(m_path_config != "")
4351 g_settings->updateConfigFile(m_path_config.c_str());
4354 void Server::notifyPlayer(const char *name, const std::wstring msg)
4356 Player *player = m_env->getPlayer(name);
4359 SendChatMessage(player->peer_id, std::wstring(L"Server: -!- ")+msg);
4362 void Server::notifyPlayers(const std::wstring msg)
4364 BroadcastChatMessage(msg);
4367 void Server::queueBlockEmerge(v3s16 blockpos, bool allow_generate)
4371 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
4372 m_emerge_queue.addBlock(PEER_ID_INEXISTENT, blockpos, flags);
4375 // IGameDef interface
4377 IItemDefManager* Server::getItemDefManager()
4381 INodeDefManager* Server::getNodeDefManager()
4385 ICraftDefManager* Server::getCraftDefManager()
4389 ITextureSource* Server::getTextureSource()
4393 u16 Server::allocateUnknownNodeId(const std::string &name)
4395 return m_nodedef->allocateDummy(name);
4397 ISoundManager* Server::getSoundManager()
4399 return &dummySoundManager;
4401 MtEventManager* Server::getEventManager()
4406 IWritableItemDefManager* Server::getWritableItemDefManager()
4410 IWritableNodeDefManager* Server::getWritableNodeDefManager()
4414 IWritableCraftDefManager* Server::getWritableCraftDefManager()
4419 const ModSpec* Server::getModSpec(const std::string &modname)
4421 for(core::list<ModSpec>::Iterator i = m_mods.begin();
4422 i != m_mods.end(); i++){
4423 const ModSpec &mod = *i;
4424 if(mod.name == modname)
4429 std::string Server::getBuiltinLuaPath()
4431 return porting::path_share + DIR_DELIM + "builtin";
4434 v3f findSpawnPos(ServerMap &map)
4436 //return v3f(50,50,50)*BS;
4441 nodepos = v2s16(0,0);
4446 // Try to find a good place a few times
4447 for(s32 i=0; i<1000; i++)
4450 // We're going to try to throw the player to this position
4451 v2s16 nodepos2d = v2s16(-range + (myrand()%(range*2)),
4452 -range + (myrand()%(range*2)));
4453 //v2s16 sectorpos = getNodeSectorPos(nodepos2d);
4454 // Get ground height at point (fallbacks to heightmap function)
4455 s16 groundheight = map.findGroundLevel(nodepos2d);
4456 // Don't go underwater
4457 if(groundheight < WATER_LEVEL)
4459 //infostream<<"-> Underwater"<<std::endl;
4462 // Don't go to high places
4463 if(groundheight > WATER_LEVEL + 4)
4465 //infostream<<"-> Underwater"<<std::endl;
4469 nodepos = v3s16(nodepos2d.X, groundheight-2, nodepos2d.Y);
4470 bool is_good = false;
4472 for(s32 i=0; i<10; i++){
4473 v3s16 blockpos = getNodeBlockPos(nodepos);
4474 map.emergeBlock(blockpos, true);
4475 MapNode n = map.getNodeNoEx(nodepos);
4476 if(n.getContent() == CONTENT_AIR){
4487 // Found a good place
4488 //infostream<<"Searched through "<<i<<" places."<<std::endl;
4494 return intToFloat(nodepos, BS);
4497 PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id)
4499 RemotePlayer *player = NULL;
4500 bool newplayer = false;
4503 Try to get an existing player
4505 player = static_cast<RemotePlayer*>(m_env->getPlayer(name));
4507 // If player is already connected, cancel
4508 if(player != NULL && player->peer_id != 0)
4510 infostream<<"emergePlayer(): Player already connected"<<std::endl;
4515 If player with the wanted peer_id already exists, cancel.
4517 if(m_env->getPlayer(peer_id) != NULL)
4519 infostream<<"emergePlayer(): Player with wrong name but same"
4520 " peer_id already exists"<<std::endl;
4525 Create a new player if it doesn't exist yet
4530 player = new RemotePlayer(this);
4531 player->updateName(name);
4533 /* Set player position */
4534 infostream<<"Server: Finding spawn place for player \""
4535 <<name<<"\""<<std::endl;
4536 v3f pos = findSpawnPos(m_env->getServerMap());
4537 player->setPosition(pos);
4539 /* Add player to environment */
4540 m_env->addPlayer(player);
4544 Create a new player active object
4546 PlayerSAO *playersao = new PlayerSAO(m_env, player, peer_id,
4547 getPlayerEffectivePrivs(player->getName()),
4550 /* Add object to environment */
4551 m_env->addActiveObject(playersao);
4555 scriptapi_on_newplayer(m_lua, playersao);
4557 scriptapi_on_joinplayer(m_lua, playersao);
4560 if(g_settings->getBool("creative_mode"))
4561 playersao->createCreativeInventory();
4566 void Server::handlePeerChange(PeerChange &c)
4568 JMutexAutoLock envlock(m_env_mutex);
4569 JMutexAutoLock conlock(m_con_mutex);
4571 if(c.type == PEER_ADDED)
4578 core::map<u16, RemoteClient*>::Node *n;
4579 n = m_clients.find(c.peer_id);
4580 // The client shouldn't already exist
4584 RemoteClient *client = new RemoteClient();
4585 client->peer_id = c.peer_id;
4586 m_clients.insert(client->peer_id, client);
4589 else if(c.type == PEER_REMOVED)
4596 core::map<u16, RemoteClient*>::Node *n;
4597 n = m_clients.find(c.peer_id);
4598 // The client should exist
4602 Mark objects to be not known by the client
4604 RemoteClient *client = n->getValue();
4606 for(core::map<u16, bool>::Iterator
4607 i = client->m_known_objects.getIterator();
4608 i.atEnd()==false; i++)
4611 u16 id = i.getNode()->getKey();
4612 ServerActiveObject* obj = m_env->getActiveObject(id);
4614 if(obj && obj->m_known_by_count > 0)
4615 obj->m_known_by_count--;
4619 Clear references to playing sounds
4621 for(std::map<s32, ServerPlayingSound>::iterator
4622 i = m_playing_sounds.begin();
4623 i != m_playing_sounds.end();)
4625 ServerPlayingSound &psound = i->second;
4626 psound.clients.erase(c.peer_id);
4627 if(psound.clients.size() == 0)
4628 m_playing_sounds.erase(i++);
4633 Player *player = m_env->getPlayer(c.peer_id);
4635 // Collect information about leaving in chat
4636 std::wstring message;
4640 std::wstring name = narrow_to_wide(player->getName());
4643 message += L" left game";
4645 message += L" (timed out)";
4649 /* Run scripts and remove from environment */
4653 PlayerSAO *playersao = player->getPlayerSAO();
4656 scriptapi_on_leaveplayer(m_lua, playersao);
4658 playersao->disconnected();
4668 std::ostringstream os(std::ios_base::binary);
4669 for(core::map<u16, RemoteClient*>::Iterator
4670 i = m_clients.getIterator();
4671 i.atEnd() == false; i++)
4673 RemoteClient *client = i.getNode()->getValue();
4674 assert(client->peer_id == i.getNode()->getKey());
4675 if(client->serialization_version == SER_FMT_VER_INVALID)
4678 Player *player = m_env->getPlayer(client->peer_id);
4681 // Get name of player
4682 os<<player->getName()<<" ";
4685 actionstream<<player->getName()<<" "
4686 <<(c.timeout?"times out.":"leaves game.")
4687 <<" List of players: "
4688 <<os.str()<<std::endl;
4693 delete m_clients[c.peer_id];
4694 m_clients.remove(c.peer_id);
4696 // Send player info to all remaining clients
4697 //SendPlayerInfos();
4699 // Send leave chat message to all remaining clients
4700 if(message.length() != 0)
4701 BroadcastChatMessage(message);
4710 void Server::handlePeerChanges()
4712 while(m_peer_change_queue.size() > 0)
4714 PeerChange c = m_peer_change_queue.pop_front();
4716 verbosestream<<"Server: Handling peer change: "
4717 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4720 handlePeerChange(c);
4724 void dedicated_server_loop(Server &server, bool &kill)
4726 DSTACK(__FUNCTION_NAME);
4728 verbosestream<<"dedicated_server_loop()"<<std::endl;
4730 IntervalLimiter m_profiler_interval;
4734 float steplen = g_settings->getFloat("dedicated_server_step");
4735 // This is kind of a hack but can be done like this
4736 // because server.step() is very light
4738 ScopeProfiler sp(g_profiler, "dedicated server sleep");
4739 sleep_ms((int)(steplen*1000.0));
4741 server.step(steplen);
4743 if(server.getShutdownRequested() || kill)
4745 infostream<<"Dedicated server quitting"<<std::endl;
4752 float profiler_print_interval =
4753 g_settings->getFloat("profiler_print_interval");
4754 if(profiler_print_interval != 0)
4756 if(m_profiler_interval.step(steplen, profiler_print_interval))
4758 infostream<<"Profiler:"<<std::endl;
4759 g_profiler->print(infostream);
4760 g_profiler->clear();