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;
2179 Player *player = m_env->getPlayer(peer_id);
2181 verbosestream<<"Server: TOSERVER_INIT2: "
2182 <<"Player not found; ignoring."<<std::endl;
2186 getClient(peer_id)->serialization_version
2187 = getClient(peer_id)->pending_serialization_version;
2190 Send some initialization data
2193 infostream<<"Server: Sending content to "
2194 <<getPlayerName(peer_id)<<std::endl;
2196 // Send item definitions
2197 SendItemDef(m_con, peer_id, m_itemdef);
2199 // Send node definitions
2200 SendNodeDef(m_con, peer_id, m_nodedef);
2202 // Send media announcement
2203 sendMediaAnnouncement(peer_id);
2206 SendPlayerPrivileges(peer_id);
2209 UpdateCrafting(peer_id);
2210 SendInventory(peer_id);
2213 SendPlayerHP(peer_id);
2215 // Show death screen if necessary
2217 SendDeathscreen(m_con, peer_id, false, v3f(0,0,0));
2221 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2222 m_env->getTimeOfDay(), g_settings->getFloat("time_speed"));
2223 m_con.Send(peer_id, 0, data, true);
2226 // Note things in chat if not in simple singleplayer mode
2227 if(!m_simple_singleplayer_mode)
2229 // Send information about server to player in chat
2230 SendChatMessage(peer_id, getStatusString());
2232 // Send information about joining in chat
2234 std::wstring name = L"unknown";
2235 Player *player = m_env->getPlayer(peer_id);
2237 name = narrow_to_wide(player->getName());
2239 std::wstring message;
2242 message += L" joined game";
2243 BroadcastChatMessage(message);
2247 // Warnings about protocol version can be issued here
2248 if(getClient(peer_id)->net_proto_version < PROTOCOL_VERSION)
2250 SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT IS OLD AND MAY WORK PROPERLY WITH THIS SERVER");
2257 std::ostringstream os(std::ios_base::binary);
2258 for(core::map<u16, RemoteClient*>::Iterator
2259 i = m_clients.getIterator();
2260 i.atEnd() == false; i++)
2262 RemoteClient *client = i.getNode()->getValue();
2263 assert(client->peer_id == i.getNode()->getKey());
2264 if(client->serialization_version == SER_FMT_VER_INVALID)
2267 Player *player = m_env->getPlayer(client->peer_id);
2270 // Get name of player
2271 os<<player->getName()<<" ";
2274 actionstream<<player->getName()<<" joins game. List of players: "
2275 <<os.str()<<std::endl;
2281 if(peer_ser_ver == SER_FMT_VER_INVALID)
2283 infostream<<"Server::ProcessData(): Cancelling: Peer"
2284 " serialization format invalid or not initialized."
2285 " Skipping incoming command="<<command<<std::endl;
2289 Player *player = m_env->getPlayer(peer_id);
2291 infostream<<"Server::ProcessData(): Cancelling: "
2292 "No player for peer_id="<<peer_id
2297 PlayerSAO *playersao = player->getPlayerSAO();
2298 if(playersao == NULL){
2299 infostream<<"Server::ProcessData(): Cancelling: "
2300 "No player object for peer_id="<<peer_id
2305 if(command == TOSERVER_PLAYERPOS)
2307 if(datasize < 2+12+12+4+4)
2311 v3s32 ps = readV3S32(&data[start+2]);
2312 v3s32 ss = readV3S32(&data[start+2+12]);
2313 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2314 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2315 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2316 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2317 pitch = wrapDegrees(pitch);
2318 yaw = wrapDegrees(yaw);
2320 player->setPosition(position);
2321 player->setSpeed(speed);
2322 player->setPitch(pitch);
2323 player->setYaw(yaw);
2325 /*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2326 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2327 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2329 else if(command == TOSERVER_GOTBLOCKS)
2342 u16 count = data[2];
2343 for(u16 i=0; i<count; i++)
2345 if((s16)datasize < 2+1+(i+1)*6)
2346 throw con::InvalidIncomingDataException
2347 ("GOTBLOCKS length is too short");
2348 v3s16 p = readV3S16(&data[2+1+i*6]);
2349 /*infostream<<"Server: GOTBLOCKS ("
2350 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2351 RemoteClient *client = getClient(peer_id);
2352 client->GotBlock(p);
2355 else if(command == TOSERVER_DELETEDBLOCKS)
2368 u16 count = data[2];
2369 for(u16 i=0; i<count; i++)
2371 if((s16)datasize < 2+1+(i+1)*6)
2372 throw con::InvalidIncomingDataException
2373 ("DELETEDBLOCKS length is too short");
2374 v3s16 p = readV3S16(&data[2+1+i*6]);
2375 /*infostream<<"Server: DELETEDBLOCKS ("
2376 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2377 RemoteClient *client = getClient(peer_id);
2378 client->SetBlockNotSent(p);
2381 else if(command == TOSERVER_CLICK_OBJECT)
2383 infostream<<"Server: CLICK_OBJECT not supported anymore"<<std::endl;
2386 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2388 infostream<<"Server: CLICK_ACTIVEOBJECT not supported anymore"<<std::endl;
2391 else if(command == TOSERVER_GROUND_ACTION)
2393 infostream<<"Server: GROUND_ACTION not supported anymore"<<std::endl;
2397 else if(command == TOSERVER_RELEASE)
2399 infostream<<"Server: RELEASE not supported anymore"<<std::endl;
2402 else if(command == TOSERVER_SIGNTEXT)
2404 infostream<<"Server: SIGNTEXT not supported anymore"
2408 else if(command == TOSERVER_SIGNNODETEXT)
2410 if(!checkPriv(player->getName(), "interact"))
2418 std::string datastring((char*)&data[2], datasize-2);
2419 std::istringstream is(datastring, std::ios_base::binary);
2422 is.read((char*)buf, 6);
2423 v3s16 p = readV3S16(buf);
2424 is.read((char*)buf, 2);
2425 u16 textlen = readU16(buf);
2427 for(u16 i=0; i<textlen; i++)
2429 is.read((char*)buf, 1);
2430 text += (char)buf[0];
2433 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
2437 meta->setText(text);
2439 actionstream<<player->getName()<<" writes \""<<text<<"\" to sign"
2440 <<" at "<<PP(p)<<std::endl;
2442 v3s16 blockpos = getNodeBlockPos(p);
2443 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
2446 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2450 setBlockNotSent(blockpos);
2452 else if(command == TOSERVER_INVENTORY_ACTION)
2454 // Strip command and create a stream
2455 std::string datastring((char*)&data[2], datasize-2);
2456 verbosestream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2457 std::istringstream is(datastring, std::ios_base::binary);
2459 InventoryAction *a = InventoryAction::deSerialize(is);
2462 infostream<<"TOSERVER_INVENTORY_ACTION: "
2463 <<"InventoryAction::deSerialize() returned NULL"
2469 Note: Always set inventory not sent, to repair cases
2470 where the client made a bad prediction.
2474 Handle restrictions and special cases of the move action
2476 if(a->getType() == IACTION_MOVE)
2478 IMoveAction *ma = (IMoveAction*)a;
2480 ma->from_inv.applyCurrentPlayer(player->getName());
2481 ma->to_inv.applyCurrentPlayer(player->getName());
2483 setInventoryModified(ma->from_inv);
2484 setInventoryModified(ma->to_inv);
2486 bool from_inv_is_current_player =
2487 (ma->from_inv.type == InventoryLocation::PLAYER) &&
2488 (ma->from_inv.name == player->getName());
2490 bool to_inv_is_current_player =
2491 (ma->to_inv.type == InventoryLocation::PLAYER) &&
2492 (ma->to_inv.name == player->getName());
2495 Disable moving items out of craftpreview
2497 if(ma->from_list == "craftpreview")
2499 infostream<<"Ignoring IMoveAction from "
2500 <<(ma->from_inv.dump())<<":"<<ma->from_list
2501 <<" to "<<(ma->to_inv.dump())<<":"<<ma->to_list
2502 <<" because src is "<<ma->from_list<<std::endl;
2508 Disable moving items into craftresult and craftpreview
2510 if(ma->to_list == "craftpreview" || ma->to_list == "craftresult")
2512 infostream<<"Ignoring IMoveAction from "
2513 <<(ma->from_inv.dump())<<":"<<ma->from_list
2514 <<" to "<<(ma->to_inv.dump())<<":"<<ma->to_list
2515 <<" because dst is "<<ma->to_list<<std::endl;
2520 // Disallow moving items in elsewhere than player's inventory
2521 // if not allowed to interact
2522 if(!checkPriv(player->getName(), "interact") &&
2523 (!from_inv_is_current_player ||
2524 !to_inv_is_current_player))
2526 infostream<<"Cannot move outside of player's inventory: "
2527 <<"No interact privilege"<<std::endl;
2532 // If player is not an admin, check for ownership of src and dst
2533 if(!checkPriv(player->getName(), "server"))
2535 std::string owner_from = getInventoryOwner(ma->from_inv);
2536 if(owner_from != "" && owner_from != player->getName())
2538 infostream<<"WARNING: "<<player->getName()
2539 <<" tried to access an inventory that"
2540 <<" belongs to "<<owner_from<<std::endl;
2545 std::string owner_to = getInventoryOwner(ma->to_inv);
2546 if(owner_to != "" && owner_to != player->getName())
2548 infostream<<"WARNING: "<<player->getName()
2549 <<" tried to access an inventory that"
2550 <<" belongs to "<<owner_to<<std::endl;
2557 Handle restrictions and special cases of the drop action
2559 else if(a->getType() == IACTION_DROP)
2561 IDropAction *da = (IDropAction*)a;
2563 da->from_inv.applyCurrentPlayer(player->getName());
2565 setInventoryModified(da->from_inv);
2567 // Disallow dropping items if not allowed to interact
2568 if(!checkPriv(player->getName(), "interact"))
2573 // If player is not an admin, check for ownership
2574 else if(!checkPriv(player->getName(), "server"))
2576 std::string owner_from = getInventoryOwner(da->from_inv);
2577 if(owner_from != "" && owner_from != player->getName())
2579 infostream<<"WARNING: "<<player->getName()
2580 <<" tried to access an inventory that"
2581 <<" belongs to "<<owner_from<<std::endl;
2588 Handle restrictions and special cases of the craft action
2590 else if(a->getType() == IACTION_CRAFT)
2592 ICraftAction *ca = (ICraftAction*)a;
2594 ca->craft_inv.applyCurrentPlayer(player->getName());
2596 setInventoryModified(ca->craft_inv);
2598 //bool craft_inv_is_current_player =
2599 // (ca->craft_inv.type == InventoryLocation::PLAYER) &&
2600 // (ca->craft_inv.name == player->getName());
2602 // Disallow crafting if not allowed to interact
2603 if(!checkPriv(player->getName(), "interact"))
2605 infostream<<"Cannot craft: "
2606 <<"No interact privilege"<<std::endl;
2611 // If player is not an admin, check for ownership of inventory
2612 if(!checkPriv(player->getName(), "server"))
2614 std::string owner_craft = getInventoryOwner(ca->craft_inv);
2615 if(owner_craft != "" && owner_craft != player->getName())
2617 infostream<<"WARNING: "<<player->getName()
2618 <<" tried to access an inventory that"
2619 <<" belongs to "<<owner_craft<<std::endl;
2627 a->apply(this, playersao, this);
2631 else if(command == TOSERVER_CHAT_MESSAGE)
2639 std::string datastring((char*)&data[2], datasize-2);
2640 std::istringstream is(datastring, std::ios_base::binary);
2643 is.read((char*)buf, 2);
2644 u16 len = readU16(buf);
2646 std::wstring message;
2647 for(u16 i=0; i<len; i++)
2649 is.read((char*)buf, 2);
2650 message += (wchar_t)readU16(buf);
2653 // Get player name of this client
2654 std::wstring name = narrow_to_wide(player->getName());
2657 bool ate = scriptapi_on_chat_message(m_lua, player->getName(),
2658 wide_to_narrow(message));
2659 // If script ate the message, don't proceed
2663 // Line to send to players
2665 // Whether to send to the player that sent the line
2666 bool send_to_sender = false;
2667 // Whether to send to other players
2668 bool send_to_others = false;
2671 if(message[0] == L'/')
2673 size_t strip_size = 1;
2674 if (message[1] == L'#') // support old-style commans
2676 message = message.substr(strip_size);
2678 WStrfnd f1(message);
2679 f1.next(L" "); // Skip over /#whatever
2680 std::wstring paramstring = f1.next(L"");
2682 ServerCommandContext *ctx = new ServerCommandContext(
2683 str_split(message, L' '),
2689 std::wstring reply(processServerCommand(ctx));
2690 send_to_sender = ctx->flags & SEND_TO_SENDER;
2691 send_to_others = ctx->flags & SEND_TO_OTHERS;
2693 if (ctx->flags & SEND_NO_PREFIX)
2696 line += L"Server: " + reply;
2703 if(checkPriv(player->getName(), "shout")){
2708 send_to_others = true;
2710 line += L"Server: You are not allowed to shout";
2711 send_to_sender = true;
2718 actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2721 Send the message to clients
2723 for(core::map<u16, RemoteClient*>::Iterator
2724 i = m_clients.getIterator();
2725 i.atEnd() == false; i++)
2727 // Get client and check that it is valid
2728 RemoteClient *client = i.getNode()->getValue();
2729 assert(client->peer_id == i.getNode()->getKey());
2730 if(client->serialization_version == SER_FMT_VER_INVALID)
2734 bool sender_selected = (peer_id == client->peer_id);
2735 if(sender_selected == true && send_to_sender == false)
2737 if(sender_selected == false && send_to_others == false)
2740 SendChatMessage(client->peer_id, line);
2744 else if(command == TOSERVER_DAMAGE)
2746 std::string datastring((char*)&data[2], datasize-2);
2747 std::istringstream is(datastring, std::ios_base::binary);
2748 u8 damage = readU8(is);
2750 actionstream<<player->getName()<<" damaged by "
2751 <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
2754 playersao->setHP(playersao->getHP() - damage);
2756 if(playersao->getHP() == 0 && playersao->m_hp_not_sent)
2759 if(playersao->m_hp_not_sent)
2760 SendPlayerHP(peer_id);
2762 else if(command == TOSERVER_PASSWORD)
2765 [0] u16 TOSERVER_PASSWORD
2766 [2] u8[28] old password
2767 [30] u8[28] new password
2770 if(datasize != 2+PASSWORD_SIZE*2)
2772 /*char password[PASSWORD_SIZE];
2773 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2774 password[i] = data[2+i];
2775 password[PASSWORD_SIZE-1] = 0;*/
2777 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2785 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2787 char c = data[2+PASSWORD_SIZE+i];
2793 infostream<<"Server: Client requests a password change from "
2794 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
2796 std::string playername = player->getName();
2798 std::string checkpwd;
2799 scriptapi_get_auth(m_lua, playername, &checkpwd, NULL);
2801 if(oldpwd != checkpwd)
2803 infostream<<"Server: invalid old password"<<std::endl;
2804 // Wrong old password supplied!!
2805 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
2809 bool success = scriptapi_set_password(m_lua, playername, newpwd);
2811 actionstream<<player->getName()<<" changes password"<<std::endl;
2812 SendChatMessage(peer_id, L"Password change successful");
2814 actionstream<<player->getName()<<" tries to change password but "
2815 <<"it fails"<<std::endl;
2816 SendChatMessage(peer_id, L"Password change failed or inavailable");
2819 else if(command == TOSERVER_PLAYERITEM)
2824 u16 item = readU16(&data[2]);
2825 playersao->setWieldIndex(item);
2827 else if(command == TOSERVER_RESPAWN)
2832 RespawnPlayer(peer_id);
2834 actionstream<<player->getName()<<" respawns at "
2835 <<PP(player->getPosition()/BS)<<std::endl;
2837 // ActiveObject is added to environment in AsyncRunStep after
2838 // the previous addition has been succesfully removed
2840 else if(command == TOSERVER_REQUEST_MEDIA) {
2841 std::string datastring((char*)&data[2], datasize-2);
2842 std::istringstream is(datastring, std::ios_base::binary);
2844 core::list<MediaRequest> tosend;
2845 u16 numfiles = readU16(is);
2847 infostream<<"Sending "<<numfiles<<" files to "
2848 <<getPlayerName(peer_id)<<std::endl;
2849 verbosestream<<"TOSERVER_REQUEST_MEDIA: "<<std::endl;
2851 for(int i = 0; i < numfiles; i++) {
2852 std::string name = deSerializeString(is);
2853 tosend.push_back(MediaRequest(name));
2854 verbosestream<<"TOSERVER_REQUEST_MEDIA: requested file "
2858 sendRequestedMedia(peer_id, tosend);
2860 // Now the client should know about everything
2861 // (definitions and files)
2862 getClient(peer_id)->definitions_sent = true;
2864 else if(command == TOSERVER_INTERACT)
2866 std::string datastring((char*)&data[2], datasize-2);
2867 std::istringstream is(datastring, std::ios_base::binary);
2873 [5] u32 length of the next item
2874 [9] serialized PointedThing
2876 0: start digging (from undersurface) or use
2877 1: stop digging (all parameters ignored)
2878 2: digging completed
2879 3: place block or item (to abovesurface)
2882 u8 action = readU8(is);
2883 u16 item_i = readU16(is);
2884 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
2885 PointedThing pointed;
2886 pointed.deSerialize(tmp_is);
2888 verbosestream<<"TOSERVER_INTERACT: action="<<(int)action<<", item="
2889 <<item_i<<", pointed="<<pointed.dump()<<std::endl;
2893 verbosestream<<"TOSERVER_INTERACT: "<<player->getName()
2894 <<" tried to interact, but is dead!"<<std::endl;
2898 v3f player_pos = playersao->getLastGoodPosition();
2900 // Update wielded item
2901 playersao->setWieldIndex(item_i);
2903 // Get pointed to node (undefined if not POINTEDTYPE_NODE)
2904 v3s16 p_under = pointed.node_undersurface;
2905 v3s16 p_above = pointed.node_abovesurface;
2907 // Get pointed to object (NULL if not POINTEDTYPE_OBJECT)
2908 ServerActiveObject *pointed_object = NULL;
2909 if(pointed.type == POINTEDTHING_OBJECT)
2911 pointed_object = m_env->getActiveObject(pointed.object_id);
2912 if(pointed_object == NULL)
2914 verbosestream<<"TOSERVER_INTERACT: "
2915 "pointed object is NULL"<<std::endl;
2921 v3f pointed_pos_under = player_pos;
2922 v3f pointed_pos_above = player_pos;
2923 if(pointed.type == POINTEDTHING_NODE)
2925 pointed_pos_under = intToFloat(p_under, BS);
2926 pointed_pos_above = intToFloat(p_above, BS);
2928 else if(pointed.type == POINTEDTHING_OBJECT)
2930 pointed_pos_under = pointed_object->getBasePosition();
2931 pointed_pos_above = pointed_pos_under;
2935 Check that target is reasonably close
2936 (only when digging or placing things)
2938 if(action == 0 || action == 2 || action == 3)
2940 float d = player_pos.getDistanceFrom(pointed_pos_under);
2941 float max_d = BS * 14; // Just some large enough value
2943 actionstream<<"Player "<<player->getName()
2944 <<" tried to access "<<pointed.dump()
2946 <<"d="<<d<<", max_d="<<max_d
2947 <<". ignoring."<<std::endl;
2948 // Re-send block to revert change on client-side
2949 RemoteClient *client = getClient(peer_id);
2950 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2951 client->SetBlockNotSent(blockpos);
2958 Make sure the player is allowed to do it
2960 if(!checkPriv(player->getName(), "interact"))
2962 actionstream<<player->getName()<<" attempted to interact with "
2963 <<pointed.dump()<<" without 'interact' privilege"
2965 // Re-send block to revert change on client-side
2966 RemoteClient *client = getClient(peer_id);
2967 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2968 client->SetBlockNotSent(blockpos);
2973 0: start digging or punch object
2977 if(pointed.type == POINTEDTHING_NODE)
2980 NOTE: This can be used in the future to check if
2981 somebody is cheating, by checking the timing.
2983 MapNode n(CONTENT_IGNORE);
2986 n = m_env->getMap().getNode(p_under);
2988 catch(InvalidPositionException &e)
2990 infostream<<"Server: Not punching: Node not found."
2991 <<" Adding block to emerge queue."
2993 m_emerge_queue.addBlock(peer_id,
2994 getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
2996 if(n.getContent() != CONTENT_IGNORE)
2997 scriptapi_node_on_punch(m_lua, p_under, n, playersao);
2999 else if(pointed.type == POINTEDTHING_OBJECT)
3001 // Skip if object has been removed
3002 if(pointed_object->m_removed)
3005 actionstream<<player->getName()<<" punches object "
3006 <<pointed.object_id<<": "
3007 <<pointed_object->getDescription()<<std::endl;
3009 ItemStack punchitem = playersao->getWieldedItem();
3010 ToolCapabilities toolcap =
3011 punchitem.getToolCapabilities(m_itemdef);
3012 v3f dir = (pointed_object->getBasePosition() -
3013 (player->getPosition() + player->getEyeOffset())
3015 float time_from_last_punch =
3016 playersao->resetTimeFromLastPunch();
3017 pointed_object->punch(dir, &toolcap, playersao,
3018 time_from_last_punch);
3026 else if(action == 1)
3031 2: Digging completed
3033 else if(action == 2)
3035 // Only complete digging of nodes
3036 if(pointed.type == POINTEDTHING_NODE)
3038 MapNode n(CONTENT_IGNORE);
3041 n = m_env->getMap().getNode(p_under);
3043 catch(InvalidPositionException &e)
3045 infostream<<"Server: Not finishing digging: Node not found."
3046 <<" Adding block to emerge queue."
3048 m_emerge_queue.addBlock(peer_id,
3049 getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
3051 if(n.getContent() != CONTENT_IGNORE)
3052 scriptapi_node_on_dig(m_lua, p_under, n, playersao);
3054 if (m_env->getMap().getNodeNoEx(p_under).getContent() != CONTENT_AIR)
3056 // Re-send block to revert change on client-side
3057 RemoteClient *client = getClient(peer_id);
3058 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
3059 client->SetBlockNotSent(blockpos);
3065 3: place block or right-click object
3067 else if(action == 3)
3069 ItemStack item = playersao->getWieldedItem();
3071 // Reset build time counter
3072 if(pointed.type == POINTEDTHING_NODE &&
3073 item.getDefinition(m_itemdef).type == ITEM_NODE)
3074 getClient(peer_id)->m_time_from_building = 0.0;
3076 if(pointed.type == POINTEDTHING_OBJECT)
3078 // Right click object
3080 // Skip if object has been removed
3081 if(pointed_object->m_removed)
3084 actionstream<<player->getName()<<" right-clicks object "
3085 <<pointed.object_id<<": "
3086 <<pointed_object->getDescription()<<std::endl;
3089 pointed_object->rightClick(playersao);
3091 else if(scriptapi_item_on_place(m_lua,
3092 item, playersao, pointed))
3094 // Placement was handled in lua
3096 // Apply returned ItemStack
3097 if(g_settings->getBool("creative_mode") == false)
3098 playersao->setWieldedItem(item);
3106 else if(action == 4)
3108 ItemStack item = playersao->getWieldedItem();
3110 actionstream<<player->getName()<<" uses "<<item.name
3111 <<", pointing at "<<pointed.dump()<<std::endl;
3113 if(scriptapi_item_on_use(m_lua,
3114 item, playersao, pointed))
3116 // Apply returned ItemStack
3117 if(g_settings->getBool("creative_mode") == false)
3118 playersao->setWieldedItem(item);
3124 Catch invalid actions
3128 infostream<<"WARNING: Server: Invalid action "
3129 <<action<<std::endl;
3132 else if(command == TOSERVER_REMOVED_SOUNDS)
3134 std::string datastring((char*)&data[2], datasize-2);
3135 std::istringstream is(datastring, std::ios_base::binary);
3137 int num = readU16(is);
3138 for(int k=0; k<num; k++){
3139 s32 id = readS32(is);
3140 std::map<s32, ServerPlayingSound>::iterator i =
3141 m_playing_sounds.find(id);
3142 if(i == m_playing_sounds.end())
3144 ServerPlayingSound &psound = i->second;
3145 psound.clients.erase(peer_id);
3146 if(psound.clients.size() == 0)
3147 m_playing_sounds.erase(i++);
3152 infostream<<"Server::ProcessData(): Ignoring "
3153 "unknown command "<<command<<std::endl;
3157 catch(SendFailedException &e)
3159 errorstream<<"Server::ProcessData(): SendFailedException: "
3165 void Server::onMapEditEvent(MapEditEvent *event)
3167 //infostream<<"Server::onMapEditEvent()"<<std::endl;
3168 if(m_ignore_map_edit_events)
3170 if(m_ignore_map_edit_events_area.contains(event->getArea()))
3172 MapEditEvent *e = event->clone();
3173 m_unsent_map_edit_queue.push_back(e);
3176 Inventory* Server::getInventory(const InventoryLocation &loc)
3179 case InventoryLocation::UNDEFINED:
3182 case InventoryLocation::CURRENT_PLAYER:
3185 case InventoryLocation::PLAYER:
3187 Player *player = m_env->getPlayer(loc.name.c_str());
3190 PlayerSAO *playersao = player->getPlayerSAO();
3193 return playersao->getInventory();
3196 case InventoryLocation::NODEMETA:
3198 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
3201 return meta->getInventory();
3209 std::string Server::getInventoryOwner(const InventoryLocation &loc)
3212 case InventoryLocation::UNDEFINED:
3215 case InventoryLocation::CURRENT_PLAYER:
3218 case InventoryLocation::PLAYER:
3223 case InventoryLocation::NODEMETA:
3225 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
3228 return meta->getOwner();
3236 void Server::setInventoryModified(const InventoryLocation &loc)
3239 case InventoryLocation::UNDEFINED:
3242 case InventoryLocation::PLAYER:
3244 Player *player = m_env->getPlayer(loc.name.c_str());
3247 PlayerSAO *playersao = player->getPlayerSAO();
3250 playersao->m_inventory_not_sent = true;
3251 playersao->m_wielded_item_not_sent = true;
3254 case InventoryLocation::NODEMETA:
3256 v3s16 blockpos = getNodeBlockPos(loc.p);
3258 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
3260 meta->inventoryModified();
3262 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3264 block->raiseModified(MOD_STATE_WRITE_NEEDED);
3266 setBlockNotSent(blockpos);
3274 core::list<PlayerInfo> Server::getPlayerInfo()
3276 DSTACK(__FUNCTION_NAME);
3277 JMutexAutoLock envlock(m_env_mutex);
3278 JMutexAutoLock conlock(m_con_mutex);
3280 core::list<PlayerInfo> list;
3282 core::list<Player*> players = m_env->getPlayers();
3284 core::list<Player*>::Iterator i;
3285 for(i = players.begin();
3286 i != players.end(); i++)
3290 Player *player = *i;
3293 // Copy info from connection to info struct
3294 info.id = player->peer_id;
3295 info.address = m_con.GetPeerAddress(player->peer_id);
3296 info.avg_rtt = m_con.GetPeerAvgRTT(player->peer_id);
3298 catch(con::PeerNotFoundException &e)
3300 // Set dummy peer info
3302 info.address = Address(0,0,0,0,0);
3306 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3307 info.position = player->getPosition();
3309 list.push_back(info);
3316 void Server::peerAdded(con::Peer *peer)
3318 DSTACK(__FUNCTION_NAME);
3319 verbosestream<<"Server::peerAdded(): peer->id="
3320 <<peer->id<<std::endl;
3323 c.type = PEER_ADDED;
3324 c.peer_id = peer->id;
3326 m_peer_change_queue.push_back(c);
3329 void Server::deletingPeer(con::Peer *peer, bool timeout)
3331 DSTACK(__FUNCTION_NAME);
3332 verbosestream<<"Server::deletingPeer(): peer->id="
3333 <<peer->id<<", timeout="<<timeout<<std::endl;
3336 c.type = PEER_REMOVED;
3337 c.peer_id = peer->id;
3338 c.timeout = timeout;
3339 m_peer_change_queue.push_back(c);
3346 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3348 DSTACK(__FUNCTION_NAME);
3349 std::ostringstream os(std::ios_base::binary);
3351 writeU16(os, TOCLIENT_HP);
3355 std::string s = os.str();
3356 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3358 con.Send(peer_id, 0, data, true);
3361 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3362 const std::wstring &reason)
3364 DSTACK(__FUNCTION_NAME);
3365 std::ostringstream os(std::ios_base::binary);
3367 writeU16(os, TOCLIENT_ACCESS_DENIED);
3368 os<<serializeWideString(reason);
3371 std::string s = os.str();
3372 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3374 con.Send(peer_id, 0, data, true);
3377 void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
3378 bool set_camera_point_target, v3f camera_point_target)
3380 DSTACK(__FUNCTION_NAME);
3381 std::ostringstream os(std::ios_base::binary);
3383 writeU16(os, TOCLIENT_DEATHSCREEN);
3384 writeU8(os, set_camera_point_target);
3385 writeV3F1000(os, camera_point_target);
3388 std::string s = os.str();
3389 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3391 con.Send(peer_id, 0, data, true);
3394 void Server::SendItemDef(con::Connection &con, u16 peer_id,
3395 IItemDefManager *itemdef)
3397 DSTACK(__FUNCTION_NAME);
3398 std::ostringstream os(std::ios_base::binary);
3402 u32 length of the next item
3403 zlib-compressed serialized ItemDefManager
3405 writeU16(os, TOCLIENT_ITEMDEF);
3406 std::ostringstream tmp_os(std::ios::binary);
3407 itemdef->serialize(tmp_os);
3408 std::ostringstream tmp_os2(std::ios::binary);
3409 compressZlib(tmp_os.str(), tmp_os2);
3410 os<<serializeLongString(tmp_os2.str());
3413 std::string s = os.str();
3414 verbosestream<<"Server: Sending item definitions to id("<<peer_id
3415 <<"): size="<<s.size()<<std::endl;
3416 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3418 con.Send(peer_id, 0, data, true);
3421 void Server::SendNodeDef(con::Connection &con, u16 peer_id,
3422 INodeDefManager *nodedef)
3424 DSTACK(__FUNCTION_NAME);
3425 std::ostringstream os(std::ios_base::binary);
3429 u32 length of the next item
3430 zlib-compressed serialized NodeDefManager
3432 writeU16(os, TOCLIENT_NODEDEF);
3433 std::ostringstream tmp_os(std::ios::binary);
3434 nodedef->serialize(tmp_os);
3435 std::ostringstream tmp_os2(std::ios::binary);
3436 compressZlib(tmp_os.str(), tmp_os2);
3437 os<<serializeLongString(tmp_os2.str());
3440 std::string s = os.str();
3441 verbosestream<<"Server: Sending node definitions to id("<<peer_id
3442 <<"): size="<<s.size()<<std::endl;
3443 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3445 con.Send(peer_id, 0, data, true);
3449 Non-static send methods
3452 void Server::SendInventory(u16 peer_id)
3454 DSTACK(__FUNCTION_NAME);
3456 PlayerSAO *playersao = getPlayerSAO(peer_id);
3459 playersao->m_inventory_not_sent = false;
3465 std::ostringstream os;
3466 playersao->getInventory()->serialize(os);
3468 std::string s = os.str();
3470 SharedBuffer<u8> data(s.size()+2);
3471 writeU16(&data[0], TOCLIENT_INVENTORY);
3472 memcpy(&data[2], s.c_str(), s.size());
3475 m_con.Send(peer_id, 0, data, true);
3478 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3480 DSTACK(__FUNCTION_NAME);
3482 std::ostringstream os(std::ios_base::binary);
3486 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3487 os.write((char*)buf, 2);
3490 writeU16(buf, message.size());
3491 os.write((char*)buf, 2);
3494 for(u32 i=0; i<message.size(); i++)
3498 os.write((char*)buf, 2);
3502 std::string s = os.str();
3503 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3505 m_con.Send(peer_id, 0, data, true);
3508 void Server::BroadcastChatMessage(const std::wstring &message)
3510 for(core::map<u16, RemoteClient*>::Iterator
3511 i = m_clients.getIterator();
3512 i.atEnd() == false; i++)
3514 // Get client and check that it is valid
3515 RemoteClient *client = i.getNode()->getValue();
3516 assert(client->peer_id == i.getNode()->getKey());
3517 if(client->serialization_version == SER_FMT_VER_INVALID)
3520 SendChatMessage(client->peer_id, message);
3524 void Server::SendPlayerHP(u16 peer_id)
3526 DSTACK(__FUNCTION_NAME);
3527 PlayerSAO *playersao = getPlayerSAO(peer_id);
3529 playersao->m_hp_not_sent = false;
3530 SendHP(m_con, peer_id, playersao->getHP());
3533 void Server::SendMovePlayer(u16 peer_id)
3535 DSTACK(__FUNCTION_NAME);
3536 Player *player = m_env->getPlayer(peer_id);
3539 std::ostringstream os(std::ios_base::binary);
3540 writeU16(os, TOCLIENT_MOVE_PLAYER);
3541 writeV3F1000(os, player->getPosition());
3542 writeF1000(os, player->getPitch());
3543 writeF1000(os, player->getYaw());
3546 v3f pos = player->getPosition();
3547 f32 pitch = player->getPitch();
3548 f32 yaw = player->getYaw();
3549 verbosestream<<"Server: Sending TOCLIENT_MOVE_PLAYER"
3550 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3557 std::string s = os.str();
3558 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3560 m_con.Send(peer_id, 0, data, true);
3563 void Server::SendPlayerPrivileges(u16 peer_id)
3565 Player *player = m_env->getPlayer(peer_id);
3567 std::set<std::string> privs;
3568 scriptapi_get_auth(m_lua, player->getName(), NULL, &privs);
3570 std::ostringstream os(std::ios_base::binary);
3571 writeU16(os, TOCLIENT_PRIVILEGES);
3572 writeU16(os, privs.size());
3573 for(std::set<std::string>::const_iterator i = privs.begin();
3574 i != privs.end(); i++){
3575 os<<serializeString(*i);
3579 std::string s = os.str();
3580 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3582 m_con.Send(peer_id, 0, data, true);
3585 s32 Server::playSound(const SimpleSoundSpec &spec,
3586 const ServerSoundParams ¶ms)
3588 // Find out initial position of sound
3589 bool pos_exists = false;
3590 v3f pos = params.getPos(m_env, &pos_exists);
3591 // If position is not found while it should be, cancel sound
3592 if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
3594 // Filter destination clients
3595 std::set<RemoteClient*> dst_clients;
3596 if(params.to_player != "")
3598 Player *player = m_env->getPlayer(params.to_player.c_str());
3600 infostream<<"Server::playSound: Player \""<<params.to_player
3601 <<"\" not found"<<std::endl;
3604 if(player->peer_id == PEER_ID_INEXISTENT){
3605 infostream<<"Server::playSound: Player \""<<params.to_player
3606 <<"\" not connected"<<std::endl;
3609 RemoteClient *client = getClient(player->peer_id);
3610 dst_clients.insert(client);
3614 for(core::map<u16, RemoteClient*>::Iterator
3615 i = m_clients.getIterator(); i.atEnd() == false; i++)
3617 RemoteClient *client = i.getNode()->getValue();
3618 Player *player = m_env->getPlayer(client->peer_id);
3622 if(player->getPosition().getDistanceFrom(pos) >
3623 params.max_hear_distance)
3626 dst_clients.insert(client);
3629 if(dst_clients.size() == 0)
3632 s32 id = m_next_sound_id++;
3633 // The sound will exist as a reference in m_playing_sounds
3634 m_playing_sounds[id] = ServerPlayingSound();
3635 ServerPlayingSound &psound = m_playing_sounds[id];
3636 psound.params = params;
3637 for(std::set<RemoteClient*>::iterator i = dst_clients.begin();
3638 i != dst_clients.end(); i++)
3639 psound.clients.insert((*i)->peer_id);
3641 std::ostringstream os(std::ios_base::binary);
3642 writeU16(os, TOCLIENT_PLAY_SOUND);
3644 os<<serializeString(spec.name);
3645 writeF1000(os, spec.gain * params.gain);
3646 writeU8(os, params.type);
3647 writeV3F1000(os, pos);
3648 writeU16(os, params.object);
3649 writeU8(os, params.loop);
3651 std::string s = os.str();
3652 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3654 for(std::set<RemoteClient*>::iterator i = dst_clients.begin();
3655 i != dst_clients.end(); i++){
3657 m_con.Send((*i)->peer_id, 0, data, true);
3661 void Server::stopSound(s32 handle)
3663 // Get sound reference
3664 std::map<s32, ServerPlayingSound>::iterator i =
3665 m_playing_sounds.find(handle);
3666 if(i == m_playing_sounds.end())
3668 ServerPlayingSound &psound = i->second;
3670 std::ostringstream os(std::ios_base::binary);
3671 writeU16(os, TOCLIENT_STOP_SOUND);
3672 writeS32(os, handle);
3674 std::string s = os.str();
3675 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3677 for(std::set<u16>::iterator i = psound.clients.begin();
3678 i != psound.clients.end(); i++){
3680 m_con.Send(*i, 0, data, true);
3682 // Remove sound reference
3683 m_playing_sounds.erase(i);
3686 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3687 core::list<u16> *far_players, float far_d_nodes)
3689 float maxd = far_d_nodes*BS;
3690 v3f p_f = intToFloat(p, BS);
3694 SharedBuffer<u8> reply(replysize);
3695 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3696 writeS16(&reply[2], p.X);
3697 writeS16(&reply[4], p.Y);
3698 writeS16(&reply[6], p.Z);
3700 for(core::map<u16, RemoteClient*>::Iterator
3701 i = m_clients.getIterator();
3702 i.atEnd() == false; i++)
3704 // Get client and check that it is valid
3705 RemoteClient *client = i.getNode()->getValue();
3706 assert(client->peer_id == i.getNode()->getKey());
3707 if(client->serialization_version == SER_FMT_VER_INVALID)
3710 // Don't send if it's the same one
3711 if(client->peer_id == ignore_id)
3717 Player *player = m_env->getPlayer(client->peer_id);
3720 // If player is far away, only set modified blocks not sent
3721 v3f player_pos = player->getPosition();
3722 if(player_pos.getDistanceFrom(p_f) > maxd)
3724 far_players->push_back(client->peer_id);
3731 m_con.Send(client->peer_id, 0, reply, true);
3735 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3736 core::list<u16> *far_players, float far_d_nodes)
3738 float maxd = far_d_nodes*BS;
3739 v3f p_f = intToFloat(p, BS);
3741 for(core::map<u16, RemoteClient*>::Iterator
3742 i = m_clients.getIterator();
3743 i.atEnd() == false; i++)
3745 // Get client and check that it is valid
3746 RemoteClient *client = i.getNode()->getValue();
3747 assert(client->peer_id == i.getNode()->getKey());
3748 if(client->serialization_version == SER_FMT_VER_INVALID)
3751 // Don't send if it's the same one
3752 if(client->peer_id == ignore_id)
3758 Player *player = m_env->getPlayer(client->peer_id);
3761 // If player is far away, only set modified blocks not sent
3762 v3f player_pos = player->getPosition();
3763 if(player_pos.getDistanceFrom(p_f) > maxd)
3765 far_players->push_back(client->peer_id);
3772 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3773 SharedBuffer<u8> reply(replysize);
3774 writeU16(&reply[0], TOCLIENT_ADDNODE);
3775 writeS16(&reply[2], p.X);
3776 writeS16(&reply[4], p.Y);
3777 writeS16(&reply[6], p.Z);
3778 n.serialize(&reply[8], client->serialization_version);
3781 m_con.Send(client->peer_id, 0, reply, true);
3785 void Server::setBlockNotSent(v3s16 p)
3787 for(core::map<u16, RemoteClient*>::Iterator
3788 i = m_clients.getIterator();
3789 i.atEnd()==false; i++)
3791 RemoteClient *client = i.getNode()->getValue();
3792 client->SetBlockNotSent(p);
3796 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3798 DSTACK(__FUNCTION_NAME);
3800 v3s16 p = block->getPos();
3804 bool completely_air = true;
3805 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3806 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3807 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3809 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
3811 completely_air = false;
3812 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
3817 infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
3819 infostream<<"[completely air] ";
3820 infostream<<std::endl;
3824 Create a packet with the block in the right format
3827 std::ostringstream os(std::ios_base::binary);
3828 block->serialize(os, ver, false);
3829 std::string s = os.str();
3830 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3832 u32 replysize = 8 + blockdata.getSize();
3833 SharedBuffer<u8> reply(replysize);
3834 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3835 writeS16(&reply[2], p.X);
3836 writeS16(&reply[4], p.Y);
3837 writeS16(&reply[6], p.Z);
3838 memcpy(&reply[8], *blockdata, blockdata.getSize());
3840 /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3841 <<": \tpacket size: "<<replysize<<std::endl;*/
3846 m_con.Send(peer_id, 1, reply, true);
3849 void Server::SendBlocks(float dtime)
3851 DSTACK(__FUNCTION_NAME);
3853 JMutexAutoLock envlock(m_env_mutex);
3854 JMutexAutoLock conlock(m_con_mutex);
3856 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
3858 core::array<PrioritySortedBlockTransfer> queue;
3860 s32 total_sending = 0;
3863 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
3865 for(core::map<u16, RemoteClient*>::Iterator
3866 i = m_clients.getIterator();
3867 i.atEnd() == false; i++)
3869 RemoteClient *client = i.getNode()->getValue();
3870 assert(client->peer_id == i.getNode()->getKey());
3872 // If definitions and textures have not been sent, don't
3873 // send MapBlocks either
3874 if(!client->definitions_sent)
3877 total_sending += client->SendingCount();
3879 if(client->serialization_version == SER_FMT_VER_INVALID)
3882 client->GetNextBlocks(this, dtime, queue);
3887 // Lowest priority number comes first.
3888 // Lowest is most important.
3891 for(u32 i=0; i<queue.size(); i++)
3893 //TODO: Calculate limit dynamically
3894 if(total_sending >= g_settings->getS32
3895 ("max_simultaneous_block_sends_server_total"))
3898 PrioritySortedBlockTransfer q = queue[i];
3900 MapBlock *block = NULL;
3903 block = m_env->getMap().getBlockNoCreate(q.pos);
3905 catch(InvalidPositionException &e)
3910 RemoteClient *client = getClient(q.peer_id);
3912 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3914 client->SentBlock(q.pos);
3920 void Server::fillMediaCache()
3922 DSTACK(__FUNCTION_NAME);
3924 infostream<<"Server: Calculating media file checksums"<<std::endl;
3926 // Collect all media file paths
3927 std::list<std::string> paths;
3928 for(core::list<ModSpec>::Iterator i = m_mods.begin();
3929 i != m_mods.end(); i++){
3930 const ModSpec &mod = *i;
3931 paths.push_back(mod.path + DIR_DELIM + "textures");
3932 paths.push_back(mod.path + DIR_DELIM + "sounds");
3933 paths.push_back(mod.path + DIR_DELIM + "media");
3936 // Collect media file information from paths into cache
3937 for(std::list<std::string>::iterator i = paths.begin();
3938 i != paths.end(); i++)
3940 std::string mediapath = *i;
3941 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
3942 for(u32 j=0; j<dirlist.size(); j++){
3943 if(dirlist[j].dir) // Ignode dirs
3945 std::string filename = dirlist[j].name;
3946 // If name contains illegal characters, ignore the file
3947 if(!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)){
3948 infostream<<"Server: ignoring illegal file name: \""
3949 <<filename<<"\""<<std::endl;
3952 // If name is not in a supported format, ignore it
3953 const char *supported_ext[] = {
3954 ".png", ".jpg", ".bmp", ".tga",
3955 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
3959 if(removeStringEnd(filename, supported_ext) == ""){
3960 infostream<<"Server: ignoring unsupported file extension: \""
3961 <<filename<<"\""<<std::endl;
3964 // Ok, attempt to load the file and add to cache
3965 std::string filepath = mediapath + DIR_DELIM + filename;
3967 std::ifstream fis(filepath.c_str(), std::ios_base::binary);
3968 if(fis.good() == false){
3969 errorstream<<"Server::fillMediaCache(): Could not open \""
3970 <<filename<<"\" for reading"<<std::endl;
3973 std::ostringstream tmp_os(std::ios_base::binary);
3977 fis.read(buf, 1024);
3978 std::streamsize len = fis.gcount();
3979 tmp_os.write(buf, len);
3988 errorstream<<"Server::fillMediaCache(): Failed to read \""
3989 <<filename<<"\""<<std::endl;
3992 if(tmp_os.str().length() == 0){
3993 errorstream<<"Server::fillMediaCache(): Empty file \""
3994 <<filepath<<"\""<<std::endl;
3999 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
4001 unsigned char *digest = sha1.getDigest();
4002 std::string sha1_base64 = base64_encode(digest, 20);
4003 std::string sha1_hex = hex_encode((char*)digest, 20);
4007 this->m_media[filename] = MediaInfo(filepath, sha1_base64);
4008 verbosestream<<"Server: "<<sha1_hex<<" is "<<filename<<std::endl;
4013 struct SendableMediaAnnouncement
4016 std::string sha1_digest;
4018 SendableMediaAnnouncement(const std::string name_="",
4019 const std::string sha1_digest_=""):
4021 sha1_digest(sha1_digest_)
4025 void Server::sendMediaAnnouncement(u16 peer_id)
4027 DSTACK(__FUNCTION_NAME);
4029 verbosestream<<"Server: Announcing files to id("<<peer_id<<")"
4032 core::list<SendableMediaAnnouncement> file_announcements;
4034 for(std::map<std::string, MediaInfo>::iterator i = m_media.begin();
4035 i != m_media.end(); i++){
4037 file_announcements.push_back(
4038 SendableMediaAnnouncement(i->first, i->second.sha1_digest));
4042 std::ostringstream os(std::ios_base::binary);
4050 u16 length of sha1_digest
4055 writeU16(os, TOCLIENT_ANNOUNCE_MEDIA);
4056 writeU16(os, file_announcements.size());
4058 for(core::list<SendableMediaAnnouncement>::Iterator
4059 j = file_announcements.begin();
4060 j != file_announcements.end(); j++){
4061 os<<serializeString(j->name);
4062 os<<serializeString(j->sha1_digest);
4066 std::string s = os.str();
4067 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4070 m_con.Send(peer_id, 0, data, true);
4074 struct SendableMedia
4080 SendableMedia(const std::string &name_="", const std::string path_="",
4081 const std::string &data_=""):
4088 void Server::sendRequestedMedia(u16 peer_id,
4089 const core::list<MediaRequest> &tosend)
4091 DSTACK(__FUNCTION_NAME);
4093 verbosestream<<"Server::sendRequestedMedia(): "
4094 <<"Sending files to client"<<std::endl;
4098 // Put 5kB in one bunch (this is not accurate)
4099 u32 bytes_per_bunch = 5000;
4101 core::array< core::list<SendableMedia> > file_bunches;
4102 file_bunches.push_back(core::list<SendableMedia>());
4104 u32 file_size_bunch_total = 0;
4106 for(core::list<MediaRequest>::ConstIterator i = tosend.begin();
4107 i != tosend.end(); i++)
4109 if(m_media.find(i->name) == m_media.end()){
4110 errorstream<<"Server::sendRequestedMedia(): Client asked for "
4111 <<"unknown file \""<<(i->name)<<"\""<<std::endl;
4115 //TODO get path + name
4116 std::string tpath = m_media[(*i).name].path;
4119 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
4120 if(fis.good() == false){
4121 errorstream<<"Server::sendRequestedMedia(): Could not open \""
4122 <<tpath<<"\" for reading"<<std::endl;
4125 std::ostringstream tmp_os(std::ios_base::binary);
4129 fis.read(buf, 1024);
4130 std::streamsize len = fis.gcount();
4131 tmp_os.write(buf, len);
4132 file_size_bunch_total += len;
4141 errorstream<<"Server::sendRequestedMedia(): Failed to read \""
4142 <<(*i).name<<"\""<<std::endl;
4145 /*infostream<<"Server::sendRequestedMedia(): Loaded \""
4146 <<tname<<"\""<<std::endl;*/
4148 file_bunches[file_bunches.size()-1].push_back(
4149 SendableMedia((*i).name, tpath, tmp_os.str()));
4151 // Start next bunch if got enough data
4152 if(file_size_bunch_total >= bytes_per_bunch){
4153 file_bunches.push_back(core::list<SendableMedia>());
4154 file_size_bunch_total = 0;
4159 /* Create and send packets */
4161 u32 num_bunches = file_bunches.size();
4162 for(u32 i=0; i<num_bunches; i++)
4164 std::ostringstream os(std::ios_base::binary);
4168 u16 total number of texture bunches
4169 u16 index of this bunch
4170 u32 number of files in this bunch
4179 writeU16(os, TOCLIENT_MEDIA);
4180 writeU16(os, num_bunches);
4182 writeU32(os, file_bunches[i].size());
4184 for(core::list<SendableMedia>::Iterator
4185 j = file_bunches[i].begin();
4186 j != file_bunches[i].end(); j++){
4187 os<<serializeString(j->name);
4188 os<<serializeLongString(j->data);
4192 std::string s = os.str();
4193 verbosestream<<"Server::sendRequestedMedia(): bunch "
4194 <<i<<"/"<<num_bunches
4195 <<" files="<<file_bunches[i].size()
4196 <<" size=" <<s.size()<<std::endl;
4197 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4199 m_con.Send(peer_id, 0, data, true);
4207 void Server::DiePlayer(u16 peer_id)
4209 DSTACK(__FUNCTION_NAME);
4211 PlayerSAO *playersao = getPlayerSAO(peer_id);
4214 infostream<<"Server::DiePlayer(): Player "
4215 <<playersao->getPlayer()->getName()
4216 <<" dies"<<std::endl;
4218 playersao->setHP(0);
4220 // Trigger scripted stuff
4221 scriptapi_on_dieplayer(m_lua, playersao);
4223 SendPlayerHP(peer_id);
4224 SendDeathscreen(m_con, peer_id, false, v3f(0,0,0));
4227 void Server::RespawnPlayer(u16 peer_id)
4229 DSTACK(__FUNCTION_NAME);
4231 PlayerSAO *playersao = getPlayerSAO(peer_id);
4234 infostream<<"Server::RespawnPlayer(): Player "
4235 <<playersao->getPlayer()->getName()
4236 <<" respawns"<<std::endl;
4238 playersao->setHP(PLAYER_MAX_HP);
4240 bool repositioned = scriptapi_on_respawnplayer(m_lua, playersao);
4242 v3f pos = findSpawnPos(m_env->getServerMap());
4243 playersao->setPos(pos);
4247 void Server::UpdateCrafting(u16 peer_id)
4249 DSTACK(__FUNCTION_NAME);
4251 Player* player = m_env->getPlayer(peer_id);
4254 // Get a preview for crafting
4256 // No crafting in creative mode
4257 if(g_settings->getBool("creative_mode") == false)
4258 getCraftingResult(&player->inventory, preview, false, this);
4260 // Put the new preview in
4261 InventoryList *plist = player->inventory.getList("craftpreview");
4263 assert(plist->getSize() >= 1);
4264 plist->changeItem(0, preview);
4267 RemoteClient* Server::getClient(u16 peer_id)
4269 DSTACK(__FUNCTION_NAME);
4270 //JMutexAutoLock lock(m_con_mutex);
4271 core::map<u16, RemoteClient*>::Node *n;
4272 n = m_clients.find(peer_id);
4273 // A client should exist for all peers
4275 return n->getValue();
4278 std::wstring Server::getStatusString()
4280 std::wostringstream os(std::ios_base::binary);
4283 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4285 os<<L", uptime="<<m_uptime.get();
4286 // Information about clients
4288 for(core::map<u16, RemoteClient*>::Iterator
4289 i = m_clients.getIterator();
4290 i.atEnd() == false; i++)
4292 // Get client and check that it is valid
4293 RemoteClient *client = i.getNode()->getValue();
4294 assert(client->peer_id == i.getNode()->getKey());
4295 if(client->serialization_version == SER_FMT_VER_INVALID)
4298 Player *player = m_env->getPlayer(client->peer_id);
4299 // Get name of player
4300 std::wstring name = L"unknown";
4302 name = narrow_to_wide(player->getName());
4303 // Add name to information string
4307 if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
4308 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4309 if(g_settings->get("motd") != "")
4310 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4314 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
4316 std::set<std::string> privs;
4317 scriptapi_get_auth(m_lua, name, NULL, &privs);
4321 bool Server::checkPriv(const std::string &name, const std::string &priv)
4323 std::set<std::string> privs = getPlayerEffectivePrivs(name);
4324 return (privs.count(priv) != 0);
4327 void Server::reportPrivsModified(const std::string &name)
4330 for(core::map<u16, RemoteClient*>::Iterator
4331 i = m_clients.getIterator();
4332 i.atEnd() == false; i++){
4333 RemoteClient *client = i.getNode()->getValue();
4334 Player *player = m_env->getPlayer(client->peer_id);
4335 reportPrivsModified(player->getName());
4338 Player *player = m_env->getPlayer(name.c_str());
4341 SendPlayerPrivileges(player->peer_id);
4342 PlayerSAO *sao = player->getPlayerSAO();
4345 sao->updatePrivileges(
4346 getPlayerEffectivePrivs(name),
4351 // Saves g_settings to configpath given at initialization
4352 void Server::saveConfig()
4354 if(m_path_config != "")
4355 g_settings->updateConfigFile(m_path_config.c_str());
4358 void Server::notifyPlayer(const char *name, const std::wstring msg)
4360 Player *player = m_env->getPlayer(name);
4363 SendChatMessage(player->peer_id, std::wstring(L"Server: -!- ")+msg);
4366 void Server::notifyPlayers(const std::wstring msg)
4368 BroadcastChatMessage(msg);
4371 void Server::queueBlockEmerge(v3s16 blockpos, bool allow_generate)
4375 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
4376 m_emerge_queue.addBlock(PEER_ID_INEXISTENT, blockpos, flags);
4379 // IGameDef interface
4381 IItemDefManager* Server::getItemDefManager()
4385 INodeDefManager* Server::getNodeDefManager()
4389 ICraftDefManager* Server::getCraftDefManager()
4393 ITextureSource* Server::getTextureSource()
4397 u16 Server::allocateUnknownNodeId(const std::string &name)
4399 return m_nodedef->allocateDummy(name);
4401 ISoundManager* Server::getSoundManager()
4403 return &dummySoundManager;
4405 MtEventManager* Server::getEventManager()
4410 IWritableItemDefManager* Server::getWritableItemDefManager()
4414 IWritableNodeDefManager* Server::getWritableNodeDefManager()
4418 IWritableCraftDefManager* Server::getWritableCraftDefManager()
4423 const ModSpec* Server::getModSpec(const std::string &modname)
4425 for(core::list<ModSpec>::Iterator i = m_mods.begin();
4426 i != m_mods.end(); i++){
4427 const ModSpec &mod = *i;
4428 if(mod.name == modname)
4433 std::string Server::getBuiltinLuaPath()
4435 return porting::path_share + DIR_DELIM + "builtin";
4438 v3f findSpawnPos(ServerMap &map)
4440 //return v3f(50,50,50)*BS;
4445 nodepos = v2s16(0,0);
4450 // Try to find a good place a few times
4451 for(s32 i=0; i<1000; i++)
4454 // We're going to try to throw the player to this position
4455 v2s16 nodepos2d = v2s16(-range + (myrand()%(range*2)),
4456 -range + (myrand()%(range*2)));
4457 //v2s16 sectorpos = getNodeSectorPos(nodepos2d);
4458 // Get ground height at point (fallbacks to heightmap function)
4459 s16 groundheight = map.findGroundLevel(nodepos2d);
4460 // Don't go underwater
4461 if(groundheight < WATER_LEVEL)
4463 //infostream<<"-> Underwater"<<std::endl;
4466 // Don't go to high places
4467 if(groundheight > WATER_LEVEL + 4)
4469 //infostream<<"-> Underwater"<<std::endl;
4473 nodepos = v3s16(nodepos2d.X, groundheight-2, nodepos2d.Y);
4474 bool is_good = false;
4476 for(s32 i=0; i<10; i++){
4477 v3s16 blockpos = getNodeBlockPos(nodepos);
4478 map.emergeBlock(blockpos, true);
4479 MapNode n = map.getNodeNoEx(nodepos);
4480 if(n.getContent() == CONTENT_AIR){
4491 // Found a good place
4492 //infostream<<"Searched through "<<i<<" places."<<std::endl;
4498 return intToFloat(nodepos, BS);
4501 PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id)
4503 RemotePlayer *player = NULL;
4504 bool newplayer = false;
4507 Try to get an existing player
4509 player = static_cast<RemotePlayer*>(m_env->getPlayer(name));
4511 // If player is already connected, cancel
4512 if(player != NULL && player->peer_id != 0)
4514 infostream<<"emergePlayer(): Player already connected"<<std::endl;
4519 If player with the wanted peer_id already exists, cancel.
4521 if(m_env->getPlayer(peer_id) != NULL)
4523 infostream<<"emergePlayer(): Player with wrong name but same"
4524 " peer_id already exists"<<std::endl;
4529 Create a new player if it doesn't exist yet
4534 player = new RemotePlayer(this);
4535 player->updateName(name);
4537 /* Set player position */
4538 infostream<<"Server: Finding spawn place for player \""
4539 <<name<<"\""<<std::endl;
4540 v3f pos = findSpawnPos(m_env->getServerMap());
4541 player->setPosition(pos);
4543 /* Add player to environment */
4544 m_env->addPlayer(player);
4548 Create a new player active object
4550 PlayerSAO *playersao = new PlayerSAO(m_env, player, peer_id,
4551 getPlayerEffectivePrivs(player->getName()),
4554 /* Add object to environment */
4555 m_env->addActiveObject(playersao);
4559 scriptapi_on_newplayer(m_lua, playersao);
4561 scriptapi_on_joinplayer(m_lua, playersao);
4564 if(g_settings->getBool("creative_mode"))
4565 playersao->createCreativeInventory();
4570 void Server::handlePeerChange(PeerChange &c)
4572 JMutexAutoLock envlock(m_env_mutex);
4573 JMutexAutoLock conlock(m_con_mutex);
4575 if(c.type == PEER_ADDED)
4582 core::map<u16, RemoteClient*>::Node *n;
4583 n = m_clients.find(c.peer_id);
4584 // The client shouldn't already exist
4588 RemoteClient *client = new RemoteClient();
4589 client->peer_id = c.peer_id;
4590 m_clients.insert(client->peer_id, client);
4593 else if(c.type == PEER_REMOVED)
4600 core::map<u16, RemoteClient*>::Node *n;
4601 n = m_clients.find(c.peer_id);
4602 // The client should exist
4606 Mark objects to be not known by the client
4608 RemoteClient *client = n->getValue();
4610 for(core::map<u16, bool>::Iterator
4611 i = client->m_known_objects.getIterator();
4612 i.atEnd()==false; i++)
4615 u16 id = i.getNode()->getKey();
4616 ServerActiveObject* obj = m_env->getActiveObject(id);
4618 if(obj && obj->m_known_by_count > 0)
4619 obj->m_known_by_count--;
4623 Clear references to playing sounds
4625 for(std::map<s32, ServerPlayingSound>::iterator
4626 i = m_playing_sounds.begin();
4627 i != m_playing_sounds.end();)
4629 ServerPlayingSound &psound = i->second;
4630 psound.clients.erase(c.peer_id);
4631 if(psound.clients.size() == 0)
4632 m_playing_sounds.erase(i++);
4637 Player *player = m_env->getPlayer(c.peer_id);
4639 // Collect information about leaving in chat
4640 std::wstring message;
4644 std::wstring name = narrow_to_wide(player->getName());
4647 message += L" left game";
4649 message += L" (timed out)";
4653 /* Run scripts and remove from environment */
4657 PlayerSAO *playersao = player->getPlayerSAO();
4660 scriptapi_on_leaveplayer(m_lua, playersao);
4662 playersao->disconnected();
4672 std::ostringstream os(std::ios_base::binary);
4673 for(core::map<u16, RemoteClient*>::Iterator
4674 i = m_clients.getIterator();
4675 i.atEnd() == false; i++)
4677 RemoteClient *client = i.getNode()->getValue();
4678 assert(client->peer_id == i.getNode()->getKey());
4679 if(client->serialization_version == SER_FMT_VER_INVALID)
4682 Player *player = m_env->getPlayer(client->peer_id);
4685 // Get name of player
4686 os<<player->getName()<<" ";
4689 actionstream<<player->getName()<<" "
4690 <<(c.timeout?"times out.":"leaves game.")
4691 <<" List of players: "
4692 <<os.str()<<std::endl;
4697 delete m_clients[c.peer_id];
4698 m_clients.remove(c.peer_id);
4700 // Send player info to all remaining clients
4701 //SendPlayerInfos();
4703 // Send leave chat message to all remaining clients
4704 if(message.length() != 0)
4705 BroadcastChatMessage(message);
4714 void Server::handlePeerChanges()
4716 while(m_peer_change_queue.size() > 0)
4718 PeerChange c = m_peer_change_queue.pop_front();
4720 verbosestream<<"Server: Handling peer change: "
4721 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4724 handlePeerChange(c);
4728 void dedicated_server_loop(Server &server, bool &kill)
4730 DSTACK(__FUNCTION_NAME);
4732 verbosestream<<"dedicated_server_loop()"<<std::endl;
4734 IntervalLimiter m_profiler_interval;
4738 float steplen = g_settings->getFloat("dedicated_server_step");
4739 // This is kind of a hack but can be done like this
4740 // because server.step() is very light
4742 ScopeProfiler sp(g_profiler, "dedicated server sleep");
4743 sleep_ms((int)(steplen*1000.0));
4745 server.step(steplen);
4747 if(server.getShutdownRequested() || kill)
4749 infostream<<"Dedicated server quitting"<<std::endl;
4756 float profiler_print_interval =
4757 g_settings->getFloat("profiler_print_interval");
4758 if(profiler_print_interval != 0)
4760 if(m_profiler_interval.step(steplen, profiler_print_interval))
4762 infostream<<"Profiler:"<<std::endl;
4763 g_profiler->print(infostream);
4764 g_profiler->clear();