3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 #include "clientserver.h"
26 #include "jmutexautolock.h"
28 #include "constants.h"
31 #include "servercommand.h"
34 #include "serverobject.h"
39 #include "scriptapi.h"
44 #include "content_mapnode.h"
45 #include "content_nodemeta.h"
46 #include "content_abm.h"
47 #include "content_sao.h"
52 #include "utility_string.h"
53 #include "sound.h" // dummySoundManager
54 #include "event_manager.h"
57 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
59 #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
61 class MapEditEventIgnorer
64 MapEditEventIgnorer(bool *flag):
73 ~MapEditEventIgnorer()
86 class MapEditEventAreaIgnorer
89 MapEditEventAreaIgnorer(VoxelArea *ignorevariable, const VoxelArea &a):
90 m_ignorevariable(ignorevariable)
92 if(m_ignorevariable->getVolume() == 0)
93 *m_ignorevariable = a;
95 m_ignorevariable = NULL;
98 ~MapEditEventAreaIgnorer()
102 assert(m_ignorevariable->getVolume() != 0);
103 *m_ignorevariable = VoxelArea();
108 VoxelArea *m_ignorevariable;
111 void * ServerThread::Thread()
115 log_register_thread("ServerThread");
117 DSTACK(__FUNCTION_NAME);
119 BEGIN_DEBUG_EXCEPTION_HANDLER
124 //TimeTaker timer("AsyncRunStep() + Receive()");
127 //TimeTaker timer("AsyncRunStep()");
128 m_server->AsyncRunStep();
131 //infostream<<"Running m_server->Receive()"<<std::endl;
134 catch(con::NoIncomingDataException &e)
137 catch(con::PeerNotFoundException &e)
139 infostream<<"Server: PeerNotFoundException"<<std::endl;
141 catch(con::ConnectionBindFailed &e)
143 m_server->setAsyncFatalError(e.what());
147 m_server->setAsyncFatalError(e.what());
151 END_DEBUG_EXCEPTION_HANDLER(errorstream)
156 void * EmergeThread::Thread()
160 log_register_thread("EmergeThread");
162 DSTACK(__FUNCTION_NAME);
164 BEGIN_DEBUG_EXCEPTION_HANDLER
166 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
169 Get block info from queue, emerge them and send them
172 After queue is empty, exit.
176 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
180 SharedPtr<QueuedBlockEmerge> q(qptr);
186 Do not generate over-limit
188 if(blockpos_over_limit(p))
191 //infostream<<"EmergeThread::Thread(): running"<<std::endl;
193 //TimeTaker timer("block emerge");
196 Try to emerge it from somewhere.
198 If it is only wanted as optional, only loading from disk
203 Check if any peer wants it as non-optional. In that case it
206 Also decrement the emerge queue count in clients.
209 bool only_from_disk = true;
212 core::map<u16, u8>::Iterator i;
213 for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
215 //u16 peer_id = i.getNode()->getKey();
218 u8 flags = i.getNode()->getValue();
219 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
220 only_from_disk = false;
225 if(enable_mapgen_debug_info)
226 infostream<<"EmergeThread: p="
227 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
228 <<"only_from_disk="<<only_from_disk<<std::endl;
230 ServerMap &map = ((ServerMap&)m_server->m_env->getMap());
232 MapBlock *block = NULL;
233 bool got_block = true;
234 core::map<v3s16, MapBlock*> modified_blocks;
237 Try to fetch block from memory or disk.
238 If not found and asked to generate, initialize generator.
241 bool started_generate = false;
242 mapgen::BlockMakeData data;
245 JMutexAutoLock envlock(m_server->m_env_mutex);
247 // Load sector if it isn't loaded
248 if(map.getSectorNoGenerateNoEx(p2d) == NULL)
249 map.loadSectorMeta(p2d);
251 // Attempt to load block
252 block = map.getBlockNoCreateNoEx(p);
253 if(!block || block->isDummy() || !block->isGenerated())
255 if(enable_mapgen_debug_info)
256 infostream<<"EmergeThread: not in memory, "
257 <<"attempting to load from disk"<<std::endl;
259 block = map.loadBlock(p);
262 // If could not load and allowed to generate, start generation
263 // inside this same envlock
264 if(only_from_disk == false &&
265 (block == NULL || block->isGenerated() == false)){
266 if(enable_mapgen_debug_info)
267 infostream<<"EmergeThread: generating"<<std::endl;
268 started_generate = true;
270 map.initBlockMake(&data, p);
275 If generator was initialized, generate now when envlock is free.
280 ScopeProfiler sp(g_profiler, "EmergeThread: mapgen::make_block",
282 TimeTaker t("mapgen::make_block()");
284 mapgen::make_block(&data);
286 if(enable_mapgen_debug_info == false)
287 t.stop(true); // Hide output
291 // Lock environment again to access the map
292 JMutexAutoLock envlock(m_server->m_env_mutex);
294 ScopeProfiler sp(g_profiler, "EmergeThread: after "
295 "mapgen::make_block (envlock)", SPT_AVG);
297 // Blit data back on map, update lighting, add mobs and
298 // whatever this does
299 map.finishBlockMake(&data, modified_blocks);
302 block = map.getBlockNoCreateNoEx(p);
304 // If block doesn't exist, don't try doing anything with it
305 // This happens if the block is not in generation boundaries
310 Do some post-generate stuff
313 v3s16 minp = data.blockpos_min*MAP_BLOCKSIZE;
314 v3s16 maxp = data.blockpos_max*MAP_BLOCKSIZE +
315 v3s16(1,1,1)*(MAP_BLOCKSIZE-1);
318 Ignore map edit events, they will not need to be
319 sent to anybody because the block hasn't been sent
322 //MapEditEventIgnorer ign(&m_server->m_ignore_map_edit_events);
323 MapEditEventAreaIgnorer ign(
324 &m_server->m_ignore_map_edit_events_area,
325 VoxelArea(minp, maxp));
327 TimeTaker timer("on_generated");
328 scriptapi_environment_on_generated(m_server->m_lua,
329 minp, maxp, mapgen::get_blockseed(data.seed, minp));
330 /*int t = timer.stop(true);
331 dstream<<"on_generated took "<<t<<"ms"<<std::endl;*/
334 if(enable_mapgen_debug_info)
335 infostream<<"EmergeThread: ended up with: "
336 <<analyze_block(block)<<std::endl;
338 // Activate objects and stuff
339 m_server->m_env->activateBlock(block, 0);
347 Set sent status of modified blocks on clients
350 // NOTE: Server's clients are also behind the connection mutex
351 JMutexAutoLock lock(m_server->m_con_mutex);
354 Add the originally fetched block to the modified list
358 modified_blocks.insert(p, block);
362 Set the modified blocks unsent for all the clients
365 for(core::map<u16, RemoteClient*>::Iterator
366 i = m_server->m_clients.getIterator();
367 i.atEnd() == false; i++)
369 RemoteClient *client = i.getNode()->getValue();
371 if(modified_blocks.size() > 0)
373 // Remove block from sent history
374 client->SetBlocksNotSent(modified_blocks);
380 END_DEBUG_EXCEPTION_HANDLER(errorstream)
382 log_deregister_thread();
387 v3f ServerSoundParams::getPos(ServerEnvironment *env, bool *pos_exists) const
389 if(pos_exists) *pos_exists = false;
394 if(pos_exists) *pos_exists = true;
399 ServerActiveObject *sao = env->getActiveObject(object);
402 if(pos_exists) *pos_exists = true;
403 return sao->getBasePosition(); }
408 void RemoteClient::GetNextBlocks(Server *server, float dtime,
409 core::array<PrioritySortedBlockTransfer> &dest)
411 DSTACK(__FUNCTION_NAME);
414 TimeTaker timer("RemoteClient::GetNextBlocks", &timer_result);*/
417 m_nothing_to_send_pause_timer -= dtime;
418 m_nearest_unsent_reset_timer += dtime;
420 if(m_nothing_to_send_pause_timer >= 0)
425 // Won't send anything if already sending
426 if(m_blocks_sending.size() >= g_settings->getU16
427 ("max_simultaneous_block_sends_per_client"))
429 //infostream<<"Not sending any blocks, Queue full."<<std::endl;
433 //TimeTaker timer("RemoteClient::GetNextBlocks");
435 Player *player = server->m_env->getPlayer(peer_id);
437 assert(player != NULL);
439 v3f playerpos = player->getPosition();
440 v3f playerspeed = player->getSpeed();
441 v3f playerspeeddir(0,0,0);
442 if(playerspeed.getLength() > 1.0*BS)
443 playerspeeddir = playerspeed / playerspeed.getLength();
444 // Predict to next block
445 v3f playerpos_predicted = playerpos + playerspeeddir*MAP_BLOCKSIZE*BS;
447 v3s16 center_nodepos = floatToInt(playerpos_predicted, BS);
449 v3s16 center = getNodeBlockPos(center_nodepos);
451 // Camera position and direction
452 v3f camera_pos = player->getEyePosition();
453 v3f camera_dir = v3f(0,0,1);
454 camera_dir.rotateYZBy(player->getPitch());
455 camera_dir.rotateXZBy(player->getYaw());
457 /*infostream<<"camera_dir=("<<camera_dir.X<<","<<camera_dir.Y<<","
458 <<camera_dir.Z<<")"<<std::endl;*/
461 Get the starting value of the block finder radius.
464 if(m_last_center != center)
466 m_nearest_unsent_d = 0;
467 m_last_center = center;
470 /*infostream<<"m_nearest_unsent_reset_timer="
471 <<m_nearest_unsent_reset_timer<<std::endl;*/
473 // Reset periodically to workaround for some bugs or stuff
474 if(m_nearest_unsent_reset_timer > 20.0)
476 m_nearest_unsent_reset_timer = 0;
477 m_nearest_unsent_d = 0;
478 //infostream<<"Resetting m_nearest_unsent_d for "
479 // <<server->getPlayerName(peer_id)<<std::endl;
482 //s16 last_nearest_unsent_d = m_nearest_unsent_d;
483 s16 d_start = m_nearest_unsent_d;
485 //infostream<<"d_start="<<d_start<<std::endl;
487 u16 max_simul_sends_setting = g_settings->getU16
488 ("max_simultaneous_block_sends_per_client");
489 u16 max_simul_sends_usually = max_simul_sends_setting;
492 Check the time from last addNode/removeNode.
494 Decrease send rate if player is building stuff.
496 m_time_from_building += dtime;
497 if(m_time_from_building < g_settings->getFloat(
498 "full_block_send_enable_min_time_from_building"))
500 max_simul_sends_usually
501 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
505 Number of blocks sending + number of blocks selected for sending
507 u32 num_blocks_selected = m_blocks_sending.size();
510 next time d will be continued from the d from which the nearest
511 unsent block was found this time.
513 This is because not necessarily any of the blocks found this
514 time are actually sent.
516 s32 new_nearest_unsent_d = -1;
518 s16 d_max = g_settings->getS16("max_block_send_distance");
519 s16 d_max_gen = g_settings->getS16("max_block_generate_distance");
521 // Don't loop very much at a time
522 s16 max_d_increment_at_time = 2;
523 if(d_max > d_start + max_d_increment_at_time)
524 d_max = d_start + max_d_increment_at_time;
525 /*if(d_max_gen > d_start+2)
526 d_max_gen = d_start+2;*/
528 //infostream<<"Starting from "<<d_start<<std::endl;
530 s32 nearest_emerged_d = -1;
531 s32 nearest_emergefull_d = -1;
532 s32 nearest_sent_d = -1;
533 bool queue_is_full = false;
536 for(d = d_start; d <= d_max; d++)
538 /*errorstream<<"checking d="<<d<<" for "
539 <<server->getPlayerName(peer_id)<<std::endl;*/
540 //infostream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
543 If m_nearest_unsent_d was changed by the EmergeThread
544 (it can change it to 0 through SetBlockNotSent),
546 Else update m_nearest_unsent_d
548 /*if(m_nearest_unsent_d != last_nearest_unsent_d)
550 d = m_nearest_unsent_d;
551 last_nearest_unsent_d = m_nearest_unsent_d;
555 Get the border/face dot coordinates of a "d-radiused"
558 core::list<v3s16> list;
559 getFacePositions(list, d);
561 core::list<v3s16>::Iterator li;
562 for(li=list.begin(); li!=list.end(); li++)
564 v3s16 p = *li + center;
568 - Don't allow too many simultaneous transfers
569 - EXCEPT when the blocks are very close
571 Also, don't send blocks that are already flying.
574 // Start with the usual maximum
575 u16 max_simul_dynamic = max_simul_sends_usually;
577 // If block is very close, allow full maximum
578 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
579 max_simul_dynamic = max_simul_sends_setting;
581 // Don't select too many blocks for sending
582 if(num_blocks_selected >= max_simul_dynamic)
584 queue_is_full = true;
585 goto queue_full_break;
588 // Don't send blocks that are currently being transferred
589 if(m_blocks_sending.find(p) != NULL)
595 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
596 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
597 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
598 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
599 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
600 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
603 // If this is true, inexistent block will be made from scratch
604 bool generate = d <= d_max_gen;
607 /*// Limit the generating area vertically to 2/3
608 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
611 // Limit the send area vertically to 1/2
612 if(abs(p.Y - center.Y) > d_max / 2)
618 If block is far away, don't generate it unless it is
624 // Block center y in nodes
625 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
626 // Don't generate if it's very high or very low
627 if(y < -64 || y > 64)
631 v2s16 p2d_nodes_center(
635 // Get ground height in nodes
636 s16 gh = server->m_env->getServerMap().findGroundLevel(
639 // If differs a lot, don't generate
640 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
642 // Actually, don't even send it
648 //infostream<<"d="<<d<<std::endl;
651 Don't generate or send if not in sight
652 FIXME This only works if the client uses a small enough
653 FOV setting. The default of 72 degrees is fine.
656 float camera_fov = (72.0*PI/180) * 4./3.;
657 if(isBlockInSight(p, camera_pos, camera_dir, camera_fov, 10000*BS) == false)
663 Don't send already sent blocks
666 if(m_blocks_sent.find(p) != NULL)
673 Check if map has this block
675 MapBlock *block = server->m_env->getMap().getBlockNoCreateNoEx(p);
677 bool surely_not_found_on_disk = false;
678 bool block_is_invalid = false;
681 // Reset usage timer, this block will be of use in the future.
682 block->resetUsageTimer();
684 // Block is dummy if data doesn't exist.
685 // It means it has been not found from disk and not generated
688 surely_not_found_on_disk = true;
691 // Block is valid if lighting is up-to-date and data exists
692 if(block->isValid() == false)
694 block_is_invalid = true;
697 /*if(block->isFullyGenerated() == false)
699 block_is_invalid = true;
704 ServerMap *map = (ServerMap*)(&server->m_env->getMap());
705 v2s16 chunkpos = map->sector_to_chunk(p2d);
706 if(map->chunkNonVolatile(chunkpos) == false)
707 block_is_invalid = true;
709 if(block->isGenerated() == false)
710 block_is_invalid = true;
713 If block is not close, don't send it unless it is near
716 Block is near ground level if night-time mesh
717 differs from day-time mesh.
721 if(block->getDayNightDiff() == false)
728 If block has been marked to not exist on disk (dummy)
729 and generating new ones is not wanted, skip block.
731 if(generate == false && surely_not_found_on_disk == true)
738 Add inexistent block to emerge queue.
740 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
742 //TODO: Get value from somewhere
743 // Allow only one block in emerge queue
744 //if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
745 // Allow two blocks in queue per client
746 //if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
748 // Make it more responsive when needing to generate stuff
749 if(surely_not_found_on_disk)
751 if(server->m_emerge_queue.peerItemCount(peer_id) < max_emerge)
753 //infostream<<"Adding block to emerge queue"<<std::endl;
755 // Add it to the emerge queue and trigger the thread
758 if(generate == false)
759 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
761 server->m_emerge_queue.addBlock(peer_id, p, flags);
762 server->m_emergethread.trigger();
764 if(nearest_emerged_d == -1)
765 nearest_emerged_d = d;
767 if(nearest_emergefull_d == -1)
768 nearest_emergefull_d = d;
775 if(nearest_sent_d == -1)
779 Add block to send queue
782 /*errorstream<<"sending from d="<<d<<" to "
783 <<server->getPlayerName(peer_id)<<std::endl;*/
785 PrioritySortedBlockTransfer q((float)d, p, peer_id);
789 num_blocks_selected += 1;
794 //infostream<<"Stopped at "<<d<<std::endl;
796 // If nothing was found for sending and nothing was queued for
797 // emerging, continue next time browsing from here
798 if(nearest_emerged_d != -1){
799 new_nearest_unsent_d = nearest_emerged_d;
800 } else if(nearest_emergefull_d != -1){
801 new_nearest_unsent_d = nearest_emergefull_d;
803 if(d > g_settings->getS16("max_block_send_distance")){
804 new_nearest_unsent_d = 0;
805 m_nothing_to_send_pause_timer = 2.0;
806 /*infostream<<"GetNextBlocks(): d wrapped around for "
807 <<server->getPlayerName(peer_id)
808 <<"; setting to 0 and pausing"<<std::endl;*/
810 if(nearest_sent_d != -1)
811 new_nearest_unsent_d = nearest_sent_d;
813 new_nearest_unsent_d = d;
817 if(new_nearest_unsent_d != -1)
818 m_nearest_unsent_d = new_nearest_unsent_d;
820 /*timer_result = timer.stop(true);
821 if(timer_result != 0)
822 infostream<<"GetNextBlocks duration: "<<timer_result<<" (!=0)"<<std::endl;*/
825 void RemoteClient::GotBlock(v3s16 p)
827 if(m_blocks_sending.find(p) != NULL)
828 m_blocks_sending.remove(p);
831 /*infostream<<"RemoteClient::GotBlock(): Didn't find in"
832 " m_blocks_sending"<<std::endl;*/
833 m_excess_gotblocks++;
835 m_blocks_sent.insert(p, true);
838 void RemoteClient::SentBlock(v3s16 p)
840 if(m_blocks_sending.find(p) == NULL)
841 m_blocks_sending.insert(p, 0.0);
843 infostream<<"RemoteClient::SentBlock(): Sent block"
844 " already in m_blocks_sending"<<std::endl;
847 void RemoteClient::SetBlockNotSent(v3s16 p)
849 m_nearest_unsent_d = 0;
851 if(m_blocks_sending.find(p) != NULL)
852 m_blocks_sending.remove(p);
853 if(m_blocks_sent.find(p) != NULL)
854 m_blocks_sent.remove(p);
857 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
859 m_nearest_unsent_d = 0;
861 for(core::map<v3s16, MapBlock*>::Iterator
862 i = blocks.getIterator();
863 i.atEnd()==false; i++)
865 v3s16 p = i.getNode()->getKey();
867 if(m_blocks_sending.find(p) != NULL)
868 m_blocks_sending.remove(p);
869 if(m_blocks_sent.find(p) != NULL)
870 m_blocks_sent.remove(p);
878 PlayerInfo::PlayerInfo()
884 void PlayerInfo::PrintLine(std::ostream *s)
887 (*s)<<"\""<<name<<"\" ("
888 <<(position.X/10)<<","<<(position.Y/10)
889 <<","<<(position.Z/10)<<") ";
891 (*s)<<" avg_rtt="<<avg_rtt;
900 const std::string &path_world,
901 const std::string &path_config,
902 const SubgameSpec &gamespec,
903 bool simple_singleplayer_mode
905 m_path_world(path_world),
906 m_path_config(path_config),
907 m_gamespec(gamespec),
908 m_simple_singleplayer_mode(simple_singleplayer_mode),
909 m_async_fatal_error(""),
911 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
912 m_banmanager(path_world+DIR_DELIM+"ipban.txt"),
914 m_itemdef(createItemDefManager()),
915 m_nodedef(createNodeDefManager()),
916 m_craftdef(createCraftDefManager()),
917 m_event(new EventManager()),
919 m_emergethread(this),
920 m_time_of_day_send_timer(0),
922 m_shutdown_requested(false),
923 m_ignore_map_edit_events(false),
924 m_ignore_map_edit_events_peer_id(0)
926 m_liquid_transform_timer = 0.0;
927 m_print_info_timer = 0.0;
928 m_objectdata_timer = 0.0;
929 m_emergethread_trigger_timer = 0.0;
930 m_savemap_timer = 0.0;
934 m_step_dtime_mutex.Init();
938 throw ServerError("Supplied empty world path");
940 if(!gamespec.isValid())
941 throw ServerError("Supplied invalid gamespec");
943 infostream<<"Server created for gameid \""<<m_gamespec.id<<"\"";
944 if(m_simple_singleplayer_mode)
945 infostream<<" in simple singleplayer mode"<<std::endl;
947 infostream<<std::endl;
948 infostream<<"- world: "<<m_path_world<<std::endl;
949 infostream<<"- config: "<<m_path_config<<std::endl;
950 infostream<<"- game: "<<m_gamespec.path<<std::endl;
952 // Add world mod search path
953 m_modspaths.push_front(m_path_world + DIR_DELIM + "worldmods");
954 // Add addon mod search path
955 for(std::set<std::string>::const_iterator i = m_gamespec.mods_paths.begin();
956 i != m_gamespec.mods_paths.end(); i++)
957 m_modspaths.push_front((*i));
959 // Print out mod search paths
960 for(core::list<std::string>::Iterator i = m_modspaths.begin();
961 i != m_modspaths.end(); i++){
962 std::string modspath = *i;
963 infostream<<"- mods: "<<modspath<<std::endl;
966 // Path to builtin.lua
967 std::string builtinpath = getBuiltinLuaPath() + DIR_DELIM + "builtin.lua";
969 // Create world if it doesn't exist
970 if(!initializeWorld(m_path_world, m_gamespec.id))
971 throw ServerError("Failed to initialize world");
974 JMutexAutoLock envlock(m_env_mutex);
975 JMutexAutoLock conlock(m_con_mutex);
977 // Initialize scripting
979 infostream<<"Server: Initializing Lua"<<std::endl;
980 m_lua = script_init();
983 scriptapi_export(m_lua, this);
984 // Load and run builtin.lua
985 infostream<<"Server: Loading builtin.lua [\""
986 <<builtinpath<<"\"]"<<std::endl;
987 bool success = scriptapi_loadmod(m_lua, builtinpath, "__builtin");
989 errorstream<<"Server: Failed to load and run "
990 <<builtinpath<<std::endl;
991 throw ModError("Failed to load and run "+builtinpath);
993 // Find mods in mod search paths
994 m_mods = getMods(m_modspaths);
996 infostream<<"Server: Loading mods: ";
997 for(core::list<ModSpec>::Iterator i = m_mods.begin();
998 i != m_mods.end(); i++){
999 const ModSpec &mod = *i;
1000 infostream<<mod.name<<" ";
1002 infostream<<std::endl;
1003 // Load and run "mod" scripts
1004 for(core::list<ModSpec>::Iterator i = m_mods.begin();
1005 i != m_mods.end(); i++){
1006 const ModSpec &mod = *i;
1007 std::string scriptpath = mod.path + DIR_DELIM + "init.lua";
1008 infostream<<" ["<<padStringRight(mod.name, 12)<<"] [\""
1009 <<scriptpath<<"\"]"<<std::endl;
1010 bool success = scriptapi_loadmod(m_lua, scriptpath, mod.name);
1012 errorstream<<"Server: Failed to load and run "
1013 <<scriptpath<<std::endl;
1014 throw ModError("Failed to load and run "+scriptpath);
1018 // Read Textures and calculate sha1 sums
1021 // Apply item aliases in the node definition manager
1022 m_nodedef->updateAliases(m_itemdef);
1024 // Initialize Environment
1026 m_env = new ServerEnvironment(new ServerMap(path_world, this), m_lua,
1029 // Give environment reference to scripting api
1030 scriptapi_add_environment(m_lua, m_env);
1032 // Register us to receive map edit events
1033 m_env->getMap().addEventReceiver(this);
1035 // If file exists, load environment metadata
1036 if(fs::PathExists(m_path_world+DIR_DELIM+"env_meta.txt"))
1038 infostream<<"Server: Loading environment metadata"<<std::endl;
1039 m_env->loadMeta(m_path_world);
1043 infostream<<"Server: Loading players"<<std::endl;
1044 m_env->deSerializePlayers(m_path_world);
1047 Add some test ActiveBlockModifiers to environment
1049 add_legacy_abms(m_env, m_nodedef);
1054 infostream<<"Server destructing"<<std::endl;
1057 Send shutdown message
1060 JMutexAutoLock conlock(m_con_mutex);
1062 std::wstring line = L"*** Server shutting down";
1065 Send the message to clients
1067 for(core::map<u16, RemoteClient*>::Iterator
1068 i = m_clients.getIterator();
1069 i.atEnd() == false; i++)
1071 // Get client and check that it is valid
1072 RemoteClient *client = i.getNode()->getValue();
1073 assert(client->peer_id == i.getNode()->getKey());
1074 if(client->serialization_version == SER_FMT_VER_INVALID)
1078 SendChatMessage(client->peer_id, line);
1080 catch(con::PeerNotFoundException &e)
1086 JMutexAutoLock envlock(m_env_mutex);
1091 infostream<<"Server: Saving players"<<std::endl;
1092 m_env->serializePlayers(m_path_world);
1095 Save environment metadata
1097 infostream<<"Server: Saving environment metadata"<<std::endl;
1098 m_env->saveMeta(m_path_world);
1110 JMutexAutoLock clientslock(m_con_mutex);
1112 for(core::map<u16, RemoteClient*>::Iterator
1113 i = m_clients.getIterator();
1114 i.atEnd() == false; i++)
1117 // NOTE: These are removed by env destructor
1119 u16 peer_id = i.getNode()->getKey();
1120 JMutexAutoLock envlock(m_env_mutex);
1121 m_env->removePlayer(peer_id);
1125 delete i.getNode()->getValue();
1129 // Delete things in the reverse order of creation
1136 // Deinitialize scripting
1137 infostream<<"Server: Deinitializing scripting"<<std::endl;
1138 script_deinit(m_lua);
1141 void Server::start(unsigned short port)
1143 DSTACK(__FUNCTION_NAME);
1144 infostream<<"Starting server on port "<<port<<"..."<<std::endl;
1146 // Stop thread if already running
1149 // Initialize connection
1150 m_con.SetTimeoutMs(30);
1154 m_thread.setRun(true);
1157 // ASCII art for the win!
1159 <<" .__ __ __ "<<std::endl
1160 <<" _____ |__| ____ _____/ |_ ____ _______/ |_ "<<std::endl
1161 <<" / \\| |/ \\_/ __ \\ __\\/ __ \\ / ___/\\ __\\"<<std::endl
1162 <<"| Y Y \\ | | \\ ___/| | \\ ___/ \\___ \\ | | "<<std::endl
1163 <<"|__|_| /__|___| /\\___ >__| \\___ >____ > |__| "<<std::endl
1164 <<" \\/ \\/ \\/ \\/ \\/ "<<std::endl;
1165 actionstream<<"World at ["<<m_path_world<<"]"<<std::endl;
1166 actionstream<<"Server for gameid=\""<<m_gamespec.id
1167 <<"\" listening on port "<<port<<"."<<std::endl;
1172 DSTACK(__FUNCTION_NAME);
1174 infostream<<"Server: Stopping and waiting threads"<<std::endl;
1176 // Stop threads (set run=false first so both start stopping)
1177 m_thread.setRun(false);
1178 m_emergethread.setRun(false);
1180 m_emergethread.stop();
1182 infostream<<"Server: Threads stopped"<<std::endl;
1185 void Server::step(float dtime)
1187 DSTACK(__FUNCTION_NAME);
1192 JMutexAutoLock lock(m_step_dtime_mutex);
1193 m_step_dtime += dtime;
1195 // Throw if fatal error occurred in thread
1196 std::string async_err = m_async_fatal_error.get();
1197 if(async_err != ""){
1198 throw ServerError(async_err);
1202 void Server::AsyncRunStep()
1204 DSTACK(__FUNCTION_NAME);
1206 g_profiler->add("Server::AsyncRunStep (num)", 1);
1210 JMutexAutoLock lock1(m_step_dtime_mutex);
1211 dtime = m_step_dtime;
1215 // Send blocks to clients
1222 g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
1224 //infostream<<"Server steps "<<dtime<<std::endl;
1225 //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1228 JMutexAutoLock lock1(m_step_dtime_mutex);
1229 m_step_dtime -= dtime;
1236 m_uptime.set(m_uptime.get() + dtime);
1240 // Process connection's timeouts
1241 JMutexAutoLock lock2(m_con_mutex);
1242 ScopeProfiler sp(g_profiler, "Server: connection timeout processing");
1243 m_con.RunTimeouts(dtime);
1247 // This has to be called so that the client list gets synced
1248 // with the peer list of the connection
1249 handlePeerChanges();
1253 Update time of day and overall game time
1256 JMutexAutoLock envlock(m_env_mutex);
1258 m_env->setTimeOfDaySpeed(g_settings->getFloat("time_speed"));
1261 Send to clients at constant intervals
1264 m_time_of_day_send_timer -= dtime;
1265 if(m_time_of_day_send_timer < 0.0)
1267 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
1269 //JMutexAutoLock envlock(m_env_mutex);
1270 JMutexAutoLock conlock(m_con_mutex);
1272 for(core::map<u16, RemoteClient*>::Iterator
1273 i = m_clients.getIterator();
1274 i.atEnd() == false; i++)
1276 RemoteClient *client = i.getNode()->getValue();
1277 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1278 m_env->getTimeOfDay(), g_settings->getFloat("time_speed"));
1280 m_con.Send(client->peer_id, 0, data, true);
1286 JMutexAutoLock lock(m_env_mutex);
1288 ScopeProfiler sp(g_profiler, "SEnv step");
1289 ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
1293 const float map_timer_and_unload_dtime = 2.92;
1294 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
1296 JMutexAutoLock lock(m_env_mutex);
1297 // Run Map's timers and unload unused data
1298 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
1299 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
1300 g_settings->getFloat("server_unload_unused_data_timeout"));
1311 JMutexAutoLock lock(m_env_mutex);
1312 JMutexAutoLock lock2(m_con_mutex);
1314 ScopeProfiler sp(g_profiler, "Server: handle players");
1316 for(core::map<u16, RemoteClient*>::Iterator
1317 i = m_clients.getIterator();
1318 i.atEnd() == false; i++)
1320 RemoteClient *client = i.getNode()->getValue();
1321 PlayerSAO *playersao = getPlayerSAO(client->peer_id);
1322 if(playersao == NULL)
1326 Handle player HPs (die if hp=0)
1328 if(playersao->getHP() == 0 && playersao->m_hp_not_sent)
1329 DiePlayer(client->peer_id);
1332 Send player inventories and HPs if necessary
1334 if(playersao->m_teleported){
1335 SendMovePlayer(client->peer_id);
1336 playersao->m_teleported = false;
1338 if(playersao->m_inventory_not_sent){
1339 UpdateCrafting(client->peer_id);
1340 SendInventory(client->peer_id);
1342 if(playersao->m_hp_not_sent){
1343 SendPlayerHP(client->peer_id);
1348 /* Transform liquids */
1349 m_liquid_transform_timer += dtime;
1350 if(m_liquid_transform_timer >= 1.00)
1352 m_liquid_transform_timer -= 1.00;
1354 JMutexAutoLock lock(m_env_mutex);
1356 ScopeProfiler sp(g_profiler, "Server: liquid transform");
1358 core::map<v3s16, MapBlock*> modified_blocks;
1359 m_env->getMap().transformLiquids(modified_blocks);
1364 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1365 ServerMap &map = ((ServerMap&)m_env->getMap());
1366 map.updateLighting(modified_blocks, lighting_modified_blocks);
1368 // Add blocks modified by lighting to modified_blocks
1369 for(core::map<v3s16, MapBlock*>::Iterator
1370 i = lighting_modified_blocks.getIterator();
1371 i.atEnd() == false; i++)
1373 MapBlock *block = i.getNode()->getValue();
1374 modified_blocks.insert(block->getPos(), block);
1378 Set the modified blocks unsent for all the clients
1381 JMutexAutoLock lock2(m_con_mutex);
1383 for(core::map<u16, RemoteClient*>::Iterator
1384 i = m_clients.getIterator();
1385 i.atEnd() == false; i++)
1387 RemoteClient *client = i.getNode()->getValue();
1389 if(modified_blocks.size() > 0)
1391 // Remove block from sent history
1392 client->SetBlocksNotSent(modified_blocks);
1397 // Periodically print some info
1399 float &counter = m_print_info_timer;
1405 JMutexAutoLock lock2(m_con_mutex);
1407 if(m_clients.size() != 0)
1408 infostream<<"Players:"<<std::endl;
1409 for(core::map<u16, RemoteClient*>::Iterator
1410 i = m_clients.getIterator();
1411 i.atEnd() == false; i++)
1413 //u16 peer_id = i.getNode()->getKey();
1414 RemoteClient *client = i.getNode()->getValue();
1415 Player *player = m_env->getPlayer(client->peer_id);
1418 infostream<<"* "<<player->getName()<<"\t";
1419 client->PrintInfo(infostream);
1424 //if(g_settings->getBool("enable_experimental"))
1428 Check added and deleted active objects
1431 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
1432 JMutexAutoLock envlock(m_env_mutex);
1433 JMutexAutoLock conlock(m_con_mutex);
1435 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
1437 // Radius inside which objects are active
1438 s16 radius = g_settings->getS16("active_object_send_range_blocks");
1439 radius *= MAP_BLOCKSIZE;
1441 for(core::map<u16, RemoteClient*>::Iterator
1442 i = m_clients.getIterator();
1443 i.atEnd() == false; i++)
1445 RemoteClient *client = i.getNode()->getValue();
1447 // If definitions and textures have not been sent, don't
1448 // send objects either
1449 if(!client->definitions_sent)
1452 Player *player = m_env->getPlayer(client->peer_id);
1455 // This can happen if the client timeouts somehow
1456 /*infostream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
1458 <<" has no associated player"<<std::endl;*/
1461 v3s16 pos = floatToInt(player->getPosition(), BS);
1463 core::map<u16, bool> removed_objects;
1464 core::map<u16, bool> added_objects;
1465 m_env->getRemovedActiveObjects(pos, radius,
1466 client->m_known_objects, removed_objects);
1467 m_env->getAddedActiveObjects(pos, radius,
1468 client->m_known_objects, added_objects);
1470 // Ignore if nothing happened
1471 if(removed_objects.size() == 0 && added_objects.size() == 0)
1473 //infostream<<"active objects: none changed"<<std::endl;
1477 std::string data_buffer;
1481 // Handle removed objects
1482 writeU16((u8*)buf, removed_objects.size());
1483 data_buffer.append(buf, 2);
1484 for(core::map<u16, bool>::Iterator
1485 i = removed_objects.getIterator();
1486 i.atEnd()==false; i++)
1489 u16 id = i.getNode()->getKey();
1490 ServerActiveObject* obj = m_env->getActiveObject(id);
1492 // Add to data buffer for sending
1493 writeU16((u8*)buf, i.getNode()->getKey());
1494 data_buffer.append(buf, 2);
1496 // Remove from known objects
1497 client->m_known_objects.remove(i.getNode()->getKey());
1499 if(obj && obj->m_known_by_count > 0)
1500 obj->m_known_by_count--;
1503 // Handle added objects
1504 writeU16((u8*)buf, added_objects.size());
1505 data_buffer.append(buf, 2);
1506 for(core::map<u16, bool>::Iterator
1507 i = added_objects.getIterator();
1508 i.atEnd()==false; i++)
1511 u16 id = i.getNode()->getKey();
1512 ServerActiveObject* obj = m_env->getActiveObject(id);
1515 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1517 infostream<<"WARNING: "<<__FUNCTION_NAME
1518 <<": NULL object"<<std::endl;
1520 type = obj->getSendType();
1522 // Add to data buffer for sending
1523 writeU16((u8*)buf, id);
1524 data_buffer.append(buf, 2);
1525 writeU8((u8*)buf, type);
1526 data_buffer.append(buf, 1);
1529 data_buffer.append(serializeLongString(
1530 obj->getClientInitializationData()));
1532 data_buffer.append(serializeLongString(""));
1534 // Add to known objects
1535 client->m_known_objects.insert(i.getNode()->getKey(), false);
1538 obj->m_known_by_count++;
1542 SharedBuffer<u8> reply(2 + data_buffer.size());
1543 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1544 memcpy((char*)&reply[2], data_buffer.c_str(),
1545 data_buffer.size());
1547 m_con.Send(client->peer_id, 0, reply, true);
1549 verbosestream<<"Server: Sent object remove/add: "
1550 <<removed_objects.size()<<" removed, "
1551 <<added_objects.size()<<" added, "
1552 <<"packet size is "<<reply.getSize()<<std::endl;
1557 Collect a list of all the objects known by the clients
1558 and report it back to the environment.
1561 core::map<u16, bool> all_known_objects;
1563 for(core::map<u16, RemoteClient*>::Iterator
1564 i = m_clients.getIterator();
1565 i.atEnd() == false; i++)
1567 RemoteClient *client = i.getNode()->getValue();
1568 // Go through all known objects of client
1569 for(core::map<u16, bool>::Iterator
1570 i = client->m_known_objects.getIterator();
1571 i.atEnd()==false; i++)
1573 u16 id = i.getNode()->getKey();
1574 all_known_objects[id] = true;
1578 m_env->setKnownActiveObjects(whatever);
1584 Send object messages
1587 JMutexAutoLock envlock(m_env_mutex);
1588 JMutexAutoLock conlock(m_con_mutex);
1590 ScopeProfiler sp(g_profiler, "Server: sending object messages");
1593 // Value = data sent by object
1594 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1596 // Get active object messages from environment
1599 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
1603 core::list<ActiveObjectMessage>* message_list = NULL;
1604 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1605 n = buffered_messages.find(aom.id);
1608 message_list = new core::list<ActiveObjectMessage>;
1609 buffered_messages.insert(aom.id, message_list);
1613 message_list = n->getValue();
1615 message_list->push_back(aom);
1618 // Route data to every client
1619 for(core::map<u16, RemoteClient*>::Iterator
1620 i = m_clients.getIterator();
1621 i.atEnd()==false; i++)
1623 RemoteClient *client = i.getNode()->getValue();
1624 std::string reliable_data;
1625 std::string unreliable_data;
1626 // Go through all objects in message buffer
1627 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1628 j = buffered_messages.getIterator();
1629 j.atEnd()==false; j++)
1631 // If object is not known by client, skip it
1632 u16 id = j.getNode()->getKey();
1633 if(client->m_known_objects.find(id) == NULL)
1635 // Get message list of object
1636 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1637 // Go through every message
1638 for(core::list<ActiveObjectMessage>::Iterator
1639 k = list->begin(); k != list->end(); k++)
1641 // Compose the full new data with header
1642 ActiveObjectMessage aom = *k;
1643 std::string new_data;
1646 writeU16((u8*)&buf[0], aom.id);
1647 new_data.append(buf, 2);
1649 new_data += serializeString(aom.datastring);
1650 // Add data to buffer
1652 reliable_data += new_data;
1654 unreliable_data += new_data;
1658 reliable_data and unreliable_data are now ready.
1661 if(reliable_data.size() > 0)
1663 SharedBuffer<u8> reply(2 + reliable_data.size());
1664 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1665 memcpy((char*)&reply[2], reliable_data.c_str(),
1666 reliable_data.size());
1668 m_con.Send(client->peer_id, 0, reply, true);
1670 if(unreliable_data.size() > 0)
1672 SharedBuffer<u8> reply(2 + unreliable_data.size());
1673 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1674 memcpy((char*)&reply[2], unreliable_data.c_str(),
1675 unreliable_data.size());
1676 // Send as unreliable
1677 m_con.Send(client->peer_id, 0, reply, false);
1680 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1682 infostream<<"Server: Size of object message data: "
1683 <<"reliable: "<<reliable_data.size()
1684 <<", unreliable: "<<unreliable_data.size()
1689 // Clear buffered_messages
1690 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1691 i = buffered_messages.getIterator();
1692 i.atEnd()==false; i++)
1694 delete i.getNode()->getValue();
1698 } // enable_experimental
1701 Send queued-for-sending map edit events.
1704 // We will be accessing the environment and the connection
1705 JMutexAutoLock lock(m_env_mutex);
1706 JMutexAutoLock conlock(m_con_mutex);
1708 // Don't send too many at a time
1711 // Single change sending is disabled if queue size is not small
1712 bool disable_single_change_sending = false;
1713 if(m_unsent_map_edit_queue.size() >= 4)
1714 disable_single_change_sending = true;
1716 int event_count = m_unsent_map_edit_queue.size();
1718 // We'll log the amount of each
1721 while(m_unsent_map_edit_queue.size() != 0)
1723 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1725 // Players far away from the change are stored here.
1726 // Instead of sending the changes, MapBlocks are set not sent
1728 core::list<u16> far_players;
1730 if(event->type == MEET_ADDNODE)
1732 //infostream<<"Server: MEET_ADDNODE"<<std::endl;
1733 prof.add("MEET_ADDNODE", 1);
1734 if(disable_single_change_sending)
1735 sendAddNode(event->p, event->n, event->already_known_by_peer,
1738 sendAddNode(event->p, event->n, event->already_known_by_peer,
1741 else if(event->type == MEET_REMOVENODE)
1743 //infostream<<"Server: MEET_REMOVENODE"<<std::endl;
1744 prof.add("MEET_REMOVENODE", 1);
1745 if(disable_single_change_sending)
1746 sendRemoveNode(event->p, event->already_known_by_peer,
1749 sendRemoveNode(event->p, event->already_known_by_peer,
1752 else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
1754 infostream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
1755 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
1756 setBlockNotSent(event->p);
1758 else if(event->type == MEET_OTHER)
1760 infostream<<"Server: MEET_OTHER"<<std::endl;
1761 prof.add("MEET_OTHER", 1);
1762 for(core::map<v3s16, bool>::Iterator
1763 i = event->modified_blocks.getIterator();
1764 i.atEnd()==false; i++)
1766 v3s16 p = i.getNode()->getKey();
1772 prof.add("unknown", 1);
1773 infostream<<"WARNING: Server: Unknown MapEditEvent "
1774 <<((u32)event->type)<<std::endl;
1778 Set blocks not sent to far players
1780 if(far_players.size() > 0)
1782 // Convert list format to that wanted by SetBlocksNotSent
1783 core::map<v3s16, MapBlock*> modified_blocks2;
1784 for(core::map<v3s16, bool>::Iterator
1785 i = event->modified_blocks.getIterator();
1786 i.atEnd()==false; i++)
1788 v3s16 p = i.getNode()->getKey();
1789 modified_blocks2.insert(p,
1790 m_env->getMap().getBlockNoCreateNoEx(p));
1792 // Set blocks not sent
1793 for(core::list<u16>::Iterator
1794 i = far_players.begin();
1795 i != far_players.end(); i++)
1798 RemoteClient *client = getClient(peer_id);
1801 client->SetBlocksNotSent(modified_blocks2);
1807 /*// Don't send too many at a time
1809 if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
1813 if(event_count >= 5){
1814 infostream<<"Server: MapEditEvents:"<<std::endl;
1815 prof.print(infostream);
1816 } else if(event_count != 0){
1817 verbosestream<<"Server: MapEditEvents:"<<std::endl;
1818 prof.print(verbosestream);
1824 Trigger emergethread (it somehow gets to a non-triggered but
1825 bysy state sometimes)
1828 float &counter = m_emergethread_trigger_timer;
1834 m_emergethread.trigger();
1838 // Save map, players and auth stuff
1840 float &counter = m_savemap_timer;
1842 if(counter >= g_settings->getFloat("server_map_save_interval"))
1845 JMutexAutoLock lock(m_env_mutex);
1847 ScopeProfiler sp(g_profiler, "Server: saving stuff");
1850 if(m_banmanager.isModified())
1851 m_banmanager.save();
1853 // Save changed parts of map
1854 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
1857 m_env->serializePlayers(m_path_world);
1859 // Save environment metadata
1860 m_env->saveMeta(m_path_world);
1865 void Server::Receive()
1867 DSTACK(__FUNCTION_NAME);
1868 SharedBuffer<u8> data;
1873 JMutexAutoLock conlock(m_con_mutex);
1874 datasize = m_con.Receive(peer_id, data);
1877 // This has to be called so that the client list gets synced
1878 // with the peer list of the connection
1879 handlePeerChanges();
1881 ProcessData(*data, datasize, peer_id);
1883 catch(con::InvalidIncomingDataException &e)
1885 infostream<<"Server::Receive(): "
1886 "InvalidIncomingDataException: what()="
1887 <<e.what()<<std::endl;
1889 catch(con::PeerNotFoundException &e)
1891 //NOTE: This is not needed anymore
1893 // The peer has been disconnected.
1894 // Find the associated player and remove it.
1896 /*JMutexAutoLock envlock(m_env_mutex);
1898 infostream<<"ServerThread: peer_id="<<peer_id
1899 <<" has apparently closed connection. "
1900 <<"Removing player."<<std::endl;
1902 m_env->removePlayer(peer_id);*/
1906 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1908 DSTACK(__FUNCTION_NAME);
1909 // Environment is locked first.
1910 JMutexAutoLock envlock(m_env_mutex);
1911 JMutexAutoLock conlock(m_con_mutex);
1913 ScopeProfiler sp(g_profiler, "Server::ProcessData");
1916 Address address = m_con.GetPeerAddress(peer_id);
1917 std::string addr_s = address.serializeString();
1919 // drop player if is ip is banned
1920 if(m_banmanager.isIpBanned(addr_s)){
1921 infostream<<"Server: A banned client tried to connect from "
1922 <<addr_s<<"; banned name was "
1923 <<m_banmanager.getBanName(addr_s)<<std::endl;
1924 // This actually doesn't seem to transfer to the client
1925 SendAccessDenied(m_con, peer_id,
1926 L"Your ip is banned. Banned name was "
1927 +narrow_to_wide(m_banmanager.getBanName(addr_s)));
1928 m_con.DeletePeer(peer_id);
1932 catch(con::PeerNotFoundException &e)
1934 infostream<<"Server::ProcessData(): Cancelling: peer "
1935 <<peer_id<<" not found"<<std::endl;
1939 std::string addr_s = m_con.GetPeerAddress(peer_id).serializeString();
1941 u8 peer_ser_ver = getClient(peer_id)->serialization_version;
1949 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1951 if(command == TOSERVER_INIT)
1953 // [0] u16 TOSERVER_INIT
1954 // [2] u8 SER_FMT_VER_HIGHEST
1955 // [3] u8[20] player_name
1956 // [23] u8[28] password <--- can be sent without this, from old versions
1958 if(datasize < 2+1+PLAYERNAME_SIZE)
1961 verbosestream<<"Server: Got TOSERVER_INIT from "
1962 <<peer_id<<std::endl;
1964 // First byte after command is maximum supported
1965 // serialization version
1966 u8 client_max = data[2];
1967 u8 our_max = SER_FMT_VER_HIGHEST;
1968 // Use the highest version supported by both
1969 u8 deployed = core::min_(client_max, our_max);
1970 // If it's lower than the lowest supported, give up.
1971 if(deployed < SER_FMT_VER_LOWEST)
1972 deployed = SER_FMT_VER_INVALID;
1974 //peer->serialization_version = deployed;
1975 getClient(peer_id)->pending_serialization_version = deployed;
1977 if(deployed == SER_FMT_VER_INVALID)
1979 actionstream<<"Server: A mismatched client tried to connect from "
1980 <<addr_s<<std::endl;
1981 infostream<<"Server: Cannot negotiate "
1982 "serialization version with peer "
1983 <<peer_id<<std::endl;
1984 SendAccessDenied(m_con, peer_id, std::wstring(
1985 L"Your client's version is not supported.\n"
1986 L"Server version is ")
1987 + narrow_to_wide(VERSION_STRING) + L"."
1993 Read and check network protocol version
1996 u16 net_proto_version = 0;
1997 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
1999 net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
2002 getClient(peer_id)->net_proto_version = net_proto_version;
2004 if(net_proto_version == 0)
2006 actionstream<<"Server: An old tried to connect from "<<addr_s
2008 SendAccessDenied(m_con, peer_id, std::wstring(
2009 L"Your client's version is not supported.\n"
2010 L"Server version is ")
2011 + narrow_to_wide(VERSION_STRING) + L"."
2016 if(g_settings->getBool("strict_protocol_version_checking"))
2018 if(net_proto_version != PROTOCOL_VERSION)
2020 actionstream<<"Server: A mismatched client tried to connect"
2021 <<" from "<<addr_s<<std::endl;
2022 SendAccessDenied(m_con, peer_id, std::wstring(
2023 L"Your client's version is not supported.\n"
2024 L"Server version is ")
2025 + narrow_to_wide(VERSION_STRING) + L",\n"
2026 + L"server's PROTOCOL_VERSION is "
2027 + narrow_to_wide(itos(PROTOCOL_VERSION))
2028 + L", client's PROTOCOL_VERSION is "
2029 + narrow_to_wide(itos(net_proto_version))
2040 char playername[PLAYERNAME_SIZE];
2041 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
2043 playername[i] = data[3+i];
2045 playername[PLAYERNAME_SIZE-1] = 0;
2047 if(playername[0]=='\0')
2049 actionstream<<"Server: Player with an empty name "
2050 <<"tried to connect from "<<addr_s<<std::endl;
2051 SendAccessDenied(m_con, peer_id,
2056 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
2058 actionstream<<"Server: Player with an invalid name "
2059 <<"tried to connect from "<<addr_s<<std::endl;
2060 SendAccessDenied(m_con, peer_id,
2061 L"Name contains unallowed characters");
2065 infostream<<"Server: New connection: \""<<playername<<"\" from "
2066 <<m_con.GetPeerAddress(peer_id).serializeString()<<std::endl;
2069 char password[PASSWORD_SIZE];
2070 if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
2072 // old version - assume blank password
2077 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2079 password[i] = data[23+i];
2081 password[PASSWORD_SIZE-1] = 0;
2084 std::string checkpwd;
2085 bool has_auth = scriptapi_get_auth(m_lua, playername, &checkpwd, NULL);
2088 std::wstring raw_default_password =
2089 narrow_to_wide(g_settings->get("default_password"));
2090 std::string use_password =
2091 translatePassword(playername, raw_default_password);
2093 // If default_password is empty, allow any initial password
2094 if (raw_default_password.length() == 0)
2095 use_password = password;
2097 scriptapi_create_auth(m_lua, playername, use_password);
2100 has_auth = scriptapi_get_auth(m_lua, playername, &checkpwd, NULL);
2103 SendAccessDenied(m_con, peer_id, L"Not allowed to login");
2107 if(password != checkpwd){
2108 infostream<<"Server: peer_id="<<peer_id
2109 <<": supplied invalid password for "
2110 <<playername<<std::endl;
2111 SendAccessDenied(m_con, peer_id, L"Invalid password");
2115 // Do not allow multiple players in simple singleplayer mode.
2116 // This isn't a perfect way to do it, but will suffice for now.
2117 if(m_simple_singleplayer_mode && m_clients.size() > 1){
2118 infostream<<"Server: Not allowing another client to connect in"
2119 <<" simple singleplayer mode"<<std::endl;
2120 SendAccessDenied(m_con, peer_id,
2121 L"Running in simple singleplayer mode.");
2125 // Enforce user limit.
2126 // Don't enforce for users that have some admin right
2127 if(m_clients.size() >= g_settings->getU16("max_users") &&
2128 !checkPriv(playername, "server") &&
2129 !checkPriv(playername, "ban") &&
2130 !checkPriv(playername, "privs") &&
2131 !checkPriv(playername, "password") &&
2132 playername != g_settings->get("name"))
2134 actionstream<<"Server: "<<playername<<" tried to join, but there"
2135 <<" are already max_users="
2136 <<g_settings->getU16("max_users")<<" players."<<std::endl;
2137 SendAccessDenied(m_con, peer_id, L"Too many users.");
2142 PlayerSAO *playersao = emergePlayer(playername, peer_id);
2144 // If failed, cancel
2145 if(playersao == NULL)
2147 errorstream<<"Server: peer_id="<<peer_id
2148 <<": failed to emerge player"<<std::endl;
2153 Answer with a TOCLIENT_INIT
2156 SharedBuffer<u8> reply(2+1+6+8);
2157 writeU16(&reply[0], TOCLIENT_INIT);
2158 writeU8(&reply[2], deployed);
2159 writeV3S16(&reply[2+1], floatToInt(playersao->getPlayer()->getPosition()+v3f(0,BS/2,0), BS));
2160 writeU64(&reply[2+1+6], m_env->getServerMap().getSeed());
2163 m_con.Send(peer_id, 0, reply, true);
2167 Send complete position information
2169 SendMovePlayer(peer_id);
2174 if(command == TOSERVER_INIT2)
2176 verbosestream<<"Server: Got TOSERVER_INIT2 from "
2177 <<peer_id<<std::endl;
2180 getClient(peer_id)->serialization_version
2181 = getClient(peer_id)->pending_serialization_version;
2184 Send some initialization data
2187 infostream<<"Server: Sending content to "
2188 <<getPlayerName(peer_id)<<std::endl;
2190 // Send item definitions
2191 SendItemDef(m_con, peer_id, m_itemdef);
2193 // Send node definitions
2194 SendNodeDef(m_con, peer_id, m_nodedef);
2196 // Send media announcement
2197 sendMediaAnnouncement(peer_id);
2200 SendPlayerPrivileges(peer_id);
2203 UpdateCrafting(peer_id);
2204 SendInventory(peer_id);
2206 Player *player = m_env->getPlayer(peer_id);
2209 SendPlayerHP(peer_id);
2211 // Show death screen if necessary
2213 SendDeathscreen(m_con, peer_id, false, v3f(0,0,0));
2217 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2218 m_env->getTimeOfDay(), g_settings->getFloat("time_speed"));
2219 m_con.Send(peer_id, 0, data, true);
2222 // Note things in chat if not in simple singleplayer mode
2223 if(!m_simple_singleplayer_mode)
2225 // Send information about server to player in chat
2226 SendChatMessage(peer_id, getStatusString());
2228 // Send information about joining in chat
2230 std::wstring name = L"unknown";
2231 Player *player = m_env->getPlayer(peer_id);
2233 name = narrow_to_wide(player->getName());
2235 std::wstring message;
2238 message += L" joined game";
2239 BroadcastChatMessage(message);
2243 // Warnings about protocol version can be issued here
2244 if(getClient(peer_id)->net_proto_version < PROTOCOL_VERSION)
2246 SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT IS OLD AND MAY WORK PROPERLY WITH THIS SERVER");
2253 std::ostringstream os(std::ios_base::binary);
2254 for(core::map<u16, RemoteClient*>::Iterator
2255 i = m_clients.getIterator();
2256 i.atEnd() == false; i++)
2258 RemoteClient *client = i.getNode()->getValue();
2259 assert(client->peer_id == i.getNode()->getKey());
2260 if(client->serialization_version == SER_FMT_VER_INVALID)
2263 Player *player = m_env->getPlayer(client->peer_id);
2266 // Get name of player
2267 os<<player->getName()<<" ";
2270 actionstream<<player->getName()<<" joins game. List of players: "
2271 <<os.str()<<std::endl;
2277 if(peer_ser_ver == SER_FMT_VER_INVALID)
2279 infostream<<"Server::ProcessData(): Cancelling: Peer"
2280 " serialization format invalid or not initialized."
2281 " Skipping incoming command="<<command<<std::endl;
2285 Player *player = m_env->getPlayer(peer_id);
2287 infostream<<"Server::ProcessData(): Cancelling: "
2288 "No player for peer_id="<<peer_id
2293 PlayerSAO *playersao = player->getPlayerSAO();
2294 if(playersao == NULL){
2295 infostream<<"Server::ProcessData(): Cancelling: "
2296 "No player object for peer_id="<<peer_id
2301 if(command == TOSERVER_PLAYERPOS)
2303 if(datasize < 2+12+12+4+4)
2307 v3s32 ps = readV3S32(&data[start+2]);
2308 v3s32 ss = readV3S32(&data[start+2+12]);
2309 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2310 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2311 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2312 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2313 pitch = wrapDegrees(pitch);
2314 yaw = wrapDegrees(yaw);
2316 player->setPosition(position);
2317 player->setSpeed(speed);
2318 player->setPitch(pitch);
2319 player->setYaw(yaw);
2321 /*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2322 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2323 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2325 else if(command == TOSERVER_GOTBLOCKS)
2338 u16 count = data[2];
2339 for(u16 i=0; i<count; i++)
2341 if((s16)datasize < 2+1+(i+1)*6)
2342 throw con::InvalidIncomingDataException
2343 ("GOTBLOCKS length is too short");
2344 v3s16 p = readV3S16(&data[2+1+i*6]);
2345 /*infostream<<"Server: GOTBLOCKS ("
2346 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2347 RemoteClient *client = getClient(peer_id);
2348 client->GotBlock(p);
2351 else if(command == TOSERVER_DELETEDBLOCKS)
2364 u16 count = data[2];
2365 for(u16 i=0; i<count; i++)
2367 if((s16)datasize < 2+1+(i+1)*6)
2368 throw con::InvalidIncomingDataException
2369 ("DELETEDBLOCKS length is too short");
2370 v3s16 p = readV3S16(&data[2+1+i*6]);
2371 /*infostream<<"Server: DELETEDBLOCKS ("
2372 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2373 RemoteClient *client = getClient(peer_id);
2374 client->SetBlockNotSent(p);
2377 else if(command == TOSERVER_CLICK_OBJECT)
2379 infostream<<"Server: CLICK_OBJECT not supported anymore"<<std::endl;
2382 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2384 infostream<<"Server: CLICK_ACTIVEOBJECT not supported anymore"<<std::endl;
2387 else if(command == TOSERVER_GROUND_ACTION)
2389 infostream<<"Server: GROUND_ACTION not supported anymore"<<std::endl;
2393 else if(command == TOSERVER_RELEASE)
2395 infostream<<"Server: RELEASE not supported anymore"<<std::endl;
2398 else if(command == TOSERVER_SIGNTEXT)
2400 infostream<<"Server: SIGNTEXT not supported anymore"
2404 else if(command == TOSERVER_SIGNNODETEXT)
2406 if(!checkPriv(player->getName(), "interact"))
2414 std::string datastring((char*)&data[2], datasize-2);
2415 std::istringstream is(datastring, std::ios_base::binary);
2418 is.read((char*)buf, 6);
2419 v3s16 p = readV3S16(buf);
2420 is.read((char*)buf, 2);
2421 u16 textlen = readU16(buf);
2423 for(u16 i=0; i<textlen; i++)
2425 is.read((char*)buf, 1);
2426 text += (char)buf[0];
2429 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
2433 meta->setText(text);
2435 actionstream<<player->getName()<<" writes \""<<text<<"\" to sign"
2436 <<" at "<<PP(p)<<std::endl;
2438 v3s16 blockpos = getNodeBlockPos(p);
2439 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
2442 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2446 setBlockNotSent(blockpos);
2448 else if(command == TOSERVER_INVENTORY_ACTION)
2450 // Strip command and create a stream
2451 std::string datastring((char*)&data[2], datasize-2);
2452 verbosestream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2453 std::istringstream is(datastring, std::ios_base::binary);
2455 InventoryAction *a = InventoryAction::deSerialize(is);
2458 infostream<<"TOSERVER_INVENTORY_ACTION: "
2459 <<"InventoryAction::deSerialize() returned NULL"
2465 Note: Always set inventory not sent, to repair cases
2466 where the client made a bad prediction.
2470 Handle restrictions and special cases of the move action
2472 if(a->getType() == IACTION_MOVE)
2474 IMoveAction *ma = (IMoveAction*)a;
2476 ma->from_inv.applyCurrentPlayer(player->getName());
2477 ma->to_inv.applyCurrentPlayer(player->getName());
2479 setInventoryModified(ma->from_inv);
2480 setInventoryModified(ma->to_inv);
2482 bool from_inv_is_current_player =
2483 (ma->from_inv.type == InventoryLocation::PLAYER) &&
2484 (ma->from_inv.name == player->getName());
2486 bool to_inv_is_current_player =
2487 (ma->to_inv.type == InventoryLocation::PLAYER) &&
2488 (ma->to_inv.name == player->getName());
2491 Disable moving items out of craftpreview
2493 if(ma->from_list == "craftpreview")
2495 infostream<<"Ignoring IMoveAction from "
2496 <<(ma->from_inv.dump())<<":"<<ma->from_list
2497 <<" to "<<(ma->to_inv.dump())<<":"<<ma->to_list
2498 <<" because src is "<<ma->from_list<<std::endl;
2504 Disable moving items into craftresult and craftpreview
2506 if(ma->to_list == "craftpreview" || ma->to_list == "craftresult")
2508 infostream<<"Ignoring IMoveAction from "
2509 <<(ma->from_inv.dump())<<":"<<ma->from_list
2510 <<" to "<<(ma->to_inv.dump())<<":"<<ma->to_list
2511 <<" because dst is "<<ma->to_list<<std::endl;
2516 // Disallow moving items in elsewhere than player's inventory
2517 // if not allowed to interact
2518 if(!checkPriv(player->getName(), "interact") &&
2519 (!from_inv_is_current_player ||
2520 !to_inv_is_current_player))
2522 infostream<<"Cannot move outside of player's inventory: "
2523 <<"No interact privilege"<<std::endl;
2528 // If player is not an admin, check for ownership of src and dst
2529 if(!checkPriv(player->getName(), "server"))
2531 std::string owner_from = getInventoryOwner(ma->from_inv);
2532 if(owner_from != "" && owner_from != player->getName())
2534 infostream<<"WARNING: "<<player->getName()
2535 <<" tried to access an inventory that"
2536 <<" belongs to "<<owner_from<<std::endl;
2541 std::string owner_to = getInventoryOwner(ma->to_inv);
2542 if(owner_to != "" && owner_to != player->getName())
2544 infostream<<"WARNING: "<<player->getName()
2545 <<" tried to access an inventory that"
2546 <<" belongs to "<<owner_to<<std::endl;
2553 Handle restrictions and special cases of the drop action
2555 else if(a->getType() == IACTION_DROP)
2557 IDropAction *da = (IDropAction*)a;
2559 da->from_inv.applyCurrentPlayer(player->getName());
2561 setInventoryModified(da->from_inv);
2563 // Disallow dropping items if not allowed to interact
2564 if(!checkPriv(player->getName(), "interact"))
2569 // If player is not an admin, check for ownership
2570 else if(!checkPriv(player->getName(), "server"))
2572 std::string owner_from = getInventoryOwner(da->from_inv);
2573 if(owner_from != "" && owner_from != player->getName())
2575 infostream<<"WARNING: "<<player->getName()
2576 <<" tried to access an inventory that"
2577 <<" belongs to "<<owner_from<<std::endl;
2584 Handle restrictions and special cases of the craft action
2586 else if(a->getType() == IACTION_CRAFT)
2588 ICraftAction *ca = (ICraftAction*)a;
2590 ca->craft_inv.applyCurrentPlayer(player->getName());
2592 setInventoryModified(ca->craft_inv);
2594 //bool craft_inv_is_current_player =
2595 // (ca->craft_inv.type == InventoryLocation::PLAYER) &&
2596 // (ca->craft_inv.name == player->getName());
2598 // Disallow crafting if not allowed to interact
2599 if(!checkPriv(player->getName(), "interact"))
2601 infostream<<"Cannot craft: "
2602 <<"No interact privilege"<<std::endl;
2607 // If player is not an admin, check for ownership of inventory
2608 if(!checkPriv(player->getName(), "server"))
2610 std::string owner_craft = getInventoryOwner(ca->craft_inv);
2611 if(owner_craft != "" && owner_craft != player->getName())
2613 infostream<<"WARNING: "<<player->getName()
2614 <<" tried to access an inventory that"
2615 <<" belongs to "<<owner_craft<<std::endl;
2623 a->apply(this, playersao, this);
2627 else if(command == TOSERVER_CHAT_MESSAGE)
2635 std::string datastring((char*)&data[2], datasize-2);
2636 std::istringstream is(datastring, std::ios_base::binary);
2639 is.read((char*)buf, 2);
2640 u16 len = readU16(buf);
2642 std::wstring message;
2643 for(u16 i=0; i<len; i++)
2645 is.read((char*)buf, 2);
2646 message += (wchar_t)readU16(buf);
2649 // Get player name of this client
2650 std::wstring name = narrow_to_wide(player->getName());
2653 bool ate = scriptapi_on_chat_message(m_lua, player->getName(),
2654 wide_to_narrow(message));
2655 // If script ate the message, don't proceed
2659 // Line to send to players
2661 // Whether to send to the player that sent the line
2662 bool send_to_sender = false;
2663 // Whether to send to other players
2664 bool send_to_others = false;
2667 if(message[0] == L'/')
2669 size_t strip_size = 1;
2670 if (message[1] == L'#') // support old-style commans
2672 message = message.substr(strip_size);
2674 WStrfnd f1(message);
2675 f1.next(L" "); // Skip over /#whatever
2676 std::wstring paramstring = f1.next(L"");
2678 ServerCommandContext *ctx = new ServerCommandContext(
2679 str_split(message, L' '),
2685 std::wstring reply(processServerCommand(ctx));
2686 send_to_sender = ctx->flags & SEND_TO_SENDER;
2687 send_to_others = ctx->flags & SEND_TO_OTHERS;
2689 if (ctx->flags & SEND_NO_PREFIX)
2692 line += L"Server: " + reply;
2699 if(checkPriv(player->getName(), "shout")){
2704 send_to_others = true;
2706 line += L"Server: You are not allowed to shout";
2707 send_to_sender = true;
2714 actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2717 Send the message to clients
2719 for(core::map<u16, RemoteClient*>::Iterator
2720 i = m_clients.getIterator();
2721 i.atEnd() == false; i++)
2723 // Get client and check that it is valid
2724 RemoteClient *client = i.getNode()->getValue();
2725 assert(client->peer_id == i.getNode()->getKey());
2726 if(client->serialization_version == SER_FMT_VER_INVALID)
2730 bool sender_selected = (peer_id == client->peer_id);
2731 if(sender_selected == true && send_to_sender == false)
2733 if(sender_selected == false && send_to_others == false)
2736 SendChatMessage(client->peer_id, line);
2740 else if(command == TOSERVER_DAMAGE)
2742 std::string datastring((char*)&data[2], datasize-2);
2743 std::istringstream is(datastring, std::ios_base::binary);
2744 u8 damage = readU8(is);
2746 actionstream<<player->getName()<<" damaged by "
2747 <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
2750 playersao->setHP(playersao->getHP() - damage);
2752 if(playersao->getHP() == 0 && playersao->m_hp_not_sent)
2755 if(playersao->m_hp_not_sent)
2756 SendPlayerHP(peer_id);
2758 else if(command == TOSERVER_PASSWORD)
2761 [0] u16 TOSERVER_PASSWORD
2762 [2] u8[28] old password
2763 [30] u8[28] new password
2766 if(datasize != 2+PASSWORD_SIZE*2)
2768 /*char password[PASSWORD_SIZE];
2769 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2770 password[i] = data[2+i];
2771 password[PASSWORD_SIZE-1] = 0;*/
2773 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2781 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2783 char c = data[2+PASSWORD_SIZE+i];
2789 infostream<<"Server: Client requests a password change from "
2790 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
2792 std::string playername = player->getName();
2794 std::string checkpwd;
2795 scriptapi_get_auth(m_lua, playername, &checkpwd, NULL);
2797 if(oldpwd != checkpwd)
2799 infostream<<"Server: invalid old password"<<std::endl;
2800 // Wrong old password supplied!!
2801 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
2805 bool success = scriptapi_set_password(m_lua, playername, newpwd);
2807 actionstream<<player->getName()<<" changes password"<<std::endl;
2808 SendChatMessage(peer_id, L"Password change successful");
2810 actionstream<<player->getName()<<" tries to change password but "
2811 <<"it fails"<<std::endl;
2812 SendChatMessage(peer_id, L"Password change failed or inavailable");
2815 else if(command == TOSERVER_PLAYERITEM)
2820 u16 item = readU16(&data[2]);
2821 playersao->setWieldIndex(item);
2823 else if(command == TOSERVER_RESPAWN)
2828 RespawnPlayer(peer_id);
2830 actionstream<<player->getName()<<" respawns at "
2831 <<PP(player->getPosition()/BS)<<std::endl;
2833 // ActiveObject is added to environment in AsyncRunStep after
2834 // the previous addition has been succesfully removed
2836 else if(command == TOSERVER_REQUEST_MEDIA) {
2837 std::string datastring((char*)&data[2], datasize-2);
2838 std::istringstream is(datastring, std::ios_base::binary);
2840 core::list<MediaRequest> tosend;
2841 u16 numfiles = readU16(is);
2843 infostream<<"Sending "<<numfiles<<" files to "
2844 <<getPlayerName(peer_id)<<std::endl;
2845 verbosestream<<"TOSERVER_REQUEST_MEDIA: "<<std::endl;
2847 for(int i = 0; i < numfiles; i++) {
2848 std::string name = deSerializeString(is);
2849 tosend.push_back(MediaRequest(name));
2850 verbosestream<<"TOSERVER_REQUEST_MEDIA: requested file "
2854 sendRequestedMedia(peer_id, tosend);
2856 // Now the client should know about everything
2857 // (definitions and files)
2858 getClient(peer_id)->definitions_sent = true;
2860 else if(command == TOSERVER_INTERACT)
2862 std::string datastring((char*)&data[2], datasize-2);
2863 std::istringstream is(datastring, std::ios_base::binary);
2869 [5] u32 length of the next item
2870 [9] serialized PointedThing
2872 0: start digging (from undersurface) or use
2873 1: stop digging (all parameters ignored)
2874 2: digging completed
2875 3: place block or item (to abovesurface)
2878 u8 action = readU8(is);
2879 u16 item_i = readU16(is);
2880 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
2881 PointedThing pointed;
2882 pointed.deSerialize(tmp_is);
2884 verbosestream<<"TOSERVER_INTERACT: action="<<(int)action<<", item="
2885 <<item_i<<", pointed="<<pointed.dump()<<std::endl;
2889 verbosestream<<"TOSERVER_INTERACT: "<<player->getName()
2890 <<" tried to interact, but is dead!"<<std::endl;
2894 v3f player_pos = playersao->getLastGoodPosition();
2896 // Update wielded item
2897 playersao->setWieldIndex(item_i);
2899 // Get pointed to node (undefined if not POINTEDTYPE_NODE)
2900 v3s16 p_under = pointed.node_undersurface;
2901 v3s16 p_above = pointed.node_abovesurface;
2903 // Get pointed to object (NULL if not POINTEDTYPE_OBJECT)
2904 ServerActiveObject *pointed_object = NULL;
2905 if(pointed.type == POINTEDTHING_OBJECT)
2907 pointed_object = m_env->getActiveObject(pointed.object_id);
2908 if(pointed_object == NULL)
2910 verbosestream<<"TOSERVER_INTERACT: "
2911 "pointed object is NULL"<<std::endl;
2917 v3f pointed_pos_under = player_pos;
2918 v3f pointed_pos_above = player_pos;
2919 if(pointed.type == POINTEDTHING_NODE)
2921 pointed_pos_under = intToFloat(p_under, BS);
2922 pointed_pos_above = intToFloat(p_above, BS);
2924 else if(pointed.type == POINTEDTHING_OBJECT)
2926 pointed_pos_under = pointed_object->getBasePosition();
2927 pointed_pos_above = pointed_pos_under;
2931 Check that target is reasonably close
2932 (only when digging or placing things)
2934 if(action == 0 || action == 2 || action == 3)
2936 float d = player_pos.getDistanceFrom(pointed_pos_under);
2937 float max_d = BS * 14; // Just some large enough value
2939 actionstream<<"Player "<<player->getName()
2940 <<" tried to access "<<pointed.dump()
2942 <<"d="<<d<<", max_d="<<max_d
2943 <<". ignoring."<<std::endl;
2944 // Re-send block to revert change on client-side
2945 RemoteClient *client = getClient(peer_id);
2946 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2947 client->SetBlockNotSent(blockpos);
2954 Make sure the player is allowed to do it
2956 if(!checkPriv(player->getName(), "interact"))
2958 infostream<<"Ignoring interaction from player "<<player->getName()
2959 <<" (no interact privilege)"<<std::endl;
2964 0: start digging or punch object
2968 if(pointed.type == POINTEDTHING_NODE)
2971 NOTE: This can be used in the future to check if
2972 somebody is cheating, by checking the timing.
2974 MapNode n(CONTENT_IGNORE);
2977 n = m_env->getMap().getNode(p_under);
2979 catch(InvalidPositionException &e)
2981 infostream<<"Server: Not punching: Node not found."
2982 <<" Adding block to emerge queue."
2984 m_emerge_queue.addBlock(peer_id,
2985 getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
2987 if(n.getContent() != CONTENT_IGNORE)
2988 scriptapi_node_on_punch(m_lua, p_under, n, playersao);
2990 else if(pointed.type == POINTEDTHING_OBJECT)
2992 // Skip if object has been removed
2993 if(pointed_object->m_removed)
2996 actionstream<<player->getName()<<" punches object "
2997 <<pointed.object_id<<": "
2998 <<pointed_object->getDescription()<<std::endl;
3000 ItemStack punchitem = playersao->getWieldedItem();
3001 ToolCapabilities toolcap =
3002 punchitem.getToolCapabilities(m_itemdef);
3003 v3f dir = (pointed_object->getBasePosition() -
3004 (player->getPosition() + player->getEyeOffset())
3006 float time_from_last_punch =
3007 playersao->resetTimeFromLastPunch();
3008 pointed_object->punch(dir, &toolcap, playersao,
3009 time_from_last_punch);
3017 else if(action == 1)
3022 2: Digging completed
3024 else if(action == 2)
3026 // Only complete digging of nodes
3027 if(pointed.type == POINTEDTHING_NODE)
3029 MapNode n(CONTENT_IGNORE);
3032 n = m_env->getMap().getNode(p_under);
3034 catch(InvalidPositionException &e)
3036 infostream<<"Server: Not finishing digging: Node not found."
3037 <<" Adding block to emerge queue."
3039 m_emerge_queue.addBlock(peer_id,
3040 getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
3042 if(n.getContent() != CONTENT_IGNORE)
3043 scriptapi_node_on_dig(m_lua, p_under, n, playersao);
3048 3: place block or right-click object
3050 else if(action == 3)
3052 ItemStack item = playersao->getWieldedItem();
3054 // Reset build time counter
3055 if(pointed.type == POINTEDTHING_NODE &&
3056 item.getDefinition(m_itemdef).type == ITEM_NODE)
3057 getClient(peer_id)->m_time_from_building = 0.0;
3059 if(pointed.type == POINTEDTHING_OBJECT)
3061 // Right click object
3063 // Skip if object has been removed
3064 if(pointed_object->m_removed)
3067 actionstream<<player->getName()<<" right-clicks object "
3068 <<pointed.object_id<<": "
3069 <<pointed_object->getDescription()<<std::endl;
3072 pointed_object->rightClick(playersao);
3074 else if(scriptapi_item_on_place(m_lua,
3075 item, playersao, pointed))
3077 // Placement was handled in lua
3079 // Apply returned ItemStack
3080 if(g_settings->getBool("creative_mode") == false)
3081 playersao->setWieldedItem(item);
3089 else if(action == 4)
3091 ItemStack item = playersao->getWieldedItem();
3093 actionstream<<player->getName()<<" uses "<<item.name
3094 <<", pointing at "<<pointed.dump()<<std::endl;
3096 if(scriptapi_item_on_use(m_lua,
3097 item, playersao, pointed))
3099 // Apply returned ItemStack
3100 if(g_settings->getBool("creative_mode") == false)
3101 playersao->setWieldedItem(item);
3107 Catch invalid actions
3111 infostream<<"WARNING: Server: Invalid action "
3112 <<action<<std::endl;
3115 else if(command == TOSERVER_REMOVED_SOUNDS)
3117 std::string datastring((char*)&data[2], datasize-2);
3118 std::istringstream is(datastring, std::ios_base::binary);
3120 int num = readU16(is);
3121 for(int k=0; k<num; k++){
3122 s32 id = readS32(is);
3123 std::map<s32, ServerPlayingSound>::iterator i =
3124 m_playing_sounds.find(id);
3125 if(i == m_playing_sounds.end())
3127 ServerPlayingSound &psound = i->second;
3128 psound.clients.erase(peer_id);
3129 if(psound.clients.size() == 0)
3130 m_playing_sounds.erase(i++);
3135 infostream<<"Server::ProcessData(): Ignoring "
3136 "unknown command "<<command<<std::endl;
3140 catch(SendFailedException &e)
3142 errorstream<<"Server::ProcessData(): SendFailedException: "
3148 void Server::onMapEditEvent(MapEditEvent *event)
3150 //infostream<<"Server::onMapEditEvent()"<<std::endl;
3151 if(m_ignore_map_edit_events)
3153 if(m_ignore_map_edit_events_area.contains(event->getArea()))
3155 MapEditEvent *e = event->clone();
3156 m_unsent_map_edit_queue.push_back(e);
3159 Inventory* Server::getInventory(const InventoryLocation &loc)
3162 case InventoryLocation::UNDEFINED:
3165 case InventoryLocation::CURRENT_PLAYER:
3168 case InventoryLocation::PLAYER:
3170 Player *player = m_env->getPlayer(loc.name.c_str());
3173 PlayerSAO *playersao = player->getPlayerSAO();
3176 return playersao->getInventory();
3179 case InventoryLocation::NODEMETA:
3181 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
3184 return meta->getInventory();
3192 std::string Server::getInventoryOwner(const InventoryLocation &loc)
3195 case InventoryLocation::UNDEFINED:
3198 case InventoryLocation::CURRENT_PLAYER:
3201 case InventoryLocation::PLAYER:
3206 case InventoryLocation::NODEMETA:
3208 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
3211 return meta->getOwner();
3219 void Server::setInventoryModified(const InventoryLocation &loc)
3222 case InventoryLocation::UNDEFINED:
3225 case InventoryLocation::PLAYER:
3227 Player *player = m_env->getPlayer(loc.name.c_str());
3230 PlayerSAO *playersao = player->getPlayerSAO();
3233 playersao->m_inventory_not_sent = true;
3234 playersao->m_wielded_item_not_sent = true;
3237 case InventoryLocation::NODEMETA:
3239 v3s16 blockpos = getNodeBlockPos(loc.p);
3241 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
3243 meta->inventoryModified();
3245 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3247 block->raiseModified(MOD_STATE_WRITE_NEEDED);
3249 setBlockNotSent(blockpos);
3257 core::list<PlayerInfo> Server::getPlayerInfo()
3259 DSTACK(__FUNCTION_NAME);
3260 JMutexAutoLock envlock(m_env_mutex);
3261 JMutexAutoLock conlock(m_con_mutex);
3263 core::list<PlayerInfo> list;
3265 core::list<Player*> players = m_env->getPlayers();
3267 core::list<Player*>::Iterator i;
3268 for(i = players.begin();
3269 i != players.end(); i++)
3273 Player *player = *i;
3276 // Copy info from connection to info struct
3277 info.id = player->peer_id;
3278 info.address = m_con.GetPeerAddress(player->peer_id);
3279 info.avg_rtt = m_con.GetPeerAvgRTT(player->peer_id);
3281 catch(con::PeerNotFoundException &e)
3283 // Set dummy peer info
3285 info.address = Address(0,0,0,0,0);
3289 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3290 info.position = player->getPosition();
3292 list.push_back(info);
3299 void Server::peerAdded(con::Peer *peer)
3301 DSTACK(__FUNCTION_NAME);
3302 verbosestream<<"Server::peerAdded(): peer->id="
3303 <<peer->id<<std::endl;
3306 c.type = PEER_ADDED;
3307 c.peer_id = peer->id;
3309 m_peer_change_queue.push_back(c);
3312 void Server::deletingPeer(con::Peer *peer, bool timeout)
3314 DSTACK(__FUNCTION_NAME);
3315 verbosestream<<"Server::deletingPeer(): peer->id="
3316 <<peer->id<<", timeout="<<timeout<<std::endl;
3319 c.type = PEER_REMOVED;
3320 c.peer_id = peer->id;
3321 c.timeout = timeout;
3322 m_peer_change_queue.push_back(c);
3329 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3331 DSTACK(__FUNCTION_NAME);
3332 std::ostringstream os(std::ios_base::binary);
3334 writeU16(os, TOCLIENT_HP);
3338 std::string s = os.str();
3339 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3341 con.Send(peer_id, 0, data, true);
3344 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3345 const std::wstring &reason)
3347 DSTACK(__FUNCTION_NAME);
3348 std::ostringstream os(std::ios_base::binary);
3350 writeU16(os, TOCLIENT_ACCESS_DENIED);
3351 os<<serializeWideString(reason);
3354 std::string s = os.str();
3355 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3357 con.Send(peer_id, 0, data, true);
3360 void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
3361 bool set_camera_point_target, v3f camera_point_target)
3363 DSTACK(__FUNCTION_NAME);
3364 std::ostringstream os(std::ios_base::binary);
3366 writeU16(os, TOCLIENT_DEATHSCREEN);
3367 writeU8(os, set_camera_point_target);
3368 writeV3F1000(os, camera_point_target);
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::SendItemDef(con::Connection &con, u16 peer_id,
3378 IItemDefManager *itemdef)
3380 DSTACK(__FUNCTION_NAME);
3381 std::ostringstream os(std::ios_base::binary);
3385 u32 length of the next item
3386 zlib-compressed serialized ItemDefManager
3388 writeU16(os, TOCLIENT_ITEMDEF);
3389 std::ostringstream tmp_os(std::ios::binary);
3390 itemdef->serialize(tmp_os);
3391 std::ostringstream tmp_os2(std::ios::binary);
3392 compressZlib(tmp_os.str(), tmp_os2);
3393 os<<serializeLongString(tmp_os2.str());
3396 std::string s = os.str();
3397 verbosestream<<"Server: Sending item definitions to id("<<peer_id
3398 <<"): size="<<s.size()<<std::endl;
3399 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3401 con.Send(peer_id, 0, data, true);
3404 void Server::SendNodeDef(con::Connection &con, u16 peer_id,
3405 INodeDefManager *nodedef)
3407 DSTACK(__FUNCTION_NAME);
3408 std::ostringstream os(std::ios_base::binary);
3412 u32 length of the next item
3413 zlib-compressed serialized NodeDefManager
3415 writeU16(os, TOCLIENT_NODEDEF);
3416 std::ostringstream tmp_os(std::ios::binary);
3417 nodedef->serialize(tmp_os);
3418 std::ostringstream tmp_os2(std::ios::binary);
3419 compressZlib(tmp_os.str(), tmp_os2);
3420 os<<serializeLongString(tmp_os2.str());
3423 std::string s = os.str();
3424 verbosestream<<"Server: Sending node definitions to id("<<peer_id
3425 <<"): size="<<s.size()<<std::endl;
3426 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3428 con.Send(peer_id, 0, data, true);
3432 Non-static send methods
3435 void Server::SendInventory(u16 peer_id)
3437 DSTACK(__FUNCTION_NAME);
3439 PlayerSAO *playersao = getPlayerSAO(peer_id);
3442 playersao->m_inventory_not_sent = false;
3448 std::ostringstream os;
3449 playersao->getInventory()->serialize(os);
3451 std::string s = os.str();
3453 SharedBuffer<u8> data(s.size()+2);
3454 writeU16(&data[0], TOCLIENT_INVENTORY);
3455 memcpy(&data[2], s.c_str(), s.size());
3458 m_con.Send(peer_id, 0, data, true);
3461 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3463 DSTACK(__FUNCTION_NAME);
3465 std::ostringstream os(std::ios_base::binary);
3469 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3470 os.write((char*)buf, 2);
3473 writeU16(buf, message.size());
3474 os.write((char*)buf, 2);
3477 for(u32 i=0; i<message.size(); i++)
3481 os.write((char*)buf, 2);
3485 std::string s = os.str();
3486 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3488 m_con.Send(peer_id, 0, data, true);
3491 void Server::BroadcastChatMessage(const std::wstring &message)
3493 for(core::map<u16, RemoteClient*>::Iterator
3494 i = m_clients.getIterator();
3495 i.atEnd() == false; i++)
3497 // Get client and check that it is valid
3498 RemoteClient *client = i.getNode()->getValue();
3499 assert(client->peer_id == i.getNode()->getKey());
3500 if(client->serialization_version == SER_FMT_VER_INVALID)
3503 SendChatMessage(client->peer_id, message);
3507 void Server::SendPlayerHP(u16 peer_id)
3509 DSTACK(__FUNCTION_NAME);
3510 PlayerSAO *playersao = getPlayerSAO(peer_id);
3512 playersao->m_hp_not_sent = false;
3513 SendHP(m_con, peer_id, playersao->getHP());
3516 void Server::SendMovePlayer(u16 peer_id)
3518 DSTACK(__FUNCTION_NAME);
3519 Player *player = m_env->getPlayer(peer_id);
3522 std::ostringstream os(std::ios_base::binary);
3523 writeU16(os, TOCLIENT_MOVE_PLAYER);
3524 writeV3F1000(os, player->getPosition());
3525 writeF1000(os, player->getPitch());
3526 writeF1000(os, player->getYaw());
3529 v3f pos = player->getPosition();
3530 f32 pitch = player->getPitch();
3531 f32 yaw = player->getYaw();
3532 verbosestream<<"Server: Sending TOCLIENT_MOVE_PLAYER"
3533 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3540 std::string s = os.str();
3541 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3543 m_con.Send(peer_id, 0, data, true);
3546 void Server::SendPlayerPrivileges(u16 peer_id)
3548 Player *player = m_env->getPlayer(peer_id);
3550 std::set<std::string> privs;
3551 scriptapi_get_auth(m_lua, player->getName(), NULL, &privs);
3553 std::ostringstream os(std::ios_base::binary);
3554 writeU16(os, TOCLIENT_PRIVILEGES);
3555 writeU16(os, privs.size());
3556 for(std::set<std::string>::const_iterator i = privs.begin();
3557 i != privs.end(); i++){
3558 os<<serializeString(*i);
3562 std::string s = os.str();
3563 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3565 m_con.Send(peer_id, 0, data, true);
3568 s32 Server::playSound(const SimpleSoundSpec &spec,
3569 const ServerSoundParams ¶ms)
3571 // Find out initial position of sound
3572 bool pos_exists = false;
3573 v3f pos = params.getPos(m_env, &pos_exists);
3574 // If position is not found while it should be, cancel sound
3575 if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
3577 // Filter destination clients
3578 std::set<RemoteClient*> dst_clients;
3579 if(params.to_player != "")
3581 Player *player = m_env->getPlayer(params.to_player.c_str());
3583 infostream<<"Server::playSound: Player \""<<params.to_player
3584 <<"\" not found"<<std::endl;
3587 if(player->peer_id == PEER_ID_INEXISTENT){
3588 infostream<<"Server::playSound: Player \""<<params.to_player
3589 <<"\" not connected"<<std::endl;
3592 RemoteClient *client = getClient(player->peer_id);
3593 dst_clients.insert(client);
3597 for(core::map<u16, RemoteClient*>::Iterator
3598 i = m_clients.getIterator(); i.atEnd() == false; i++)
3600 RemoteClient *client = i.getNode()->getValue();
3601 Player *player = m_env->getPlayer(client->peer_id);
3605 if(player->getPosition().getDistanceFrom(pos) >
3606 params.max_hear_distance)
3609 dst_clients.insert(client);
3612 if(dst_clients.size() == 0)
3615 s32 id = m_next_sound_id++;
3616 // The sound will exist as a reference in m_playing_sounds
3617 m_playing_sounds[id] = ServerPlayingSound();
3618 ServerPlayingSound &psound = m_playing_sounds[id];
3619 psound.params = params;
3620 for(std::set<RemoteClient*>::iterator i = dst_clients.begin();
3621 i != dst_clients.end(); i++)
3622 psound.clients.insert((*i)->peer_id);
3624 std::ostringstream os(std::ios_base::binary);
3625 writeU16(os, TOCLIENT_PLAY_SOUND);
3627 os<<serializeString(spec.name);
3628 writeF1000(os, spec.gain * params.gain);
3629 writeU8(os, params.type);
3630 writeV3F1000(os, pos);
3631 writeU16(os, params.object);
3632 writeU8(os, params.loop);
3634 std::string s = os.str();
3635 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3637 for(std::set<RemoteClient*>::iterator i = dst_clients.begin();
3638 i != dst_clients.end(); i++){
3640 m_con.Send((*i)->peer_id, 0, data, true);
3644 void Server::stopSound(s32 handle)
3646 // Get sound reference
3647 std::map<s32, ServerPlayingSound>::iterator i =
3648 m_playing_sounds.find(handle);
3649 if(i == m_playing_sounds.end())
3651 ServerPlayingSound &psound = i->second;
3653 std::ostringstream os(std::ios_base::binary);
3654 writeU16(os, TOCLIENT_STOP_SOUND);
3655 writeS32(os, handle);
3657 std::string s = os.str();
3658 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3660 for(std::set<u16>::iterator i = psound.clients.begin();
3661 i != psound.clients.end(); i++){
3663 m_con.Send(*i, 0, data, true);
3665 // Remove sound reference
3666 m_playing_sounds.erase(i);
3669 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3670 core::list<u16> *far_players, float far_d_nodes)
3672 float maxd = far_d_nodes*BS;
3673 v3f p_f = intToFloat(p, BS);
3677 SharedBuffer<u8> reply(replysize);
3678 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3679 writeS16(&reply[2], p.X);
3680 writeS16(&reply[4], p.Y);
3681 writeS16(&reply[6], p.Z);
3683 for(core::map<u16, RemoteClient*>::Iterator
3684 i = m_clients.getIterator();
3685 i.atEnd() == false; i++)
3687 // Get client and check that it is valid
3688 RemoteClient *client = i.getNode()->getValue();
3689 assert(client->peer_id == i.getNode()->getKey());
3690 if(client->serialization_version == SER_FMT_VER_INVALID)
3693 // Don't send if it's the same one
3694 if(client->peer_id == ignore_id)
3700 Player *player = m_env->getPlayer(client->peer_id);
3703 // If player is far away, only set modified blocks not sent
3704 v3f player_pos = player->getPosition();
3705 if(player_pos.getDistanceFrom(p_f) > maxd)
3707 far_players->push_back(client->peer_id);
3714 m_con.Send(client->peer_id, 0, reply, true);
3718 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3719 core::list<u16> *far_players, float far_d_nodes)
3721 float maxd = far_d_nodes*BS;
3722 v3f p_f = intToFloat(p, BS);
3724 for(core::map<u16, RemoteClient*>::Iterator
3725 i = m_clients.getIterator();
3726 i.atEnd() == false; i++)
3728 // Get client and check that it is valid
3729 RemoteClient *client = i.getNode()->getValue();
3730 assert(client->peer_id == i.getNode()->getKey());
3731 if(client->serialization_version == SER_FMT_VER_INVALID)
3734 // Don't send if it's the same one
3735 if(client->peer_id == ignore_id)
3741 Player *player = m_env->getPlayer(client->peer_id);
3744 // If player is far away, only set modified blocks not sent
3745 v3f player_pos = player->getPosition();
3746 if(player_pos.getDistanceFrom(p_f) > maxd)
3748 far_players->push_back(client->peer_id);
3755 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3756 SharedBuffer<u8> reply(replysize);
3757 writeU16(&reply[0], TOCLIENT_ADDNODE);
3758 writeS16(&reply[2], p.X);
3759 writeS16(&reply[4], p.Y);
3760 writeS16(&reply[6], p.Z);
3761 n.serialize(&reply[8], client->serialization_version);
3764 m_con.Send(client->peer_id, 0, reply, true);
3768 void Server::setBlockNotSent(v3s16 p)
3770 for(core::map<u16, RemoteClient*>::Iterator
3771 i = m_clients.getIterator();
3772 i.atEnd()==false; i++)
3774 RemoteClient *client = i.getNode()->getValue();
3775 client->SetBlockNotSent(p);
3779 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3781 DSTACK(__FUNCTION_NAME);
3783 v3s16 p = block->getPos();
3787 bool completely_air = true;
3788 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3789 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3790 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3792 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
3794 completely_air = false;
3795 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
3800 infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
3802 infostream<<"[completely air] ";
3803 infostream<<std::endl;
3807 Create a packet with the block in the right format
3810 std::ostringstream os(std::ios_base::binary);
3811 block->serialize(os, ver, false);
3812 std::string s = os.str();
3813 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3815 u32 replysize = 8 + blockdata.getSize();
3816 SharedBuffer<u8> reply(replysize);
3817 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3818 writeS16(&reply[2], p.X);
3819 writeS16(&reply[4], p.Y);
3820 writeS16(&reply[6], p.Z);
3821 memcpy(&reply[8], *blockdata, blockdata.getSize());
3823 /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3824 <<": \tpacket size: "<<replysize<<std::endl;*/
3829 m_con.Send(peer_id, 1, reply, true);
3832 void Server::SendBlocks(float dtime)
3834 DSTACK(__FUNCTION_NAME);
3836 JMutexAutoLock envlock(m_env_mutex);
3837 JMutexAutoLock conlock(m_con_mutex);
3839 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
3841 core::array<PrioritySortedBlockTransfer> queue;
3843 s32 total_sending = 0;
3846 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
3848 for(core::map<u16, RemoteClient*>::Iterator
3849 i = m_clients.getIterator();
3850 i.atEnd() == false; i++)
3852 RemoteClient *client = i.getNode()->getValue();
3853 assert(client->peer_id == i.getNode()->getKey());
3855 // If definitions and textures have not been sent, don't
3856 // send MapBlocks either
3857 if(!client->definitions_sent)
3860 total_sending += client->SendingCount();
3862 if(client->serialization_version == SER_FMT_VER_INVALID)
3865 client->GetNextBlocks(this, dtime, queue);
3870 // Lowest priority number comes first.
3871 // Lowest is most important.
3874 for(u32 i=0; i<queue.size(); i++)
3876 //TODO: Calculate limit dynamically
3877 if(total_sending >= g_settings->getS32
3878 ("max_simultaneous_block_sends_server_total"))
3881 PrioritySortedBlockTransfer q = queue[i];
3883 MapBlock *block = NULL;
3886 block = m_env->getMap().getBlockNoCreate(q.pos);
3888 catch(InvalidPositionException &e)
3893 RemoteClient *client = getClient(q.peer_id);
3895 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3897 client->SentBlock(q.pos);
3903 void Server::fillMediaCache()
3905 DSTACK(__FUNCTION_NAME);
3907 infostream<<"Server: Calculating media file checksums"<<std::endl;
3909 // Collect all media file paths
3910 std::list<std::string> paths;
3911 for(core::list<ModSpec>::Iterator i = m_mods.begin();
3912 i != m_mods.end(); i++){
3913 const ModSpec &mod = *i;
3914 paths.push_back(mod.path + DIR_DELIM + "textures");
3915 paths.push_back(mod.path + DIR_DELIM + "sounds");
3916 paths.push_back(mod.path + DIR_DELIM + "media");
3919 // Collect media file information from paths into cache
3920 for(std::list<std::string>::iterator i = paths.begin();
3921 i != paths.end(); i++)
3923 std::string mediapath = *i;
3924 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
3925 for(u32 j=0; j<dirlist.size(); j++){
3926 if(dirlist[j].dir) // Ignode dirs
3928 std::string filename = dirlist[j].name;
3929 // If name contains illegal characters, ignore the file
3930 if(!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)){
3931 infostream<<"Server: ignoring illegal file name: \""
3932 <<filename<<"\""<<std::endl;
3935 // If name is not in a supported format, ignore it
3936 const char *supported_ext[] = {
3937 ".png", ".jpg", ".bmp", ".tga",
3938 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
3942 if(removeStringEnd(filename, supported_ext) == ""){
3943 infostream<<"Server: ignoring unsupported file extension: \""
3944 <<filename<<"\""<<std::endl;
3947 // Ok, attempt to load the file and add to cache
3948 std::string filepath = mediapath + DIR_DELIM + filename;
3950 std::ifstream fis(filepath.c_str(), std::ios_base::binary);
3951 if(fis.good() == false){
3952 errorstream<<"Server::fillMediaCache(): Could not open \""
3953 <<filename<<"\" for reading"<<std::endl;
3956 std::ostringstream tmp_os(std::ios_base::binary);
3960 fis.read(buf, 1024);
3961 std::streamsize len = fis.gcount();
3962 tmp_os.write(buf, len);
3971 errorstream<<"Server::fillMediaCache(): Failed to read \""
3972 <<filename<<"\""<<std::endl;
3975 if(tmp_os.str().length() == 0){
3976 errorstream<<"Server::fillMediaCache(): Empty file \""
3977 <<filepath<<"\""<<std::endl;
3982 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
3984 unsigned char *digest = sha1.getDigest();
3985 std::string sha1_base64 = base64_encode(digest, 20);
3986 std::string sha1_hex = hex_encode((char*)digest, 20);
3990 this->m_media[filename] = MediaInfo(filepath, sha1_base64);
3991 verbosestream<<"Server: "<<sha1_hex<<" is "<<filename<<std::endl;
3996 struct SendableMediaAnnouncement
3999 std::string sha1_digest;
4001 SendableMediaAnnouncement(const std::string name_="",
4002 const std::string sha1_digest_=""):
4004 sha1_digest(sha1_digest_)
4008 void Server::sendMediaAnnouncement(u16 peer_id)
4010 DSTACK(__FUNCTION_NAME);
4012 verbosestream<<"Server: Announcing files to id("<<peer_id<<")"
4015 core::list<SendableMediaAnnouncement> file_announcements;
4017 for(std::map<std::string, MediaInfo>::iterator i = m_media.begin();
4018 i != m_media.end(); i++){
4020 file_announcements.push_back(
4021 SendableMediaAnnouncement(i->first, i->second.sha1_digest));
4025 std::ostringstream os(std::ios_base::binary);
4033 u16 length of sha1_digest
4038 writeU16(os, TOCLIENT_ANNOUNCE_MEDIA);
4039 writeU16(os, file_announcements.size());
4041 for(core::list<SendableMediaAnnouncement>::Iterator
4042 j = file_announcements.begin();
4043 j != file_announcements.end(); j++){
4044 os<<serializeString(j->name);
4045 os<<serializeString(j->sha1_digest);
4049 std::string s = os.str();
4050 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4053 m_con.Send(peer_id, 0, data, true);
4057 struct SendableMedia
4063 SendableMedia(const std::string &name_="", const std::string path_="",
4064 const std::string &data_=""):
4071 void Server::sendRequestedMedia(u16 peer_id,
4072 const core::list<MediaRequest> &tosend)
4074 DSTACK(__FUNCTION_NAME);
4076 verbosestream<<"Server::sendRequestedMedia(): "
4077 <<"Sending files to client"<<std::endl;
4081 // Put 5kB in one bunch (this is not accurate)
4082 u32 bytes_per_bunch = 5000;
4084 core::array< core::list<SendableMedia> > file_bunches;
4085 file_bunches.push_back(core::list<SendableMedia>());
4087 u32 file_size_bunch_total = 0;
4089 for(core::list<MediaRequest>::ConstIterator i = tosend.begin();
4090 i != tosend.end(); i++)
4092 if(m_media.find(i->name) == m_media.end()){
4093 errorstream<<"Server::sendRequestedMedia(): Client asked for "
4094 <<"unknown file \""<<(i->name)<<"\""<<std::endl;
4098 //TODO get path + name
4099 std::string tpath = m_media[(*i).name].path;
4102 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
4103 if(fis.good() == false){
4104 errorstream<<"Server::sendRequestedMedia(): Could not open \""
4105 <<tpath<<"\" for reading"<<std::endl;
4108 std::ostringstream tmp_os(std::ios_base::binary);
4112 fis.read(buf, 1024);
4113 std::streamsize len = fis.gcount();
4114 tmp_os.write(buf, len);
4115 file_size_bunch_total += len;
4124 errorstream<<"Server::sendRequestedMedia(): Failed to read \""
4125 <<(*i).name<<"\""<<std::endl;
4128 /*infostream<<"Server::sendRequestedMedia(): Loaded \""
4129 <<tname<<"\""<<std::endl;*/
4131 file_bunches[file_bunches.size()-1].push_back(
4132 SendableMedia((*i).name, tpath, tmp_os.str()));
4134 // Start next bunch if got enough data
4135 if(file_size_bunch_total >= bytes_per_bunch){
4136 file_bunches.push_back(core::list<SendableMedia>());
4137 file_size_bunch_total = 0;
4142 /* Create and send packets */
4144 u32 num_bunches = file_bunches.size();
4145 for(u32 i=0; i<num_bunches; i++)
4147 std::ostringstream os(std::ios_base::binary);
4151 u16 total number of texture bunches
4152 u16 index of this bunch
4153 u32 number of files in this bunch
4162 writeU16(os, TOCLIENT_MEDIA);
4163 writeU16(os, num_bunches);
4165 writeU32(os, file_bunches[i].size());
4167 for(core::list<SendableMedia>::Iterator
4168 j = file_bunches[i].begin();
4169 j != file_bunches[i].end(); j++){
4170 os<<serializeString(j->name);
4171 os<<serializeLongString(j->data);
4175 std::string s = os.str();
4176 verbosestream<<"Server::sendRequestedMedia(): bunch "
4177 <<i<<"/"<<num_bunches
4178 <<" files="<<file_bunches[i].size()
4179 <<" size=" <<s.size()<<std::endl;
4180 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4182 m_con.Send(peer_id, 0, data, true);
4190 void Server::DiePlayer(u16 peer_id)
4192 DSTACK(__FUNCTION_NAME);
4194 PlayerSAO *playersao = getPlayerSAO(peer_id);
4197 infostream<<"Server::DiePlayer(): Player "
4198 <<playersao->getPlayer()->getName()
4199 <<" dies"<<std::endl;
4201 playersao->setHP(0);
4203 // Trigger scripted stuff
4204 scriptapi_on_dieplayer(m_lua, playersao);
4206 SendPlayerHP(peer_id);
4207 SendDeathscreen(m_con, peer_id, false, v3f(0,0,0));
4210 void Server::RespawnPlayer(u16 peer_id)
4212 DSTACK(__FUNCTION_NAME);
4214 PlayerSAO *playersao = getPlayerSAO(peer_id);
4217 infostream<<"Server::RespawnPlayer(): Player "
4218 <<playersao->getPlayer()->getName()
4219 <<" respawns"<<std::endl;
4221 playersao->setHP(PLAYER_MAX_HP);
4223 bool repositioned = scriptapi_on_respawnplayer(m_lua, playersao);
4225 v3f pos = findSpawnPos(m_env->getServerMap());
4226 playersao->setPos(pos);
4230 void Server::UpdateCrafting(u16 peer_id)
4232 DSTACK(__FUNCTION_NAME);
4234 Player* player = m_env->getPlayer(peer_id);
4237 // Get a preview for crafting
4239 // No crafting in creative mode
4240 if(g_settings->getBool("creative_mode") == false)
4241 getCraftingResult(&player->inventory, preview, false, this);
4243 // Put the new preview in
4244 InventoryList *plist = player->inventory.getList("craftpreview");
4246 assert(plist->getSize() >= 1);
4247 plist->changeItem(0, preview);
4250 RemoteClient* Server::getClient(u16 peer_id)
4252 DSTACK(__FUNCTION_NAME);
4253 //JMutexAutoLock lock(m_con_mutex);
4254 core::map<u16, RemoteClient*>::Node *n;
4255 n = m_clients.find(peer_id);
4256 // A client should exist for all peers
4258 return n->getValue();
4261 std::wstring Server::getStatusString()
4263 std::wostringstream os(std::ios_base::binary);
4266 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4268 os<<L", uptime="<<m_uptime.get();
4269 // Information about clients
4271 for(core::map<u16, RemoteClient*>::Iterator
4272 i = m_clients.getIterator();
4273 i.atEnd() == false; i++)
4275 // Get client and check that it is valid
4276 RemoteClient *client = i.getNode()->getValue();
4277 assert(client->peer_id == i.getNode()->getKey());
4278 if(client->serialization_version == SER_FMT_VER_INVALID)
4281 Player *player = m_env->getPlayer(client->peer_id);
4282 // Get name of player
4283 std::wstring name = L"unknown";
4285 name = narrow_to_wide(player->getName());
4286 // Add name to information string
4290 if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
4291 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4292 if(g_settings->get("motd") != "")
4293 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4297 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
4299 std::set<std::string> privs;
4300 scriptapi_get_auth(m_lua, name, NULL, &privs);
4304 bool Server::checkPriv(const std::string &name, const std::string &priv)
4306 std::set<std::string> privs = getPlayerEffectivePrivs(name);
4307 return (privs.count(priv) != 0);
4310 void Server::reportPrivsModified(const std::string &name)
4313 for(core::map<u16, RemoteClient*>::Iterator
4314 i = m_clients.getIterator();
4315 i.atEnd() == false; i++){
4316 RemoteClient *client = i.getNode()->getValue();
4317 Player *player = m_env->getPlayer(client->peer_id);
4318 reportPrivsModified(player->getName());
4321 Player *player = m_env->getPlayer(name.c_str());
4324 SendPlayerPrivileges(player->peer_id);
4325 player->getPlayerSAO()->updatePrivileges(
4326 getPlayerEffectivePrivs(name),
4331 // Saves g_settings to configpath given at initialization
4332 void Server::saveConfig()
4334 if(m_path_config != "")
4335 g_settings->updateConfigFile(m_path_config.c_str());
4338 void Server::notifyPlayer(const char *name, const std::wstring msg)
4340 Player *player = m_env->getPlayer(name);
4343 SendChatMessage(player->peer_id, std::wstring(L"Server: -!- ")+msg);
4346 void Server::notifyPlayers(const std::wstring msg)
4348 BroadcastChatMessage(msg);
4351 void Server::queueBlockEmerge(v3s16 blockpos, bool allow_generate)
4355 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
4356 m_emerge_queue.addBlock(PEER_ID_INEXISTENT, blockpos, flags);
4359 // IGameDef interface
4361 IItemDefManager* Server::getItemDefManager()
4365 INodeDefManager* Server::getNodeDefManager()
4369 ICraftDefManager* Server::getCraftDefManager()
4373 ITextureSource* Server::getTextureSource()
4377 u16 Server::allocateUnknownNodeId(const std::string &name)
4379 return m_nodedef->allocateDummy(name);
4381 ISoundManager* Server::getSoundManager()
4383 return &dummySoundManager;
4385 MtEventManager* Server::getEventManager()
4390 IWritableItemDefManager* Server::getWritableItemDefManager()
4394 IWritableNodeDefManager* Server::getWritableNodeDefManager()
4398 IWritableCraftDefManager* Server::getWritableCraftDefManager()
4403 const ModSpec* Server::getModSpec(const std::string &modname)
4405 for(core::list<ModSpec>::Iterator i = m_mods.begin();
4406 i != m_mods.end(); i++){
4407 const ModSpec &mod = *i;
4408 if(mod.name == modname)
4413 std::string Server::getBuiltinLuaPath()
4415 return porting::path_share + DIR_DELIM + "builtin";
4418 v3f findSpawnPos(ServerMap &map)
4420 //return v3f(50,50,50)*BS;
4425 nodepos = v2s16(0,0);
4430 // Try to find a good place a few times
4431 for(s32 i=0; i<1000; i++)
4434 // We're going to try to throw the player to this position
4435 v2s16 nodepos2d = v2s16(-range + (myrand()%(range*2)),
4436 -range + (myrand()%(range*2)));
4437 //v2s16 sectorpos = getNodeSectorPos(nodepos2d);
4438 // Get ground height at point (fallbacks to heightmap function)
4439 s16 groundheight = map.findGroundLevel(nodepos2d);
4440 // Don't go underwater
4441 if(groundheight < WATER_LEVEL)
4443 //infostream<<"-> Underwater"<<std::endl;
4446 // Don't go to high places
4447 if(groundheight > WATER_LEVEL + 4)
4449 //infostream<<"-> Underwater"<<std::endl;
4453 nodepos = v3s16(nodepos2d.X, groundheight-2, nodepos2d.Y);
4454 bool is_good = false;
4456 for(s32 i=0; i<10; i++){
4457 v3s16 blockpos = getNodeBlockPos(nodepos);
4458 map.emergeBlock(blockpos, true);
4459 MapNode n = map.getNodeNoEx(nodepos);
4460 if(n.getContent() == CONTENT_AIR){
4471 // Found a good place
4472 //infostream<<"Searched through "<<i<<" places."<<std::endl;
4478 return intToFloat(nodepos, BS);
4481 PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id)
4483 RemotePlayer *player = NULL;
4484 bool newplayer = false;
4487 Try to get an existing player
4489 player = static_cast<RemotePlayer*>(m_env->getPlayer(name));
4491 // If player is already connected, cancel
4492 if(player != NULL && player->peer_id != 0)
4494 infostream<<"emergePlayer(): Player already connected"<<std::endl;
4499 If player with the wanted peer_id already exists, cancel.
4501 if(m_env->getPlayer(peer_id) != NULL)
4503 infostream<<"emergePlayer(): Player with wrong name but same"
4504 " peer_id already exists"<<std::endl;
4509 Create a new player if it doesn't exist yet
4514 player = new RemotePlayer(this);
4515 player->updateName(name);
4517 /* Set player position */
4518 infostream<<"Server: Finding spawn place for player \""
4519 <<name<<"\""<<std::endl;
4520 v3f pos = findSpawnPos(m_env->getServerMap());
4521 player->setPosition(pos);
4523 /* Add player to environment */
4524 m_env->addPlayer(player);
4528 Create a new player active object
4530 PlayerSAO *playersao = new PlayerSAO(m_env, player, peer_id,
4531 getPlayerEffectivePrivs(player->getName()),
4534 /* Add object to environment */
4535 m_env->addActiveObject(playersao);
4539 scriptapi_on_newplayer(m_lua, playersao);
4541 scriptapi_on_joinplayer(m_lua, playersao);
4544 if(g_settings->getBool("creative_mode"))
4545 playersao->createCreativeInventory();
4550 void Server::handlePeerChange(PeerChange &c)
4552 JMutexAutoLock envlock(m_env_mutex);
4553 JMutexAutoLock conlock(m_con_mutex);
4555 if(c.type == PEER_ADDED)
4562 core::map<u16, RemoteClient*>::Node *n;
4563 n = m_clients.find(c.peer_id);
4564 // The client shouldn't already exist
4568 RemoteClient *client = new RemoteClient();
4569 client->peer_id = c.peer_id;
4570 m_clients.insert(client->peer_id, client);
4573 else if(c.type == PEER_REMOVED)
4580 core::map<u16, RemoteClient*>::Node *n;
4581 n = m_clients.find(c.peer_id);
4582 // The client should exist
4586 Mark objects to be not known by the client
4588 RemoteClient *client = n->getValue();
4590 for(core::map<u16, bool>::Iterator
4591 i = client->m_known_objects.getIterator();
4592 i.atEnd()==false; i++)
4595 u16 id = i.getNode()->getKey();
4596 ServerActiveObject* obj = m_env->getActiveObject(id);
4598 if(obj && obj->m_known_by_count > 0)
4599 obj->m_known_by_count--;
4603 Clear references to playing sounds
4605 for(std::map<s32, ServerPlayingSound>::iterator
4606 i = m_playing_sounds.begin();
4607 i != m_playing_sounds.end();)
4609 ServerPlayingSound &psound = i->second;
4610 psound.clients.erase(c.peer_id);
4611 if(psound.clients.size() == 0)
4612 m_playing_sounds.erase(i++);
4617 Player *player = m_env->getPlayer(c.peer_id);
4619 // Collect information about leaving in chat
4620 std::wstring message;
4624 std::wstring name = narrow_to_wide(player->getName());
4627 message += L" left game";
4629 message += L" (timed out)";
4633 /* Run scripts and remove from environment */
4637 PlayerSAO *playersao = player->getPlayerSAO();
4640 scriptapi_on_leaveplayer(m_lua, playersao);
4642 playersao->disconnected();
4652 std::ostringstream os(std::ios_base::binary);
4653 for(core::map<u16, RemoteClient*>::Iterator
4654 i = m_clients.getIterator();
4655 i.atEnd() == false; i++)
4657 RemoteClient *client = i.getNode()->getValue();
4658 assert(client->peer_id == i.getNode()->getKey());
4659 if(client->serialization_version == SER_FMT_VER_INVALID)
4662 Player *player = m_env->getPlayer(client->peer_id);
4665 // Get name of player
4666 os<<player->getName()<<" ";
4669 actionstream<<player->getName()<<" "
4670 <<(c.timeout?"times out.":"leaves game.")
4671 <<" List of players: "
4672 <<os.str()<<std::endl;
4677 delete m_clients[c.peer_id];
4678 m_clients.remove(c.peer_id);
4680 // Send player info to all remaining clients
4681 //SendPlayerInfos();
4683 // Send leave chat message to all remaining clients
4684 if(message.length() != 0)
4685 BroadcastChatMessage(message);
4694 void Server::handlePeerChanges()
4696 while(m_peer_change_queue.size() > 0)
4698 PeerChange c = m_peer_change_queue.pop_front();
4700 verbosestream<<"Server: Handling peer change: "
4701 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4704 handlePeerChange(c);
4708 void dedicated_server_loop(Server &server, bool &kill)
4710 DSTACK(__FUNCTION_NAME);
4712 verbosestream<<"dedicated_server_loop()"<<std::endl;
4714 IntervalLimiter m_profiler_interval;
4718 float steplen = g_settings->getFloat("dedicated_server_step");
4719 // This is kind of a hack but can be done like this
4720 // because server.step() is very light
4722 ScopeProfiler sp(g_profiler, "dedicated server sleep");
4723 sleep_ms((int)(steplen*1000.0));
4725 server.step(steplen);
4727 if(server.getShutdownRequested() || kill)
4729 infostream<<"Dedicated server quitting"<<std::endl;
4736 float profiler_print_interval =
4737 g_settings->getFloat("profiler_print_interval");
4738 if(profiler_print_interval != 0)
4740 if(m_profiler_interval.step(steplen, profiler_print_interval))
4742 infostream<<"Profiler:"<<std::endl;
4743 g_profiler->print(infostream);
4744 g_profiler->clear();