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 = porting::path_share + DIR_DELIM + "builtin"
968 + DIR_DELIM + "builtin.lua";
970 // Create world if it doesn't exist
971 if(!initializeWorld(m_path_world, m_gamespec.id))
972 throw ServerError("Failed to initialize world");
975 JMutexAutoLock envlock(m_env_mutex);
976 JMutexAutoLock conlock(m_con_mutex);
978 // Initialize scripting
980 infostream<<"Server: Initializing Lua"<<std::endl;
981 m_lua = script_init();
984 scriptapi_export(m_lua, this);
985 // Load and run builtin.lua
986 infostream<<"Server: Loading builtin.lua [\""
987 <<builtinpath<<"\"]"<<std::endl;
988 bool success = scriptapi_loadmod(m_lua, builtinpath, "__builtin");
990 errorstream<<"Server: Failed to load and run "
991 <<builtinpath<<std::endl;
992 throw ModError("Failed to load and run "+builtinpath);
994 // Find mods in mod search paths
995 m_mods = getMods(m_modspaths);
997 infostream<<"Server: Loading mods: ";
998 for(core::list<ModSpec>::Iterator i = m_mods.begin();
999 i != m_mods.end(); i++){
1000 const ModSpec &mod = *i;
1001 infostream<<mod.name<<" ";
1003 infostream<<std::endl;
1004 // Load and run "mod" scripts
1005 for(core::list<ModSpec>::Iterator i = m_mods.begin();
1006 i != m_mods.end(); i++){
1007 const ModSpec &mod = *i;
1008 std::string scriptpath = mod.path + DIR_DELIM + "init.lua";
1009 infostream<<" ["<<padStringRight(mod.name, 12)<<"] [\""
1010 <<scriptpath<<"\"]"<<std::endl;
1011 bool success = scriptapi_loadmod(m_lua, scriptpath, mod.name);
1013 errorstream<<"Server: Failed to load and run "
1014 <<scriptpath<<std::endl;
1015 throw ModError("Failed to load and run "+scriptpath);
1019 // Read Textures and calculate sha1 sums
1022 // Apply item aliases in the node definition manager
1023 m_nodedef->updateAliases(m_itemdef);
1025 // Initialize Environment
1027 m_env = new ServerEnvironment(new ServerMap(path_world, this), m_lua,
1030 // Give environment reference to scripting api
1031 scriptapi_add_environment(m_lua, m_env);
1033 // Register us to receive map edit events
1034 m_env->getMap().addEventReceiver(this);
1036 // If file exists, load environment metadata
1037 if(fs::PathExists(m_path_world+DIR_DELIM+"env_meta.txt"))
1039 infostream<<"Server: Loading environment metadata"<<std::endl;
1040 m_env->loadMeta(m_path_world);
1044 infostream<<"Server: Loading players"<<std::endl;
1045 m_env->deSerializePlayers(m_path_world);
1048 Add some test ActiveBlockModifiers to environment
1050 add_legacy_abms(m_env, m_nodedef);
1055 infostream<<"Server destructing"<<std::endl;
1058 Send shutdown message
1061 JMutexAutoLock conlock(m_con_mutex);
1063 std::wstring line = L"*** Server shutting down";
1066 Send the message to clients
1068 for(core::map<u16, RemoteClient*>::Iterator
1069 i = m_clients.getIterator();
1070 i.atEnd() == false; i++)
1072 // Get client and check that it is valid
1073 RemoteClient *client = i.getNode()->getValue();
1074 assert(client->peer_id == i.getNode()->getKey());
1075 if(client->serialization_version == SER_FMT_VER_INVALID)
1079 SendChatMessage(client->peer_id, line);
1081 catch(con::PeerNotFoundException &e)
1087 JMutexAutoLock envlock(m_env_mutex);
1092 infostream<<"Server: Saving players"<<std::endl;
1093 m_env->serializePlayers(m_path_world);
1096 Save environment metadata
1098 infostream<<"Server: Saving environment metadata"<<std::endl;
1099 m_env->saveMeta(m_path_world);
1111 JMutexAutoLock clientslock(m_con_mutex);
1113 for(core::map<u16, RemoteClient*>::Iterator
1114 i = m_clients.getIterator();
1115 i.atEnd() == false; i++)
1118 // NOTE: These are removed by env destructor
1120 u16 peer_id = i.getNode()->getKey();
1121 JMutexAutoLock envlock(m_env_mutex);
1122 m_env->removePlayer(peer_id);
1126 delete i.getNode()->getValue();
1130 // Delete things in the reverse order of creation
1137 // Deinitialize scripting
1138 infostream<<"Server: Deinitializing scripting"<<std::endl;
1139 script_deinit(m_lua);
1142 void Server::start(unsigned short port)
1144 DSTACK(__FUNCTION_NAME);
1145 infostream<<"Starting server on port "<<port<<"..."<<std::endl;
1147 // Stop thread if already running
1150 // Initialize connection
1151 m_con.SetTimeoutMs(30);
1155 m_thread.setRun(true);
1158 // ASCII art for the win!
1160 <<" .__ __ __ "<<std::endl
1161 <<" _____ |__| ____ _____/ |_ ____ _______/ |_ "<<std::endl
1162 <<" / \\| |/ \\_/ __ \\ __\\/ __ \\ / ___/\\ __\\"<<std::endl
1163 <<"| Y Y \\ | | \\ ___/| | \\ ___/ \\___ \\ | | "<<std::endl
1164 <<"|__|_| /__|___| /\\___ >__| \\___ >____ > |__| "<<std::endl
1165 <<" \\/ \\/ \\/ \\/ \\/ "<<std::endl;
1166 actionstream<<"World at ["<<m_path_world<<"]"<<std::endl;
1167 actionstream<<"Server for gameid=\""<<m_gamespec.id
1168 <<"\" listening on port "<<port<<"."<<std::endl;
1173 DSTACK(__FUNCTION_NAME);
1175 infostream<<"Server: Stopping and waiting threads"<<std::endl;
1177 // Stop threads (set run=false first so both start stopping)
1178 m_thread.setRun(false);
1179 m_emergethread.setRun(false);
1181 m_emergethread.stop();
1183 infostream<<"Server: Threads stopped"<<std::endl;
1186 void Server::step(float dtime)
1188 DSTACK(__FUNCTION_NAME);
1193 JMutexAutoLock lock(m_step_dtime_mutex);
1194 m_step_dtime += dtime;
1196 // Throw if fatal error occurred in thread
1197 std::string async_err = m_async_fatal_error.get();
1198 if(async_err != ""){
1199 throw ServerError(async_err);
1203 void Server::AsyncRunStep()
1205 DSTACK(__FUNCTION_NAME);
1207 g_profiler->add("Server::AsyncRunStep (num)", 1);
1211 JMutexAutoLock lock1(m_step_dtime_mutex);
1212 dtime = m_step_dtime;
1216 // Send blocks to clients
1223 g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
1225 //infostream<<"Server steps "<<dtime<<std::endl;
1226 //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1229 JMutexAutoLock lock1(m_step_dtime_mutex);
1230 m_step_dtime -= dtime;
1237 m_uptime.set(m_uptime.get() + dtime);
1241 // Process connection's timeouts
1242 JMutexAutoLock lock2(m_con_mutex);
1243 ScopeProfiler sp(g_profiler, "Server: connection timeout processing");
1244 m_con.RunTimeouts(dtime);
1248 // This has to be called so that the client list gets synced
1249 // with the peer list of the connection
1250 handlePeerChanges();
1254 Update time of day and overall game time
1257 JMutexAutoLock envlock(m_env_mutex);
1259 m_env->setTimeOfDaySpeed(g_settings->getFloat("time_speed"));
1262 Send to clients at constant intervals
1265 m_time_of_day_send_timer -= dtime;
1266 if(m_time_of_day_send_timer < 0.0)
1268 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
1270 //JMutexAutoLock envlock(m_env_mutex);
1271 JMutexAutoLock conlock(m_con_mutex);
1273 for(core::map<u16, RemoteClient*>::Iterator
1274 i = m_clients.getIterator();
1275 i.atEnd() == false; i++)
1277 RemoteClient *client = i.getNode()->getValue();
1278 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1279 m_env->getTimeOfDay(), g_settings->getFloat("time_speed"));
1281 m_con.Send(client->peer_id, 0, data, true);
1287 JMutexAutoLock lock(m_env_mutex);
1289 ScopeProfiler sp(g_profiler, "SEnv step");
1290 ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
1294 const float map_timer_and_unload_dtime = 2.92;
1295 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
1297 JMutexAutoLock lock(m_env_mutex);
1298 // Run Map's timers and unload unused data
1299 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
1300 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
1301 g_settings->getFloat("server_unload_unused_data_timeout"));
1312 JMutexAutoLock lock(m_env_mutex);
1313 JMutexAutoLock lock2(m_con_mutex);
1315 ScopeProfiler sp(g_profiler, "Server: handle players");
1317 for(core::map<u16, RemoteClient*>::Iterator
1318 i = m_clients.getIterator();
1319 i.atEnd() == false; i++)
1321 RemoteClient *client = i.getNode()->getValue();
1322 PlayerSAO *playersao = getPlayerSAO(client->peer_id);
1323 if(playersao == NULL)
1327 Handle player HPs (die if hp=0)
1329 if(playersao->getHP() == 0 && playersao->m_hp_not_sent)
1330 DiePlayer(client->peer_id);
1333 Send player inventories and HPs if necessary
1335 if(playersao->m_teleported){
1336 SendMovePlayer(client->peer_id);
1337 playersao->m_teleported = false;
1339 if(playersao->m_inventory_not_sent){
1340 UpdateCrafting(client->peer_id);
1341 SendInventory(client->peer_id);
1343 if(playersao->m_hp_not_sent){
1344 SendPlayerHP(client->peer_id);
1349 /* Transform liquids */
1350 m_liquid_transform_timer += dtime;
1351 if(m_liquid_transform_timer >= 1.00)
1353 m_liquid_transform_timer -= 1.00;
1355 JMutexAutoLock lock(m_env_mutex);
1357 ScopeProfiler sp(g_profiler, "Server: liquid transform");
1359 core::map<v3s16, MapBlock*> modified_blocks;
1360 m_env->getMap().transformLiquids(modified_blocks);
1365 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1366 ServerMap &map = ((ServerMap&)m_env->getMap());
1367 map.updateLighting(modified_blocks, lighting_modified_blocks);
1369 // Add blocks modified by lighting to modified_blocks
1370 for(core::map<v3s16, MapBlock*>::Iterator
1371 i = lighting_modified_blocks.getIterator();
1372 i.atEnd() == false; i++)
1374 MapBlock *block = i.getNode()->getValue();
1375 modified_blocks.insert(block->getPos(), block);
1379 Set the modified blocks unsent for all the clients
1382 JMutexAutoLock lock2(m_con_mutex);
1384 for(core::map<u16, RemoteClient*>::Iterator
1385 i = m_clients.getIterator();
1386 i.atEnd() == false; i++)
1388 RemoteClient *client = i.getNode()->getValue();
1390 if(modified_blocks.size() > 0)
1392 // Remove block from sent history
1393 client->SetBlocksNotSent(modified_blocks);
1398 // Periodically print some info
1400 float &counter = m_print_info_timer;
1406 JMutexAutoLock lock2(m_con_mutex);
1408 if(m_clients.size() != 0)
1409 infostream<<"Players:"<<std::endl;
1410 for(core::map<u16, RemoteClient*>::Iterator
1411 i = m_clients.getIterator();
1412 i.atEnd() == false; i++)
1414 //u16 peer_id = i.getNode()->getKey();
1415 RemoteClient *client = i.getNode()->getValue();
1416 Player *player = m_env->getPlayer(client->peer_id);
1419 infostream<<"* "<<player->getName()<<"\t";
1420 client->PrintInfo(infostream);
1425 //if(g_settings->getBool("enable_experimental"))
1429 Check added and deleted active objects
1432 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
1433 JMutexAutoLock envlock(m_env_mutex);
1434 JMutexAutoLock conlock(m_con_mutex);
1436 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
1438 // Radius inside which objects are active
1439 s16 radius = g_settings->getS16("active_object_send_range_blocks");
1440 radius *= MAP_BLOCKSIZE;
1442 for(core::map<u16, RemoteClient*>::Iterator
1443 i = m_clients.getIterator();
1444 i.atEnd() == false; i++)
1446 RemoteClient *client = i.getNode()->getValue();
1448 // If definitions and textures have not been sent, don't
1449 // send objects either
1450 if(!client->definitions_sent)
1453 Player *player = m_env->getPlayer(client->peer_id);
1456 // This can happen if the client timeouts somehow
1457 /*infostream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
1459 <<" has no associated player"<<std::endl;*/
1462 v3s16 pos = floatToInt(player->getPosition(), BS);
1464 core::map<u16, bool> removed_objects;
1465 core::map<u16, bool> added_objects;
1466 m_env->getRemovedActiveObjects(pos, radius,
1467 client->m_known_objects, removed_objects);
1468 m_env->getAddedActiveObjects(pos, radius,
1469 client->m_known_objects, added_objects);
1471 // Ignore if nothing happened
1472 if(removed_objects.size() == 0 && added_objects.size() == 0)
1474 //infostream<<"active objects: none changed"<<std::endl;
1478 std::string data_buffer;
1482 // Handle removed objects
1483 writeU16((u8*)buf, removed_objects.size());
1484 data_buffer.append(buf, 2);
1485 for(core::map<u16, bool>::Iterator
1486 i = removed_objects.getIterator();
1487 i.atEnd()==false; i++)
1490 u16 id = i.getNode()->getKey();
1491 ServerActiveObject* obj = m_env->getActiveObject(id);
1493 // Add to data buffer for sending
1494 writeU16((u8*)buf, i.getNode()->getKey());
1495 data_buffer.append(buf, 2);
1497 // Remove from known objects
1498 client->m_known_objects.remove(i.getNode()->getKey());
1500 if(obj && obj->m_known_by_count > 0)
1501 obj->m_known_by_count--;
1504 // Handle added objects
1505 writeU16((u8*)buf, added_objects.size());
1506 data_buffer.append(buf, 2);
1507 for(core::map<u16, bool>::Iterator
1508 i = added_objects.getIterator();
1509 i.atEnd()==false; i++)
1512 u16 id = i.getNode()->getKey();
1513 ServerActiveObject* obj = m_env->getActiveObject(id);
1516 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1518 infostream<<"WARNING: "<<__FUNCTION_NAME
1519 <<": NULL object"<<std::endl;
1521 type = obj->getSendType();
1523 // Add to data buffer for sending
1524 writeU16((u8*)buf, id);
1525 data_buffer.append(buf, 2);
1526 writeU8((u8*)buf, type);
1527 data_buffer.append(buf, 1);
1530 data_buffer.append(serializeLongString(
1531 obj->getClientInitializationData()));
1533 data_buffer.append(serializeLongString(""));
1535 // Add to known objects
1536 client->m_known_objects.insert(i.getNode()->getKey(), false);
1539 obj->m_known_by_count++;
1543 SharedBuffer<u8> reply(2 + data_buffer.size());
1544 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1545 memcpy((char*)&reply[2], data_buffer.c_str(),
1546 data_buffer.size());
1548 m_con.Send(client->peer_id, 0, reply, true);
1550 verbosestream<<"Server: Sent object remove/add: "
1551 <<removed_objects.size()<<" removed, "
1552 <<added_objects.size()<<" added, "
1553 <<"packet size is "<<reply.getSize()<<std::endl;
1558 Collect a list of all the objects known by the clients
1559 and report it back to the environment.
1562 core::map<u16, bool> all_known_objects;
1564 for(core::map<u16, RemoteClient*>::Iterator
1565 i = m_clients.getIterator();
1566 i.atEnd() == false; i++)
1568 RemoteClient *client = i.getNode()->getValue();
1569 // Go through all known objects of client
1570 for(core::map<u16, bool>::Iterator
1571 i = client->m_known_objects.getIterator();
1572 i.atEnd()==false; i++)
1574 u16 id = i.getNode()->getKey();
1575 all_known_objects[id] = true;
1579 m_env->setKnownActiveObjects(whatever);
1585 Send object messages
1588 JMutexAutoLock envlock(m_env_mutex);
1589 JMutexAutoLock conlock(m_con_mutex);
1591 ScopeProfiler sp(g_profiler, "Server: sending object messages");
1594 // Value = data sent by object
1595 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1597 // Get active object messages from environment
1600 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
1604 core::list<ActiveObjectMessage>* message_list = NULL;
1605 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1606 n = buffered_messages.find(aom.id);
1609 message_list = new core::list<ActiveObjectMessage>;
1610 buffered_messages.insert(aom.id, message_list);
1614 message_list = n->getValue();
1616 message_list->push_back(aom);
1619 // Route data to every client
1620 for(core::map<u16, RemoteClient*>::Iterator
1621 i = m_clients.getIterator();
1622 i.atEnd()==false; i++)
1624 RemoteClient *client = i.getNode()->getValue();
1625 std::string reliable_data;
1626 std::string unreliable_data;
1627 // Go through all objects in message buffer
1628 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1629 j = buffered_messages.getIterator();
1630 j.atEnd()==false; j++)
1632 // If object is not known by client, skip it
1633 u16 id = j.getNode()->getKey();
1634 if(client->m_known_objects.find(id) == NULL)
1636 // Get message list of object
1637 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1638 // Go through every message
1639 for(core::list<ActiveObjectMessage>::Iterator
1640 k = list->begin(); k != list->end(); k++)
1642 // Compose the full new data with header
1643 ActiveObjectMessage aom = *k;
1644 std::string new_data;
1647 writeU16((u8*)&buf[0], aom.id);
1648 new_data.append(buf, 2);
1650 new_data += serializeString(aom.datastring);
1651 // Add data to buffer
1653 reliable_data += new_data;
1655 unreliable_data += new_data;
1659 reliable_data and unreliable_data are now ready.
1662 if(reliable_data.size() > 0)
1664 SharedBuffer<u8> reply(2 + reliable_data.size());
1665 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1666 memcpy((char*)&reply[2], reliable_data.c_str(),
1667 reliable_data.size());
1669 m_con.Send(client->peer_id, 0, reply, true);
1671 if(unreliable_data.size() > 0)
1673 SharedBuffer<u8> reply(2 + unreliable_data.size());
1674 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1675 memcpy((char*)&reply[2], unreliable_data.c_str(),
1676 unreliable_data.size());
1677 // Send as unreliable
1678 m_con.Send(client->peer_id, 0, reply, false);
1681 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1683 infostream<<"Server: Size of object message data: "
1684 <<"reliable: "<<reliable_data.size()
1685 <<", unreliable: "<<unreliable_data.size()
1690 // Clear buffered_messages
1691 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1692 i = buffered_messages.getIterator();
1693 i.atEnd()==false; i++)
1695 delete i.getNode()->getValue();
1699 } // enable_experimental
1702 Send queued-for-sending map edit events.
1705 // We will be accessing the environment and the connection
1706 JMutexAutoLock lock(m_env_mutex);
1707 JMutexAutoLock conlock(m_con_mutex);
1709 // Don't send too many at a time
1712 // Single change sending is disabled if queue size is not small
1713 bool disable_single_change_sending = false;
1714 if(m_unsent_map_edit_queue.size() >= 4)
1715 disable_single_change_sending = true;
1717 int event_count = m_unsent_map_edit_queue.size();
1719 // We'll log the amount of each
1722 while(m_unsent_map_edit_queue.size() != 0)
1724 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1726 // Players far away from the change are stored here.
1727 // Instead of sending the changes, MapBlocks are set not sent
1729 core::list<u16> far_players;
1731 if(event->type == MEET_ADDNODE)
1733 //infostream<<"Server: MEET_ADDNODE"<<std::endl;
1734 prof.add("MEET_ADDNODE", 1);
1735 if(disable_single_change_sending)
1736 sendAddNode(event->p, event->n, event->already_known_by_peer,
1739 sendAddNode(event->p, event->n, event->already_known_by_peer,
1742 else if(event->type == MEET_REMOVENODE)
1744 //infostream<<"Server: MEET_REMOVENODE"<<std::endl;
1745 prof.add("MEET_REMOVENODE", 1);
1746 if(disable_single_change_sending)
1747 sendRemoveNode(event->p, event->already_known_by_peer,
1750 sendRemoveNode(event->p, event->already_known_by_peer,
1753 else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
1755 infostream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
1756 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
1757 setBlockNotSent(event->p);
1759 else if(event->type == MEET_OTHER)
1761 infostream<<"Server: MEET_OTHER"<<std::endl;
1762 prof.add("MEET_OTHER", 1);
1763 for(core::map<v3s16, bool>::Iterator
1764 i = event->modified_blocks.getIterator();
1765 i.atEnd()==false; i++)
1767 v3s16 p = i.getNode()->getKey();
1773 prof.add("unknown", 1);
1774 infostream<<"WARNING: Server: Unknown MapEditEvent "
1775 <<((u32)event->type)<<std::endl;
1779 Set blocks not sent to far players
1781 if(far_players.size() > 0)
1783 // Convert list format to that wanted by SetBlocksNotSent
1784 core::map<v3s16, MapBlock*> modified_blocks2;
1785 for(core::map<v3s16, bool>::Iterator
1786 i = event->modified_blocks.getIterator();
1787 i.atEnd()==false; i++)
1789 v3s16 p = i.getNode()->getKey();
1790 modified_blocks2.insert(p,
1791 m_env->getMap().getBlockNoCreateNoEx(p));
1793 // Set blocks not sent
1794 for(core::list<u16>::Iterator
1795 i = far_players.begin();
1796 i != far_players.end(); i++)
1799 RemoteClient *client = getClient(peer_id);
1802 client->SetBlocksNotSent(modified_blocks2);
1808 /*// Don't send too many at a time
1810 if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
1814 if(event_count >= 5){
1815 infostream<<"Server: MapEditEvents:"<<std::endl;
1816 prof.print(infostream);
1817 } else if(event_count != 0){
1818 verbosestream<<"Server: MapEditEvents:"<<std::endl;
1819 prof.print(verbosestream);
1825 Trigger emergethread (it somehow gets to a non-triggered but
1826 bysy state sometimes)
1829 float &counter = m_emergethread_trigger_timer;
1835 m_emergethread.trigger();
1839 // Save map, players and auth stuff
1841 float &counter = m_savemap_timer;
1843 if(counter >= g_settings->getFloat("server_map_save_interval"))
1846 JMutexAutoLock lock(m_env_mutex);
1848 ScopeProfiler sp(g_profiler, "Server: saving stuff");
1851 if(m_banmanager.isModified())
1852 m_banmanager.save();
1854 // Save changed parts of map
1855 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
1858 m_env->serializePlayers(m_path_world);
1860 // Save environment metadata
1861 m_env->saveMeta(m_path_world);
1866 void Server::Receive()
1868 DSTACK(__FUNCTION_NAME);
1869 SharedBuffer<u8> data;
1874 JMutexAutoLock conlock(m_con_mutex);
1875 datasize = m_con.Receive(peer_id, data);
1878 // This has to be called so that the client list gets synced
1879 // with the peer list of the connection
1880 handlePeerChanges();
1882 ProcessData(*data, datasize, peer_id);
1884 catch(con::InvalidIncomingDataException &e)
1886 infostream<<"Server::Receive(): "
1887 "InvalidIncomingDataException: what()="
1888 <<e.what()<<std::endl;
1890 catch(con::PeerNotFoundException &e)
1892 //NOTE: This is not needed anymore
1894 // The peer has been disconnected.
1895 // Find the associated player and remove it.
1897 /*JMutexAutoLock envlock(m_env_mutex);
1899 infostream<<"ServerThread: peer_id="<<peer_id
1900 <<" has apparently closed connection. "
1901 <<"Removing player."<<std::endl;
1903 m_env->removePlayer(peer_id);*/
1907 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1909 DSTACK(__FUNCTION_NAME);
1910 // Environment is locked first.
1911 JMutexAutoLock envlock(m_env_mutex);
1912 JMutexAutoLock conlock(m_con_mutex);
1914 ScopeProfiler sp(g_profiler, "Server::ProcessData");
1917 Address address = m_con.GetPeerAddress(peer_id);
1918 std::string addr_s = address.serializeString();
1920 // drop player if is ip is banned
1921 if(m_banmanager.isIpBanned(addr_s)){
1922 infostream<<"Server: A banned client tried to connect from "
1923 <<addr_s<<"; banned name was "
1924 <<m_banmanager.getBanName(addr_s)<<std::endl;
1925 // This actually doesn't seem to transfer to the client
1926 SendAccessDenied(m_con, peer_id,
1927 L"Your ip is banned. Banned name was "
1928 +narrow_to_wide(m_banmanager.getBanName(addr_s)));
1929 m_con.DeletePeer(peer_id);
1933 catch(con::PeerNotFoundException &e)
1935 infostream<<"Server::ProcessData(): Cancelling: peer "
1936 <<peer_id<<" not found"<<std::endl;
1940 std::string addr_s = m_con.GetPeerAddress(peer_id).serializeString();
1942 u8 peer_ser_ver = getClient(peer_id)->serialization_version;
1950 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1952 if(command == TOSERVER_INIT)
1954 // [0] u16 TOSERVER_INIT
1955 // [2] u8 SER_FMT_VER_HIGHEST
1956 // [3] u8[20] player_name
1957 // [23] u8[28] password <--- can be sent without this, from old versions
1959 if(datasize < 2+1+PLAYERNAME_SIZE)
1962 verbosestream<<"Server: Got TOSERVER_INIT from "
1963 <<peer_id<<std::endl;
1965 // First byte after command is maximum supported
1966 // serialization version
1967 u8 client_max = data[2];
1968 u8 our_max = SER_FMT_VER_HIGHEST;
1969 // Use the highest version supported by both
1970 u8 deployed = core::min_(client_max, our_max);
1971 // If it's lower than the lowest supported, give up.
1972 if(deployed < SER_FMT_VER_LOWEST)
1973 deployed = SER_FMT_VER_INVALID;
1975 //peer->serialization_version = deployed;
1976 getClient(peer_id)->pending_serialization_version = deployed;
1978 if(deployed == SER_FMT_VER_INVALID)
1980 actionstream<<"Server: A mismatched client tried to connect from "
1981 <<addr_s<<std::endl;
1982 infostream<<"Server: Cannot negotiate "
1983 "serialization version with peer "
1984 <<peer_id<<std::endl;
1985 SendAccessDenied(m_con, peer_id, std::wstring(
1986 L"Your client's version is not supported.\n"
1987 L"Server version is ")
1988 + narrow_to_wide(VERSION_STRING) + L"."
1994 Read and check network protocol version
1997 u16 net_proto_version = 0;
1998 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
2000 net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
2003 getClient(peer_id)->net_proto_version = net_proto_version;
2005 if(net_proto_version == 0)
2007 actionstream<<"Server: An old tried to connect from "<<addr_s
2009 SendAccessDenied(m_con, peer_id, std::wstring(
2010 L"Your client's version is not supported.\n"
2011 L"Server version is ")
2012 + narrow_to_wide(VERSION_STRING) + L"."
2017 if(g_settings->getBool("strict_protocol_version_checking"))
2019 if(net_proto_version != PROTOCOL_VERSION)
2021 actionstream<<"Server: A mismatched client tried to connect"
2022 <<" from "<<addr_s<<std::endl;
2023 SendAccessDenied(m_con, peer_id, std::wstring(
2024 L"Your client's version is not supported.\n"
2025 L"Server version is ")
2026 + narrow_to_wide(VERSION_STRING) + L",\n"
2027 + L"server's PROTOCOL_VERSION is "
2028 + narrow_to_wide(itos(PROTOCOL_VERSION))
2029 + L", client's PROTOCOL_VERSION is "
2030 + narrow_to_wide(itos(net_proto_version))
2041 char playername[PLAYERNAME_SIZE];
2042 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
2044 playername[i] = data[3+i];
2046 playername[PLAYERNAME_SIZE-1] = 0;
2048 if(playername[0]=='\0')
2050 actionstream<<"Server: Player with an empty name "
2051 <<"tried to connect from "<<addr_s<<std::endl;
2052 SendAccessDenied(m_con, peer_id,
2057 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
2059 actionstream<<"Server: Player with an invalid name "
2060 <<"tried to connect from "<<addr_s<<std::endl;
2061 SendAccessDenied(m_con, peer_id,
2062 L"Name contains unallowed characters");
2066 infostream<<"Server: New connection: \""<<playername<<"\" from "
2067 <<m_con.GetPeerAddress(peer_id).serializeString()<<std::endl;
2070 char password[PASSWORD_SIZE];
2071 if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
2073 // old version - assume blank password
2078 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2080 password[i] = data[23+i];
2082 password[PASSWORD_SIZE-1] = 0;
2085 std::string checkpwd;
2086 bool has_auth = scriptapi_get_auth(m_lua, playername, &checkpwd, NULL);
2089 std::wstring raw_default_password =
2090 narrow_to_wide(g_settings->get("default_password"));
2091 std::string use_password =
2092 translatePassword(playername, raw_default_password);
2094 // If default_password is empty, allow any initial password
2095 if (raw_default_password.length() == 0)
2096 use_password = password;
2098 scriptapi_create_auth(m_lua, playername, use_password);
2101 has_auth = scriptapi_get_auth(m_lua, playername, &checkpwd, NULL);
2104 SendAccessDenied(m_con, peer_id, L"Not allowed to login");
2108 if(password != checkpwd){
2109 infostream<<"Server: peer_id="<<peer_id
2110 <<": supplied invalid password for "
2111 <<playername<<std::endl;
2112 SendAccessDenied(m_con, peer_id, L"Invalid password");
2116 // Do not allow multiple players in simple singleplayer mode.
2117 // This isn't a perfect way to do it, but will suffice for now.
2118 if(m_simple_singleplayer_mode && m_clients.size() > 1){
2119 infostream<<"Server: Not allowing another client to connect in"
2120 <<" simple singleplayer mode"<<std::endl;
2121 SendAccessDenied(m_con, peer_id,
2122 L"Running in simple singleplayer mode.");
2126 // Enforce user limit.
2127 // Don't enforce for users that have some admin right
2128 if(m_clients.size() >= g_settings->getU16("max_users") &&
2129 !checkPriv(playername, "server") &&
2130 !checkPriv(playername, "ban") &&
2131 !checkPriv(playername, "privs") &&
2132 !checkPriv(playername, "password") &&
2133 playername != g_settings->get("name"))
2135 actionstream<<"Server: "<<playername<<" tried to join, but there"
2136 <<" are already max_users="
2137 <<g_settings->getU16("max_users")<<" players."<<std::endl;
2138 SendAccessDenied(m_con, peer_id, L"Too many users.");
2143 PlayerSAO *playersao = emergePlayer(playername, peer_id);
2145 // If failed, cancel
2146 if(playersao == NULL)
2148 errorstream<<"Server: peer_id="<<peer_id
2149 <<": failed to emerge player"<<std::endl;
2154 Answer with a TOCLIENT_INIT
2157 SharedBuffer<u8> reply(2+1+6+8);
2158 writeU16(&reply[0], TOCLIENT_INIT);
2159 writeU8(&reply[2], deployed);
2160 writeV3S16(&reply[2+1], floatToInt(playersao->getPlayer()->getPosition()+v3f(0,BS/2,0), BS));
2161 writeU64(&reply[2+1+6], m_env->getServerMap().getSeed());
2164 m_con.Send(peer_id, 0, reply, true);
2168 Send complete position information
2170 SendMovePlayer(peer_id);
2175 if(command == TOSERVER_INIT2)
2177 verbosestream<<"Server: Got TOSERVER_INIT2 from "
2178 <<peer_id<<std::endl;
2181 getClient(peer_id)->serialization_version
2182 = getClient(peer_id)->pending_serialization_version;
2185 Send some initialization data
2188 infostream<<"Server: Sending content to "
2189 <<getPlayerName(peer_id)<<std::endl;
2191 // Send item definitions
2192 SendItemDef(m_con, peer_id, m_itemdef);
2194 // Send node definitions
2195 SendNodeDef(m_con, peer_id, m_nodedef);
2197 // Send media announcement
2198 sendMediaAnnouncement(peer_id);
2201 SendPlayerPrivileges(peer_id);
2204 UpdateCrafting(peer_id);
2205 SendInventory(peer_id);
2207 Player *player = m_env->getPlayer(peer_id);
2210 SendPlayerHP(peer_id);
2212 // Show death screen if necessary
2214 SendDeathscreen(m_con, peer_id, false, v3f(0,0,0));
2218 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2219 m_env->getTimeOfDay(), g_settings->getFloat("time_speed"));
2220 m_con.Send(peer_id, 0, data, true);
2223 // Note things in chat if not in simple singleplayer mode
2224 if(!m_simple_singleplayer_mode)
2226 // Send information about server to player in chat
2227 SendChatMessage(peer_id, getStatusString());
2229 // Send information about joining in chat
2231 std::wstring name = L"unknown";
2232 Player *player = m_env->getPlayer(peer_id);
2234 name = narrow_to_wide(player->getName());
2236 std::wstring message;
2239 message += L" joined game";
2240 BroadcastChatMessage(message);
2244 // Warnings about protocol version can be issued here
2245 if(getClient(peer_id)->net_proto_version < PROTOCOL_VERSION)
2247 SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT IS OLD AND MAY WORK PROPERLY WITH THIS SERVER");
2254 std::ostringstream os(std::ios_base::binary);
2255 for(core::map<u16, RemoteClient*>::Iterator
2256 i = m_clients.getIterator();
2257 i.atEnd() == false; i++)
2259 RemoteClient *client = i.getNode()->getValue();
2260 assert(client->peer_id == i.getNode()->getKey());
2261 if(client->serialization_version == SER_FMT_VER_INVALID)
2264 Player *player = m_env->getPlayer(client->peer_id);
2267 // Get name of player
2268 os<<player->getName()<<" ";
2271 actionstream<<player->getName()<<" joins game. List of players: "
2272 <<os.str()<<std::endl;
2278 if(peer_ser_ver == SER_FMT_VER_INVALID)
2280 infostream<<"Server::ProcessData(): Cancelling: Peer"
2281 " serialization format invalid or not initialized."
2282 " Skipping incoming command="<<command<<std::endl;
2286 Player *player = m_env->getPlayer(peer_id);
2288 infostream<<"Server::ProcessData(): Cancelling: "
2289 "No player for peer_id="<<peer_id
2294 PlayerSAO *playersao = player->getPlayerSAO();
2295 if(playersao == NULL){
2296 infostream<<"Server::ProcessData(): Cancelling: "
2297 "No player object for peer_id="<<peer_id
2302 if(command == TOSERVER_PLAYERPOS)
2304 if(datasize < 2+12+12+4+4)
2308 v3s32 ps = readV3S32(&data[start+2]);
2309 v3s32 ss = readV3S32(&data[start+2+12]);
2310 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2311 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2312 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2313 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2314 pitch = wrapDegrees(pitch);
2315 yaw = wrapDegrees(yaw);
2317 player->setPosition(position);
2318 player->setSpeed(speed);
2319 player->setPitch(pitch);
2320 player->setYaw(yaw);
2322 /*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2323 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2324 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2326 else if(command == TOSERVER_GOTBLOCKS)
2339 u16 count = data[2];
2340 for(u16 i=0; i<count; i++)
2342 if((s16)datasize < 2+1+(i+1)*6)
2343 throw con::InvalidIncomingDataException
2344 ("GOTBLOCKS length is too short");
2345 v3s16 p = readV3S16(&data[2+1+i*6]);
2346 /*infostream<<"Server: GOTBLOCKS ("
2347 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2348 RemoteClient *client = getClient(peer_id);
2349 client->GotBlock(p);
2352 else if(command == TOSERVER_DELETEDBLOCKS)
2365 u16 count = data[2];
2366 for(u16 i=0; i<count; i++)
2368 if((s16)datasize < 2+1+(i+1)*6)
2369 throw con::InvalidIncomingDataException
2370 ("DELETEDBLOCKS length is too short");
2371 v3s16 p = readV3S16(&data[2+1+i*6]);
2372 /*infostream<<"Server: DELETEDBLOCKS ("
2373 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2374 RemoteClient *client = getClient(peer_id);
2375 client->SetBlockNotSent(p);
2378 else if(command == TOSERVER_CLICK_OBJECT)
2380 infostream<<"Server: CLICK_OBJECT not supported anymore"<<std::endl;
2383 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2385 infostream<<"Server: CLICK_ACTIVEOBJECT not supported anymore"<<std::endl;
2388 else if(command == TOSERVER_GROUND_ACTION)
2390 infostream<<"Server: GROUND_ACTION not supported anymore"<<std::endl;
2394 else if(command == TOSERVER_RELEASE)
2396 infostream<<"Server: RELEASE not supported anymore"<<std::endl;
2399 else if(command == TOSERVER_SIGNTEXT)
2401 infostream<<"Server: SIGNTEXT not supported anymore"
2405 else if(command == TOSERVER_SIGNNODETEXT)
2407 if(!checkPriv(player->getName(), "interact"))
2415 std::string datastring((char*)&data[2], datasize-2);
2416 std::istringstream is(datastring, std::ios_base::binary);
2419 is.read((char*)buf, 6);
2420 v3s16 p = readV3S16(buf);
2421 is.read((char*)buf, 2);
2422 u16 textlen = readU16(buf);
2424 for(u16 i=0; i<textlen; i++)
2426 is.read((char*)buf, 1);
2427 text += (char)buf[0];
2430 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
2434 meta->setText(text);
2436 actionstream<<player->getName()<<" writes \""<<text<<"\" to sign"
2437 <<" at "<<PP(p)<<std::endl;
2439 v3s16 blockpos = getNodeBlockPos(p);
2440 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
2443 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2447 setBlockNotSent(blockpos);
2449 else if(command == TOSERVER_INVENTORY_ACTION)
2451 // Strip command and create a stream
2452 std::string datastring((char*)&data[2], datasize-2);
2453 verbosestream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2454 std::istringstream is(datastring, std::ios_base::binary);
2456 InventoryAction *a = InventoryAction::deSerialize(is);
2459 infostream<<"TOSERVER_INVENTORY_ACTION: "
2460 <<"InventoryAction::deSerialize() returned NULL"
2466 Note: Always set inventory not sent, to repair cases
2467 where the client made a bad prediction.
2471 Handle restrictions and special cases of the move action
2473 if(a->getType() == IACTION_MOVE)
2475 IMoveAction *ma = (IMoveAction*)a;
2477 ma->from_inv.applyCurrentPlayer(player->getName());
2478 ma->to_inv.applyCurrentPlayer(player->getName());
2480 setInventoryModified(ma->from_inv);
2481 setInventoryModified(ma->to_inv);
2483 bool from_inv_is_current_player =
2484 (ma->from_inv.type == InventoryLocation::PLAYER) &&
2485 (ma->from_inv.name == player->getName());
2487 bool to_inv_is_current_player =
2488 (ma->to_inv.type == InventoryLocation::PLAYER) &&
2489 (ma->to_inv.name == player->getName());
2492 Disable moving items out of craftpreview
2494 if(ma->from_list == "craftpreview")
2496 infostream<<"Ignoring IMoveAction from "
2497 <<(ma->from_inv.dump())<<":"<<ma->from_list
2498 <<" to "<<(ma->to_inv.dump())<<":"<<ma->to_list
2499 <<" because src is "<<ma->from_list<<std::endl;
2505 Disable moving items into craftresult and craftpreview
2507 if(ma->to_list == "craftpreview" || ma->to_list == "craftresult")
2509 infostream<<"Ignoring IMoveAction from "
2510 <<(ma->from_inv.dump())<<":"<<ma->from_list
2511 <<" to "<<(ma->to_inv.dump())<<":"<<ma->to_list
2512 <<" because dst is "<<ma->to_list<<std::endl;
2517 // Disallow moving items in elsewhere than player's inventory
2518 // if not allowed to interact
2519 if(!checkPriv(player->getName(), "interact") &&
2520 (!from_inv_is_current_player ||
2521 !to_inv_is_current_player))
2523 infostream<<"Cannot move outside of player's inventory: "
2524 <<"No interact privilege"<<std::endl;
2529 // If player is not an admin, check for ownership of src and dst
2530 if(!checkPriv(player->getName(), "server"))
2532 std::string owner_from = getInventoryOwner(ma->from_inv);
2533 if(owner_from != "" && owner_from != player->getName())
2535 infostream<<"WARNING: "<<player->getName()
2536 <<" tried to access an inventory that"
2537 <<" belongs to "<<owner_from<<std::endl;
2542 std::string owner_to = getInventoryOwner(ma->to_inv);
2543 if(owner_to != "" && owner_to != player->getName())
2545 infostream<<"WARNING: "<<player->getName()
2546 <<" tried to access an inventory that"
2547 <<" belongs to "<<owner_to<<std::endl;
2554 Handle restrictions and special cases of the drop action
2556 else if(a->getType() == IACTION_DROP)
2558 IDropAction *da = (IDropAction*)a;
2560 da->from_inv.applyCurrentPlayer(player->getName());
2562 setInventoryModified(da->from_inv);
2564 // Disallow dropping items if not allowed to interact
2565 if(!checkPriv(player->getName(), "interact"))
2570 // If player is not an admin, check for ownership
2571 else if(!checkPriv(player->getName(), "server"))
2573 std::string owner_from = getInventoryOwner(da->from_inv);
2574 if(owner_from != "" && owner_from != player->getName())
2576 infostream<<"WARNING: "<<player->getName()
2577 <<" tried to access an inventory that"
2578 <<" belongs to "<<owner_from<<std::endl;
2585 Handle restrictions and special cases of the craft action
2587 else if(a->getType() == IACTION_CRAFT)
2589 ICraftAction *ca = (ICraftAction*)a;
2591 ca->craft_inv.applyCurrentPlayer(player->getName());
2593 setInventoryModified(ca->craft_inv);
2595 //bool craft_inv_is_current_player =
2596 // (ca->craft_inv.type == InventoryLocation::PLAYER) &&
2597 // (ca->craft_inv.name == player->getName());
2599 // Disallow crafting if not allowed to interact
2600 if(!checkPriv(player->getName(), "interact"))
2602 infostream<<"Cannot craft: "
2603 <<"No interact privilege"<<std::endl;
2608 // If player is not an admin, check for ownership of inventory
2609 if(!checkPriv(player->getName(), "server"))
2611 std::string owner_craft = getInventoryOwner(ca->craft_inv);
2612 if(owner_craft != "" && owner_craft != player->getName())
2614 infostream<<"WARNING: "<<player->getName()
2615 <<" tried to access an inventory that"
2616 <<" belongs to "<<owner_craft<<std::endl;
2624 a->apply(this, playersao, this);
2628 else if(command == TOSERVER_CHAT_MESSAGE)
2636 std::string datastring((char*)&data[2], datasize-2);
2637 std::istringstream is(datastring, std::ios_base::binary);
2640 is.read((char*)buf, 2);
2641 u16 len = readU16(buf);
2643 std::wstring message;
2644 for(u16 i=0; i<len; i++)
2646 is.read((char*)buf, 2);
2647 message += (wchar_t)readU16(buf);
2650 // Get player name of this client
2651 std::wstring name = narrow_to_wide(player->getName());
2654 bool ate = scriptapi_on_chat_message(m_lua, player->getName(),
2655 wide_to_narrow(message));
2656 // If script ate the message, don't proceed
2660 // Line to send to players
2662 // Whether to send to the player that sent the line
2663 bool send_to_sender = false;
2664 // Whether to send to other players
2665 bool send_to_others = false;
2668 if(message[0] == L'/')
2670 size_t strip_size = 1;
2671 if (message[1] == L'#') // support old-style commans
2673 message = message.substr(strip_size);
2675 WStrfnd f1(message);
2676 f1.next(L" "); // Skip over /#whatever
2677 std::wstring paramstring = f1.next(L"");
2679 ServerCommandContext *ctx = new ServerCommandContext(
2680 str_split(message, L' '),
2686 std::wstring reply(processServerCommand(ctx));
2687 send_to_sender = ctx->flags & SEND_TO_SENDER;
2688 send_to_others = ctx->flags & SEND_TO_OTHERS;
2690 if (ctx->flags & SEND_NO_PREFIX)
2693 line += L"Server: " + reply;
2700 if(checkPriv(player->getName(), "shout")){
2705 send_to_others = true;
2707 line += L"Server: You are not allowed to shout";
2708 send_to_sender = true;
2715 actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2718 Send the message to clients
2720 for(core::map<u16, RemoteClient*>::Iterator
2721 i = m_clients.getIterator();
2722 i.atEnd() == false; i++)
2724 // Get client and check that it is valid
2725 RemoteClient *client = i.getNode()->getValue();
2726 assert(client->peer_id == i.getNode()->getKey());
2727 if(client->serialization_version == SER_FMT_VER_INVALID)
2731 bool sender_selected = (peer_id == client->peer_id);
2732 if(sender_selected == true && send_to_sender == false)
2734 if(sender_selected == false && send_to_others == false)
2737 SendChatMessage(client->peer_id, line);
2741 else if(command == TOSERVER_DAMAGE)
2743 std::string datastring((char*)&data[2], datasize-2);
2744 std::istringstream is(datastring, std::ios_base::binary);
2745 u8 damage = readU8(is);
2747 actionstream<<player->getName()<<" damaged by "
2748 <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
2751 playersao->setHP(playersao->getHP() - damage);
2753 if(playersao->getHP() == 0 && playersao->m_hp_not_sent)
2756 if(playersao->m_hp_not_sent)
2757 SendPlayerHP(peer_id);
2759 else if(command == TOSERVER_PASSWORD)
2762 [0] u16 TOSERVER_PASSWORD
2763 [2] u8[28] old password
2764 [30] u8[28] new password
2767 if(datasize != 2+PASSWORD_SIZE*2)
2769 /*char password[PASSWORD_SIZE];
2770 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2771 password[i] = data[2+i];
2772 password[PASSWORD_SIZE-1] = 0;*/
2774 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2782 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2784 char c = data[2+PASSWORD_SIZE+i];
2790 infostream<<"Server: Client requests a password change from "
2791 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
2793 std::string playername = player->getName();
2795 std::string checkpwd;
2796 scriptapi_get_auth(m_lua, playername, &checkpwd, NULL);
2798 if(oldpwd != checkpwd)
2800 infostream<<"Server: invalid old password"<<std::endl;
2801 // Wrong old password supplied!!
2802 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
2806 bool success = scriptapi_set_password(m_lua, playername, newpwd);
2808 actionstream<<player->getName()<<" changes password"<<std::endl;
2809 SendChatMessage(peer_id, L"Password change successful");
2811 actionstream<<player->getName()<<" tries to change password but "
2812 <<"it fails"<<std::endl;
2813 SendChatMessage(peer_id, L"Password change failed or inavailable");
2816 else if(command == TOSERVER_PLAYERITEM)
2821 u16 item = readU16(&data[2]);
2822 playersao->setWieldIndex(item);
2824 else if(command == TOSERVER_RESPAWN)
2829 RespawnPlayer(peer_id);
2831 actionstream<<player->getName()<<" respawns at "
2832 <<PP(player->getPosition()/BS)<<std::endl;
2834 // ActiveObject is added to environment in AsyncRunStep after
2835 // the previous addition has been succesfully removed
2837 else if(command == TOSERVER_REQUEST_MEDIA) {
2838 std::string datastring((char*)&data[2], datasize-2);
2839 std::istringstream is(datastring, std::ios_base::binary);
2841 core::list<MediaRequest> tosend;
2842 u16 numfiles = readU16(is);
2844 infostream<<"Sending "<<numfiles<<" files to "
2845 <<getPlayerName(peer_id)<<std::endl;
2846 verbosestream<<"TOSERVER_REQUEST_MEDIA: "<<std::endl;
2848 for(int i = 0; i < numfiles; i++) {
2849 std::string name = deSerializeString(is);
2850 tosend.push_back(MediaRequest(name));
2851 verbosestream<<"TOSERVER_REQUEST_MEDIA: requested file "
2855 sendRequestedMedia(peer_id, tosend);
2857 // Now the client should know about everything
2858 // (definitions and files)
2859 getClient(peer_id)->definitions_sent = true;
2861 else if(command == TOSERVER_INTERACT)
2863 std::string datastring((char*)&data[2], datasize-2);
2864 std::istringstream is(datastring, std::ios_base::binary);
2870 [5] u32 length of the next item
2871 [9] serialized PointedThing
2873 0: start digging (from undersurface) or use
2874 1: stop digging (all parameters ignored)
2875 2: digging completed
2876 3: place block or item (to abovesurface)
2879 u8 action = readU8(is);
2880 u16 item_i = readU16(is);
2881 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
2882 PointedThing pointed;
2883 pointed.deSerialize(tmp_is);
2885 verbosestream<<"TOSERVER_INTERACT: action="<<(int)action<<", item="
2886 <<item_i<<", pointed="<<pointed.dump()<<std::endl;
2890 verbosestream<<"TOSERVER_INTERACT: "<<player->getName()
2891 <<" tried to interact, but is dead!"<<std::endl;
2895 v3f player_pos = playersao->getLastGoodPosition();
2897 // Update wielded item
2898 playersao->setWieldIndex(item_i);
2900 // Get pointed to node (undefined if not POINTEDTYPE_NODE)
2901 v3s16 p_under = pointed.node_undersurface;
2902 v3s16 p_above = pointed.node_abovesurface;
2904 // Get pointed to object (NULL if not POINTEDTYPE_OBJECT)
2905 ServerActiveObject *pointed_object = NULL;
2906 if(pointed.type == POINTEDTHING_OBJECT)
2908 pointed_object = m_env->getActiveObject(pointed.object_id);
2909 if(pointed_object == NULL)
2911 verbosestream<<"TOSERVER_INTERACT: "
2912 "pointed object is NULL"<<std::endl;
2918 v3f pointed_pos_under = player_pos;
2919 v3f pointed_pos_above = player_pos;
2920 if(pointed.type == POINTEDTHING_NODE)
2922 pointed_pos_under = intToFloat(p_under, BS);
2923 pointed_pos_above = intToFloat(p_above, BS);
2925 else if(pointed.type == POINTEDTHING_OBJECT)
2927 pointed_pos_under = pointed_object->getBasePosition();
2928 pointed_pos_above = pointed_pos_under;
2932 Check that target is reasonably close
2933 (only when digging or placing things)
2935 if(action == 0 || action == 2 || action == 3)
2937 float d = player_pos.getDistanceFrom(pointed_pos_under);
2938 float max_d = BS * 14; // Just some large enough value
2940 actionstream<<"Player "<<player->getName()
2941 <<" tried to access "<<pointed.dump()
2943 <<"d="<<d<<", max_d="<<max_d
2944 <<". ignoring."<<std::endl;
2945 // Re-send block to revert change on client-side
2946 RemoteClient *client = getClient(peer_id);
2947 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2948 client->SetBlockNotSent(blockpos);
2955 Make sure the player is allowed to do it
2957 if(!checkPriv(player->getName(), "interact"))
2959 infostream<<"Ignoring interaction from player "<<player->getName()
2960 <<" (no interact privilege)"<<std::endl;
2965 0: start digging or punch object
2969 if(pointed.type == POINTEDTHING_NODE)
2972 NOTE: This can be used in the future to check if
2973 somebody is cheating, by checking the timing.
2975 MapNode n(CONTENT_IGNORE);
2978 n = m_env->getMap().getNode(p_under);
2980 catch(InvalidPositionException &e)
2982 infostream<<"Server: Not punching: Node not found."
2983 <<" Adding block to emerge queue."
2985 m_emerge_queue.addBlock(peer_id,
2986 getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
2988 if(n.getContent() != CONTENT_IGNORE)
2989 scriptapi_node_on_punch(m_lua, p_under, n, playersao);
2991 else if(pointed.type == POINTEDTHING_OBJECT)
2993 // Skip if object has been removed
2994 if(pointed_object->m_removed)
2997 actionstream<<player->getName()<<" punches object "
2998 <<pointed.object_id<<": "
2999 <<pointed_object->getDescription()<<std::endl;
3001 ItemStack punchitem = playersao->getWieldedItem();
3002 ToolCapabilities toolcap =
3003 punchitem.getToolCapabilities(m_itemdef);
3004 v3f dir = (pointed_object->getBasePosition() -
3005 (player->getPosition() + player->getEyeOffset())
3007 float time_from_last_punch =
3008 playersao->resetTimeFromLastPunch();
3009 pointed_object->punch(dir, &toolcap, playersao,
3010 time_from_last_punch);
3018 else if(action == 1)
3023 2: Digging completed
3025 else if(action == 2)
3027 // Only complete digging of nodes
3028 if(pointed.type == POINTEDTHING_NODE)
3030 MapNode n(CONTENT_IGNORE);
3033 n = m_env->getMap().getNode(p_under);
3035 catch(InvalidPositionException &e)
3037 infostream<<"Server: Not finishing digging: Node not found."
3038 <<" Adding block to emerge queue."
3040 m_emerge_queue.addBlock(peer_id,
3041 getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
3043 if(n.getContent() != CONTENT_IGNORE)
3044 scriptapi_node_on_dig(m_lua, p_under, n, playersao);
3049 3: place block or right-click object
3051 else if(action == 3)
3053 ItemStack item = playersao->getWieldedItem();
3055 // Reset build time counter
3056 if(pointed.type == POINTEDTHING_NODE &&
3057 item.getDefinition(m_itemdef).type == ITEM_NODE)
3058 getClient(peer_id)->m_time_from_building = 0.0;
3060 if(pointed.type == POINTEDTHING_OBJECT)
3062 // Right click object
3064 // Skip if object has been removed
3065 if(pointed_object->m_removed)
3068 actionstream<<player->getName()<<" right-clicks object "
3069 <<pointed.object_id<<": "
3070 <<pointed_object->getDescription()<<std::endl;
3073 pointed_object->rightClick(playersao);
3075 else if(scriptapi_item_on_place(m_lua,
3076 item, playersao, pointed))
3078 // Placement was handled in lua
3080 // Apply returned ItemStack
3081 if(g_settings->getBool("creative_mode") == false)
3082 playersao->setWieldedItem(item);
3090 else if(action == 4)
3092 ItemStack item = playersao->getWieldedItem();
3094 actionstream<<player->getName()<<" uses "<<item.name
3095 <<", pointing at "<<pointed.dump()<<std::endl;
3097 if(scriptapi_item_on_use(m_lua,
3098 item, playersao, pointed))
3100 // Apply returned ItemStack
3101 if(g_settings->getBool("creative_mode") == false)
3102 playersao->setWieldedItem(item);
3108 Catch invalid actions
3112 infostream<<"WARNING: Server: Invalid action "
3113 <<action<<std::endl;
3116 else if(command == TOSERVER_REMOVED_SOUNDS)
3118 std::string datastring((char*)&data[2], datasize-2);
3119 std::istringstream is(datastring, std::ios_base::binary);
3121 int num = readU16(is);
3122 for(int k=0; k<num; k++){
3123 s32 id = readS32(is);
3124 std::map<s32, ServerPlayingSound>::iterator i =
3125 m_playing_sounds.find(id);
3126 if(i == m_playing_sounds.end())
3128 ServerPlayingSound &psound = i->second;
3129 psound.clients.erase(peer_id);
3130 if(psound.clients.size() == 0)
3131 m_playing_sounds.erase(i++);
3136 infostream<<"Server::ProcessData(): Ignoring "
3137 "unknown command "<<command<<std::endl;
3141 catch(SendFailedException &e)
3143 errorstream<<"Server::ProcessData(): SendFailedException: "
3149 void Server::onMapEditEvent(MapEditEvent *event)
3151 //infostream<<"Server::onMapEditEvent()"<<std::endl;
3152 if(m_ignore_map_edit_events)
3154 if(m_ignore_map_edit_events_area.contains(event->getArea()))
3156 MapEditEvent *e = event->clone();
3157 m_unsent_map_edit_queue.push_back(e);
3160 Inventory* Server::getInventory(const InventoryLocation &loc)
3163 case InventoryLocation::UNDEFINED:
3166 case InventoryLocation::CURRENT_PLAYER:
3169 case InventoryLocation::PLAYER:
3171 Player *player = m_env->getPlayer(loc.name.c_str());
3174 PlayerSAO *playersao = player->getPlayerSAO();
3177 return playersao->getInventory();
3180 case InventoryLocation::NODEMETA:
3182 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
3185 return meta->getInventory();
3193 std::string Server::getInventoryOwner(const InventoryLocation &loc)
3196 case InventoryLocation::UNDEFINED:
3199 case InventoryLocation::CURRENT_PLAYER:
3202 case InventoryLocation::PLAYER:
3207 case InventoryLocation::NODEMETA:
3209 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
3212 return meta->getOwner();
3220 void Server::setInventoryModified(const InventoryLocation &loc)
3223 case InventoryLocation::UNDEFINED:
3226 case InventoryLocation::PLAYER:
3228 Player *player = m_env->getPlayer(loc.name.c_str());
3231 PlayerSAO *playersao = player->getPlayerSAO();
3234 playersao->m_inventory_not_sent = true;
3235 playersao->m_wielded_item_not_sent = true;
3238 case InventoryLocation::NODEMETA:
3240 v3s16 blockpos = getNodeBlockPos(loc.p);
3242 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
3244 meta->inventoryModified();
3246 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3248 block->raiseModified(MOD_STATE_WRITE_NEEDED);
3250 setBlockNotSent(blockpos);
3258 core::list<PlayerInfo> Server::getPlayerInfo()
3260 DSTACK(__FUNCTION_NAME);
3261 JMutexAutoLock envlock(m_env_mutex);
3262 JMutexAutoLock conlock(m_con_mutex);
3264 core::list<PlayerInfo> list;
3266 core::list<Player*> players = m_env->getPlayers();
3268 core::list<Player*>::Iterator i;
3269 for(i = players.begin();
3270 i != players.end(); i++)
3274 Player *player = *i;
3277 // Copy info from connection to info struct
3278 info.id = player->peer_id;
3279 info.address = m_con.GetPeerAddress(player->peer_id);
3280 info.avg_rtt = m_con.GetPeerAvgRTT(player->peer_id);
3282 catch(con::PeerNotFoundException &e)
3284 // Set dummy peer info
3286 info.address = Address(0,0,0,0,0);
3290 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3291 info.position = player->getPosition();
3293 list.push_back(info);
3300 void Server::peerAdded(con::Peer *peer)
3302 DSTACK(__FUNCTION_NAME);
3303 verbosestream<<"Server::peerAdded(): peer->id="
3304 <<peer->id<<std::endl;
3307 c.type = PEER_ADDED;
3308 c.peer_id = peer->id;
3310 m_peer_change_queue.push_back(c);
3313 void Server::deletingPeer(con::Peer *peer, bool timeout)
3315 DSTACK(__FUNCTION_NAME);
3316 verbosestream<<"Server::deletingPeer(): peer->id="
3317 <<peer->id<<", timeout="<<timeout<<std::endl;
3320 c.type = PEER_REMOVED;
3321 c.peer_id = peer->id;
3322 c.timeout = timeout;
3323 m_peer_change_queue.push_back(c);
3330 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3332 DSTACK(__FUNCTION_NAME);
3333 std::ostringstream os(std::ios_base::binary);
3335 writeU16(os, TOCLIENT_HP);
3339 std::string s = os.str();
3340 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3342 con.Send(peer_id, 0, data, true);
3345 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3346 const std::wstring &reason)
3348 DSTACK(__FUNCTION_NAME);
3349 std::ostringstream os(std::ios_base::binary);
3351 writeU16(os, TOCLIENT_ACCESS_DENIED);
3352 os<<serializeWideString(reason);
3355 std::string s = os.str();
3356 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3358 con.Send(peer_id, 0, data, true);
3361 void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
3362 bool set_camera_point_target, v3f camera_point_target)
3364 DSTACK(__FUNCTION_NAME);
3365 std::ostringstream os(std::ios_base::binary);
3367 writeU16(os, TOCLIENT_DEATHSCREEN);
3368 writeU8(os, set_camera_point_target);
3369 writeV3F1000(os, camera_point_target);
3372 std::string s = os.str();
3373 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3375 con.Send(peer_id, 0, data, true);
3378 void Server::SendItemDef(con::Connection &con, u16 peer_id,
3379 IItemDefManager *itemdef)
3381 DSTACK(__FUNCTION_NAME);
3382 std::ostringstream os(std::ios_base::binary);
3386 u32 length of the next item
3387 zlib-compressed serialized ItemDefManager
3389 writeU16(os, TOCLIENT_ITEMDEF);
3390 std::ostringstream tmp_os(std::ios::binary);
3391 itemdef->serialize(tmp_os);
3392 std::ostringstream tmp_os2(std::ios::binary);
3393 compressZlib(tmp_os.str(), tmp_os2);
3394 os<<serializeLongString(tmp_os2.str());
3397 std::string s = os.str();
3398 verbosestream<<"Server: Sending item definitions to id("<<peer_id
3399 <<"): size="<<s.size()<<std::endl;
3400 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3402 con.Send(peer_id, 0, data, true);
3405 void Server::SendNodeDef(con::Connection &con, u16 peer_id,
3406 INodeDefManager *nodedef)
3408 DSTACK(__FUNCTION_NAME);
3409 std::ostringstream os(std::ios_base::binary);
3413 u32 length of the next item
3414 zlib-compressed serialized NodeDefManager
3416 writeU16(os, TOCLIENT_NODEDEF);
3417 std::ostringstream tmp_os(std::ios::binary);
3418 nodedef->serialize(tmp_os);
3419 std::ostringstream tmp_os2(std::ios::binary);
3420 compressZlib(tmp_os.str(), tmp_os2);
3421 os<<serializeLongString(tmp_os2.str());
3424 std::string s = os.str();
3425 verbosestream<<"Server: Sending node definitions to id("<<peer_id
3426 <<"): size="<<s.size()<<std::endl;
3427 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3429 con.Send(peer_id, 0, data, true);
3433 Non-static send methods
3436 void Server::SendInventory(u16 peer_id)
3438 DSTACK(__FUNCTION_NAME);
3440 PlayerSAO *playersao = getPlayerSAO(peer_id);
3443 playersao->m_inventory_not_sent = false;
3449 std::ostringstream os;
3450 playersao->getInventory()->serialize(os);
3452 std::string s = os.str();
3454 SharedBuffer<u8> data(s.size()+2);
3455 writeU16(&data[0], TOCLIENT_INVENTORY);
3456 memcpy(&data[2], s.c_str(), s.size());
3459 m_con.Send(peer_id, 0, data, true);
3462 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3464 DSTACK(__FUNCTION_NAME);
3466 std::ostringstream os(std::ios_base::binary);
3470 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3471 os.write((char*)buf, 2);
3474 writeU16(buf, message.size());
3475 os.write((char*)buf, 2);
3478 for(u32 i=0; i<message.size(); i++)
3482 os.write((char*)buf, 2);
3486 std::string s = os.str();
3487 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3489 m_con.Send(peer_id, 0, data, true);
3492 void Server::BroadcastChatMessage(const std::wstring &message)
3494 for(core::map<u16, RemoteClient*>::Iterator
3495 i = m_clients.getIterator();
3496 i.atEnd() == false; i++)
3498 // Get client and check that it is valid
3499 RemoteClient *client = i.getNode()->getValue();
3500 assert(client->peer_id == i.getNode()->getKey());
3501 if(client->serialization_version == SER_FMT_VER_INVALID)
3504 SendChatMessage(client->peer_id, message);
3508 void Server::SendPlayerHP(u16 peer_id)
3510 DSTACK(__FUNCTION_NAME);
3511 PlayerSAO *playersao = getPlayerSAO(peer_id);
3513 playersao->m_hp_not_sent = false;
3514 SendHP(m_con, peer_id, playersao->getHP());
3517 void Server::SendMovePlayer(u16 peer_id)
3519 DSTACK(__FUNCTION_NAME);
3520 Player *player = m_env->getPlayer(peer_id);
3523 std::ostringstream os(std::ios_base::binary);
3524 writeU16(os, TOCLIENT_MOVE_PLAYER);
3525 writeV3F1000(os, player->getPosition());
3526 writeF1000(os, player->getPitch());
3527 writeF1000(os, player->getYaw());
3530 v3f pos = player->getPosition();
3531 f32 pitch = player->getPitch();
3532 f32 yaw = player->getYaw();
3533 verbosestream<<"Server: Sending TOCLIENT_MOVE_PLAYER"
3534 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3541 std::string s = os.str();
3542 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3544 m_con.Send(peer_id, 0, data, true);
3547 void Server::SendPlayerPrivileges(u16 peer_id)
3549 Player *player = m_env->getPlayer(peer_id);
3551 std::set<std::string> privs;
3552 scriptapi_get_auth(m_lua, player->getName(), NULL, &privs);
3554 std::ostringstream os(std::ios_base::binary);
3555 writeU16(os, TOCLIENT_PRIVILEGES);
3556 writeU16(os, privs.size());
3557 for(std::set<std::string>::const_iterator i = privs.begin();
3558 i != privs.end(); i++){
3559 os<<serializeString(*i);
3563 std::string s = os.str();
3564 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3566 m_con.Send(peer_id, 0, data, true);
3569 s32 Server::playSound(const SimpleSoundSpec &spec,
3570 const ServerSoundParams ¶ms)
3572 // Find out initial position of sound
3573 bool pos_exists = false;
3574 v3f pos = params.getPos(m_env, &pos_exists);
3575 // If position is not found while it should be, cancel sound
3576 if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
3578 // Filter destination clients
3579 std::set<RemoteClient*> dst_clients;
3580 if(params.to_player != "")
3582 Player *player = m_env->getPlayer(params.to_player.c_str());
3584 infostream<<"Server::playSound: Player \""<<params.to_player
3585 <<"\" not found"<<std::endl;
3588 if(player->peer_id == PEER_ID_INEXISTENT){
3589 infostream<<"Server::playSound: Player \""<<params.to_player
3590 <<"\" not connected"<<std::endl;
3593 RemoteClient *client = getClient(player->peer_id);
3594 dst_clients.insert(client);
3598 for(core::map<u16, RemoteClient*>::Iterator
3599 i = m_clients.getIterator(); i.atEnd() == false; i++)
3601 RemoteClient *client = i.getNode()->getValue();
3602 Player *player = m_env->getPlayer(client->peer_id);
3606 if(player->getPosition().getDistanceFrom(pos) >
3607 params.max_hear_distance)
3610 dst_clients.insert(client);
3613 if(dst_clients.size() == 0)
3616 s32 id = m_next_sound_id++;
3617 // The sound will exist as a reference in m_playing_sounds
3618 m_playing_sounds[id] = ServerPlayingSound();
3619 ServerPlayingSound &psound = m_playing_sounds[id];
3620 psound.params = params;
3621 for(std::set<RemoteClient*>::iterator i = dst_clients.begin();
3622 i != dst_clients.end(); i++)
3623 psound.clients.insert((*i)->peer_id);
3625 std::ostringstream os(std::ios_base::binary);
3626 writeU16(os, TOCLIENT_PLAY_SOUND);
3628 os<<serializeString(spec.name);
3629 writeF1000(os, spec.gain * params.gain);
3630 writeU8(os, params.type);
3631 writeV3F1000(os, pos);
3632 writeU16(os, params.object);
3633 writeU8(os, params.loop);
3635 std::string s = os.str();
3636 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3638 for(std::set<RemoteClient*>::iterator i = dst_clients.begin();
3639 i != dst_clients.end(); i++){
3641 m_con.Send((*i)->peer_id, 0, data, true);
3645 void Server::stopSound(s32 handle)
3647 // Get sound reference
3648 std::map<s32, ServerPlayingSound>::iterator i =
3649 m_playing_sounds.find(handle);
3650 if(i == m_playing_sounds.end())
3652 ServerPlayingSound &psound = i->second;
3654 std::ostringstream os(std::ios_base::binary);
3655 writeU16(os, TOCLIENT_STOP_SOUND);
3656 writeS32(os, handle);
3658 std::string s = os.str();
3659 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3661 for(std::set<u16>::iterator i = psound.clients.begin();
3662 i != psound.clients.end(); i++){
3664 m_con.Send(*i, 0, data, true);
3666 // Remove sound reference
3667 m_playing_sounds.erase(i);
3670 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3671 core::list<u16> *far_players, float far_d_nodes)
3673 float maxd = far_d_nodes*BS;
3674 v3f p_f = intToFloat(p, BS);
3678 SharedBuffer<u8> reply(replysize);
3679 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3680 writeS16(&reply[2], p.X);
3681 writeS16(&reply[4], p.Y);
3682 writeS16(&reply[6], p.Z);
3684 for(core::map<u16, RemoteClient*>::Iterator
3685 i = m_clients.getIterator();
3686 i.atEnd() == false; i++)
3688 // Get client and check that it is valid
3689 RemoteClient *client = i.getNode()->getValue();
3690 assert(client->peer_id == i.getNode()->getKey());
3691 if(client->serialization_version == SER_FMT_VER_INVALID)
3694 // Don't send if it's the same one
3695 if(client->peer_id == ignore_id)
3701 Player *player = m_env->getPlayer(client->peer_id);
3704 // If player is far away, only set modified blocks not sent
3705 v3f player_pos = player->getPosition();
3706 if(player_pos.getDistanceFrom(p_f) > maxd)
3708 far_players->push_back(client->peer_id);
3715 m_con.Send(client->peer_id, 0, reply, true);
3719 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3720 core::list<u16> *far_players, float far_d_nodes)
3722 float maxd = far_d_nodes*BS;
3723 v3f p_f = intToFloat(p, BS);
3725 for(core::map<u16, RemoteClient*>::Iterator
3726 i = m_clients.getIterator();
3727 i.atEnd() == false; i++)
3729 // Get client and check that it is valid
3730 RemoteClient *client = i.getNode()->getValue();
3731 assert(client->peer_id == i.getNode()->getKey());
3732 if(client->serialization_version == SER_FMT_VER_INVALID)
3735 // Don't send if it's the same one
3736 if(client->peer_id == ignore_id)
3742 Player *player = m_env->getPlayer(client->peer_id);
3745 // If player is far away, only set modified blocks not sent
3746 v3f player_pos = player->getPosition();
3747 if(player_pos.getDistanceFrom(p_f) > maxd)
3749 far_players->push_back(client->peer_id);
3756 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3757 SharedBuffer<u8> reply(replysize);
3758 writeU16(&reply[0], TOCLIENT_ADDNODE);
3759 writeS16(&reply[2], p.X);
3760 writeS16(&reply[4], p.Y);
3761 writeS16(&reply[6], p.Z);
3762 n.serialize(&reply[8], client->serialization_version);
3765 m_con.Send(client->peer_id, 0, reply, true);
3769 void Server::setBlockNotSent(v3s16 p)
3771 for(core::map<u16, RemoteClient*>::Iterator
3772 i = m_clients.getIterator();
3773 i.atEnd()==false; i++)
3775 RemoteClient *client = i.getNode()->getValue();
3776 client->SetBlockNotSent(p);
3780 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3782 DSTACK(__FUNCTION_NAME);
3784 v3s16 p = block->getPos();
3788 bool completely_air = true;
3789 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3790 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3791 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3793 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
3795 completely_air = false;
3796 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
3801 infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
3803 infostream<<"[completely air] ";
3804 infostream<<std::endl;
3808 Create a packet with the block in the right format
3811 std::ostringstream os(std::ios_base::binary);
3812 block->serialize(os, ver, false);
3813 std::string s = os.str();
3814 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3816 u32 replysize = 8 + blockdata.getSize();
3817 SharedBuffer<u8> reply(replysize);
3818 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3819 writeS16(&reply[2], p.X);
3820 writeS16(&reply[4], p.Y);
3821 writeS16(&reply[6], p.Z);
3822 memcpy(&reply[8], *blockdata, blockdata.getSize());
3824 /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3825 <<": \tpacket size: "<<replysize<<std::endl;*/
3830 m_con.Send(peer_id, 1, reply, true);
3833 void Server::SendBlocks(float dtime)
3835 DSTACK(__FUNCTION_NAME);
3837 JMutexAutoLock envlock(m_env_mutex);
3838 JMutexAutoLock conlock(m_con_mutex);
3840 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
3842 core::array<PrioritySortedBlockTransfer> queue;
3844 s32 total_sending = 0;
3847 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
3849 for(core::map<u16, RemoteClient*>::Iterator
3850 i = m_clients.getIterator();
3851 i.atEnd() == false; i++)
3853 RemoteClient *client = i.getNode()->getValue();
3854 assert(client->peer_id == i.getNode()->getKey());
3856 // If definitions and textures have not been sent, don't
3857 // send MapBlocks either
3858 if(!client->definitions_sent)
3861 total_sending += client->SendingCount();
3863 if(client->serialization_version == SER_FMT_VER_INVALID)
3866 client->GetNextBlocks(this, dtime, queue);
3871 // Lowest priority number comes first.
3872 // Lowest is most important.
3875 for(u32 i=0; i<queue.size(); i++)
3877 //TODO: Calculate limit dynamically
3878 if(total_sending >= g_settings->getS32
3879 ("max_simultaneous_block_sends_server_total"))
3882 PrioritySortedBlockTransfer q = queue[i];
3884 MapBlock *block = NULL;
3887 block = m_env->getMap().getBlockNoCreate(q.pos);
3889 catch(InvalidPositionException &e)
3894 RemoteClient *client = getClient(q.peer_id);
3896 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3898 client->SentBlock(q.pos);
3904 void Server::fillMediaCache()
3906 DSTACK(__FUNCTION_NAME);
3908 infostream<<"Server: Calculating media file checksums"<<std::endl;
3910 // Collect all media file paths
3911 std::list<std::string> paths;
3912 for(core::list<ModSpec>::Iterator i = m_mods.begin();
3913 i != m_mods.end(); i++){
3914 const ModSpec &mod = *i;
3915 paths.push_back(mod.path + DIR_DELIM + "textures");
3916 paths.push_back(mod.path + DIR_DELIM + "sounds");
3917 paths.push_back(mod.path + DIR_DELIM + "media");
3920 // Collect media file information from paths into cache
3921 for(std::list<std::string>::iterator i = paths.begin();
3922 i != paths.end(); i++)
3924 std::string mediapath = *i;
3925 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
3926 for(u32 j=0; j<dirlist.size(); j++){
3927 if(dirlist[j].dir) // Ignode dirs
3929 std::string filename = dirlist[j].name;
3930 // If name contains illegal characters, ignore the file
3931 if(!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)){
3932 infostream<<"Server: ignoring illegal file name: \""
3933 <<filename<<"\""<<std::endl;
3936 // If name is not in a supported format, ignore it
3937 const char *supported_ext[] = {
3938 ".png", ".jpg", ".bmp", ".tga",
3939 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
3943 if(removeStringEnd(filename, supported_ext) == ""){
3944 infostream<<"Server: ignoring unsupported file extension: \""
3945 <<filename<<"\""<<std::endl;
3948 // Ok, attempt to load the file and add to cache
3949 std::string filepath = mediapath + DIR_DELIM + filename;
3951 std::ifstream fis(filepath.c_str(), std::ios_base::binary);
3952 if(fis.good() == false){
3953 errorstream<<"Server::fillMediaCache(): Could not open \""
3954 <<filename<<"\" for reading"<<std::endl;
3957 std::ostringstream tmp_os(std::ios_base::binary);
3961 fis.read(buf, 1024);
3962 std::streamsize len = fis.gcount();
3963 tmp_os.write(buf, len);
3972 errorstream<<"Server::fillMediaCache(): Failed to read \""
3973 <<filename<<"\""<<std::endl;
3976 if(tmp_os.str().length() == 0){
3977 errorstream<<"Server::fillMediaCache(): Empty file \""
3978 <<filepath<<"\""<<std::endl;
3983 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
3985 unsigned char *digest = sha1.getDigest();
3986 std::string sha1_base64 = base64_encode(digest, 20);
3987 std::string sha1_hex = hex_encode((char*)digest, 20);
3991 this->m_media[filename] = MediaInfo(filepath, sha1_base64);
3992 verbosestream<<"Server: "<<sha1_hex<<" is "<<filename<<std::endl;
3997 struct SendableMediaAnnouncement
4000 std::string sha1_digest;
4002 SendableMediaAnnouncement(const std::string name_="",
4003 const std::string sha1_digest_=""):
4005 sha1_digest(sha1_digest_)
4009 void Server::sendMediaAnnouncement(u16 peer_id)
4011 DSTACK(__FUNCTION_NAME);
4013 verbosestream<<"Server: Announcing files to id("<<peer_id<<")"
4016 core::list<SendableMediaAnnouncement> file_announcements;
4018 for(std::map<std::string, MediaInfo>::iterator i = m_media.begin();
4019 i != m_media.end(); i++){
4021 file_announcements.push_back(
4022 SendableMediaAnnouncement(i->first, i->second.sha1_digest));
4026 std::ostringstream os(std::ios_base::binary);
4034 u16 length of sha1_digest
4039 writeU16(os, TOCLIENT_ANNOUNCE_MEDIA);
4040 writeU16(os, file_announcements.size());
4042 for(core::list<SendableMediaAnnouncement>::Iterator
4043 j = file_announcements.begin();
4044 j != file_announcements.end(); j++){
4045 os<<serializeString(j->name);
4046 os<<serializeString(j->sha1_digest);
4050 std::string s = os.str();
4051 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4054 m_con.Send(peer_id, 0, data, true);
4058 struct SendableMedia
4064 SendableMedia(const std::string &name_="", const std::string path_="",
4065 const std::string &data_=""):
4072 void Server::sendRequestedMedia(u16 peer_id,
4073 const core::list<MediaRequest> &tosend)
4075 DSTACK(__FUNCTION_NAME);
4077 verbosestream<<"Server::sendRequestedMedia(): "
4078 <<"Sending files to client"<<std::endl;
4082 // Put 5kB in one bunch (this is not accurate)
4083 u32 bytes_per_bunch = 5000;
4085 core::array< core::list<SendableMedia> > file_bunches;
4086 file_bunches.push_back(core::list<SendableMedia>());
4088 u32 file_size_bunch_total = 0;
4090 for(core::list<MediaRequest>::ConstIterator i = tosend.begin();
4091 i != tosend.end(); i++)
4093 if(m_media.find(i->name) == m_media.end()){
4094 errorstream<<"Server::sendRequestedMedia(): Client asked for "
4095 <<"unknown file \""<<(i->name)<<"\""<<std::endl;
4099 //TODO get path + name
4100 std::string tpath = m_media[(*i).name].path;
4103 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
4104 if(fis.good() == false){
4105 errorstream<<"Server::sendRequestedMedia(): Could not open \""
4106 <<tpath<<"\" for reading"<<std::endl;
4109 std::ostringstream tmp_os(std::ios_base::binary);
4113 fis.read(buf, 1024);
4114 std::streamsize len = fis.gcount();
4115 tmp_os.write(buf, len);
4116 file_size_bunch_total += len;
4125 errorstream<<"Server::sendRequestedMedia(): Failed to read \""
4126 <<(*i).name<<"\""<<std::endl;
4129 /*infostream<<"Server::sendRequestedMedia(): Loaded \""
4130 <<tname<<"\""<<std::endl;*/
4132 file_bunches[file_bunches.size()-1].push_back(
4133 SendableMedia((*i).name, tpath, tmp_os.str()));
4135 // Start next bunch if got enough data
4136 if(file_size_bunch_total >= bytes_per_bunch){
4137 file_bunches.push_back(core::list<SendableMedia>());
4138 file_size_bunch_total = 0;
4143 /* Create and send packets */
4145 u32 num_bunches = file_bunches.size();
4146 for(u32 i=0; i<num_bunches; i++)
4148 std::ostringstream os(std::ios_base::binary);
4152 u16 total number of texture bunches
4153 u16 index of this bunch
4154 u32 number of files in this bunch
4163 writeU16(os, TOCLIENT_MEDIA);
4164 writeU16(os, num_bunches);
4166 writeU32(os, file_bunches[i].size());
4168 for(core::list<SendableMedia>::Iterator
4169 j = file_bunches[i].begin();
4170 j != file_bunches[i].end(); j++){
4171 os<<serializeString(j->name);
4172 os<<serializeLongString(j->data);
4176 std::string s = os.str();
4177 verbosestream<<"Server::sendRequestedMedia(): bunch "
4178 <<i<<"/"<<num_bunches
4179 <<" files="<<file_bunches[i].size()
4180 <<" size=" <<s.size()<<std::endl;
4181 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4183 m_con.Send(peer_id, 0, data, true);
4191 void Server::DiePlayer(u16 peer_id)
4193 DSTACK(__FUNCTION_NAME);
4195 PlayerSAO *playersao = getPlayerSAO(peer_id);
4198 infostream<<"Server::DiePlayer(): Player "
4199 <<playersao->getPlayer()->getName()
4200 <<" dies"<<std::endl;
4202 playersao->setHP(0);
4204 // Trigger scripted stuff
4205 scriptapi_on_dieplayer(m_lua, playersao);
4207 SendPlayerHP(peer_id);
4208 SendDeathscreen(m_con, peer_id, false, v3f(0,0,0));
4211 void Server::RespawnPlayer(u16 peer_id)
4213 DSTACK(__FUNCTION_NAME);
4215 PlayerSAO *playersao = getPlayerSAO(peer_id);
4218 infostream<<"Server::RespawnPlayer(): Player "
4219 <<playersao->getPlayer()->getName()
4220 <<" respawns"<<std::endl;
4222 playersao->setHP(PLAYER_MAX_HP);
4224 bool repositioned = scriptapi_on_respawnplayer(m_lua, playersao);
4226 v3f pos = findSpawnPos(m_env->getServerMap());
4227 playersao->setPos(pos);
4231 void Server::UpdateCrafting(u16 peer_id)
4233 DSTACK(__FUNCTION_NAME);
4235 Player* player = m_env->getPlayer(peer_id);
4238 // Get a preview for crafting
4240 // No crafting in creative mode
4241 if(g_settings->getBool("creative_mode") == false)
4242 getCraftingResult(&player->inventory, preview, false, this);
4244 // Put the new preview in
4245 InventoryList *plist = player->inventory.getList("craftpreview");
4247 assert(plist->getSize() >= 1);
4248 plist->changeItem(0, preview);
4251 RemoteClient* Server::getClient(u16 peer_id)
4253 DSTACK(__FUNCTION_NAME);
4254 //JMutexAutoLock lock(m_con_mutex);
4255 core::map<u16, RemoteClient*>::Node *n;
4256 n = m_clients.find(peer_id);
4257 // A client should exist for all peers
4259 return n->getValue();
4262 std::wstring Server::getStatusString()
4264 std::wostringstream os(std::ios_base::binary);
4267 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4269 os<<L", uptime="<<m_uptime.get();
4270 // Information about clients
4272 for(core::map<u16, RemoteClient*>::Iterator
4273 i = m_clients.getIterator();
4274 i.atEnd() == false; i++)
4276 // Get client and check that it is valid
4277 RemoteClient *client = i.getNode()->getValue();
4278 assert(client->peer_id == i.getNode()->getKey());
4279 if(client->serialization_version == SER_FMT_VER_INVALID)
4282 Player *player = m_env->getPlayer(client->peer_id);
4283 // Get name of player
4284 std::wstring name = L"unknown";
4286 name = narrow_to_wide(player->getName());
4287 // Add name to information string
4291 if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
4292 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4293 if(g_settings->get("motd") != "")
4294 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4298 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
4300 std::set<std::string> privs;
4301 scriptapi_get_auth(m_lua, name, NULL, &privs);
4305 bool Server::checkPriv(const std::string &name, const std::string &priv)
4307 std::set<std::string> privs = getPlayerEffectivePrivs(name);
4308 return (privs.count(priv) != 0);
4311 void Server::reportPrivsModified(const std::string &name)
4314 for(core::map<u16, RemoteClient*>::Iterator
4315 i = m_clients.getIterator();
4316 i.atEnd() == false; i++){
4317 RemoteClient *client = i.getNode()->getValue();
4318 Player *player = m_env->getPlayer(client->peer_id);
4319 reportPrivsModified(player->getName());
4322 Player *player = m_env->getPlayer(name.c_str());
4325 SendPlayerPrivileges(player->peer_id);
4326 player->getPlayerSAO()->updatePrivileges(
4327 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)
4414 v3f findSpawnPos(ServerMap &map)
4416 //return v3f(50,50,50)*BS;
4421 nodepos = v2s16(0,0);
4426 // Try to find a good place a few times
4427 for(s32 i=0; i<1000; i++)
4430 // We're going to try to throw the player to this position
4431 v2s16 nodepos2d = v2s16(-range + (myrand()%(range*2)),
4432 -range + (myrand()%(range*2)));
4433 //v2s16 sectorpos = getNodeSectorPos(nodepos2d);
4434 // Get ground height at point (fallbacks to heightmap function)
4435 s16 groundheight = map.findGroundLevel(nodepos2d);
4436 // Don't go underwater
4437 if(groundheight < WATER_LEVEL)
4439 //infostream<<"-> Underwater"<<std::endl;
4442 // Don't go to high places
4443 if(groundheight > WATER_LEVEL + 4)
4445 //infostream<<"-> Underwater"<<std::endl;
4449 nodepos = v3s16(nodepos2d.X, groundheight-2, nodepos2d.Y);
4450 bool is_good = false;
4452 for(s32 i=0; i<10; i++){
4453 v3s16 blockpos = getNodeBlockPos(nodepos);
4454 map.emergeBlock(blockpos, true);
4455 MapNode n = map.getNodeNoEx(nodepos);
4456 if(n.getContent() == CONTENT_AIR){
4467 // Found a good place
4468 //infostream<<"Searched through "<<i<<" places."<<std::endl;
4474 return intToFloat(nodepos, BS);
4477 PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id)
4479 RemotePlayer *player = NULL;
4480 bool newplayer = false;
4483 Try to get an existing player
4485 player = static_cast<RemotePlayer*>(m_env->getPlayer(name));
4487 // If player is already connected, cancel
4488 if(player != NULL && player->peer_id != 0)
4490 infostream<<"emergePlayer(): Player already connected"<<std::endl;
4495 If player with the wanted peer_id already exists, cancel.
4497 if(m_env->getPlayer(peer_id) != NULL)
4499 infostream<<"emergePlayer(): Player with wrong name but same"
4500 " peer_id already exists"<<std::endl;
4505 Create a new player if it doesn't exist yet
4510 player = new RemotePlayer(this);
4511 player->updateName(name);
4513 /* Set player position */
4514 infostream<<"Server: Finding spawn place for player \""
4515 <<name<<"\""<<std::endl;
4516 v3f pos = findSpawnPos(m_env->getServerMap());
4517 player->setPosition(pos);
4519 /* Add player to environment */
4520 m_env->addPlayer(player);
4524 Create a new player active object
4526 PlayerSAO *playersao = new PlayerSAO(m_env, player, peer_id,
4527 getPlayerEffectivePrivs(player->getName()));
4529 /* Add object to environment */
4530 m_env->addActiveObject(playersao);
4534 scriptapi_on_newplayer(m_lua, playersao);
4536 scriptapi_on_joinplayer(m_lua, playersao);
4539 if(g_settings->getBool("creative_mode"))
4540 playersao->createCreativeInventory();
4545 void Server::handlePeerChange(PeerChange &c)
4547 JMutexAutoLock envlock(m_env_mutex);
4548 JMutexAutoLock conlock(m_con_mutex);
4550 if(c.type == PEER_ADDED)
4557 core::map<u16, RemoteClient*>::Node *n;
4558 n = m_clients.find(c.peer_id);
4559 // The client shouldn't already exist
4563 RemoteClient *client = new RemoteClient();
4564 client->peer_id = c.peer_id;
4565 m_clients.insert(client->peer_id, client);
4568 else if(c.type == PEER_REMOVED)
4575 core::map<u16, RemoteClient*>::Node *n;
4576 n = m_clients.find(c.peer_id);
4577 // The client should exist
4581 Mark objects to be not known by the client
4583 RemoteClient *client = n->getValue();
4585 for(core::map<u16, bool>::Iterator
4586 i = client->m_known_objects.getIterator();
4587 i.atEnd()==false; i++)
4590 u16 id = i.getNode()->getKey();
4591 ServerActiveObject* obj = m_env->getActiveObject(id);
4593 if(obj && obj->m_known_by_count > 0)
4594 obj->m_known_by_count--;
4598 Clear references to playing sounds
4600 for(std::map<s32, ServerPlayingSound>::iterator
4601 i = m_playing_sounds.begin();
4602 i != m_playing_sounds.end();)
4604 ServerPlayingSound &psound = i->second;
4605 psound.clients.erase(c.peer_id);
4606 if(psound.clients.size() == 0)
4607 m_playing_sounds.erase(i++);
4612 Player *player = m_env->getPlayer(c.peer_id);
4614 // Collect information about leaving in chat
4615 std::wstring message;
4619 std::wstring name = narrow_to_wide(player->getName());
4622 message += L" left game";
4624 message += L" (timed out)";
4628 /* Run scripts and remove from environment */
4632 PlayerSAO *playersao = player->getPlayerSAO();
4635 scriptapi_on_leaveplayer(m_lua, playersao);
4637 playersao->disconnected();
4647 std::ostringstream os(std::ios_base::binary);
4648 for(core::map<u16, RemoteClient*>::Iterator
4649 i = m_clients.getIterator();
4650 i.atEnd() == false; i++)
4652 RemoteClient *client = i.getNode()->getValue();
4653 assert(client->peer_id == i.getNode()->getKey());
4654 if(client->serialization_version == SER_FMT_VER_INVALID)
4657 Player *player = m_env->getPlayer(client->peer_id);
4660 // Get name of player
4661 os<<player->getName()<<" ";
4664 actionstream<<player->getName()<<" "
4665 <<(c.timeout?"times out.":"leaves game.")
4666 <<" List of players: "
4667 <<os.str()<<std::endl;
4672 delete m_clients[c.peer_id];
4673 m_clients.remove(c.peer_id);
4675 // Send player info to all remaining clients
4676 //SendPlayerInfos();
4678 // Send leave chat message to all remaining clients
4679 if(message.length() != 0)
4680 BroadcastChatMessage(message);
4689 void Server::handlePeerChanges()
4691 while(m_peer_change_queue.size() > 0)
4693 PeerChange c = m_peer_change_queue.pop_front();
4695 verbosestream<<"Server: Handling peer change: "
4696 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4699 handlePeerChange(c);
4703 void dedicated_server_loop(Server &server, bool &kill)
4705 DSTACK(__FUNCTION_NAME);
4707 verbosestream<<"dedicated_server_loop()"<<std::endl;
4709 IntervalLimiter m_profiler_interval;
4713 float steplen = g_settings->getFloat("dedicated_server_step");
4714 // This is kind of a hack but can be done like this
4715 // because server.step() is very light
4717 ScopeProfiler sp(g_profiler, "dedicated server sleep");
4718 sleep_ms((int)(steplen*1000.0));
4720 server.step(steplen);
4722 if(server.getShutdownRequested() || kill)
4724 infostream<<"Dedicated server quitting"<<std::endl;
4731 float profiler_print_interval =
4732 g_settings->getFloat("profiler_print_interval");
4733 if(profiler_print_interval != 0)
4735 if(m_profiler_interval.step(steplen, profiler_print_interval))
4737 infostream<<"Profiler:"<<std::endl;
4738 g_profiler->print(infostream);
4739 g_profiler->clear();