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);
378 catch(VersionMismatchException &e)
380 m_server->setAsyncFatalError(std::string(
381 "World data version mismatch (server-side) (world probably saved by a newer version of Minetest): ")+e.what());
384 END_DEBUG_EXCEPTION_HANDLER(errorstream)
386 log_deregister_thread();
391 v3f ServerSoundParams::getPos(ServerEnvironment *env, bool *pos_exists) const
393 if(pos_exists) *pos_exists = false;
398 if(pos_exists) *pos_exists = true;
403 ServerActiveObject *sao = env->getActiveObject(object);
406 if(pos_exists) *pos_exists = true;
407 return sao->getBasePosition(); }
412 void RemoteClient::GetNextBlocks(Server *server, float dtime,
413 core::array<PrioritySortedBlockTransfer> &dest)
415 DSTACK(__FUNCTION_NAME);
418 TimeTaker timer("RemoteClient::GetNextBlocks", &timer_result);*/
421 m_nothing_to_send_pause_timer -= dtime;
422 m_nearest_unsent_reset_timer += dtime;
424 if(m_nothing_to_send_pause_timer >= 0)
429 // Won't send anything if already sending
430 if(m_blocks_sending.size() >= g_settings->getU16
431 ("max_simultaneous_block_sends_per_client"))
433 //infostream<<"Not sending any blocks, Queue full."<<std::endl;
437 //TimeTaker timer("RemoteClient::GetNextBlocks");
439 Player *player = server->m_env->getPlayer(peer_id);
441 assert(player != NULL);
443 v3f playerpos = player->getPosition();
444 v3f playerspeed = player->getSpeed();
445 v3f playerspeeddir(0,0,0);
446 if(playerspeed.getLength() > 1.0*BS)
447 playerspeeddir = playerspeed / playerspeed.getLength();
448 // Predict to next block
449 v3f playerpos_predicted = playerpos + playerspeeddir*MAP_BLOCKSIZE*BS;
451 v3s16 center_nodepos = floatToInt(playerpos_predicted, BS);
453 v3s16 center = getNodeBlockPos(center_nodepos);
455 // Camera position and direction
456 v3f camera_pos = player->getEyePosition();
457 v3f camera_dir = v3f(0,0,1);
458 camera_dir.rotateYZBy(player->getPitch());
459 camera_dir.rotateXZBy(player->getYaw());
461 /*infostream<<"camera_dir=("<<camera_dir.X<<","<<camera_dir.Y<<","
462 <<camera_dir.Z<<")"<<std::endl;*/
465 Get the starting value of the block finder radius.
468 if(m_last_center != center)
470 m_nearest_unsent_d = 0;
471 m_last_center = center;
474 /*infostream<<"m_nearest_unsent_reset_timer="
475 <<m_nearest_unsent_reset_timer<<std::endl;*/
477 // Reset periodically to workaround for some bugs or stuff
478 if(m_nearest_unsent_reset_timer > 20.0)
480 m_nearest_unsent_reset_timer = 0;
481 m_nearest_unsent_d = 0;
482 //infostream<<"Resetting m_nearest_unsent_d for "
483 // <<server->getPlayerName(peer_id)<<std::endl;
486 //s16 last_nearest_unsent_d = m_nearest_unsent_d;
487 s16 d_start = m_nearest_unsent_d;
489 //infostream<<"d_start="<<d_start<<std::endl;
491 u16 max_simul_sends_setting = g_settings->getU16
492 ("max_simultaneous_block_sends_per_client");
493 u16 max_simul_sends_usually = max_simul_sends_setting;
496 Check the time from last addNode/removeNode.
498 Decrease send rate if player is building stuff.
500 m_time_from_building += dtime;
501 if(m_time_from_building < g_settings->getFloat(
502 "full_block_send_enable_min_time_from_building"))
504 max_simul_sends_usually
505 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
509 Number of blocks sending + number of blocks selected for sending
511 u32 num_blocks_selected = m_blocks_sending.size();
514 next time d will be continued from the d from which the nearest
515 unsent block was found this time.
517 This is because not necessarily any of the blocks found this
518 time are actually sent.
520 s32 new_nearest_unsent_d = -1;
522 s16 d_max = g_settings->getS16("max_block_send_distance");
523 s16 d_max_gen = g_settings->getS16("max_block_generate_distance");
525 // Don't loop very much at a time
526 s16 max_d_increment_at_time = 2;
527 if(d_max > d_start + max_d_increment_at_time)
528 d_max = d_start + max_d_increment_at_time;
529 /*if(d_max_gen > d_start+2)
530 d_max_gen = d_start+2;*/
532 //infostream<<"Starting from "<<d_start<<std::endl;
534 s32 nearest_emerged_d = -1;
535 s32 nearest_emergefull_d = -1;
536 s32 nearest_sent_d = -1;
537 bool queue_is_full = false;
540 for(d = d_start; d <= d_max; d++)
542 /*errorstream<<"checking d="<<d<<" for "
543 <<server->getPlayerName(peer_id)<<std::endl;*/
544 //infostream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
547 If m_nearest_unsent_d was changed by the EmergeThread
548 (it can change it to 0 through SetBlockNotSent),
550 Else update m_nearest_unsent_d
552 /*if(m_nearest_unsent_d != last_nearest_unsent_d)
554 d = m_nearest_unsent_d;
555 last_nearest_unsent_d = m_nearest_unsent_d;
559 Get the border/face dot coordinates of a "d-radiused"
562 core::list<v3s16> list;
563 getFacePositions(list, d);
565 core::list<v3s16>::Iterator li;
566 for(li=list.begin(); li!=list.end(); li++)
568 v3s16 p = *li + center;
572 - Don't allow too many simultaneous transfers
573 - EXCEPT when the blocks are very close
575 Also, don't send blocks that are already flying.
578 // Start with the usual maximum
579 u16 max_simul_dynamic = max_simul_sends_usually;
581 // If block is very close, allow full maximum
582 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
583 max_simul_dynamic = max_simul_sends_setting;
585 // Don't select too many blocks for sending
586 if(num_blocks_selected >= max_simul_dynamic)
588 queue_is_full = true;
589 goto queue_full_break;
592 // Don't send blocks that are currently being transferred
593 if(m_blocks_sending.find(p) != NULL)
599 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
600 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
601 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
602 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
603 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
604 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
607 // If this is true, inexistent block will be made from scratch
608 bool generate = d <= d_max_gen;
611 /*// Limit the generating area vertically to 2/3
612 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
615 // Limit the send area vertically to 1/2
616 if(abs(p.Y - center.Y) > d_max / 2)
622 If block is far away, don't generate it unless it is
628 // Block center y in nodes
629 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
630 // Don't generate if it's very high or very low
631 if(y < -64 || y > 64)
635 v2s16 p2d_nodes_center(
639 // Get ground height in nodes
640 s16 gh = server->m_env->getServerMap().findGroundLevel(
643 // If differs a lot, don't generate
644 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
646 // Actually, don't even send it
652 //infostream<<"d="<<d<<std::endl;
655 Don't generate or send if not in sight
656 FIXME This only works if the client uses a small enough
657 FOV setting. The default of 72 degrees is fine.
660 float camera_fov = (72.0*PI/180) * 4./3.;
661 if(isBlockInSight(p, camera_pos, camera_dir, camera_fov, 10000*BS) == false)
667 Don't send already sent blocks
670 if(m_blocks_sent.find(p) != NULL)
677 Check if map has this block
679 MapBlock *block = server->m_env->getMap().getBlockNoCreateNoEx(p);
681 bool surely_not_found_on_disk = false;
682 bool block_is_invalid = false;
685 // Reset usage timer, this block will be of use in the future.
686 block->resetUsageTimer();
688 // Block is dummy if data doesn't exist.
689 // It means it has been not found from disk and not generated
692 surely_not_found_on_disk = true;
695 // Block is valid if lighting is up-to-date and data exists
696 if(block->isValid() == false)
698 block_is_invalid = true;
701 /*if(block->isFullyGenerated() == false)
703 block_is_invalid = true;
708 ServerMap *map = (ServerMap*)(&server->m_env->getMap());
709 v2s16 chunkpos = map->sector_to_chunk(p2d);
710 if(map->chunkNonVolatile(chunkpos) == false)
711 block_is_invalid = true;
713 if(block->isGenerated() == false)
714 block_is_invalid = true;
717 If block is not close, don't send it unless it is near
720 Block is near ground level if night-time mesh
721 differs from day-time mesh.
725 if(block->getDayNightDiff() == false)
732 If block has been marked to not exist on disk (dummy)
733 and generating new ones is not wanted, skip block.
735 if(generate == false && surely_not_found_on_disk == true)
742 Add inexistent block to emerge queue.
744 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
746 //TODO: Get value from somewhere
747 // Allow only one block in emerge queue
748 //if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
749 // Allow two blocks in queue per client
750 //if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
752 // Make it more responsive when needing to generate stuff
753 if(surely_not_found_on_disk)
755 if(server->m_emerge_queue.peerItemCount(peer_id) < max_emerge)
757 //infostream<<"Adding block to emerge queue"<<std::endl;
759 // Add it to the emerge queue and trigger the thread
762 if(generate == false)
763 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
765 server->m_emerge_queue.addBlock(peer_id, p, flags);
766 server->m_emergethread.trigger();
768 if(nearest_emerged_d == -1)
769 nearest_emerged_d = d;
771 if(nearest_emergefull_d == -1)
772 nearest_emergefull_d = d;
779 if(nearest_sent_d == -1)
783 Add block to send queue
786 /*errorstream<<"sending from d="<<d<<" to "
787 <<server->getPlayerName(peer_id)<<std::endl;*/
789 PrioritySortedBlockTransfer q((float)d, p, peer_id);
793 num_blocks_selected += 1;
798 //infostream<<"Stopped at "<<d<<std::endl;
800 // If nothing was found for sending and nothing was queued for
801 // emerging, continue next time browsing from here
802 if(nearest_emerged_d != -1){
803 new_nearest_unsent_d = nearest_emerged_d;
804 } else if(nearest_emergefull_d != -1){
805 new_nearest_unsent_d = nearest_emergefull_d;
807 if(d > g_settings->getS16("max_block_send_distance")){
808 new_nearest_unsent_d = 0;
809 m_nothing_to_send_pause_timer = 2.0;
810 /*infostream<<"GetNextBlocks(): d wrapped around for "
811 <<server->getPlayerName(peer_id)
812 <<"; setting to 0 and pausing"<<std::endl;*/
814 if(nearest_sent_d != -1)
815 new_nearest_unsent_d = nearest_sent_d;
817 new_nearest_unsent_d = d;
821 if(new_nearest_unsent_d != -1)
822 m_nearest_unsent_d = new_nearest_unsent_d;
824 /*timer_result = timer.stop(true);
825 if(timer_result != 0)
826 infostream<<"GetNextBlocks duration: "<<timer_result<<" (!=0)"<<std::endl;*/
829 void RemoteClient::GotBlock(v3s16 p)
831 if(m_blocks_sending.find(p) != NULL)
832 m_blocks_sending.remove(p);
835 /*infostream<<"RemoteClient::GotBlock(): Didn't find in"
836 " m_blocks_sending"<<std::endl;*/
837 m_excess_gotblocks++;
839 m_blocks_sent.insert(p, true);
842 void RemoteClient::SentBlock(v3s16 p)
844 if(m_blocks_sending.find(p) == NULL)
845 m_blocks_sending.insert(p, 0.0);
847 infostream<<"RemoteClient::SentBlock(): Sent block"
848 " already in m_blocks_sending"<<std::endl;
851 void RemoteClient::SetBlockNotSent(v3s16 p)
853 m_nearest_unsent_d = 0;
855 if(m_blocks_sending.find(p) != NULL)
856 m_blocks_sending.remove(p);
857 if(m_blocks_sent.find(p) != NULL)
858 m_blocks_sent.remove(p);
861 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
863 m_nearest_unsent_d = 0;
865 for(core::map<v3s16, MapBlock*>::Iterator
866 i = blocks.getIterator();
867 i.atEnd()==false; i++)
869 v3s16 p = i.getNode()->getKey();
871 if(m_blocks_sending.find(p) != NULL)
872 m_blocks_sending.remove(p);
873 if(m_blocks_sent.find(p) != NULL)
874 m_blocks_sent.remove(p);
882 PlayerInfo::PlayerInfo()
888 void PlayerInfo::PrintLine(std::ostream *s)
891 (*s)<<"\""<<name<<"\" ("
892 <<(position.X/10)<<","<<(position.Y/10)
893 <<","<<(position.Z/10)<<") ";
895 (*s)<<" avg_rtt="<<avg_rtt;
904 const std::string &path_world,
905 const std::string &path_config,
906 const SubgameSpec &gamespec,
907 bool simple_singleplayer_mode
909 m_path_world(path_world),
910 m_path_config(path_config),
911 m_gamespec(gamespec),
912 m_simple_singleplayer_mode(simple_singleplayer_mode),
913 m_async_fatal_error(""),
915 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
916 m_banmanager(path_world+DIR_DELIM+"ipban.txt"),
918 m_itemdef(createItemDefManager()),
919 m_nodedef(createNodeDefManager()),
920 m_craftdef(createCraftDefManager()),
921 m_event(new EventManager()),
923 m_emergethread(this),
924 m_time_of_day_send_timer(0),
926 m_shutdown_requested(false),
927 m_ignore_map_edit_events(false),
928 m_ignore_map_edit_events_peer_id(0)
930 m_liquid_transform_timer = 0.0;
931 m_print_info_timer = 0.0;
932 m_objectdata_timer = 0.0;
933 m_emergethread_trigger_timer = 0.0;
934 m_savemap_timer = 0.0;
938 m_step_dtime_mutex.Init();
942 throw ServerError("Supplied empty world path");
944 if(!gamespec.isValid())
945 throw ServerError("Supplied invalid gamespec");
947 infostream<<"Server created for gameid \""<<m_gamespec.id<<"\"";
948 if(m_simple_singleplayer_mode)
949 infostream<<" in simple singleplayer mode"<<std::endl;
951 infostream<<std::endl;
952 infostream<<"- world: "<<m_path_world<<std::endl;
953 infostream<<"- config: "<<m_path_config<<std::endl;
954 infostream<<"- game: "<<m_gamespec.path<<std::endl;
956 // Add world mod search path
957 m_modspaths.push_front(m_path_world + DIR_DELIM + "worldmods");
958 // Add addon mod search path
959 for(std::set<std::string>::const_iterator i = m_gamespec.mods_paths.begin();
960 i != m_gamespec.mods_paths.end(); i++)
961 m_modspaths.push_front((*i));
963 // Print out mod search paths
964 for(core::list<std::string>::Iterator i = m_modspaths.begin();
965 i != m_modspaths.end(); i++){
966 std::string modspath = *i;
967 infostream<<"- mods: "<<modspath<<std::endl;
970 // Path to builtin.lua
971 std::string builtinpath = getBuiltinLuaPath() + DIR_DELIM + "builtin.lua";
973 // Create world if it doesn't exist
974 if(!initializeWorld(m_path_world, m_gamespec.id))
975 throw ServerError("Failed to initialize world");
978 JMutexAutoLock envlock(m_env_mutex);
979 JMutexAutoLock conlock(m_con_mutex);
981 // Initialize scripting
983 infostream<<"Server: Initializing Lua"<<std::endl;
984 m_lua = script_init();
987 scriptapi_export(m_lua, this);
988 // Load and run builtin.lua
989 infostream<<"Server: Loading builtin.lua [\""
990 <<builtinpath<<"\"]"<<std::endl;
991 bool success = scriptapi_loadmod(m_lua, builtinpath, "__builtin");
993 errorstream<<"Server: Failed to load and run "
994 <<builtinpath<<std::endl;
995 throw ModError("Failed to load and run "+builtinpath);
997 // Find mods in mod search paths
998 m_mods = getMods(m_modspaths);
1000 infostream<<"Server: Loading mods: ";
1001 for(core::list<ModSpec>::Iterator i = m_mods.begin();
1002 i != m_mods.end(); i++){
1003 const ModSpec &mod = *i;
1004 infostream<<mod.name<<" ";
1006 infostream<<std::endl;
1007 // Load and run "mod" scripts
1008 for(core::list<ModSpec>::Iterator i = m_mods.begin();
1009 i != m_mods.end(); i++){
1010 const ModSpec &mod = *i;
1011 std::string scriptpath = mod.path + DIR_DELIM + "init.lua";
1012 infostream<<" ["<<padStringRight(mod.name, 12)<<"] [\""
1013 <<scriptpath<<"\"]"<<std::endl;
1014 bool success = scriptapi_loadmod(m_lua, scriptpath, mod.name);
1016 errorstream<<"Server: Failed to load and run "
1017 <<scriptpath<<std::endl;
1018 throw ModError("Failed to load and run "+scriptpath);
1022 // Read Textures and calculate sha1 sums
1025 // Apply item aliases in the node definition manager
1026 m_nodedef->updateAliases(m_itemdef);
1028 // Initialize Environment
1030 m_env = new ServerEnvironment(new ServerMap(path_world, this), m_lua,
1033 // Give environment reference to scripting api
1034 scriptapi_add_environment(m_lua, m_env);
1036 // Register us to receive map edit events
1037 m_env->getMap().addEventReceiver(this);
1039 // If file exists, load environment metadata
1040 if(fs::PathExists(m_path_world+DIR_DELIM+"env_meta.txt"))
1042 infostream<<"Server: Loading environment metadata"<<std::endl;
1043 m_env->loadMeta(m_path_world);
1047 infostream<<"Server: Loading players"<<std::endl;
1048 m_env->deSerializePlayers(m_path_world);
1051 Add some test ActiveBlockModifiers to environment
1053 add_legacy_abms(m_env, m_nodedef);
1058 infostream<<"Server destructing"<<std::endl;
1061 Send shutdown message
1064 JMutexAutoLock conlock(m_con_mutex);
1066 std::wstring line = L"*** Server shutting down";
1069 Send the message to clients
1071 for(core::map<u16, RemoteClient*>::Iterator
1072 i = m_clients.getIterator();
1073 i.atEnd() == false; i++)
1075 // Get client and check that it is valid
1076 RemoteClient *client = i.getNode()->getValue();
1077 assert(client->peer_id == i.getNode()->getKey());
1078 if(client->serialization_version == SER_FMT_VER_INVALID)
1082 SendChatMessage(client->peer_id, line);
1084 catch(con::PeerNotFoundException &e)
1090 JMutexAutoLock envlock(m_env_mutex);
1095 infostream<<"Server: Saving players"<<std::endl;
1096 m_env->serializePlayers(m_path_world);
1099 Save environment metadata
1101 infostream<<"Server: Saving environment metadata"<<std::endl;
1102 m_env->saveMeta(m_path_world);
1114 JMutexAutoLock clientslock(m_con_mutex);
1116 for(core::map<u16, RemoteClient*>::Iterator
1117 i = m_clients.getIterator();
1118 i.atEnd() == false; i++)
1121 // NOTE: These are removed by env destructor
1123 u16 peer_id = i.getNode()->getKey();
1124 JMutexAutoLock envlock(m_env_mutex);
1125 m_env->removePlayer(peer_id);
1129 delete i.getNode()->getValue();
1133 // Delete things in the reverse order of creation
1140 // Deinitialize scripting
1141 infostream<<"Server: Deinitializing scripting"<<std::endl;
1142 script_deinit(m_lua);
1145 void Server::start(unsigned short port)
1147 DSTACK(__FUNCTION_NAME);
1148 infostream<<"Starting server on port "<<port<<"..."<<std::endl;
1150 // Stop thread if already running
1153 // Initialize connection
1154 m_con.SetTimeoutMs(30);
1158 m_thread.setRun(true);
1161 // ASCII art for the win!
1163 <<" .__ __ __ "<<std::endl
1164 <<" _____ |__| ____ _____/ |_ ____ _______/ |_ "<<std::endl
1165 <<" / \\| |/ \\_/ __ \\ __\\/ __ \\ / ___/\\ __\\"<<std::endl
1166 <<"| Y Y \\ | | \\ ___/| | \\ ___/ \\___ \\ | | "<<std::endl
1167 <<"|__|_| /__|___| /\\___ >__| \\___ >____ > |__| "<<std::endl
1168 <<" \\/ \\/ \\/ \\/ \\/ "<<std::endl;
1169 actionstream<<"World at ["<<m_path_world<<"]"<<std::endl;
1170 actionstream<<"Server for gameid=\""<<m_gamespec.id
1171 <<"\" listening on port "<<port<<"."<<std::endl;
1176 DSTACK(__FUNCTION_NAME);
1178 infostream<<"Server: Stopping and waiting threads"<<std::endl;
1180 // Stop threads (set run=false first so both start stopping)
1181 m_thread.setRun(false);
1182 m_emergethread.setRun(false);
1184 m_emergethread.stop();
1186 infostream<<"Server: Threads stopped"<<std::endl;
1189 void Server::step(float dtime)
1191 DSTACK(__FUNCTION_NAME);
1196 JMutexAutoLock lock(m_step_dtime_mutex);
1197 m_step_dtime += dtime;
1199 // Throw if fatal error occurred in thread
1200 std::string async_err = m_async_fatal_error.get();
1201 if(async_err != ""){
1202 throw ServerError(async_err);
1206 void Server::AsyncRunStep()
1208 DSTACK(__FUNCTION_NAME);
1210 g_profiler->add("Server::AsyncRunStep (num)", 1);
1214 JMutexAutoLock lock1(m_step_dtime_mutex);
1215 dtime = m_step_dtime;
1219 // Send blocks to clients
1226 g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
1228 //infostream<<"Server steps "<<dtime<<std::endl;
1229 //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1232 JMutexAutoLock lock1(m_step_dtime_mutex);
1233 m_step_dtime -= dtime;
1240 m_uptime.set(m_uptime.get() + dtime);
1244 // Process connection's timeouts
1245 JMutexAutoLock lock2(m_con_mutex);
1246 ScopeProfiler sp(g_profiler, "Server: connection timeout processing");
1247 m_con.RunTimeouts(dtime);
1251 // This has to be called so that the client list gets synced
1252 // with the peer list of the connection
1253 handlePeerChanges();
1257 Update time of day and overall game time
1260 JMutexAutoLock envlock(m_env_mutex);
1262 m_env->setTimeOfDaySpeed(g_settings->getFloat("time_speed"));
1265 Send to clients at constant intervals
1268 m_time_of_day_send_timer -= dtime;
1269 if(m_time_of_day_send_timer < 0.0)
1271 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
1273 //JMutexAutoLock envlock(m_env_mutex);
1274 JMutexAutoLock conlock(m_con_mutex);
1276 for(core::map<u16, RemoteClient*>::Iterator
1277 i = m_clients.getIterator();
1278 i.atEnd() == false; i++)
1280 RemoteClient *client = i.getNode()->getValue();
1281 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1282 m_env->getTimeOfDay(), g_settings->getFloat("time_speed"));
1284 m_con.Send(client->peer_id, 0, data, true);
1290 JMutexAutoLock lock(m_env_mutex);
1292 ScopeProfiler sp(g_profiler, "SEnv step");
1293 ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
1297 const float map_timer_and_unload_dtime = 2.92;
1298 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
1300 JMutexAutoLock lock(m_env_mutex);
1301 // Run Map's timers and unload unused data
1302 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
1303 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
1304 g_settings->getFloat("server_unload_unused_data_timeout"));
1315 JMutexAutoLock lock(m_env_mutex);
1316 JMutexAutoLock lock2(m_con_mutex);
1318 ScopeProfiler sp(g_profiler, "Server: handle players");
1320 for(core::map<u16, RemoteClient*>::Iterator
1321 i = m_clients.getIterator();
1322 i.atEnd() == false; i++)
1324 RemoteClient *client = i.getNode()->getValue();
1325 PlayerSAO *playersao = getPlayerSAO(client->peer_id);
1326 if(playersao == NULL)
1330 Handle player HPs (die if hp=0)
1332 if(playersao->getHP() == 0 && playersao->m_hp_not_sent)
1333 DiePlayer(client->peer_id);
1336 Send player inventories and HPs if necessary
1338 if(playersao->m_teleported){
1339 SendMovePlayer(client->peer_id);
1340 playersao->m_teleported = false;
1342 if(playersao->m_inventory_not_sent){
1343 UpdateCrafting(client->peer_id);
1344 SendInventory(client->peer_id);
1346 if(playersao->m_hp_not_sent){
1347 SendPlayerHP(client->peer_id);
1352 /* Transform liquids */
1353 m_liquid_transform_timer += dtime;
1354 if(m_liquid_transform_timer >= 1.00)
1356 m_liquid_transform_timer -= 1.00;
1358 JMutexAutoLock lock(m_env_mutex);
1360 ScopeProfiler sp(g_profiler, "Server: liquid transform");
1362 core::map<v3s16, MapBlock*> modified_blocks;
1363 m_env->getMap().transformLiquids(modified_blocks);
1368 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1369 ServerMap &map = ((ServerMap&)m_env->getMap());
1370 map.updateLighting(modified_blocks, lighting_modified_blocks);
1372 // Add blocks modified by lighting to modified_blocks
1373 for(core::map<v3s16, MapBlock*>::Iterator
1374 i = lighting_modified_blocks.getIterator();
1375 i.atEnd() == false; i++)
1377 MapBlock *block = i.getNode()->getValue();
1378 modified_blocks.insert(block->getPos(), block);
1382 Set the modified blocks unsent for all the clients
1385 JMutexAutoLock lock2(m_con_mutex);
1387 for(core::map<u16, RemoteClient*>::Iterator
1388 i = m_clients.getIterator();
1389 i.atEnd() == false; i++)
1391 RemoteClient *client = i.getNode()->getValue();
1393 if(modified_blocks.size() > 0)
1395 // Remove block from sent history
1396 client->SetBlocksNotSent(modified_blocks);
1401 // Periodically print some info
1403 float &counter = m_print_info_timer;
1409 JMutexAutoLock lock2(m_con_mutex);
1411 if(m_clients.size() != 0)
1412 infostream<<"Players:"<<std::endl;
1413 for(core::map<u16, RemoteClient*>::Iterator
1414 i = m_clients.getIterator();
1415 i.atEnd() == false; i++)
1417 //u16 peer_id = i.getNode()->getKey();
1418 RemoteClient *client = i.getNode()->getValue();
1419 Player *player = m_env->getPlayer(client->peer_id);
1422 infostream<<"* "<<player->getName()<<"\t";
1423 client->PrintInfo(infostream);
1428 //if(g_settings->getBool("enable_experimental"))
1432 Check added and deleted active objects
1435 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
1436 JMutexAutoLock envlock(m_env_mutex);
1437 JMutexAutoLock conlock(m_con_mutex);
1439 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
1441 // Radius inside which objects are active
1442 s16 radius = g_settings->getS16("active_object_send_range_blocks");
1443 radius *= MAP_BLOCKSIZE;
1445 for(core::map<u16, RemoteClient*>::Iterator
1446 i = m_clients.getIterator();
1447 i.atEnd() == false; i++)
1449 RemoteClient *client = i.getNode()->getValue();
1451 // If definitions and textures have not been sent, don't
1452 // send objects either
1453 if(!client->definitions_sent)
1456 Player *player = m_env->getPlayer(client->peer_id);
1459 // This can happen if the client timeouts somehow
1460 /*infostream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
1462 <<" has no associated player"<<std::endl;*/
1465 v3s16 pos = floatToInt(player->getPosition(), BS);
1467 core::map<u16, bool> removed_objects;
1468 core::map<u16, bool> added_objects;
1469 m_env->getRemovedActiveObjects(pos, radius,
1470 client->m_known_objects, removed_objects);
1471 m_env->getAddedActiveObjects(pos, radius,
1472 client->m_known_objects, added_objects);
1474 // Ignore if nothing happened
1475 if(removed_objects.size() == 0 && added_objects.size() == 0)
1477 //infostream<<"active objects: none changed"<<std::endl;
1481 std::string data_buffer;
1485 // Handle removed objects
1486 writeU16((u8*)buf, removed_objects.size());
1487 data_buffer.append(buf, 2);
1488 for(core::map<u16, bool>::Iterator
1489 i = removed_objects.getIterator();
1490 i.atEnd()==false; i++)
1493 u16 id = i.getNode()->getKey();
1494 ServerActiveObject* obj = m_env->getActiveObject(id);
1496 // Add to data buffer for sending
1497 writeU16((u8*)buf, i.getNode()->getKey());
1498 data_buffer.append(buf, 2);
1500 // Remove from known objects
1501 client->m_known_objects.remove(i.getNode()->getKey());
1503 if(obj && obj->m_known_by_count > 0)
1504 obj->m_known_by_count--;
1507 // Handle added objects
1508 writeU16((u8*)buf, added_objects.size());
1509 data_buffer.append(buf, 2);
1510 for(core::map<u16, bool>::Iterator
1511 i = added_objects.getIterator();
1512 i.atEnd()==false; i++)
1515 u16 id = i.getNode()->getKey();
1516 ServerActiveObject* obj = m_env->getActiveObject(id);
1519 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1521 infostream<<"WARNING: "<<__FUNCTION_NAME
1522 <<": NULL object"<<std::endl;
1524 type = obj->getSendType();
1526 // Add to data buffer for sending
1527 writeU16((u8*)buf, id);
1528 data_buffer.append(buf, 2);
1529 writeU8((u8*)buf, type);
1530 data_buffer.append(buf, 1);
1533 data_buffer.append(serializeLongString(
1534 obj->getClientInitializationData()));
1536 data_buffer.append(serializeLongString(""));
1538 // Add to known objects
1539 client->m_known_objects.insert(i.getNode()->getKey(), false);
1542 obj->m_known_by_count++;
1546 SharedBuffer<u8> reply(2 + data_buffer.size());
1547 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1548 memcpy((char*)&reply[2], data_buffer.c_str(),
1549 data_buffer.size());
1551 m_con.Send(client->peer_id, 0, reply, true);
1553 verbosestream<<"Server: Sent object remove/add: "
1554 <<removed_objects.size()<<" removed, "
1555 <<added_objects.size()<<" added, "
1556 <<"packet size is "<<reply.getSize()<<std::endl;
1561 Collect a list of all the objects known by the clients
1562 and report it back to the environment.
1565 core::map<u16, bool> all_known_objects;
1567 for(core::map<u16, RemoteClient*>::Iterator
1568 i = m_clients.getIterator();
1569 i.atEnd() == false; i++)
1571 RemoteClient *client = i.getNode()->getValue();
1572 // Go through all known objects of client
1573 for(core::map<u16, bool>::Iterator
1574 i = client->m_known_objects.getIterator();
1575 i.atEnd()==false; i++)
1577 u16 id = i.getNode()->getKey();
1578 all_known_objects[id] = true;
1582 m_env->setKnownActiveObjects(whatever);
1588 Send object messages
1591 JMutexAutoLock envlock(m_env_mutex);
1592 JMutexAutoLock conlock(m_con_mutex);
1594 ScopeProfiler sp(g_profiler, "Server: sending object messages");
1597 // Value = data sent by object
1598 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1600 // Get active object messages from environment
1603 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
1607 core::list<ActiveObjectMessage>* message_list = NULL;
1608 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1609 n = buffered_messages.find(aom.id);
1612 message_list = new core::list<ActiveObjectMessage>;
1613 buffered_messages.insert(aom.id, message_list);
1617 message_list = n->getValue();
1619 message_list->push_back(aom);
1622 // Route data to every client
1623 for(core::map<u16, RemoteClient*>::Iterator
1624 i = m_clients.getIterator();
1625 i.atEnd()==false; i++)
1627 RemoteClient *client = i.getNode()->getValue();
1628 std::string reliable_data;
1629 std::string unreliable_data;
1630 // Go through all objects in message buffer
1631 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1632 j = buffered_messages.getIterator();
1633 j.atEnd()==false; j++)
1635 // If object is not known by client, skip it
1636 u16 id = j.getNode()->getKey();
1637 if(client->m_known_objects.find(id) == NULL)
1639 // Get message list of object
1640 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1641 // Go through every message
1642 for(core::list<ActiveObjectMessage>::Iterator
1643 k = list->begin(); k != list->end(); k++)
1645 // Compose the full new data with header
1646 ActiveObjectMessage aom = *k;
1647 std::string new_data;
1650 writeU16((u8*)&buf[0], aom.id);
1651 new_data.append(buf, 2);
1653 new_data += serializeString(aom.datastring);
1654 // Add data to buffer
1656 reliable_data += new_data;
1658 unreliable_data += new_data;
1662 reliable_data and unreliable_data are now ready.
1665 if(reliable_data.size() > 0)
1667 SharedBuffer<u8> reply(2 + reliable_data.size());
1668 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1669 memcpy((char*)&reply[2], reliable_data.c_str(),
1670 reliable_data.size());
1672 m_con.Send(client->peer_id, 0, reply, true);
1674 if(unreliable_data.size() > 0)
1676 SharedBuffer<u8> reply(2 + unreliable_data.size());
1677 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1678 memcpy((char*)&reply[2], unreliable_data.c_str(),
1679 unreliable_data.size());
1680 // Send as unreliable
1681 m_con.Send(client->peer_id, 0, reply, false);
1684 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1686 infostream<<"Server: Size of object message data: "
1687 <<"reliable: "<<reliable_data.size()
1688 <<", unreliable: "<<unreliable_data.size()
1693 // Clear buffered_messages
1694 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1695 i = buffered_messages.getIterator();
1696 i.atEnd()==false; i++)
1698 delete i.getNode()->getValue();
1702 } // enable_experimental
1705 Send queued-for-sending map edit events.
1708 // We will be accessing the environment and the connection
1709 JMutexAutoLock lock(m_env_mutex);
1710 JMutexAutoLock conlock(m_con_mutex);
1712 // Don't send too many at a time
1715 // Single change sending is disabled if queue size is not small
1716 bool disable_single_change_sending = false;
1717 if(m_unsent_map_edit_queue.size() >= 4)
1718 disable_single_change_sending = true;
1720 int event_count = m_unsent_map_edit_queue.size();
1722 // We'll log the amount of each
1725 while(m_unsent_map_edit_queue.size() != 0)
1727 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1729 // Players far away from the change are stored here.
1730 // Instead of sending the changes, MapBlocks are set not sent
1732 core::list<u16> far_players;
1734 if(event->type == MEET_ADDNODE)
1736 //infostream<<"Server: MEET_ADDNODE"<<std::endl;
1737 prof.add("MEET_ADDNODE", 1);
1738 if(disable_single_change_sending)
1739 sendAddNode(event->p, event->n, event->already_known_by_peer,
1742 sendAddNode(event->p, event->n, event->already_known_by_peer,
1745 else if(event->type == MEET_REMOVENODE)
1747 //infostream<<"Server: MEET_REMOVENODE"<<std::endl;
1748 prof.add("MEET_REMOVENODE", 1);
1749 if(disable_single_change_sending)
1750 sendRemoveNode(event->p, event->already_known_by_peer,
1753 sendRemoveNode(event->p, event->already_known_by_peer,
1756 else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
1758 infostream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
1759 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
1760 setBlockNotSent(event->p);
1762 else if(event->type == MEET_OTHER)
1764 infostream<<"Server: MEET_OTHER"<<std::endl;
1765 prof.add("MEET_OTHER", 1);
1766 for(core::map<v3s16, bool>::Iterator
1767 i = event->modified_blocks.getIterator();
1768 i.atEnd()==false; i++)
1770 v3s16 p = i.getNode()->getKey();
1776 prof.add("unknown", 1);
1777 infostream<<"WARNING: Server: Unknown MapEditEvent "
1778 <<((u32)event->type)<<std::endl;
1782 Set blocks not sent to far players
1784 if(far_players.size() > 0)
1786 // Convert list format to that wanted by SetBlocksNotSent
1787 core::map<v3s16, MapBlock*> modified_blocks2;
1788 for(core::map<v3s16, bool>::Iterator
1789 i = event->modified_blocks.getIterator();
1790 i.atEnd()==false; i++)
1792 v3s16 p = i.getNode()->getKey();
1793 modified_blocks2.insert(p,
1794 m_env->getMap().getBlockNoCreateNoEx(p));
1796 // Set blocks not sent
1797 for(core::list<u16>::Iterator
1798 i = far_players.begin();
1799 i != far_players.end(); i++)
1802 RemoteClient *client = getClient(peer_id);
1805 client->SetBlocksNotSent(modified_blocks2);
1811 /*// Don't send too many at a time
1813 if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
1817 if(event_count >= 5){
1818 infostream<<"Server: MapEditEvents:"<<std::endl;
1819 prof.print(infostream);
1820 } else if(event_count != 0){
1821 verbosestream<<"Server: MapEditEvents:"<<std::endl;
1822 prof.print(verbosestream);
1828 Trigger emergethread (it somehow gets to a non-triggered but
1829 bysy state sometimes)
1832 float &counter = m_emergethread_trigger_timer;
1838 m_emergethread.trigger();
1842 // Save map, players and auth stuff
1844 float &counter = m_savemap_timer;
1846 if(counter >= g_settings->getFloat("server_map_save_interval"))
1849 JMutexAutoLock lock(m_env_mutex);
1851 ScopeProfiler sp(g_profiler, "Server: saving stuff");
1854 if(m_banmanager.isModified())
1855 m_banmanager.save();
1857 // Save changed parts of map
1858 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
1861 m_env->serializePlayers(m_path_world);
1863 // Save environment metadata
1864 m_env->saveMeta(m_path_world);
1869 void Server::Receive()
1871 DSTACK(__FUNCTION_NAME);
1872 SharedBuffer<u8> data;
1877 JMutexAutoLock conlock(m_con_mutex);
1878 datasize = m_con.Receive(peer_id, data);
1881 // This has to be called so that the client list gets synced
1882 // with the peer list of the connection
1883 handlePeerChanges();
1885 ProcessData(*data, datasize, peer_id);
1887 catch(con::InvalidIncomingDataException &e)
1889 infostream<<"Server::Receive(): "
1890 "InvalidIncomingDataException: what()="
1891 <<e.what()<<std::endl;
1893 catch(con::PeerNotFoundException &e)
1895 //NOTE: This is not needed anymore
1897 // The peer has been disconnected.
1898 // Find the associated player and remove it.
1900 /*JMutexAutoLock envlock(m_env_mutex);
1902 infostream<<"ServerThread: peer_id="<<peer_id
1903 <<" has apparently closed connection. "
1904 <<"Removing player."<<std::endl;
1906 m_env->removePlayer(peer_id);*/
1910 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1912 DSTACK(__FUNCTION_NAME);
1913 // Environment is locked first.
1914 JMutexAutoLock envlock(m_env_mutex);
1915 JMutexAutoLock conlock(m_con_mutex);
1917 ScopeProfiler sp(g_profiler, "Server::ProcessData");
1920 Address address = m_con.GetPeerAddress(peer_id);
1921 std::string addr_s = address.serializeString();
1923 // drop player if is ip is banned
1924 if(m_banmanager.isIpBanned(addr_s)){
1925 infostream<<"Server: A banned client tried to connect from "
1926 <<addr_s<<"; banned name was "
1927 <<m_banmanager.getBanName(addr_s)<<std::endl;
1928 // This actually doesn't seem to transfer to the client
1929 SendAccessDenied(m_con, peer_id,
1930 L"Your ip is banned. Banned name was "
1931 +narrow_to_wide(m_banmanager.getBanName(addr_s)));
1932 m_con.DeletePeer(peer_id);
1936 catch(con::PeerNotFoundException &e)
1938 infostream<<"Server::ProcessData(): Cancelling: peer "
1939 <<peer_id<<" not found"<<std::endl;
1943 std::string addr_s = m_con.GetPeerAddress(peer_id).serializeString();
1945 u8 peer_ser_ver = getClient(peer_id)->serialization_version;
1953 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1955 if(command == TOSERVER_INIT)
1957 // [0] u16 TOSERVER_INIT
1958 // [2] u8 SER_FMT_VER_HIGHEST
1959 // [3] u8[20] player_name
1960 // [23] u8[28] password <--- can be sent without this, from old versions
1962 if(datasize < 2+1+PLAYERNAME_SIZE)
1965 verbosestream<<"Server: Got TOSERVER_INIT from "
1966 <<peer_id<<std::endl;
1968 // First byte after command is maximum supported
1969 // serialization version
1970 u8 client_max = data[2];
1971 u8 our_max = SER_FMT_VER_HIGHEST;
1972 // Use the highest version supported by both
1973 u8 deployed = core::min_(client_max, our_max);
1974 // If it's lower than the lowest supported, give up.
1975 if(deployed < SER_FMT_VER_LOWEST)
1976 deployed = SER_FMT_VER_INVALID;
1978 //peer->serialization_version = deployed;
1979 getClient(peer_id)->pending_serialization_version = deployed;
1981 if(deployed == SER_FMT_VER_INVALID)
1983 actionstream<<"Server: A mismatched client tried to connect from "
1984 <<addr_s<<std::endl;
1985 infostream<<"Server: Cannot negotiate "
1986 "serialization version with peer "
1987 <<peer_id<<std::endl;
1988 SendAccessDenied(m_con, peer_id, std::wstring(
1989 L"Your client's version is not supported.\n"
1990 L"Server version is ")
1991 + narrow_to_wide(VERSION_STRING) + L"."
1997 Read and check network protocol version
2000 u16 net_proto_version = 0;
2001 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
2003 net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
2006 getClient(peer_id)->net_proto_version = net_proto_version;
2008 if(net_proto_version == 0)
2010 actionstream<<"Server: An old tried to connect from "<<addr_s
2012 SendAccessDenied(m_con, peer_id, std::wstring(
2013 L"Your client's version is not supported.\n"
2014 L"Server version is ")
2015 + narrow_to_wide(VERSION_STRING) + L"."
2020 if(g_settings->getBool("strict_protocol_version_checking"))
2022 if(net_proto_version != PROTOCOL_VERSION)
2024 actionstream<<"Server: A mismatched client tried to connect"
2025 <<" from "<<addr_s<<std::endl;
2026 SendAccessDenied(m_con, peer_id, std::wstring(
2027 L"Your client's version is not supported.\n"
2028 L"Server version is ")
2029 + narrow_to_wide(VERSION_STRING) + L",\n"
2030 + L"server's PROTOCOL_VERSION is "
2031 + narrow_to_wide(itos(PROTOCOL_VERSION))
2032 + L", client's PROTOCOL_VERSION is "
2033 + narrow_to_wide(itos(net_proto_version))
2044 char playername[PLAYERNAME_SIZE];
2045 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
2047 playername[i] = data[3+i];
2049 playername[PLAYERNAME_SIZE-1] = 0;
2051 if(playername[0]=='\0')
2053 actionstream<<"Server: Player with an empty name "
2054 <<"tried to connect from "<<addr_s<<std::endl;
2055 SendAccessDenied(m_con, peer_id,
2060 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
2062 actionstream<<"Server: Player with an invalid name "
2063 <<"tried to connect from "<<addr_s<<std::endl;
2064 SendAccessDenied(m_con, peer_id,
2065 L"Name contains unallowed characters");
2069 infostream<<"Server: New connection: \""<<playername<<"\" from "
2070 <<m_con.GetPeerAddress(peer_id).serializeString()<<std::endl;
2073 char password[PASSWORD_SIZE];
2074 if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
2076 // old version - assume blank password
2081 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2083 password[i] = data[23+i];
2085 password[PASSWORD_SIZE-1] = 0;
2088 if(!base64_is_valid(password)){
2089 infostream<<"Server: "<<playername<<" supplied invalid password hash"<<std::endl;
2090 SendAccessDenied(m_con, peer_id, L"Invalid password hash");
2094 std::string checkpwd;
2095 bool has_auth = scriptapi_get_auth(m_lua, playername, &checkpwd, NULL);
2098 std::wstring raw_default_password =
2099 narrow_to_wide(g_settings->get("default_password"));
2100 std::string use_password =
2101 translatePassword(playername, raw_default_password);
2103 // If default_password is empty, allow any initial password
2104 if (raw_default_password.length() == 0)
2105 use_password = password;
2107 scriptapi_create_auth(m_lua, playername, use_password);
2110 has_auth = scriptapi_get_auth(m_lua, playername, &checkpwd, NULL);
2113 SendAccessDenied(m_con, peer_id, L"Not allowed to login");
2117 if(password != checkpwd){
2118 infostream<<"Server: peer_id="<<peer_id
2119 <<": supplied invalid password for "
2120 <<playername<<std::endl;
2121 SendAccessDenied(m_con, peer_id, L"Invalid password");
2125 // Do not allow multiple players in simple singleplayer mode.
2126 // This isn't a perfect way to do it, but will suffice for now.
2127 if(m_simple_singleplayer_mode && m_clients.size() > 1){
2128 infostream<<"Server: Not allowing another client to connect in"
2129 <<" simple singleplayer mode"<<std::endl;
2130 SendAccessDenied(m_con, peer_id,
2131 L"Running in simple singleplayer mode.");
2135 // Enforce user limit.
2136 // Don't enforce for users that have some admin right
2137 if(m_clients.size() >= g_settings->getU16("max_users") &&
2138 !checkPriv(playername, "server") &&
2139 !checkPriv(playername, "ban") &&
2140 !checkPriv(playername, "privs") &&
2141 !checkPriv(playername, "password") &&
2142 playername != g_settings->get("name"))
2144 actionstream<<"Server: "<<playername<<" tried to join, but there"
2145 <<" are already max_users="
2146 <<g_settings->getU16("max_users")<<" players."<<std::endl;
2147 SendAccessDenied(m_con, peer_id, L"Too many users.");
2152 PlayerSAO *playersao = emergePlayer(playername, peer_id);
2154 // If failed, cancel
2155 if(playersao == NULL)
2157 errorstream<<"Server: peer_id="<<peer_id
2158 <<": failed to emerge player"<<std::endl;
2163 Answer with a TOCLIENT_INIT
2166 SharedBuffer<u8> reply(2+1+6+8);
2167 writeU16(&reply[0], TOCLIENT_INIT);
2168 writeU8(&reply[2], deployed);
2169 writeV3S16(&reply[2+1], floatToInt(playersao->getPlayer()->getPosition()+v3f(0,BS/2,0), BS));
2170 writeU64(&reply[2+1+6], m_env->getServerMap().getSeed());
2173 m_con.Send(peer_id, 0, reply, true);
2177 Send complete position information
2179 SendMovePlayer(peer_id);
2184 if(command == TOSERVER_INIT2)
2186 verbosestream<<"Server: Got TOSERVER_INIT2 from "
2187 <<peer_id<<std::endl;
2189 Player *player = m_env->getPlayer(peer_id);
2191 verbosestream<<"Server: TOSERVER_INIT2: "
2192 <<"Player not found; ignoring."<<std::endl;
2196 getClient(peer_id)->serialization_version
2197 = getClient(peer_id)->pending_serialization_version;
2200 Send some initialization data
2203 infostream<<"Server: Sending content to "
2204 <<getPlayerName(peer_id)<<std::endl;
2206 // Send item definitions
2207 SendItemDef(m_con, peer_id, m_itemdef);
2209 // Send node definitions
2210 SendNodeDef(m_con, peer_id, m_nodedef);
2212 // Send media announcement
2213 sendMediaAnnouncement(peer_id);
2216 SendPlayerPrivileges(peer_id);
2219 UpdateCrafting(peer_id);
2220 SendInventory(peer_id);
2223 SendPlayerHP(peer_id);
2225 // Show death screen if necessary
2227 SendDeathscreen(m_con, peer_id, false, v3f(0,0,0));
2231 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2232 m_env->getTimeOfDay(), g_settings->getFloat("time_speed"));
2233 m_con.Send(peer_id, 0, data, true);
2236 // Note things in chat if not in simple singleplayer mode
2237 if(!m_simple_singleplayer_mode)
2239 // Send information about server to player in chat
2240 SendChatMessage(peer_id, getStatusString());
2242 // Send information about joining in chat
2244 std::wstring name = L"unknown";
2245 Player *player = m_env->getPlayer(peer_id);
2247 name = narrow_to_wide(player->getName());
2249 std::wstring message;
2252 message += L" joined game";
2253 BroadcastChatMessage(message);
2257 // Warnings about protocol version can be issued here
2258 if(getClient(peer_id)->net_proto_version < PROTOCOL_VERSION)
2260 SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT IS OLD AND MAY WORK PROPERLY WITH THIS SERVER");
2267 std::ostringstream os(std::ios_base::binary);
2268 for(core::map<u16, RemoteClient*>::Iterator
2269 i = m_clients.getIterator();
2270 i.atEnd() == false; i++)
2272 RemoteClient *client = i.getNode()->getValue();
2273 assert(client->peer_id == i.getNode()->getKey());
2274 if(client->serialization_version == SER_FMT_VER_INVALID)
2277 Player *player = m_env->getPlayer(client->peer_id);
2280 // Get name of player
2281 os<<player->getName()<<" ";
2284 actionstream<<player->getName()<<" joins game. List of players: "
2285 <<os.str()<<std::endl;
2291 if(peer_ser_ver == SER_FMT_VER_INVALID)
2293 infostream<<"Server::ProcessData(): Cancelling: Peer"
2294 " serialization format invalid or not initialized."
2295 " Skipping incoming command="<<command<<std::endl;
2299 Player *player = m_env->getPlayer(peer_id);
2301 infostream<<"Server::ProcessData(): Cancelling: "
2302 "No player for peer_id="<<peer_id
2307 PlayerSAO *playersao = player->getPlayerSAO();
2308 if(playersao == NULL){
2309 infostream<<"Server::ProcessData(): Cancelling: "
2310 "No player object for peer_id="<<peer_id
2315 if(command == TOSERVER_PLAYERPOS)
2317 if(datasize < 2+12+12+4+4)
2321 v3s32 ps = readV3S32(&data[start+2]);
2322 v3s32 ss = readV3S32(&data[start+2+12]);
2323 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2324 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2325 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2326 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2327 pitch = wrapDegrees(pitch);
2328 yaw = wrapDegrees(yaw);
2330 player->setPosition(position);
2331 player->setSpeed(speed);
2332 player->setPitch(pitch);
2333 player->setYaw(yaw);
2335 /*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2336 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2337 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2339 else if(command == TOSERVER_GOTBLOCKS)
2352 u16 count = data[2];
2353 for(u16 i=0; i<count; i++)
2355 if((s16)datasize < 2+1+(i+1)*6)
2356 throw con::InvalidIncomingDataException
2357 ("GOTBLOCKS length is too short");
2358 v3s16 p = readV3S16(&data[2+1+i*6]);
2359 /*infostream<<"Server: GOTBLOCKS ("
2360 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2361 RemoteClient *client = getClient(peer_id);
2362 client->GotBlock(p);
2365 else if(command == TOSERVER_DELETEDBLOCKS)
2378 u16 count = data[2];
2379 for(u16 i=0; i<count; i++)
2381 if((s16)datasize < 2+1+(i+1)*6)
2382 throw con::InvalidIncomingDataException
2383 ("DELETEDBLOCKS length is too short");
2384 v3s16 p = readV3S16(&data[2+1+i*6]);
2385 /*infostream<<"Server: DELETEDBLOCKS ("
2386 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2387 RemoteClient *client = getClient(peer_id);
2388 client->SetBlockNotSent(p);
2391 else if(command == TOSERVER_CLICK_OBJECT)
2393 infostream<<"Server: CLICK_OBJECT not supported anymore"<<std::endl;
2396 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2398 infostream<<"Server: CLICK_ACTIVEOBJECT not supported anymore"<<std::endl;
2401 else if(command == TOSERVER_GROUND_ACTION)
2403 infostream<<"Server: GROUND_ACTION not supported anymore"<<std::endl;
2407 else if(command == TOSERVER_RELEASE)
2409 infostream<<"Server: RELEASE not supported anymore"<<std::endl;
2412 else if(command == TOSERVER_SIGNTEXT)
2414 infostream<<"Server: SIGNTEXT not supported anymore"
2418 else if(command == TOSERVER_SIGNNODETEXT)
2420 if(!checkPriv(player->getName(), "interact"))
2428 std::string datastring((char*)&data[2], datasize-2);
2429 std::istringstream is(datastring, std::ios_base::binary);
2432 is.read((char*)buf, 6);
2433 v3s16 p = readV3S16(buf);
2434 is.read((char*)buf, 2);
2435 u16 textlen = readU16(buf);
2437 for(u16 i=0; i<textlen; i++)
2439 is.read((char*)buf, 1);
2440 text += (char)buf[0];
2443 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
2447 meta->setText(text);
2449 actionstream<<player->getName()<<" writes \""<<text<<"\" to sign"
2450 <<" at "<<PP(p)<<std::endl;
2452 v3s16 blockpos = getNodeBlockPos(p);
2453 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
2456 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2460 setBlockNotSent(blockpos);
2462 else if(command == TOSERVER_INVENTORY_ACTION)
2464 // Strip command and create a stream
2465 std::string datastring((char*)&data[2], datasize-2);
2466 verbosestream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2467 std::istringstream is(datastring, std::ios_base::binary);
2469 InventoryAction *a = InventoryAction::deSerialize(is);
2472 infostream<<"TOSERVER_INVENTORY_ACTION: "
2473 <<"InventoryAction::deSerialize() returned NULL"
2479 Note: Always set inventory not sent, to repair cases
2480 where the client made a bad prediction.
2484 Handle restrictions and special cases of the move action
2486 if(a->getType() == IACTION_MOVE)
2488 IMoveAction *ma = (IMoveAction*)a;
2490 ma->from_inv.applyCurrentPlayer(player->getName());
2491 ma->to_inv.applyCurrentPlayer(player->getName());
2493 setInventoryModified(ma->from_inv);
2494 setInventoryModified(ma->to_inv);
2496 bool from_inv_is_current_player =
2497 (ma->from_inv.type == InventoryLocation::PLAYER) &&
2498 (ma->from_inv.name == player->getName());
2500 bool to_inv_is_current_player =
2501 (ma->to_inv.type == InventoryLocation::PLAYER) &&
2502 (ma->to_inv.name == player->getName());
2505 Disable moving items out of craftpreview
2507 if(ma->from_list == "craftpreview")
2509 infostream<<"Ignoring IMoveAction from "
2510 <<(ma->from_inv.dump())<<":"<<ma->from_list
2511 <<" to "<<(ma->to_inv.dump())<<":"<<ma->to_list
2512 <<" because src is "<<ma->from_list<<std::endl;
2518 Disable moving items into craftresult and craftpreview
2520 if(ma->to_list == "craftpreview" || ma->to_list == "craftresult")
2522 infostream<<"Ignoring IMoveAction from "
2523 <<(ma->from_inv.dump())<<":"<<ma->from_list
2524 <<" to "<<(ma->to_inv.dump())<<":"<<ma->to_list
2525 <<" because dst is "<<ma->to_list<<std::endl;
2530 // Disallow moving items in elsewhere than player's inventory
2531 // if not allowed to interact
2532 if(!checkPriv(player->getName(), "interact") &&
2533 (!from_inv_is_current_player ||
2534 !to_inv_is_current_player))
2536 infostream<<"Cannot move outside of player's inventory: "
2537 <<"No interact privilege"<<std::endl;
2542 // If player is not an admin, check for ownership of src and dst
2543 if(!checkPriv(player->getName(), "server"))
2545 std::string owner_from = getInventoryOwner(ma->from_inv);
2546 if(owner_from != "" && owner_from != player->getName())
2548 infostream<<"WARNING: "<<player->getName()
2549 <<" tried to access an inventory that"
2550 <<" belongs to "<<owner_from<<std::endl;
2555 std::string owner_to = getInventoryOwner(ma->to_inv);
2556 if(owner_to != "" && owner_to != player->getName())
2558 infostream<<"WARNING: "<<player->getName()
2559 <<" tried to access an inventory that"
2560 <<" belongs to "<<owner_to<<std::endl;
2567 Handle restrictions and special cases of the drop action
2569 else if(a->getType() == IACTION_DROP)
2571 IDropAction *da = (IDropAction*)a;
2573 da->from_inv.applyCurrentPlayer(player->getName());
2575 setInventoryModified(da->from_inv);
2577 // Disallow dropping items if not allowed to interact
2578 if(!checkPriv(player->getName(), "interact"))
2583 // If player is not an admin, check for ownership
2584 else if(!checkPriv(player->getName(), "server"))
2586 std::string owner_from = getInventoryOwner(da->from_inv);
2587 if(owner_from != "" && owner_from != player->getName())
2589 infostream<<"WARNING: "<<player->getName()
2590 <<" tried to access an inventory that"
2591 <<" belongs to "<<owner_from<<std::endl;
2598 Handle restrictions and special cases of the craft action
2600 else if(a->getType() == IACTION_CRAFT)
2602 ICraftAction *ca = (ICraftAction*)a;
2604 ca->craft_inv.applyCurrentPlayer(player->getName());
2606 setInventoryModified(ca->craft_inv);
2608 //bool craft_inv_is_current_player =
2609 // (ca->craft_inv.type == InventoryLocation::PLAYER) &&
2610 // (ca->craft_inv.name == player->getName());
2612 // Disallow crafting if not allowed to interact
2613 if(!checkPriv(player->getName(), "interact"))
2615 infostream<<"Cannot craft: "
2616 <<"No interact privilege"<<std::endl;
2621 // If player is not an admin, check for ownership of inventory
2622 if(!checkPriv(player->getName(), "server"))
2624 std::string owner_craft = getInventoryOwner(ca->craft_inv);
2625 if(owner_craft != "" && owner_craft != player->getName())
2627 infostream<<"WARNING: "<<player->getName()
2628 <<" tried to access an inventory that"
2629 <<" belongs to "<<owner_craft<<std::endl;
2637 a->apply(this, playersao, this);
2641 else if(command == TOSERVER_CHAT_MESSAGE)
2649 std::string datastring((char*)&data[2], datasize-2);
2650 std::istringstream is(datastring, std::ios_base::binary);
2653 is.read((char*)buf, 2);
2654 u16 len = readU16(buf);
2656 std::wstring message;
2657 for(u16 i=0; i<len; i++)
2659 is.read((char*)buf, 2);
2660 message += (wchar_t)readU16(buf);
2663 // Get player name of this client
2664 std::wstring name = narrow_to_wide(player->getName());
2667 bool ate = scriptapi_on_chat_message(m_lua, player->getName(),
2668 wide_to_narrow(message));
2669 // If script ate the message, don't proceed
2673 // Line to send to players
2675 // Whether to send to the player that sent the line
2676 bool send_to_sender = false;
2677 // Whether to send to other players
2678 bool send_to_others = false;
2681 if(message[0] == L'/')
2683 size_t strip_size = 1;
2684 if (message[1] == L'#') // support old-style commans
2686 message = message.substr(strip_size);
2688 WStrfnd f1(message);
2689 f1.next(L" "); // Skip over /#whatever
2690 std::wstring paramstring = f1.next(L"");
2692 ServerCommandContext *ctx = new ServerCommandContext(
2693 str_split(message, L' '),
2699 std::wstring reply(processServerCommand(ctx));
2700 send_to_sender = ctx->flags & SEND_TO_SENDER;
2701 send_to_others = ctx->flags & SEND_TO_OTHERS;
2703 if (ctx->flags & SEND_NO_PREFIX)
2706 line += L"Server: " + reply;
2713 if(checkPriv(player->getName(), "shout")){
2718 send_to_others = true;
2720 line += L"Server: You are not allowed to shout";
2721 send_to_sender = true;
2728 actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2731 Send the message to clients
2733 for(core::map<u16, RemoteClient*>::Iterator
2734 i = m_clients.getIterator();
2735 i.atEnd() == false; i++)
2737 // Get client and check that it is valid
2738 RemoteClient *client = i.getNode()->getValue();
2739 assert(client->peer_id == i.getNode()->getKey());
2740 if(client->serialization_version == SER_FMT_VER_INVALID)
2744 bool sender_selected = (peer_id == client->peer_id);
2745 if(sender_selected == true && send_to_sender == false)
2747 if(sender_selected == false && send_to_others == false)
2750 SendChatMessage(client->peer_id, line);
2754 else if(command == TOSERVER_DAMAGE)
2756 std::string datastring((char*)&data[2], datasize-2);
2757 std::istringstream is(datastring, std::ios_base::binary);
2758 u8 damage = readU8(is);
2760 actionstream<<player->getName()<<" damaged by "
2761 <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
2764 playersao->setHP(playersao->getHP() - damage);
2766 if(playersao->getHP() == 0 && playersao->m_hp_not_sent)
2769 if(playersao->m_hp_not_sent)
2770 SendPlayerHP(peer_id);
2772 else if(command == TOSERVER_PASSWORD)
2775 [0] u16 TOSERVER_PASSWORD
2776 [2] u8[28] old password
2777 [30] u8[28] new password
2780 if(datasize != 2+PASSWORD_SIZE*2)
2782 /*char password[PASSWORD_SIZE];
2783 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2784 password[i] = data[2+i];
2785 password[PASSWORD_SIZE-1] = 0;*/
2787 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2795 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2797 char c = data[2+PASSWORD_SIZE+i];
2803 if(!base64_is_valid(newpwd)){
2804 infostream<<"Server: "<<player->getName()<<" supplied invalid password hash"<<std::endl;
2805 // Wrong old password supplied!!
2806 SendChatMessage(peer_id, L"Invalid new password hash supplied. Password NOT changed.");
2810 infostream<<"Server: Client requests a password change from "
2811 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
2813 std::string playername = player->getName();
2815 std::string checkpwd;
2816 scriptapi_get_auth(m_lua, playername, &checkpwd, NULL);
2818 if(oldpwd != checkpwd)
2820 infostream<<"Server: invalid old password"<<std::endl;
2821 // Wrong old password supplied!!
2822 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
2826 bool success = scriptapi_set_password(m_lua, playername, newpwd);
2828 actionstream<<player->getName()<<" changes password"<<std::endl;
2829 SendChatMessage(peer_id, L"Password change successful");
2831 actionstream<<player->getName()<<" tries to change password but "
2832 <<"it fails"<<std::endl;
2833 SendChatMessage(peer_id, L"Password change failed or inavailable");
2836 else if(command == TOSERVER_PLAYERITEM)
2841 u16 item = readU16(&data[2]);
2842 playersao->setWieldIndex(item);
2844 else if(command == TOSERVER_RESPAWN)
2849 RespawnPlayer(peer_id);
2851 actionstream<<player->getName()<<" respawns at "
2852 <<PP(player->getPosition()/BS)<<std::endl;
2854 // ActiveObject is added to environment in AsyncRunStep after
2855 // the previous addition has been succesfully removed
2857 else if(command == TOSERVER_REQUEST_MEDIA) {
2858 std::string datastring((char*)&data[2], datasize-2);
2859 std::istringstream is(datastring, std::ios_base::binary);
2861 core::list<MediaRequest> tosend;
2862 u16 numfiles = readU16(is);
2864 infostream<<"Sending "<<numfiles<<" files to "
2865 <<getPlayerName(peer_id)<<std::endl;
2866 verbosestream<<"TOSERVER_REQUEST_MEDIA: "<<std::endl;
2868 for(int i = 0; i < numfiles; i++) {
2869 std::string name = deSerializeString(is);
2870 tosend.push_back(MediaRequest(name));
2871 verbosestream<<"TOSERVER_REQUEST_MEDIA: requested file "
2875 sendRequestedMedia(peer_id, tosend);
2877 // Now the client should know about everything
2878 // (definitions and files)
2879 getClient(peer_id)->definitions_sent = true;
2881 else if(command == TOSERVER_INTERACT)
2883 std::string datastring((char*)&data[2], datasize-2);
2884 std::istringstream is(datastring, std::ios_base::binary);
2890 [5] u32 length of the next item
2891 [9] serialized PointedThing
2893 0: start digging (from undersurface) or use
2894 1: stop digging (all parameters ignored)
2895 2: digging completed
2896 3: place block or item (to abovesurface)
2899 u8 action = readU8(is);
2900 u16 item_i = readU16(is);
2901 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
2902 PointedThing pointed;
2903 pointed.deSerialize(tmp_is);
2905 verbosestream<<"TOSERVER_INTERACT: action="<<(int)action<<", item="
2906 <<item_i<<", pointed="<<pointed.dump()<<std::endl;
2910 verbosestream<<"TOSERVER_INTERACT: "<<player->getName()
2911 <<" tried to interact, but is dead!"<<std::endl;
2915 v3f player_pos = playersao->getLastGoodPosition();
2917 // Update wielded item
2918 playersao->setWieldIndex(item_i);
2920 // Get pointed to node (undefined if not POINTEDTYPE_NODE)
2921 v3s16 p_under = pointed.node_undersurface;
2922 v3s16 p_above = pointed.node_abovesurface;
2924 // Get pointed to object (NULL if not POINTEDTYPE_OBJECT)
2925 ServerActiveObject *pointed_object = NULL;
2926 if(pointed.type == POINTEDTHING_OBJECT)
2928 pointed_object = m_env->getActiveObject(pointed.object_id);
2929 if(pointed_object == NULL)
2931 verbosestream<<"TOSERVER_INTERACT: "
2932 "pointed object is NULL"<<std::endl;
2938 v3f pointed_pos_under = player_pos;
2939 v3f pointed_pos_above = player_pos;
2940 if(pointed.type == POINTEDTHING_NODE)
2942 pointed_pos_under = intToFloat(p_under, BS);
2943 pointed_pos_above = intToFloat(p_above, BS);
2945 else if(pointed.type == POINTEDTHING_OBJECT)
2947 pointed_pos_under = pointed_object->getBasePosition();
2948 pointed_pos_above = pointed_pos_under;
2952 Check that target is reasonably close
2953 (only when digging or placing things)
2955 if(action == 0 || action == 2 || action == 3)
2957 float d = player_pos.getDistanceFrom(pointed_pos_under);
2958 float max_d = BS * 14; // Just some large enough value
2960 actionstream<<"Player "<<player->getName()
2961 <<" tried to access "<<pointed.dump()
2963 <<"d="<<d<<", max_d="<<max_d
2964 <<". ignoring."<<std::endl;
2965 // Re-send block to revert change on client-side
2966 RemoteClient *client = getClient(peer_id);
2967 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2968 client->SetBlockNotSent(blockpos);
2975 Make sure the player is allowed to do it
2977 if(!checkPriv(player->getName(), "interact"))
2979 actionstream<<player->getName()<<" attempted to interact with "
2980 <<pointed.dump()<<" without 'interact' privilege"
2982 // Re-send block to revert change on client-side
2983 RemoteClient *client = getClient(peer_id);
2984 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2985 client->SetBlockNotSent(blockpos);
2990 0: start digging or punch object
2994 if(pointed.type == POINTEDTHING_NODE)
2997 NOTE: This can be used in the future to check if
2998 somebody is cheating, by checking the timing.
3000 MapNode n(CONTENT_IGNORE);
3003 n = m_env->getMap().getNode(p_under);
3005 catch(InvalidPositionException &e)
3007 infostream<<"Server: Not punching: Node not found."
3008 <<" Adding block to emerge queue."
3010 m_emerge_queue.addBlock(peer_id,
3011 getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
3013 if(n.getContent() != CONTENT_IGNORE)
3014 scriptapi_node_on_punch(m_lua, p_under, n, playersao);
3016 else if(pointed.type == POINTEDTHING_OBJECT)
3018 // Skip if object has been removed
3019 if(pointed_object->m_removed)
3022 actionstream<<player->getName()<<" punches object "
3023 <<pointed.object_id<<": "
3024 <<pointed_object->getDescription()<<std::endl;
3026 ItemStack punchitem = playersao->getWieldedItem();
3027 ToolCapabilities toolcap =
3028 punchitem.getToolCapabilities(m_itemdef);
3029 v3f dir = (pointed_object->getBasePosition() -
3030 (player->getPosition() + player->getEyeOffset())
3032 float time_from_last_punch =
3033 playersao->resetTimeFromLastPunch();
3034 pointed_object->punch(dir, &toolcap, playersao,
3035 time_from_last_punch);
3043 else if(action == 1)
3048 2: Digging completed
3050 else if(action == 2)
3052 // Only complete digging of nodes
3053 if(pointed.type == POINTEDTHING_NODE)
3055 MapNode n(CONTENT_IGNORE);
3058 n = m_env->getMap().getNode(p_under);
3060 catch(InvalidPositionException &e)
3062 infostream<<"Server: Not finishing digging: Node not found."
3063 <<" Adding block to emerge queue."
3065 m_emerge_queue.addBlock(peer_id,
3066 getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
3068 if(n.getContent() != CONTENT_IGNORE)
3069 scriptapi_node_on_dig(m_lua, p_under, n, playersao);
3071 if (m_env->getMap().getNodeNoEx(p_under).getContent() != CONTENT_AIR)
3073 // Re-send block to revert change on client-side
3074 RemoteClient *client = getClient(peer_id);
3075 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
3076 client->SetBlockNotSent(blockpos);
3082 3: place block or right-click object
3084 else if(action == 3)
3086 ItemStack item = playersao->getWieldedItem();
3088 // Reset build time counter
3089 if(pointed.type == POINTEDTHING_NODE &&
3090 item.getDefinition(m_itemdef).type == ITEM_NODE)
3091 getClient(peer_id)->m_time_from_building = 0.0;
3093 if(pointed.type == POINTEDTHING_OBJECT)
3095 // Right click object
3097 // Skip if object has been removed
3098 if(pointed_object->m_removed)
3101 actionstream<<player->getName()<<" right-clicks object "
3102 <<pointed.object_id<<": "
3103 <<pointed_object->getDescription()<<std::endl;
3106 pointed_object->rightClick(playersao);
3108 else if(scriptapi_item_on_place(m_lua,
3109 item, playersao, pointed))
3111 // Placement was handled in lua
3113 // Apply returned ItemStack
3114 if(g_settings->getBool("creative_mode") == false)
3115 playersao->setWieldedItem(item);
3123 else if(action == 4)
3125 ItemStack item = playersao->getWieldedItem();
3127 actionstream<<player->getName()<<" uses "<<item.name
3128 <<", pointing at "<<pointed.dump()<<std::endl;
3130 if(scriptapi_item_on_use(m_lua,
3131 item, playersao, pointed))
3133 // Apply returned ItemStack
3134 if(g_settings->getBool("creative_mode") == false)
3135 playersao->setWieldedItem(item);
3141 Catch invalid actions
3145 infostream<<"WARNING: Server: Invalid action "
3146 <<action<<std::endl;
3149 else if(command == TOSERVER_REMOVED_SOUNDS)
3151 std::string datastring((char*)&data[2], datasize-2);
3152 std::istringstream is(datastring, std::ios_base::binary);
3154 int num = readU16(is);
3155 for(int k=0; k<num; k++){
3156 s32 id = readS32(is);
3157 std::map<s32, ServerPlayingSound>::iterator i =
3158 m_playing_sounds.find(id);
3159 if(i == m_playing_sounds.end())
3161 ServerPlayingSound &psound = i->second;
3162 psound.clients.erase(peer_id);
3163 if(psound.clients.size() == 0)
3164 m_playing_sounds.erase(i++);
3169 infostream<<"Server::ProcessData(): Ignoring "
3170 "unknown command "<<command<<std::endl;
3174 catch(SendFailedException &e)
3176 errorstream<<"Server::ProcessData(): SendFailedException: "
3182 void Server::onMapEditEvent(MapEditEvent *event)
3184 //infostream<<"Server::onMapEditEvent()"<<std::endl;
3185 if(m_ignore_map_edit_events)
3187 if(m_ignore_map_edit_events_area.contains(event->getArea()))
3189 MapEditEvent *e = event->clone();
3190 m_unsent_map_edit_queue.push_back(e);
3193 Inventory* Server::getInventory(const InventoryLocation &loc)
3196 case InventoryLocation::UNDEFINED:
3199 case InventoryLocation::CURRENT_PLAYER:
3202 case InventoryLocation::PLAYER:
3204 Player *player = m_env->getPlayer(loc.name.c_str());
3207 PlayerSAO *playersao = player->getPlayerSAO();
3210 return playersao->getInventory();
3213 case InventoryLocation::NODEMETA:
3215 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
3218 return meta->getInventory();
3226 std::string Server::getInventoryOwner(const InventoryLocation &loc)
3229 case InventoryLocation::UNDEFINED:
3232 case InventoryLocation::CURRENT_PLAYER:
3235 case InventoryLocation::PLAYER:
3240 case InventoryLocation::NODEMETA:
3242 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
3245 return meta->getOwner();
3253 void Server::setInventoryModified(const InventoryLocation &loc)
3256 case InventoryLocation::UNDEFINED:
3259 case InventoryLocation::PLAYER:
3261 Player *player = m_env->getPlayer(loc.name.c_str());
3264 PlayerSAO *playersao = player->getPlayerSAO();
3267 playersao->m_inventory_not_sent = true;
3268 playersao->m_wielded_item_not_sent = true;
3271 case InventoryLocation::NODEMETA:
3273 v3s16 blockpos = getNodeBlockPos(loc.p);
3275 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
3277 meta->inventoryModified();
3279 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3281 block->raiseModified(MOD_STATE_WRITE_NEEDED);
3283 setBlockNotSent(blockpos);
3291 core::list<PlayerInfo> Server::getPlayerInfo()
3293 DSTACK(__FUNCTION_NAME);
3294 JMutexAutoLock envlock(m_env_mutex);
3295 JMutexAutoLock conlock(m_con_mutex);
3297 core::list<PlayerInfo> list;
3299 core::list<Player*> players = m_env->getPlayers();
3301 core::list<Player*>::Iterator i;
3302 for(i = players.begin();
3303 i != players.end(); i++)
3307 Player *player = *i;
3310 // Copy info from connection to info struct
3311 info.id = player->peer_id;
3312 info.address = m_con.GetPeerAddress(player->peer_id);
3313 info.avg_rtt = m_con.GetPeerAvgRTT(player->peer_id);
3315 catch(con::PeerNotFoundException &e)
3317 // Set dummy peer info
3319 info.address = Address(0,0,0,0,0);
3323 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3324 info.position = player->getPosition();
3326 list.push_back(info);
3333 void Server::peerAdded(con::Peer *peer)
3335 DSTACK(__FUNCTION_NAME);
3336 verbosestream<<"Server::peerAdded(): peer->id="
3337 <<peer->id<<std::endl;
3340 c.type = PEER_ADDED;
3341 c.peer_id = peer->id;
3343 m_peer_change_queue.push_back(c);
3346 void Server::deletingPeer(con::Peer *peer, bool timeout)
3348 DSTACK(__FUNCTION_NAME);
3349 verbosestream<<"Server::deletingPeer(): peer->id="
3350 <<peer->id<<", timeout="<<timeout<<std::endl;
3353 c.type = PEER_REMOVED;
3354 c.peer_id = peer->id;
3355 c.timeout = timeout;
3356 m_peer_change_queue.push_back(c);
3363 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3365 DSTACK(__FUNCTION_NAME);
3366 std::ostringstream os(std::ios_base::binary);
3368 writeU16(os, TOCLIENT_HP);
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::SendAccessDenied(con::Connection &con, u16 peer_id,
3379 const std::wstring &reason)
3381 DSTACK(__FUNCTION_NAME);
3382 std::ostringstream os(std::ios_base::binary);
3384 writeU16(os, TOCLIENT_ACCESS_DENIED);
3385 os<<serializeWideString(reason);
3388 std::string s = os.str();
3389 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3391 con.Send(peer_id, 0, data, true);
3394 void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
3395 bool set_camera_point_target, v3f camera_point_target)
3397 DSTACK(__FUNCTION_NAME);
3398 std::ostringstream os(std::ios_base::binary);
3400 writeU16(os, TOCLIENT_DEATHSCREEN);
3401 writeU8(os, set_camera_point_target);
3402 writeV3F1000(os, camera_point_target);
3405 std::string s = os.str();
3406 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3408 con.Send(peer_id, 0, data, true);
3411 void Server::SendItemDef(con::Connection &con, u16 peer_id,
3412 IItemDefManager *itemdef)
3414 DSTACK(__FUNCTION_NAME);
3415 std::ostringstream os(std::ios_base::binary);
3419 u32 length of the next item
3420 zlib-compressed serialized ItemDefManager
3422 writeU16(os, TOCLIENT_ITEMDEF);
3423 std::ostringstream tmp_os(std::ios::binary);
3424 itemdef->serialize(tmp_os);
3425 std::ostringstream tmp_os2(std::ios::binary);
3426 compressZlib(tmp_os.str(), tmp_os2);
3427 os<<serializeLongString(tmp_os2.str());
3430 std::string s = os.str();
3431 verbosestream<<"Server: Sending item definitions to id("<<peer_id
3432 <<"): size="<<s.size()<<std::endl;
3433 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3435 con.Send(peer_id, 0, data, true);
3438 void Server::SendNodeDef(con::Connection &con, u16 peer_id,
3439 INodeDefManager *nodedef)
3441 DSTACK(__FUNCTION_NAME);
3442 std::ostringstream os(std::ios_base::binary);
3446 u32 length of the next item
3447 zlib-compressed serialized NodeDefManager
3449 writeU16(os, TOCLIENT_NODEDEF);
3450 std::ostringstream tmp_os(std::ios::binary);
3451 nodedef->serialize(tmp_os);
3452 std::ostringstream tmp_os2(std::ios::binary);
3453 compressZlib(tmp_os.str(), tmp_os2);
3454 os<<serializeLongString(tmp_os2.str());
3457 std::string s = os.str();
3458 verbosestream<<"Server: Sending node definitions to id("<<peer_id
3459 <<"): size="<<s.size()<<std::endl;
3460 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3462 con.Send(peer_id, 0, data, true);
3466 Non-static send methods
3469 void Server::SendInventory(u16 peer_id)
3471 DSTACK(__FUNCTION_NAME);
3473 PlayerSAO *playersao = getPlayerSAO(peer_id);
3476 playersao->m_inventory_not_sent = false;
3482 std::ostringstream os;
3483 playersao->getInventory()->serialize(os);
3485 std::string s = os.str();
3487 SharedBuffer<u8> data(s.size()+2);
3488 writeU16(&data[0], TOCLIENT_INVENTORY);
3489 memcpy(&data[2], s.c_str(), s.size());
3492 m_con.Send(peer_id, 0, data, true);
3495 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3497 DSTACK(__FUNCTION_NAME);
3499 std::ostringstream os(std::ios_base::binary);
3503 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3504 os.write((char*)buf, 2);
3507 writeU16(buf, message.size());
3508 os.write((char*)buf, 2);
3511 for(u32 i=0; i<message.size(); i++)
3515 os.write((char*)buf, 2);
3519 std::string s = os.str();
3520 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3522 m_con.Send(peer_id, 0, data, true);
3525 void Server::BroadcastChatMessage(const std::wstring &message)
3527 for(core::map<u16, RemoteClient*>::Iterator
3528 i = m_clients.getIterator();
3529 i.atEnd() == false; i++)
3531 // Get client and check that it is valid
3532 RemoteClient *client = i.getNode()->getValue();
3533 assert(client->peer_id == i.getNode()->getKey());
3534 if(client->serialization_version == SER_FMT_VER_INVALID)
3537 SendChatMessage(client->peer_id, message);
3541 void Server::SendPlayerHP(u16 peer_id)
3543 DSTACK(__FUNCTION_NAME);
3544 PlayerSAO *playersao = getPlayerSAO(peer_id);
3546 playersao->m_hp_not_sent = false;
3547 SendHP(m_con, peer_id, playersao->getHP());
3550 void Server::SendMovePlayer(u16 peer_id)
3552 DSTACK(__FUNCTION_NAME);
3553 Player *player = m_env->getPlayer(peer_id);
3556 std::ostringstream os(std::ios_base::binary);
3557 writeU16(os, TOCLIENT_MOVE_PLAYER);
3558 writeV3F1000(os, player->getPosition());
3559 writeF1000(os, player->getPitch());
3560 writeF1000(os, player->getYaw());
3563 v3f pos = player->getPosition();
3564 f32 pitch = player->getPitch();
3565 f32 yaw = player->getYaw();
3566 verbosestream<<"Server: Sending TOCLIENT_MOVE_PLAYER"
3567 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3574 std::string s = os.str();
3575 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3577 m_con.Send(peer_id, 0, data, true);
3580 void Server::SendPlayerPrivileges(u16 peer_id)
3582 Player *player = m_env->getPlayer(peer_id);
3584 std::set<std::string> privs;
3585 scriptapi_get_auth(m_lua, player->getName(), NULL, &privs);
3587 std::ostringstream os(std::ios_base::binary);
3588 writeU16(os, TOCLIENT_PRIVILEGES);
3589 writeU16(os, privs.size());
3590 for(std::set<std::string>::const_iterator i = privs.begin();
3591 i != privs.end(); i++){
3592 os<<serializeString(*i);
3596 std::string s = os.str();
3597 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3599 m_con.Send(peer_id, 0, data, true);
3602 s32 Server::playSound(const SimpleSoundSpec &spec,
3603 const ServerSoundParams ¶ms)
3605 // Find out initial position of sound
3606 bool pos_exists = false;
3607 v3f pos = params.getPos(m_env, &pos_exists);
3608 // If position is not found while it should be, cancel sound
3609 if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
3611 // Filter destination clients
3612 std::set<RemoteClient*> dst_clients;
3613 if(params.to_player != "")
3615 Player *player = m_env->getPlayer(params.to_player.c_str());
3617 infostream<<"Server::playSound: Player \""<<params.to_player
3618 <<"\" not found"<<std::endl;
3621 if(player->peer_id == PEER_ID_INEXISTENT){
3622 infostream<<"Server::playSound: Player \""<<params.to_player
3623 <<"\" not connected"<<std::endl;
3626 RemoteClient *client = getClient(player->peer_id);
3627 dst_clients.insert(client);
3631 for(core::map<u16, RemoteClient*>::Iterator
3632 i = m_clients.getIterator(); i.atEnd() == false; i++)
3634 RemoteClient *client = i.getNode()->getValue();
3635 Player *player = m_env->getPlayer(client->peer_id);
3639 if(player->getPosition().getDistanceFrom(pos) >
3640 params.max_hear_distance)
3643 dst_clients.insert(client);
3646 if(dst_clients.size() == 0)
3649 s32 id = m_next_sound_id++;
3650 // The sound will exist as a reference in m_playing_sounds
3651 m_playing_sounds[id] = ServerPlayingSound();
3652 ServerPlayingSound &psound = m_playing_sounds[id];
3653 psound.params = params;
3654 for(std::set<RemoteClient*>::iterator i = dst_clients.begin();
3655 i != dst_clients.end(); i++)
3656 psound.clients.insert((*i)->peer_id);
3658 std::ostringstream os(std::ios_base::binary);
3659 writeU16(os, TOCLIENT_PLAY_SOUND);
3661 os<<serializeString(spec.name);
3662 writeF1000(os, spec.gain * params.gain);
3663 writeU8(os, params.type);
3664 writeV3F1000(os, pos);
3665 writeU16(os, params.object);
3666 writeU8(os, params.loop);
3668 std::string s = os.str();
3669 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3671 for(std::set<RemoteClient*>::iterator i = dst_clients.begin();
3672 i != dst_clients.end(); i++){
3674 m_con.Send((*i)->peer_id, 0, data, true);
3678 void Server::stopSound(s32 handle)
3680 // Get sound reference
3681 std::map<s32, ServerPlayingSound>::iterator i =
3682 m_playing_sounds.find(handle);
3683 if(i == m_playing_sounds.end())
3685 ServerPlayingSound &psound = i->second;
3687 std::ostringstream os(std::ios_base::binary);
3688 writeU16(os, TOCLIENT_STOP_SOUND);
3689 writeS32(os, handle);
3691 std::string s = os.str();
3692 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3694 for(std::set<u16>::iterator i = psound.clients.begin();
3695 i != psound.clients.end(); i++){
3697 m_con.Send(*i, 0, data, true);
3699 // Remove sound reference
3700 m_playing_sounds.erase(i);
3703 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3704 core::list<u16> *far_players, float far_d_nodes)
3706 float maxd = far_d_nodes*BS;
3707 v3f p_f = intToFloat(p, BS);
3711 SharedBuffer<u8> reply(replysize);
3712 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3713 writeS16(&reply[2], p.X);
3714 writeS16(&reply[4], p.Y);
3715 writeS16(&reply[6], p.Z);
3717 for(core::map<u16, RemoteClient*>::Iterator
3718 i = m_clients.getIterator();
3719 i.atEnd() == false; i++)
3721 // Get client and check that it is valid
3722 RemoteClient *client = i.getNode()->getValue();
3723 assert(client->peer_id == i.getNode()->getKey());
3724 if(client->serialization_version == SER_FMT_VER_INVALID)
3727 // Don't send if it's the same one
3728 if(client->peer_id == ignore_id)
3734 Player *player = m_env->getPlayer(client->peer_id);
3737 // If player is far away, only set modified blocks not sent
3738 v3f player_pos = player->getPosition();
3739 if(player_pos.getDistanceFrom(p_f) > maxd)
3741 far_players->push_back(client->peer_id);
3748 m_con.Send(client->peer_id, 0, reply, true);
3752 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3753 core::list<u16> *far_players, float far_d_nodes)
3755 float maxd = far_d_nodes*BS;
3756 v3f p_f = intToFloat(p, BS);
3758 for(core::map<u16, RemoteClient*>::Iterator
3759 i = m_clients.getIterator();
3760 i.atEnd() == false; i++)
3762 // Get client and check that it is valid
3763 RemoteClient *client = i.getNode()->getValue();
3764 assert(client->peer_id == i.getNode()->getKey());
3765 if(client->serialization_version == SER_FMT_VER_INVALID)
3768 // Don't send if it's the same one
3769 if(client->peer_id == ignore_id)
3775 Player *player = m_env->getPlayer(client->peer_id);
3778 // If player is far away, only set modified blocks not sent
3779 v3f player_pos = player->getPosition();
3780 if(player_pos.getDistanceFrom(p_f) > maxd)
3782 far_players->push_back(client->peer_id);
3789 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3790 SharedBuffer<u8> reply(replysize);
3791 writeU16(&reply[0], TOCLIENT_ADDNODE);
3792 writeS16(&reply[2], p.X);
3793 writeS16(&reply[4], p.Y);
3794 writeS16(&reply[6], p.Z);
3795 n.serialize(&reply[8], client->serialization_version);
3798 m_con.Send(client->peer_id, 0, reply, true);
3802 void Server::setBlockNotSent(v3s16 p)
3804 for(core::map<u16, RemoteClient*>::Iterator
3805 i = m_clients.getIterator();
3806 i.atEnd()==false; i++)
3808 RemoteClient *client = i.getNode()->getValue();
3809 client->SetBlockNotSent(p);
3813 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3815 DSTACK(__FUNCTION_NAME);
3817 v3s16 p = block->getPos();
3821 bool completely_air = true;
3822 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3823 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3824 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3826 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
3828 completely_air = false;
3829 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
3834 infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
3836 infostream<<"[completely air] ";
3837 infostream<<std::endl;
3841 Create a packet with the block in the right format
3844 std::ostringstream os(std::ios_base::binary);
3845 block->serialize(os, ver, false);
3846 std::string s = os.str();
3847 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3849 u32 replysize = 8 + blockdata.getSize();
3850 SharedBuffer<u8> reply(replysize);
3851 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3852 writeS16(&reply[2], p.X);
3853 writeS16(&reply[4], p.Y);
3854 writeS16(&reply[6], p.Z);
3855 memcpy(&reply[8], *blockdata, blockdata.getSize());
3857 /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3858 <<": \tpacket size: "<<replysize<<std::endl;*/
3863 m_con.Send(peer_id, 1, reply, true);
3866 void Server::SendBlocks(float dtime)
3868 DSTACK(__FUNCTION_NAME);
3870 JMutexAutoLock envlock(m_env_mutex);
3871 JMutexAutoLock conlock(m_con_mutex);
3873 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
3875 core::array<PrioritySortedBlockTransfer> queue;
3877 s32 total_sending = 0;
3880 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
3882 for(core::map<u16, RemoteClient*>::Iterator
3883 i = m_clients.getIterator();
3884 i.atEnd() == false; i++)
3886 RemoteClient *client = i.getNode()->getValue();
3887 assert(client->peer_id == i.getNode()->getKey());
3889 // If definitions and textures have not been sent, don't
3890 // send MapBlocks either
3891 if(!client->definitions_sent)
3894 total_sending += client->SendingCount();
3896 if(client->serialization_version == SER_FMT_VER_INVALID)
3899 client->GetNextBlocks(this, dtime, queue);
3904 // Lowest priority number comes first.
3905 // Lowest is most important.
3908 for(u32 i=0; i<queue.size(); i++)
3910 //TODO: Calculate limit dynamically
3911 if(total_sending >= g_settings->getS32
3912 ("max_simultaneous_block_sends_server_total"))
3915 PrioritySortedBlockTransfer q = queue[i];
3917 MapBlock *block = NULL;
3920 block = m_env->getMap().getBlockNoCreate(q.pos);
3922 catch(InvalidPositionException &e)
3927 RemoteClient *client = getClient(q.peer_id);
3929 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3931 client->SentBlock(q.pos);
3937 void Server::fillMediaCache()
3939 DSTACK(__FUNCTION_NAME);
3941 infostream<<"Server: Calculating media file checksums"<<std::endl;
3943 // Collect all media file paths
3944 std::list<std::string> paths;
3945 for(core::list<ModSpec>::Iterator i = m_mods.begin();
3946 i != m_mods.end(); i++){
3947 const ModSpec &mod = *i;
3948 paths.push_back(mod.path + DIR_DELIM + "textures");
3949 paths.push_back(mod.path + DIR_DELIM + "sounds");
3950 paths.push_back(mod.path + DIR_DELIM + "media");
3953 // Collect media file information from paths into cache
3954 for(std::list<std::string>::iterator i = paths.begin();
3955 i != paths.end(); i++)
3957 std::string mediapath = *i;
3958 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
3959 for(u32 j=0; j<dirlist.size(); j++){
3960 if(dirlist[j].dir) // Ignode dirs
3962 std::string filename = dirlist[j].name;
3963 // If name contains illegal characters, ignore the file
3964 if(!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)){
3965 infostream<<"Server: ignoring illegal file name: \""
3966 <<filename<<"\""<<std::endl;
3969 // If name is not in a supported format, ignore it
3970 const char *supported_ext[] = {
3971 ".png", ".jpg", ".bmp", ".tga",
3972 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
3976 if(removeStringEnd(filename, supported_ext) == ""){
3977 infostream<<"Server: ignoring unsupported file extension: \""
3978 <<filename<<"\""<<std::endl;
3981 // Ok, attempt to load the file and add to cache
3982 std::string filepath = mediapath + DIR_DELIM + filename;
3984 std::ifstream fis(filepath.c_str(), std::ios_base::binary);
3985 if(fis.good() == false){
3986 errorstream<<"Server::fillMediaCache(): Could not open \""
3987 <<filename<<"\" for reading"<<std::endl;
3990 std::ostringstream tmp_os(std::ios_base::binary);
3994 fis.read(buf, 1024);
3995 std::streamsize len = fis.gcount();
3996 tmp_os.write(buf, len);
4005 errorstream<<"Server::fillMediaCache(): Failed to read \""
4006 <<filename<<"\""<<std::endl;
4009 if(tmp_os.str().length() == 0){
4010 errorstream<<"Server::fillMediaCache(): Empty file \""
4011 <<filepath<<"\""<<std::endl;
4016 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
4018 unsigned char *digest = sha1.getDigest();
4019 std::string sha1_base64 = base64_encode(digest, 20);
4020 std::string sha1_hex = hex_encode((char*)digest, 20);
4024 this->m_media[filename] = MediaInfo(filepath, sha1_base64);
4025 verbosestream<<"Server: "<<sha1_hex<<" is "<<filename<<std::endl;
4030 struct SendableMediaAnnouncement
4033 std::string sha1_digest;
4035 SendableMediaAnnouncement(const std::string name_="",
4036 const std::string sha1_digest_=""):
4038 sha1_digest(sha1_digest_)
4042 void Server::sendMediaAnnouncement(u16 peer_id)
4044 DSTACK(__FUNCTION_NAME);
4046 verbosestream<<"Server: Announcing files to id("<<peer_id<<")"
4049 core::list<SendableMediaAnnouncement> file_announcements;
4051 for(std::map<std::string, MediaInfo>::iterator i = m_media.begin();
4052 i != m_media.end(); i++){
4054 file_announcements.push_back(
4055 SendableMediaAnnouncement(i->first, i->second.sha1_digest));
4059 std::ostringstream os(std::ios_base::binary);
4067 u16 length of sha1_digest
4072 writeU16(os, TOCLIENT_ANNOUNCE_MEDIA);
4073 writeU16(os, file_announcements.size());
4075 for(core::list<SendableMediaAnnouncement>::Iterator
4076 j = file_announcements.begin();
4077 j != file_announcements.end(); j++){
4078 os<<serializeString(j->name);
4079 os<<serializeString(j->sha1_digest);
4083 std::string s = os.str();
4084 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4087 m_con.Send(peer_id, 0, data, true);
4091 struct SendableMedia
4097 SendableMedia(const std::string &name_="", const std::string path_="",
4098 const std::string &data_=""):
4105 void Server::sendRequestedMedia(u16 peer_id,
4106 const core::list<MediaRequest> &tosend)
4108 DSTACK(__FUNCTION_NAME);
4110 verbosestream<<"Server::sendRequestedMedia(): "
4111 <<"Sending files to client"<<std::endl;
4115 // Put 5kB in one bunch (this is not accurate)
4116 u32 bytes_per_bunch = 5000;
4118 core::array< core::list<SendableMedia> > file_bunches;
4119 file_bunches.push_back(core::list<SendableMedia>());
4121 u32 file_size_bunch_total = 0;
4123 for(core::list<MediaRequest>::ConstIterator i = tosend.begin();
4124 i != tosend.end(); i++)
4126 if(m_media.find(i->name) == m_media.end()){
4127 errorstream<<"Server::sendRequestedMedia(): Client asked for "
4128 <<"unknown file \""<<(i->name)<<"\""<<std::endl;
4132 //TODO get path + name
4133 std::string tpath = m_media[(*i).name].path;
4136 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
4137 if(fis.good() == false){
4138 errorstream<<"Server::sendRequestedMedia(): Could not open \""
4139 <<tpath<<"\" for reading"<<std::endl;
4142 std::ostringstream tmp_os(std::ios_base::binary);
4146 fis.read(buf, 1024);
4147 std::streamsize len = fis.gcount();
4148 tmp_os.write(buf, len);
4149 file_size_bunch_total += len;
4158 errorstream<<"Server::sendRequestedMedia(): Failed to read \""
4159 <<(*i).name<<"\""<<std::endl;
4162 /*infostream<<"Server::sendRequestedMedia(): Loaded \""
4163 <<tname<<"\""<<std::endl;*/
4165 file_bunches[file_bunches.size()-1].push_back(
4166 SendableMedia((*i).name, tpath, tmp_os.str()));
4168 // Start next bunch if got enough data
4169 if(file_size_bunch_total >= bytes_per_bunch){
4170 file_bunches.push_back(core::list<SendableMedia>());
4171 file_size_bunch_total = 0;
4176 /* Create and send packets */
4178 u32 num_bunches = file_bunches.size();
4179 for(u32 i=0; i<num_bunches; i++)
4181 std::ostringstream os(std::ios_base::binary);
4185 u16 total number of texture bunches
4186 u16 index of this bunch
4187 u32 number of files in this bunch
4196 writeU16(os, TOCLIENT_MEDIA);
4197 writeU16(os, num_bunches);
4199 writeU32(os, file_bunches[i].size());
4201 for(core::list<SendableMedia>::Iterator
4202 j = file_bunches[i].begin();
4203 j != file_bunches[i].end(); j++){
4204 os<<serializeString(j->name);
4205 os<<serializeLongString(j->data);
4209 std::string s = os.str();
4210 verbosestream<<"Server::sendRequestedMedia(): bunch "
4211 <<i<<"/"<<num_bunches
4212 <<" files="<<file_bunches[i].size()
4213 <<" size=" <<s.size()<<std::endl;
4214 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4216 m_con.Send(peer_id, 0, data, true);
4224 void Server::DiePlayer(u16 peer_id)
4226 DSTACK(__FUNCTION_NAME);
4228 PlayerSAO *playersao = getPlayerSAO(peer_id);
4231 infostream<<"Server::DiePlayer(): Player "
4232 <<playersao->getPlayer()->getName()
4233 <<" dies"<<std::endl;
4235 playersao->setHP(0);
4237 // Trigger scripted stuff
4238 scriptapi_on_dieplayer(m_lua, playersao);
4240 SendPlayerHP(peer_id);
4241 SendDeathscreen(m_con, peer_id, false, v3f(0,0,0));
4244 void Server::RespawnPlayer(u16 peer_id)
4246 DSTACK(__FUNCTION_NAME);
4248 PlayerSAO *playersao = getPlayerSAO(peer_id);
4251 infostream<<"Server::RespawnPlayer(): Player "
4252 <<playersao->getPlayer()->getName()
4253 <<" respawns"<<std::endl;
4255 playersao->setHP(PLAYER_MAX_HP);
4257 bool repositioned = scriptapi_on_respawnplayer(m_lua, playersao);
4259 v3f pos = findSpawnPos(m_env->getServerMap());
4260 playersao->setPos(pos);
4264 void Server::UpdateCrafting(u16 peer_id)
4266 DSTACK(__FUNCTION_NAME);
4268 Player* player = m_env->getPlayer(peer_id);
4271 // Get a preview for crafting
4273 // No crafting in creative mode
4274 if(g_settings->getBool("creative_mode") == false)
4275 getCraftingResult(&player->inventory, preview, false, this);
4277 // Put the new preview in
4278 InventoryList *plist = player->inventory.getList("craftpreview");
4280 assert(plist->getSize() >= 1);
4281 plist->changeItem(0, preview);
4284 RemoteClient* Server::getClient(u16 peer_id)
4286 DSTACK(__FUNCTION_NAME);
4287 //JMutexAutoLock lock(m_con_mutex);
4288 core::map<u16, RemoteClient*>::Node *n;
4289 n = m_clients.find(peer_id);
4290 // A client should exist for all peers
4292 return n->getValue();
4295 std::wstring Server::getStatusString()
4297 std::wostringstream os(std::ios_base::binary);
4300 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4302 os<<L", uptime="<<m_uptime.get();
4303 // Information about clients
4305 for(core::map<u16, RemoteClient*>::Iterator
4306 i = m_clients.getIterator();
4307 i.atEnd() == false; i++)
4309 // Get client and check that it is valid
4310 RemoteClient *client = i.getNode()->getValue();
4311 assert(client->peer_id == i.getNode()->getKey());
4312 if(client->serialization_version == SER_FMT_VER_INVALID)
4315 Player *player = m_env->getPlayer(client->peer_id);
4316 // Get name of player
4317 std::wstring name = L"unknown";
4319 name = narrow_to_wide(player->getName());
4320 // Add name to information string
4324 if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
4325 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4326 if(g_settings->get("motd") != "")
4327 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4331 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
4333 std::set<std::string> privs;
4334 scriptapi_get_auth(m_lua, name, NULL, &privs);
4338 bool Server::checkPriv(const std::string &name, const std::string &priv)
4340 std::set<std::string> privs = getPlayerEffectivePrivs(name);
4341 return (privs.count(priv) != 0);
4344 void Server::reportPrivsModified(const std::string &name)
4347 for(core::map<u16, RemoteClient*>::Iterator
4348 i = m_clients.getIterator();
4349 i.atEnd() == false; i++){
4350 RemoteClient *client = i.getNode()->getValue();
4351 Player *player = m_env->getPlayer(client->peer_id);
4352 reportPrivsModified(player->getName());
4355 Player *player = m_env->getPlayer(name.c_str());
4358 SendPlayerPrivileges(player->peer_id);
4359 PlayerSAO *sao = player->getPlayerSAO();
4362 sao->updatePrivileges(
4363 getPlayerEffectivePrivs(name),
4368 // Saves g_settings to configpath given at initialization
4369 void Server::saveConfig()
4371 if(m_path_config != "")
4372 g_settings->updateConfigFile(m_path_config.c_str());
4375 void Server::notifyPlayer(const char *name, const std::wstring msg)
4377 Player *player = m_env->getPlayer(name);
4380 SendChatMessage(player->peer_id, std::wstring(L"Server: -!- ")+msg);
4383 void Server::notifyPlayers(const std::wstring msg)
4385 BroadcastChatMessage(msg);
4388 void Server::queueBlockEmerge(v3s16 blockpos, bool allow_generate)
4392 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
4393 m_emerge_queue.addBlock(PEER_ID_INEXISTENT, blockpos, flags);
4396 // IGameDef interface
4398 IItemDefManager* Server::getItemDefManager()
4402 INodeDefManager* Server::getNodeDefManager()
4406 ICraftDefManager* Server::getCraftDefManager()
4410 ITextureSource* Server::getTextureSource()
4414 u16 Server::allocateUnknownNodeId(const std::string &name)
4416 return m_nodedef->allocateDummy(name);
4418 ISoundManager* Server::getSoundManager()
4420 return &dummySoundManager;
4422 MtEventManager* Server::getEventManager()
4427 IWritableItemDefManager* Server::getWritableItemDefManager()
4431 IWritableNodeDefManager* Server::getWritableNodeDefManager()
4435 IWritableCraftDefManager* Server::getWritableCraftDefManager()
4440 const ModSpec* Server::getModSpec(const std::string &modname)
4442 for(core::list<ModSpec>::Iterator i = m_mods.begin();
4443 i != m_mods.end(); i++){
4444 const ModSpec &mod = *i;
4445 if(mod.name == modname)
4450 std::string Server::getBuiltinLuaPath()
4452 return porting::path_share + DIR_DELIM + "builtin";
4455 v3f findSpawnPos(ServerMap &map)
4457 //return v3f(50,50,50)*BS;
4462 nodepos = v2s16(0,0);
4467 // Try to find a good place a few times
4468 for(s32 i=0; i<1000; i++)
4471 // We're going to try to throw the player to this position
4472 v2s16 nodepos2d = v2s16(-range + (myrand()%(range*2)),
4473 -range + (myrand()%(range*2)));
4474 //v2s16 sectorpos = getNodeSectorPos(nodepos2d);
4475 // Get ground height at point (fallbacks to heightmap function)
4476 s16 groundheight = map.findGroundLevel(nodepos2d);
4477 // Don't go underwater
4478 if(groundheight < WATER_LEVEL)
4480 //infostream<<"-> Underwater"<<std::endl;
4483 // Don't go to high places
4484 if(groundheight > WATER_LEVEL + 4)
4486 //infostream<<"-> Underwater"<<std::endl;
4490 nodepos = v3s16(nodepos2d.X, groundheight-2, nodepos2d.Y);
4491 bool is_good = false;
4493 for(s32 i=0; i<10; i++){
4494 v3s16 blockpos = getNodeBlockPos(nodepos);
4495 map.emergeBlock(blockpos, true);
4496 MapNode n = map.getNodeNoEx(nodepos);
4497 if(n.getContent() == CONTENT_AIR){
4508 // Found a good place
4509 //infostream<<"Searched through "<<i<<" places."<<std::endl;
4515 return intToFloat(nodepos, BS);
4518 PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id)
4520 RemotePlayer *player = NULL;
4521 bool newplayer = false;
4524 Try to get an existing player
4526 player = static_cast<RemotePlayer*>(m_env->getPlayer(name));
4528 // If player is already connected, cancel
4529 if(player != NULL && player->peer_id != 0)
4531 infostream<<"emergePlayer(): Player already connected"<<std::endl;
4536 If player with the wanted peer_id already exists, cancel.
4538 if(m_env->getPlayer(peer_id) != NULL)
4540 infostream<<"emergePlayer(): Player with wrong name but same"
4541 " peer_id already exists"<<std::endl;
4546 Create a new player if it doesn't exist yet
4551 player = new RemotePlayer(this);
4552 player->updateName(name);
4554 /* Set player position */
4555 infostream<<"Server: Finding spawn place for player \""
4556 <<name<<"\""<<std::endl;
4557 v3f pos = findSpawnPos(m_env->getServerMap());
4558 player->setPosition(pos);
4560 /* Add player to environment */
4561 m_env->addPlayer(player);
4565 Create a new player active object
4567 PlayerSAO *playersao = new PlayerSAO(m_env, player, peer_id,
4568 getPlayerEffectivePrivs(player->getName()),
4571 /* Add object to environment */
4572 m_env->addActiveObject(playersao);
4576 scriptapi_on_newplayer(m_lua, playersao);
4578 scriptapi_on_joinplayer(m_lua, playersao);
4581 if(g_settings->getBool("creative_mode"))
4582 playersao->createCreativeInventory();
4587 void Server::handlePeerChange(PeerChange &c)
4589 JMutexAutoLock envlock(m_env_mutex);
4590 JMutexAutoLock conlock(m_con_mutex);
4592 if(c.type == PEER_ADDED)
4599 core::map<u16, RemoteClient*>::Node *n;
4600 n = m_clients.find(c.peer_id);
4601 // The client shouldn't already exist
4605 RemoteClient *client = new RemoteClient();
4606 client->peer_id = c.peer_id;
4607 m_clients.insert(client->peer_id, client);
4610 else if(c.type == PEER_REMOVED)
4617 core::map<u16, RemoteClient*>::Node *n;
4618 n = m_clients.find(c.peer_id);
4619 // The client should exist
4623 Mark objects to be not known by the client
4625 RemoteClient *client = n->getValue();
4627 for(core::map<u16, bool>::Iterator
4628 i = client->m_known_objects.getIterator();
4629 i.atEnd()==false; i++)
4632 u16 id = i.getNode()->getKey();
4633 ServerActiveObject* obj = m_env->getActiveObject(id);
4635 if(obj && obj->m_known_by_count > 0)
4636 obj->m_known_by_count--;
4640 Clear references to playing sounds
4642 for(std::map<s32, ServerPlayingSound>::iterator
4643 i = m_playing_sounds.begin();
4644 i != m_playing_sounds.end();)
4646 ServerPlayingSound &psound = i->second;
4647 psound.clients.erase(c.peer_id);
4648 if(psound.clients.size() == 0)
4649 m_playing_sounds.erase(i++);
4654 Player *player = m_env->getPlayer(c.peer_id);
4656 // Collect information about leaving in chat
4657 std::wstring message;
4661 std::wstring name = narrow_to_wide(player->getName());
4664 message += L" left game";
4666 message += L" (timed out)";
4670 /* Run scripts and remove from environment */
4674 PlayerSAO *playersao = player->getPlayerSAO();
4677 scriptapi_on_leaveplayer(m_lua, playersao);
4679 playersao->disconnected();
4689 std::ostringstream os(std::ios_base::binary);
4690 for(core::map<u16, RemoteClient*>::Iterator
4691 i = m_clients.getIterator();
4692 i.atEnd() == false; i++)
4694 RemoteClient *client = i.getNode()->getValue();
4695 assert(client->peer_id == i.getNode()->getKey());
4696 if(client->serialization_version == SER_FMT_VER_INVALID)
4699 Player *player = m_env->getPlayer(client->peer_id);
4702 // Get name of player
4703 os<<player->getName()<<" ";
4706 actionstream<<player->getName()<<" "
4707 <<(c.timeout?"times out.":"leaves game.")
4708 <<" List of players: "
4709 <<os.str()<<std::endl;
4714 delete m_clients[c.peer_id];
4715 m_clients.remove(c.peer_id);
4717 // Send player info to all remaining clients
4718 //SendPlayerInfos();
4720 // Send leave chat message to all remaining clients
4721 if(message.length() != 0)
4722 BroadcastChatMessage(message);
4731 void Server::handlePeerChanges()
4733 while(m_peer_change_queue.size() > 0)
4735 PeerChange c = m_peer_change_queue.pop_front();
4737 verbosestream<<"Server: Handling peer change: "
4738 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4741 handlePeerChange(c);
4745 void dedicated_server_loop(Server &server, bool &kill)
4747 DSTACK(__FUNCTION_NAME);
4749 verbosestream<<"dedicated_server_loop()"<<std::endl;
4751 IntervalLimiter m_profiler_interval;
4755 float steplen = g_settings->getFloat("dedicated_server_step");
4756 // This is kind of a hack but can be done like this
4757 // because server.step() is very light
4759 ScopeProfiler sp(g_profiler, "dedicated server sleep");
4760 sleep_ms((int)(steplen*1000.0));
4762 server.step(steplen);
4764 if(server.getShutdownRequested() || kill)
4766 infostream<<"Dedicated server quitting"<<std::endl;
4773 float profiler_print_interval =
4774 g_settings->getFloat("profiler_print_interval");
4775 if(profiler_print_interval != 0)
4777 if(m_profiler_interval.step(steplen, profiler_print_interval))
4779 infostream<<"Profiler:"<<std::endl;
4780 g_profiler->print(infostream);
4781 g_profiler->clear();