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 Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser 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.
23 #include "clientserver.h"
25 #include "jmutexautolock.h"
27 #include "constants.h"
30 #include "servercommand.h"
33 #include "serverobject.h"
38 #include "scriptapi.h"
43 #include "content_mapnode.h"
44 #include "content_nodemeta.h"
45 #include "content_abm.h"
46 #include "content_sao.h"
51 #include "sound.h" // dummySoundManager
52 #include "event_manager.h"
54 #include "util/string.h"
55 #include "util/pointedthing.h"
56 #include "util/mathconstants.h"
58 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
60 #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
62 class MapEditEventIgnorer
65 MapEditEventIgnorer(bool *flag):
74 ~MapEditEventIgnorer()
87 class MapEditEventAreaIgnorer
90 MapEditEventAreaIgnorer(VoxelArea *ignorevariable, const VoxelArea &a):
91 m_ignorevariable(ignorevariable)
93 if(m_ignorevariable->getVolume() == 0)
94 *m_ignorevariable = a;
96 m_ignorevariable = NULL;
99 ~MapEditEventAreaIgnorer()
103 assert(m_ignorevariable->getVolume() != 0);
104 *m_ignorevariable = VoxelArea();
109 VoxelArea *m_ignorevariable;
112 void * ServerThread::Thread()
116 log_register_thread("ServerThread");
118 DSTACK(__FUNCTION_NAME);
120 BEGIN_DEBUG_EXCEPTION_HANDLER
125 //TimeTaker timer("AsyncRunStep() + Receive()");
128 //TimeTaker timer("AsyncRunStep()");
129 m_server->AsyncRunStep();
132 //infostream<<"Running m_server->Receive()"<<std::endl;
135 catch(con::NoIncomingDataException &e)
138 catch(con::PeerNotFoundException &e)
140 infostream<<"Server: PeerNotFoundException"<<std::endl;
142 catch(con::ConnectionBindFailed &e)
144 m_server->setAsyncFatalError(e.what());
148 m_server->setAsyncFatalError(e.what());
152 END_DEBUG_EXCEPTION_HANDLER(errorstream)
157 void * EmergeThread::Thread()
161 log_register_thread("EmergeThread");
163 DSTACK(__FUNCTION_NAME);
165 BEGIN_DEBUG_EXCEPTION_HANDLER
167 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
169 v3s16 last_tried_pos(-32768,-32768,-32768); // For error output
172 Get block info from queue, emerge them and send them
175 After queue is empty, exit.
179 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
183 SharedPtr<QueuedBlockEmerge> q(qptr);
191 Do not generate over-limit
193 if(blockpos_over_limit(p))
196 //infostream<<"EmergeThread::Thread(): running"<<std::endl;
198 //TimeTaker timer("block emerge");
201 Try to emerge it from somewhere.
203 If it is only wanted as optional, only loading from disk
208 Check if any peer wants it as non-optional. In that case it
211 Also decrement the emerge queue count in clients.
214 bool only_from_disk = true;
217 core::map<u16, u8>::Iterator i;
218 for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
220 //u16 peer_id = i.getNode()->getKey();
223 u8 flags = i.getNode()->getValue();
224 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
225 only_from_disk = false;
230 if(enable_mapgen_debug_info)
231 infostream<<"EmergeThread: p="
232 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
233 <<"only_from_disk="<<only_from_disk<<std::endl;
235 ServerMap &map = ((ServerMap&)m_server->m_env->getMap());
237 MapBlock *block = NULL;
238 bool got_block = true;
239 core::map<v3s16, MapBlock*> modified_blocks;
242 Try to fetch block from memory or disk.
243 If not found and asked to generate, initialize generator.
246 bool started_generate = false;
247 mapgen::BlockMakeData data;
250 JMutexAutoLock envlock(m_server->m_env_mutex);
252 // Load sector if it isn't loaded
253 if(map.getSectorNoGenerateNoEx(p2d) == NULL)
254 map.loadSectorMeta(p2d);
256 // Attempt to load block
257 block = map.getBlockNoCreateNoEx(p);
258 if(!block || block->isDummy() || !block->isGenerated())
260 if(enable_mapgen_debug_info)
261 infostream<<"EmergeThread: not in memory, "
262 <<"attempting to load from disk"<<std::endl;
264 block = map.loadBlock(p);
267 // If could not load and allowed to generate, start generation
268 // inside this same envlock
269 if(only_from_disk == false &&
270 (block == NULL || block->isGenerated() == false)){
271 if(enable_mapgen_debug_info)
272 infostream<<"EmergeThread: generating"<<std::endl;
273 started_generate = true;
275 map.initBlockMake(&data, p);
280 If generator was initialized, generate now when envlock is free.
285 ScopeProfiler sp(g_profiler, "EmergeThread: mapgen::make_block",
287 TimeTaker t("mapgen::make_block()");
289 mapgen::make_block(&data);
291 if(enable_mapgen_debug_info == false)
292 t.stop(true); // Hide output
296 // Lock environment again to access the map
297 JMutexAutoLock envlock(m_server->m_env_mutex);
299 ScopeProfiler sp(g_profiler, "EmergeThread: after "
300 "mapgen::make_block (envlock)", SPT_AVG);
302 // Blit data back on map, update lighting, add mobs and
303 // whatever this does
304 map.finishBlockMake(&data, modified_blocks);
307 block = map.getBlockNoCreateNoEx(p);
309 // If block doesn't exist, don't try doing anything with it
310 // This happens if the block is not in generation boundaries
315 Do some post-generate stuff
318 v3s16 minp = data.blockpos_min*MAP_BLOCKSIZE;
319 v3s16 maxp = data.blockpos_max*MAP_BLOCKSIZE +
320 v3s16(1,1,1)*(MAP_BLOCKSIZE-1);
323 Ignore map edit events, they will not need to be
324 sent to anybody because the block hasn't been sent
327 //MapEditEventIgnorer ign(&m_server->m_ignore_map_edit_events);
328 MapEditEventAreaIgnorer ign(
329 &m_server->m_ignore_map_edit_events_area,
330 VoxelArea(minp, maxp));
332 TimeTaker timer("on_generated");
333 scriptapi_environment_on_generated(m_server->m_lua,
334 minp, maxp, mapgen::get_blockseed(data.seed, minp));
335 /*int t = timer.stop(true);
336 dstream<<"on_generated took "<<t<<"ms"<<std::endl;*/
339 if(enable_mapgen_debug_info)
340 infostream<<"EmergeThread: ended up with: "
341 <<analyze_block(block)<<std::endl;
343 // Activate objects and stuff
344 m_server->m_env->activateBlock(block, 0);
352 Set sent status of modified blocks on clients
355 // NOTE: Server's clients are also behind the connection mutex
356 JMutexAutoLock lock(m_server->m_con_mutex);
359 Add the originally fetched block to the modified list
363 modified_blocks.insert(p, block);
367 Set the modified blocks unsent for all the clients
370 for(core::map<u16, RemoteClient*>::Iterator
371 i = m_server->m_clients.getIterator();
372 i.atEnd() == false; i++)
374 RemoteClient *client = i.getNode()->getValue();
376 if(modified_blocks.size() > 0)
378 // Remove block from sent history
379 client->SetBlocksNotSent(modified_blocks);
383 catch(VersionMismatchException &e)
385 std::ostringstream err;
386 err<<"World data version mismatch in MapBlock "<<PP(last_tried_pos)<<std::endl;
387 err<<"----"<<std::endl;
388 err<<"\""<<e.what()<<"\""<<std::endl;
389 err<<"See debug.txt."<<std::endl;
390 err<<"World probably saved by a newer version of Minetest."<<std::endl;
391 m_server->setAsyncFatalError(err.str());
393 catch(SerializationError &e)
395 std::ostringstream err;
396 err<<"Invalid data in MapBlock "<<PP(last_tried_pos)<<std::endl;
397 err<<"----"<<std::endl;
398 err<<"\""<<e.what()<<"\""<<std::endl;
399 err<<"See debug.txt."<<std::endl;
400 err<<"You can ignore this using [ignore_world_load_errors = true]."<<std::endl;
401 m_server->setAsyncFatalError(err.str());
404 END_DEBUG_EXCEPTION_HANDLER(errorstream)
406 log_deregister_thread();
411 v3f ServerSoundParams::getPos(ServerEnvironment *env, bool *pos_exists) const
413 if(pos_exists) *pos_exists = false;
418 if(pos_exists) *pos_exists = true;
423 ServerActiveObject *sao = env->getActiveObject(object);
426 if(pos_exists) *pos_exists = true;
427 return sao->getBasePosition(); }
432 void RemoteClient::GetNextBlocks(Server *server, float dtime,
433 core::array<PrioritySortedBlockTransfer> &dest)
435 DSTACK(__FUNCTION_NAME);
438 TimeTaker timer("RemoteClient::GetNextBlocks", &timer_result);*/
441 m_nothing_to_send_pause_timer -= dtime;
442 m_nearest_unsent_reset_timer += dtime;
444 if(m_nothing_to_send_pause_timer >= 0)
449 // Won't send anything if already sending
450 if(m_blocks_sending.size() >= g_settings->getU16
451 ("max_simultaneous_block_sends_per_client"))
453 //infostream<<"Not sending any blocks, Queue full."<<std::endl;
457 //TimeTaker timer("RemoteClient::GetNextBlocks");
459 Player *player = server->m_env->getPlayer(peer_id);
461 assert(player != NULL);
463 v3f playerpos = player->getPosition();
464 v3f playerspeed = player->getSpeed();
465 v3f playerspeeddir(0,0,0);
466 if(playerspeed.getLength() > 1.0*BS)
467 playerspeeddir = playerspeed / playerspeed.getLength();
468 // Predict to next block
469 v3f playerpos_predicted = playerpos + playerspeeddir*MAP_BLOCKSIZE*BS;
471 v3s16 center_nodepos = floatToInt(playerpos_predicted, BS);
473 v3s16 center = getNodeBlockPos(center_nodepos);
475 // Camera position and direction
476 v3f camera_pos = player->getEyePosition();
477 v3f camera_dir = v3f(0,0,1);
478 camera_dir.rotateYZBy(player->getPitch());
479 camera_dir.rotateXZBy(player->getYaw());
481 /*infostream<<"camera_dir=("<<camera_dir.X<<","<<camera_dir.Y<<","
482 <<camera_dir.Z<<")"<<std::endl;*/
485 Get the starting value of the block finder radius.
488 if(m_last_center != center)
490 m_nearest_unsent_d = 0;
491 m_last_center = center;
494 /*infostream<<"m_nearest_unsent_reset_timer="
495 <<m_nearest_unsent_reset_timer<<std::endl;*/
497 // Reset periodically to workaround for some bugs or stuff
498 if(m_nearest_unsent_reset_timer > 20.0)
500 m_nearest_unsent_reset_timer = 0;
501 m_nearest_unsent_d = 0;
502 //infostream<<"Resetting m_nearest_unsent_d for "
503 // <<server->getPlayerName(peer_id)<<std::endl;
506 //s16 last_nearest_unsent_d = m_nearest_unsent_d;
507 s16 d_start = m_nearest_unsent_d;
509 //infostream<<"d_start="<<d_start<<std::endl;
511 u16 max_simul_sends_setting = g_settings->getU16
512 ("max_simultaneous_block_sends_per_client");
513 u16 max_simul_sends_usually = max_simul_sends_setting;
516 Check the time from last addNode/removeNode.
518 Decrease send rate if player is building stuff.
520 m_time_from_building += dtime;
521 if(m_time_from_building < g_settings->getFloat(
522 "full_block_send_enable_min_time_from_building"))
524 max_simul_sends_usually
525 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
529 Number of blocks sending + number of blocks selected for sending
531 u32 num_blocks_selected = m_blocks_sending.size();
534 next time d will be continued from the d from which the nearest
535 unsent block was found this time.
537 This is because not necessarily any of the blocks found this
538 time are actually sent.
540 s32 new_nearest_unsent_d = -1;
542 s16 d_max = g_settings->getS16("max_block_send_distance");
543 s16 d_max_gen = g_settings->getS16("max_block_generate_distance");
545 // Don't loop very much at a time
546 s16 max_d_increment_at_time = 2;
547 if(d_max > d_start + max_d_increment_at_time)
548 d_max = d_start + max_d_increment_at_time;
549 /*if(d_max_gen > d_start+2)
550 d_max_gen = d_start+2;*/
552 //infostream<<"Starting from "<<d_start<<std::endl;
554 s32 nearest_emerged_d = -1;
555 s32 nearest_emergefull_d = -1;
556 s32 nearest_sent_d = -1;
557 bool queue_is_full = false;
560 for(d = d_start; d <= d_max; d++)
562 /*errorstream<<"checking d="<<d<<" for "
563 <<server->getPlayerName(peer_id)<<std::endl;*/
564 //infostream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
567 If m_nearest_unsent_d was changed by the EmergeThread
568 (it can change it to 0 through SetBlockNotSent),
570 Else update m_nearest_unsent_d
572 /*if(m_nearest_unsent_d != last_nearest_unsent_d)
574 d = m_nearest_unsent_d;
575 last_nearest_unsent_d = m_nearest_unsent_d;
579 Get the border/face dot coordinates of a "d-radiused"
582 core::list<v3s16> list;
583 getFacePositions(list, d);
585 core::list<v3s16>::Iterator li;
586 for(li=list.begin(); li!=list.end(); li++)
588 v3s16 p = *li + center;
592 - Don't allow too many simultaneous transfers
593 - EXCEPT when the blocks are very close
595 Also, don't send blocks that are already flying.
598 // Start with the usual maximum
599 u16 max_simul_dynamic = max_simul_sends_usually;
601 // If block is very close, allow full maximum
602 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
603 max_simul_dynamic = max_simul_sends_setting;
605 // Don't select too many blocks for sending
606 if(num_blocks_selected >= max_simul_dynamic)
608 queue_is_full = true;
609 goto queue_full_break;
612 // Don't send blocks that are currently being transferred
613 if(m_blocks_sending.find(p) != NULL)
619 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
620 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
621 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
622 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
623 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
624 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
627 // If this is true, inexistent block will be made from scratch
628 bool generate = d <= d_max_gen;
631 /*// Limit the generating area vertically to 2/3
632 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
635 // Limit the send area vertically to 1/2
636 if(abs(p.Y - center.Y) > d_max / 2)
642 If block is far away, don't generate it unless it is
648 // Block center y in nodes
649 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
650 // Don't generate if it's very high or very low
651 if(y < -64 || y > 64)
655 v2s16 p2d_nodes_center(
659 // Get ground height in nodes
660 s16 gh = server->m_env->getServerMap().findGroundLevel(
663 // If differs a lot, don't generate
664 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
666 // Actually, don't even send it
672 //infostream<<"d="<<d<<std::endl;
675 Don't generate or send if not in sight
676 FIXME This only works if the client uses a small enough
677 FOV setting. The default of 72 degrees is fine.
680 float camera_fov = (72.0*M_PI/180) * 4./3.;
681 if(isBlockInSight(p, camera_pos, camera_dir, camera_fov, 10000*BS) == false)
687 Don't send already sent blocks
690 if(m_blocks_sent.find(p) != NULL)
697 Check if map has this block
699 MapBlock *block = server->m_env->getMap().getBlockNoCreateNoEx(p);
701 bool surely_not_found_on_disk = false;
702 bool block_is_invalid = false;
705 // Reset usage timer, this block will be of use in the future.
706 block->resetUsageTimer();
708 // Block is dummy if data doesn't exist.
709 // It means it has been not found from disk and not generated
712 surely_not_found_on_disk = true;
715 // Block is valid if lighting is up-to-date and data exists
716 if(block->isValid() == false)
718 block_is_invalid = true;
721 /*if(block->isFullyGenerated() == false)
723 block_is_invalid = true;
728 ServerMap *map = (ServerMap*)(&server->m_env->getMap());
729 v2s16 chunkpos = map->sector_to_chunk(p2d);
730 if(map->chunkNonVolatile(chunkpos) == false)
731 block_is_invalid = true;
733 if(block->isGenerated() == false)
734 block_is_invalid = true;
737 If block is not close, don't send it unless it is near
740 Block is near ground level if night-time mesh
741 differs from day-time mesh.
745 if(block->getDayNightDiff() == false)
752 If block has been marked to not exist on disk (dummy)
753 and generating new ones is not wanted, skip block.
755 if(generate == false && surely_not_found_on_disk == true)
762 Add inexistent block to emerge queue.
764 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
766 //TODO: Get value from somewhere
767 // Allow only one block in emerge queue
768 //if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
769 // Allow two blocks in queue per client
770 //if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
772 // Make it more responsive when needing to generate stuff
773 if(surely_not_found_on_disk)
775 if(server->m_emerge_queue.peerItemCount(peer_id) < max_emerge)
777 //infostream<<"Adding block to emerge queue"<<std::endl;
779 // Add it to the emerge queue and trigger the thread
782 if(generate == false)
783 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
785 server->m_emerge_queue.addBlock(peer_id, p, flags);
786 server->m_emergethread.trigger();
788 if(nearest_emerged_d == -1)
789 nearest_emerged_d = d;
791 if(nearest_emergefull_d == -1)
792 nearest_emergefull_d = d;
799 if(nearest_sent_d == -1)
803 Add block to send queue
806 /*errorstream<<"sending from d="<<d<<" to "
807 <<server->getPlayerName(peer_id)<<std::endl;*/
809 PrioritySortedBlockTransfer q((float)d, p, peer_id);
813 num_blocks_selected += 1;
818 //infostream<<"Stopped at "<<d<<std::endl;
820 // If nothing was found for sending and nothing was queued for
821 // emerging, continue next time browsing from here
822 if(nearest_emerged_d != -1){
823 new_nearest_unsent_d = nearest_emerged_d;
824 } else if(nearest_emergefull_d != -1){
825 new_nearest_unsent_d = nearest_emergefull_d;
827 if(d > g_settings->getS16("max_block_send_distance")){
828 new_nearest_unsent_d = 0;
829 m_nothing_to_send_pause_timer = 2.0;
830 /*infostream<<"GetNextBlocks(): d wrapped around for "
831 <<server->getPlayerName(peer_id)
832 <<"; setting to 0 and pausing"<<std::endl;*/
834 if(nearest_sent_d != -1)
835 new_nearest_unsent_d = nearest_sent_d;
837 new_nearest_unsent_d = d;
841 if(new_nearest_unsent_d != -1)
842 m_nearest_unsent_d = new_nearest_unsent_d;
844 /*timer_result = timer.stop(true);
845 if(timer_result != 0)
846 infostream<<"GetNextBlocks duration: "<<timer_result<<" (!=0)"<<std::endl;*/
849 void RemoteClient::GotBlock(v3s16 p)
851 if(m_blocks_sending.find(p) != NULL)
852 m_blocks_sending.remove(p);
855 /*infostream<<"RemoteClient::GotBlock(): Didn't find in"
856 " m_blocks_sending"<<std::endl;*/
857 m_excess_gotblocks++;
859 m_blocks_sent.insert(p, true);
862 void RemoteClient::SentBlock(v3s16 p)
864 if(m_blocks_sending.find(p) == NULL)
865 m_blocks_sending.insert(p, 0.0);
867 infostream<<"RemoteClient::SentBlock(): Sent block"
868 " already in m_blocks_sending"<<std::endl;
871 void RemoteClient::SetBlockNotSent(v3s16 p)
873 m_nearest_unsent_d = 0;
875 if(m_blocks_sending.find(p) != NULL)
876 m_blocks_sending.remove(p);
877 if(m_blocks_sent.find(p) != NULL)
878 m_blocks_sent.remove(p);
881 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
883 m_nearest_unsent_d = 0;
885 for(core::map<v3s16, MapBlock*>::Iterator
886 i = blocks.getIterator();
887 i.atEnd()==false; i++)
889 v3s16 p = i.getNode()->getKey();
891 if(m_blocks_sending.find(p) != NULL)
892 m_blocks_sending.remove(p);
893 if(m_blocks_sent.find(p) != NULL)
894 m_blocks_sent.remove(p);
902 PlayerInfo::PlayerInfo()
908 void PlayerInfo::PrintLine(std::ostream *s)
911 (*s)<<"\""<<name<<"\" ("
912 <<(position.X/10)<<","<<(position.Y/10)
913 <<","<<(position.Z/10)<<") ";
915 (*s)<<" avg_rtt="<<avg_rtt;
924 const std::string &path_world,
925 const std::string &path_config,
926 const SubgameSpec &gamespec,
927 bool simple_singleplayer_mode
929 m_path_world(path_world),
930 m_path_config(path_config),
931 m_gamespec(gamespec),
932 m_simple_singleplayer_mode(simple_singleplayer_mode),
933 m_async_fatal_error(""),
935 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
936 m_banmanager(path_world+DIR_DELIM+"ipban.txt"),
938 m_itemdef(createItemDefManager()),
939 m_nodedef(createNodeDefManager()),
940 m_craftdef(createCraftDefManager()),
941 m_event(new EventManager()),
943 m_emergethread(this),
944 m_time_of_day_send_timer(0),
946 m_shutdown_requested(false),
947 m_ignore_map_edit_events(false),
948 m_ignore_map_edit_events_peer_id(0)
950 m_liquid_transform_timer = 0.0;
951 m_print_info_timer = 0.0;
952 m_objectdata_timer = 0.0;
953 m_emergethread_trigger_timer = 0.0;
954 m_savemap_timer = 0.0;
958 m_step_dtime_mutex.Init();
962 throw ServerError("Supplied empty world path");
964 if(!gamespec.isValid())
965 throw ServerError("Supplied invalid gamespec");
967 infostream<<"Server created for gameid \""<<m_gamespec.id<<"\"";
968 if(m_simple_singleplayer_mode)
969 infostream<<" in simple singleplayer mode"<<std::endl;
971 infostream<<std::endl;
972 infostream<<"- world: "<<m_path_world<<std::endl;
973 infostream<<"- config: "<<m_path_config<<std::endl;
974 infostream<<"- game: "<<m_gamespec.path<<std::endl;
976 // Add world mod search path
977 m_modspaths.push_front(m_path_world + DIR_DELIM + "worldmods");
978 // Add addon mod search path
979 for(std::set<std::string>::const_iterator i = m_gamespec.mods_paths.begin();
980 i != m_gamespec.mods_paths.end(); i++)
981 m_modspaths.push_front((*i));
983 // Print out mod search paths
984 for(core::list<std::string>::Iterator i = m_modspaths.begin();
985 i != m_modspaths.end(); i++){
986 std::string modspath = *i;
987 infostream<<"- mods: "<<modspath<<std::endl;
990 // Path to builtin.lua
991 std::string builtinpath = getBuiltinLuaPath() + DIR_DELIM + "builtin.lua";
993 // Create world if it doesn't exist
994 if(!initializeWorld(m_path_world, m_gamespec.id))
995 throw ServerError("Failed to initialize world");
998 JMutexAutoLock envlock(m_env_mutex);
999 JMutexAutoLock conlock(m_con_mutex);
1001 // Initialize scripting
1003 infostream<<"Server: Initializing Lua"<<std::endl;
1004 m_lua = script_init();
1007 scriptapi_export(m_lua, this);
1008 // Load and run builtin.lua
1009 infostream<<"Server: Loading builtin.lua [\""
1010 <<builtinpath<<"\"]"<<std::endl;
1011 bool success = scriptapi_loadmod(m_lua, builtinpath, "__builtin");
1013 errorstream<<"Server: Failed to load and run "
1014 <<builtinpath<<std::endl;
1015 throw ModError("Failed to load and run "+builtinpath);
1017 // Find mods in mod search paths
1018 m_mods = getMods(m_modspaths);
1020 infostream<<"Server: Loading mods: ";
1021 for(core::list<ModSpec>::Iterator i = m_mods.begin();
1022 i != m_mods.end(); i++){
1023 const ModSpec &mod = *i;
1024 infostream<<mod.name<<" ";
1026 infostream<<std::endl;
1027 // Load and run "mod" scripts
1028 for(core::list<ModSpec>::Iterator i = m_mods.begin();
1029 i != m_mods.end(); i++){
1030 const ModSpec &mod = *i;
1031 std::string scriptpath = mod.path + DIR_DELIM + "init.lua";
1032 infostream<<" ["<<padStringRight(mod.name, 12)<<"] [\""
1033 <<scriptpath<<"\"]"<<std::endl;
1034 bool success = scriptapi_loadmod(m_lua, scriptpath, mod.name);
1036 errorstream<<"Server: Failed to load and run "
1037 <<scriptpath<<std::endl;
1038 throw ModError("Failed to load and run "+scriptpath);
1042 // Read Textures and calculate sha1 sums
1045 // Apply item aliases in the node definition manager
1046 m_nodedef->updateAliases(m_itemdef);
1048 // Initialize Environment
1050 m_env = new ServerEnvironment(new ServerMap(path_world, this), m_lua,
1053 // Give environment reference to scripting api
1054 scriptapi_add_environment(m_lua, m_env);
1056 // Register us to receive map edit events
1057 m_env->getMap().addEventReceiver(this);
1059 // If file exists, load environment metadata
1060 if(fs::PathExists(m_path_world+DIR_DELIM+"env_meta.txt"))
1062 infostream<<"Server: Loading environment metadata"<<std::endl;
1063 m_env->loadMeta(m_path_world);
1067 infostream<<"Server: Loading players"<<std::endl;
1068 m_env->deSerializePlayers(m_path_world);
1071 Add some test ActiveBlockModifiers to environment
1073 add_legacy_abms(m_env, m_nodedef);
1078 infostream<<"Server destructing"<<std::endl;
1081 Send shutdown message
1084 JMutexAutoLock conlock(m_con_mutex);
1086 std::wstring line = L"*** Server shutting down";
1089 Send the message to clients
1091 for(core::map<u16, RemoteClient*>::Iterator
1092 i = m_clients.getIterator();
1093 i.atEnd() == false; i++)
1095 // Get client and check that it is valid
1096 RemoteClient *client = i.getNode()->getValue();
1097 assert(client->peer_id == i.getNode()->getKey());
1098 if(client->serialization_version == SER_FMT_VER_INVALID)
1102 SendChatMessage(client->peer_id, line);
1104 catch(con::PeerNotFoundException &e)
1110 JMutexAutoLock envlock(m_env_mutex);
1115 infostream<<"Server: Saving players"<<std::endl;
1116 m_env->serializePlayers(m_path_world);
1119 Save environment metadata
1121 infostream<<"Server: Saving environment metadata"<<std::endl;
1122 m_env->saveMeta(m_path_world);
1134 JMutexAutoLock clientslock(m_con_mutex);
1136 for(core::map<u16, RemoteClient*>::Iterator
1137 i = m_clients.getIterator();
1138 i.atEnd() == false; i++)
1141 // NOTE: These are removed by env destructor
1143 u16 peer_id = i.getNode()->getKey();
1144 JMutexAutoLock envlock(m_env_mutex);
1145 m_env->removePlayer(peer_id);
1149 delete i.getNode()->getValue();
1153 // Delete things in the reverse order of creation
1160 // Deinitialize scripting
1161 infostream<<"Server: Deinitializing scripting"<<std::endl;
1162 script_deinit(m_lua);
1165 void Server::start(unsigned short port)
1167 DSTACK(__FUNCTION_NAME);
1168 infostream<<"Starting server on port "<<port<<"..."<<std::endl;
1170 // Stop thread if already running
1173 // Initialize connection
1174 m_con.SetTimeoutMs(30);
1178 m_thread.setRun(true);
1181 // ASCII art for the win!
1183 <<" .__ __ __ "<<std::endl
1184 <<" _____ |__| ____ _____/ |_ ____ _______/ |_ "<<std::endl
1185 <<" / \\| |/ \\_/ __ \\ __\\/ __ \\ / ___/\\ __\\"<<std::endl
1186 <<"| Y Y \\ | | \\ ___/| | \\ ___/ \\___ \\ | | "<<std::endl
1187 <<"|__|_| /__|___| /\\___ >__| \\___ >____ > |__| "<<std::endl
1188 <<" \\/ \\/ \\/ \\/ \\/ "<<std::endl;
1189 actionstream<<"World at ["<<m_path_world<<"]"<<std::endl;
1190 actionstream<<"Server for gameid=\""<<m_gamespec.id
1191 <<"\" listening on port "<<port<<"."<<std::endl;
1196 DSTACK(__FUNCTION_NAME);
1198 infostream<<"Server: Stopping and waiting threads"<<std::endl;
1200 // Stop threads (set run=false first so both start stopping)
1201 m_thread.setRun(false);
1202 m_emergethread.setRun(false);
1204 m_emergethread.stop();
1206 infostream<<"Server: Threads stopped"<<std::endl;
1209 void Server::step(float dtime)
1211 DSTACK(__FUNCTION_NAME);
1216 JMutexAutoLock lock(m_step_dtime_mutex);
1217 m_step_dtime += dtime;
1219 // Throw if fatal error occurred in thread
1220 std::string async_err = m_async_fatal_error.get();
1221 if(async_err != ""){
1222 throw ServerError(async_err);
1226 void Server::AsyncRunStep()
1228 DSTACK(__FUNCTION_NAME);
1230 g_profiler->add("Server::AsyncRunStep (num)", 1);
1234 JMutexAutoLock lock1(m_step_dtime_mutex);
1235 dtime = m_step_dtime;
1239 // Send blocks to clients
1246 g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
1248 //infostream<<"Server steps "<<dtime<<std::endl;
1249 //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1252 JMutexAutoLock lock1(m_step_dtime_mutex);
1253 m_step_dtime -= dtime;
1260 m_uptime.set(m_uptime.get() + dtime);
1264 // Process connection's timeouts
1265 JMutexAutoLock lock2(m_con_mutex);
1266 ScopeProfiler sp(g_profiler, "Server: connection timeout processing");
1267 m_con.RunTimeouts(dtime);
1271 // This has to be called so that the client list gets synced
1272 // with the peer list of the connection
1273 handlePeerChanges();
1277 Update time of day and overall game time
1280 JMutexAutoLock envlock(m_env_mutex);
1282 m_env->setTimeOfDaySpeed(g_settings->getFloat("time_speed"));
1285 Send to clients at constant intervals
1288 m_time_of_day_send_timer -= dtime;
1289 if(m_time_of_day_send_timer < 0.0)
1291 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
1293 //JMutexAutoLock envlock(m_env_mutex);
1294 JMutexAutoLock conlock(m_con_mutex);
1296 for(core::map<u16, RemoteClient*>::Iterator
1297 i = m_clients.getIterator();
1298 i.atEnd() == false; i++)
1300 RemoteClient *client = i.getNode()->getValue();
1301 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1302 m_env->getTimeOfDay(), g_settings->getFloat("time_speed"));
1304 m_con.Send(client->peer_id, 0, data, true);
1310 JMutexAutoLock lock(m_env_mutex);
1312 ScopeProfiler sp(g_profiler, "SEnv step");
1313 ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
1317 const float map_timer_and_unload_dtime = 2.92;
1318 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
1320 JMutexAutoLock lock(m_env_mutex);
1321 // Run Map's timers and unload unused data
1322 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
1323 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
1324 g_settings->getFloat("server_unload_unused_data_timeout"));
1335 JMutexAutoLock lock(m_env_mutex);
1336 JMutexAutoLock lock2(m_con_mutex);
1338 ScopeProfiler sp(g_profiler, "Server: handle players");
1340 for(core::map<u16, RemoteClient*>::Iterator
1341 i = m_clients.getIterator();
1342 i.atEnd() == false; i++)
1344 RemoteClient *client = i.getNode()->getValue();
1345 PlayerSAO *playersao = getPlayerSAO(client->peer_id);
1346 if(playersao == NULL)
1350 Handle player HPs (die if hp=0)
1352 if(playersao->getHP() == 0 && playersao->m_hp_not_sent)
1353 DiePlayer(client->peer_id);
1356 Send player inventories and HPs if necessary
1358 if(playersao->m_teleported){
1359 SendMovePlayer(client->peer_id);
1360 playersao->m_teleported = false;
1362 if(playersao->m_inventory_not_sent){
1363 UpdateCrafting(client->peer_id);
1364 SendInventory(client->peer_id);
1366 if(playersao->m_hp_not_sent){
1367 SendPlayerHP(client->peer_id);
1372 /* Transform liquids */
1373 m_liquid_transform_timer += dtime;
1374 if(m_liquid_transform_timer >= 1.00)
1376 m_liquid_transform_timer -= 1.00;
1378 JMutexAutoLock lock(m_env_mutex);
1380 ScopeProfiler sp(g_profiler, "Server: liquid transform");
1382 core::map<v3s16, MapBlock*> modified_blocks;
1383 m_env->getMap().transformLiquids(modified_blocks);
1388 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1389 ServerMap &map = ((ServerMap&)m_env->getMap());
1390 map.updateLighting(modified_blocks, lighting_modified_blocks);
1392 // Add blocks modified by lighting to modified_blocks
1393 for(core::map<v3s16, MapBlock*>::Iterator
1394 i = lighting_modified_blocks.getIterator();
1395 i.atEnd() == false; i++)
1397 MapBlock *block = i.getNode()->getValue();
1398 modified_blocks.insert(block->getPos(), block);
1402 Set the modified blocks unsent for all the clients
1405 JMutexAutoLock lock2(m_con_mutex);
1407 for(core::map<u16, RemoteClient*>::Iterator
1408 i = m_clients.getIterator();
1409 i.atEnd() == false; i++)
1411 RemoteClient *client = i.getNode()->getValue();
1413 if(modified_blocks.size() > 0)
1415 // Remove block from sent history
1416 client->SetBlocksNotSent(modified_blocks);
1421 // Periodically print some info
1423 float &counter = m_print_info_timer;
1429 JMutexAutoLock lock2(m_con_mutex);
1431 if(m_clients.size() != 0)
1432 infostream<<"Players:"<<std::endl;
1433 for(core::map<u16, RemoteClient*>::Iterator
1434 i = m_clients.getIterator();
1435 i.atEnd() == false; i++)
1437 //u16 peer_id = i.getNode()->getKey();
1438 RemoteClient *client = i.getNode()->getValue();
1439 Player *player = m_env->getPlayer(client->peer_id);
1442 infostream<<"* "<<player->getName()<<"\t";
1443 client->PrintInfo(infostream);
1448 //if(g_settings->getBool("enable_experimental"))
1452 Check added and deleted active objects
1455 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
1456 JMutexAutoLock envlock(m_env_mutex);
1457 JMutexAutoLock conlock(m_con_mutex);
1459 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
1461 // Radius inside which objects are active
1462 s16 radius = g_settings->getS16("active_object_send_range_blocks");
1463 radius *= MAP_BLOCKSIZE;
1465 for(core::map<u16, RemoteClient*>::Iterator
1466 i = m_clients.getIterator();
1467 i.atEnd() == false; i++)
1469 RemoteClient *client = i.getNode()->getValue();
1471 // If definitions and textures have not been sent, don't
1472 // send objects either
1473 if(!client->definitions_sent)
1476 Player *player = m_env->getPlayer(client->peer_id);
1479 // This can happen if the client timeouts somehow
1480 /*infostream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
1482 <<" has no associated player"<<std::endl;*/
1485 v3s16 pos = floatToInt(player->getPosition(), BS);
1487 core::map<u16, bool> removed_objects;
1488 core::map<u16, bool> added_objects;
1489 m_env->getRemovedActiveObjects(pos, radius,
1490 client->m_known_objects, removed_objects);
1491 m_env->getAddedActiveObjects(pos, radius,
1492 client->m_known_objects, added_objects);
1494 // Ignore if nothing happened
1495 if(removed_objects.size() == 0 && added_objects.size() == 0)
1497 //infostream<<"active objects: none changed"<<std::endl;
1501 std::string data_buffer;
1505 // Handle removed objects
1506 writeU16((u8*)buf, removed_objects.size());
1507 data_buffer.append(buf, 2);
1508 for(core::map<u16, bool>::Iterator
1509 i = removed_objects.getIterator();
1510 i.atEnd()==false; i++)
1513 u16 id = i.getNode()->getKey();
1514 ServerActiveObject* obj = m_env->getActiveObject(id);
1516 // Add to data buffer for sending
1517 writeU16((u8*)buf, i.getNode()->getKey());
1518 data_buffer.append(buf, 2);
1520 // Remove from known objects
1521 client->m_known_objects.remove(i.getNode()->getKey());
1523 if(obj && obj->m_known_by_count > 0)
1524 obj->m_known_by_count--;
1527 // Handle added objects
1528 writeU16((u8*)buf, added_objects.size());
1529 data_buffer.append(buf, 2);
1530 for(core::map<u16, bool>::Iterator
1531 i = added_objects.getIterator();
1532 i.atEnd()==false; i++)
1535 u16 id = i.getNode()->getKey();
1536 ServerActiveObject* obj = m_env->getActiveObject(id);
1539 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1541 infostream<<"WARNING: "<<__FUNCTION_NAME
1542 <<": NULL object"<<std::endl;
1544 type = obj->getSendType();
1546 // Add to data buffer for sending
1547 writeU16((u8*)buf, id);
1548 data_buffer.append(buf, 2);
1549 writeU8((u8*)buf, type);
1550 data_buffer.append(buf, 1);
1553 data_buffer.append(serializeLongString(
1554 obj->getClientInitializationData()));
1556 data_buffer.append(serializeLongString(""));
1558 // Add to known objects
1559 client->m_known_objects.insert(i.getNode()->getKey(), false);
1562 obj->m_known_by_count++;
1566 SharedBuffer<u8> reply(2 + data_buffer.size());
1567 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1568 memcpy((char*)&reply[2], data_buffer.c_str(),
1569 data_buffer.size());
1571 m_con.Send(client->peer_id, 0, reply, true);
1573 verbosestream<<"Server: Sent object remove/add: "
1574 <<removed_objects.size()<<" removed, "
1575 <<added_objects.size()<<" added, "
1576 <<"packet size is "<<reply.getSize()<<std::endl;
1581 Collect a list of all the objects known by the clients
1582 and report it back to the environment.
1585 core::map<u16, bool> all_known_objects;
1587 for(core::map<u16, RemoteClient*>::Iterator
1588 i = m_clients.getIterator();
1589 i.atEnd() == false; i++)
1591 RemoteClient *client = i.getNode()->getValue();
1592 // Go through all known objects of client
1593 for(core::map<u16, bool>::Iterator
1594 i = client->m_known_objects.getIterator();
1595 i.atEnd()==false; i++)
1597 u16 id = i.getNode()->getKey();
1598 all_known_objects[id] = true;
1602 m_env->setKnownActiveObjects(whatever);
1608 Send object messages
1611 JMutexAutoLock envlock(m_env_mutex);
1612 JMutexAutoLock conlock(m_con_mutex);
1614 ScopeProfiler sp(g_profiler, "Server: sending object messages");
1617 // Value = data sent by object
1618 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1620 // Get active object messages from environment
1623 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
1627 core::list<ActiveObjectMessage>* message_list = NULL;
1628 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1629 n = buffered_messages.find(aom.id);
1632 message_list = new core::list<ActiveObjectMessage>;
1633 buffered_messages.insert(aom.id, message_list);
1637 message_list = n->getValue();
1639 message_list->push_back(aom);
1642 // Route data to every client
1643 for(core::map<u16, RemoteClient*>::Iterator
1644 i = m_clients.getIterator();
1645 i.atEnd()==false; i++)
1647 RemoteClient *client = i.getNode()->getValue();
1648 std::string reliable_data;
1649 std::string unreliable_data;
1650 // Go through all objects in message buffer
1651 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1652 j = buffered_messages.getIterator();
1653 j.atEnd()==false; j++)
1655 // If object is not known by client, skip it
1656 u16 id = j.getNode()->getKey();
1657 if(client->m_known_objects.find(id) == NULL)
1659 // Get message list of object
1660 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1661 // Go through every message
1662 for(core::list<ActiveObjectMessage>::Iterator
1663 k = list->begin(); k != list->end(); k++)
1665 // Compose the full new data with header
1666 ActiveObjectMessage aom = *k;
1667 std::string new_data;
1670 writeU16((u8*)&buf[0], aom.id);
1671 new_data.append(buf, 2);
1673 new_data += serializeString(aom.datastring);
1674 // Add data to buffer
1676 reliable_data += new_data;
1678 unreliable_data += new_data;
1682 reliable_data and unreliable_data are now ready.
1685 if(reliable_data.size() > 0)
1687 SharedBuffer<u8> reply(2 + reliable_data.size());
1688 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1689 memcpy((char*)&reply[2], reliable_data.c_str(),
1690 reliable_data.size());
1692 m_con.Send(client->peer_id, 0, reply, true);
1694 if(unreliable_data.size() > 0)
1696 SharedBuffer<u8> reply(2 + unreliable_data.size());
1697 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1698 memcpy((char*)&reply[2], unreliable_data.c_str(),
1699 unreliable_data.size());
1700 // Send as unreliable
1701 m_con.Send(client->peer_id, 0, reply, false);
1704 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1706 infostream<<"Server: Size of object message data: "
1707 <<"reliable: "<<reliable_data.size()
1708 <<", unreliable: "<<unreliable_data.size()
1713 // Clear buffered_messages
1714 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1715 i = buffered_messages.getIterator();
1716 i.atEnd()==false; i++)
1718 delete i.getNode()->getValue();
1722 } // enable_experimental
1725 Send queued-for-sending map edit events.
1728 // We will be accessing the environment and the connection
1729 JMutexAutoLock lock(m_env_mutex);
1730 JMutexAutoLock conlock(m_con_mutex);
1732 // Don't send too many at a time
1735 // Single change sending is disabled if queue size is not small
1736 bool disable_single_change_sending = false;
1737 if(m_unsent_map_edit_queue.size() >= 4)
1738 disable_single_change_sending = true;
1740 int event_count = m_unsent_map_edit_queue.size();
1742 // We'll log the amount of each
1745 while(m_unsent_map_edit_queue.size() != 0)
1747 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1749 // Players far away from the change are stored here.
1750 // Instead of sending the changes, MapBlocks are set not sent
1752 core::list<u16> far_players;
1754 if(event->type == MEET_ADDNODE)
1756 //infostream<<"Server: MEET_ADDNODE"<<std::endl;
1757 prof.add("MEET_ADDNODE", 1);
1758 if(disable_single_change_sending)
1759 sendAddNode(event->p, event->n, event->already_known_by_peer,
1762 sendAddNode(event->p, event->n, event->already_known_by_peer,
1765 else if(event->type == MEET_REMOVENODE)
1767 //infostream<<"Server: MEET_REMOVENODE"<<std::endl;
1768 prof.add("MEET_REMOVENODE", 1);
1769 if(disable_single_change_sending)
1770 sendRemoveNode(event->p, event->already_known_by_peer,
1773 sendRemoveNode(event->p, event->already_known_by_peer,
1776 else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
1778 infostream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
1779 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
1780 setBlockNotSent(event->p);
1782 else if(event->type == MEET_OTHER)
1784 infostream<<"Server: MEET_OTHER"<<std::endl;
1785 prof.add("MEET_OTHER", 1);
1786 for(core::map<v3s16, bool>::Iterator
1787 i = event->modified_blocks.getIterator();
1788 i.atEnd()==false; i++)
1790 v3s16 p = i.getNode()->getKey();
1796 prof.add("unknown", 1);
1797 infostream<<"WARNING: Server: Unknown MapEditEvent "
1798 <<((u32)event->type)<<std::endl;
1802 Set blocks not sent to far players
1804 if(far_players.size() > 0)
1806 // Convert list format to that wanted by SetBlocksNotSent
1807 core::map<v3s16, MapBlock*> modified_blocks2;
1808 for(core::map<v3s16, bool>::Iterator
1809 i = event->modified_blocks.getIterator();
1810 i.atEnd()==false; i++)
1812 v3s16 p = i.getNode()->getKey();
1813 modified_blocks2.insert(p,
1814 m_env->getMap().getBlockNoCreateNoEx(p));
1816 // Set blocks not sent
1817 for(core::list<u16>::Iterator
1818 i = far_players.begin();
1819 i != far_players.end(); i++)
1822 RemoteClient *client = getClient(peer_id);
1825 client->SetBlocksNotSent(modified_blocks2);
1831 /*// Don't send too many at a time
1833 if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
1837 if(event_count >= 5){
1838 infostream<<"Server: MapEditEvents:"<<std::endl;
1839 prof.print(infostream);
1840 } else if(event_count != 0){
1841 verbosestream<<"Server: MapEditEvents:"<<std::endl;
1842 prof.print(verbosestream);
1848 Trigger emergethread (it somehow gets to a non-triggered but
1849 bysy state sometimes)
1852 float &counter = m_emergethread_trigger_timer;
1858 m_emergethread.trigger();
1862 // Save map, players and auth stuff
1864 float &counter = m_savemap_timer;
1866 if(counter >= g_settings->getFloat("server_map_save_interval"))
1869 JMutexAutoLock lock(m_env_mutex);
1871 ScopeProfiler sp(g_profiler, "Server: saving stuff");
1874 if(m_banmanager.isModified())
1875 m_banmanager.save();
1877 // Save changed parts of map
1878 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
1881 m_env->serializePlayers(m_path_world);
1883 // Save environment metadata
1884 m_env->saveMeta(m_path_world);
1889 void Server::Receive()
1891 DSTACK(__FUNCTION_NAME);
1892 SharedBuffer<u8> data;
1897 JMutexAutoLock conlock(m_con_mutex);
1898 datasize = m_con.Receive(peer_id, data);
1901 // This has to be called so that the client list gets synced
1902 // with the peer list of the connection
1903 handlePeerChanges();
1905 ProcessData(*data, datasize, peer_id);
1907 catch(con::InvalidIncomingDataException &e)
1909 infostream<<"Server::Receive(): "
1910 "InvalidIncomingDataException: what()="
1911 <<e.what()<<std::endl;
1913 catch(con::PeerNotFoundException &e)
1915 //NOTE: This is not needed anymore
1917 // The peer has been disconnected.
1918 // Find the associated player and remove it.
1920 /*JMutexAutoLock envlock(m_env_mutex);
1922 infostream<<"ServerThread: peer_id="<<peer_id
1923 <<" has apparently closed connection. "
1924 <<"Removing player."<<std::endl;
1926 m_env->removePlayer(peer_id);*/
1930 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1932 DSTACK(__FUNCTION_NAME);
1933 // Environment is locked first.
1934 JMutexAutoLock envlock(m_env_mutex);
1935 JMutexAutoLock conlock(m_con_mutex);
1937 ScopeProfiler sp(g_profiler, "Server::ProcessData");
1940 Address address = m_con.GetPeerAddress(peer_id);
1941 std::string addr_s = address.serializeString();
1943 // drop player if is ip is banned
1944 if(m_banmanager.isIpBanned(addr_s)){
1945 infostream<<"Server: A banned client tried to connect from "
1946 <<addr_s<<"; banned name was "
1947 <<m_banmanager.getBanName(addr_s)<<std::endl;
1948 // This actually doesn't seem to transfer to the client
1949 SendAccessDenied(m_con, peer_id,
1950 L"Your ip is banned. Banned name was "
1951 +narrow_to_wide(m_banmanager.getBanName(addr_s)));
1952 m_con.DeletePeer(peer_id);
1956 catch(con::PeerNotFoundException &e)
1958 infostream<<"Server::ProcessData(): Cancelling: peer "
1959 <<peer_id<<" not found"<<std::endl;
1963 std::string addr_s = m_con.GetPeerAddress(peer_id).serializeString();
1965 u8 peer_ser_ver = getClient(peer_id)->serialization_version;
1973 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1975 if(command == TOSERVER_INIT)
1977 // [0] u16 TOSERVER_INIT
1978 // [2] u8 SER_FMT_VER_HIGHEST
1979 // [3] u8[20] player_name
1980 // [23] u8[28] password <--- can be sent without this, from old versions
1982 if(datasize < 2+1+PLAYERNAME_SIZE)
1985 verbosestream<<"Server: Got TOSERVER_INIT from "
1986 <<peer_id<<std::endl;
1988 // First byte after command is maximum supported
1989 // serialization version
1990 u8 client_max = data[2];
1991 u8 our_max = SER_FMT_VER_HIGHEST;
1992 // Use the highest version supported by both
1993 u8 deployed = core::min_(client_max, our_max);
1994 // If it's lower than the lowest supported, give up.
1995 if(deployed < SER_FMT_VER_LOWEST)
1996 deployed = SER_FMT_VER_INVALID;
1998 //peer->serialization_version = deployed;
1999 getClient(peer_id)->pending_serialization_version = deployed;
2001 if(deployed == SER_FMT_VER_INVALID)
2003 actionstream<<"Server: A mismatched client tried to connect from "
2004 <<addr_s<<std::endl;
2005 infostream<<"Server: Cannot negotiate "
2006 "serialization version with peer "
2007 <<peer_id<<std::endl;
2008 SendAccessDenied(m_con, peer_id, std::wstring(
2009 L"Your client's version is not supported.\n"
2010 L"Server version is ")
2011 + narrow_to_wide(VERSION_STRING) + L"."
2017 Read and check network protocol version
2020 u16 net_proto_version = 0;
2021 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
2023 net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
2026 getClient(peer_id)->net_proto_version = net_proto_version;
2028 if(net_proto_version == 0)
2030 actionstream<<"Server: An old tried to connect from "<<addr_s
2032 SendAccessDenied(m_con, peer_id, std::wstring(
2033 L"Your client's version is not supported.\n"
2034 L"Server version is ")
2035 + narrow_to_wide(VERSION_STRING) + L"."
2040 if(g_settings->getBool("strict_protocol_version_checking"))
2042 if(net_proto_version != PROTOCOL_VERSION)
2044 actionstream<<"Server: A mismatched client tried to connect"
2045 <<" from "<<addr_s<<std::endl;
2046 SendAccessDenied(m_con, peer_id, std::wstring(
2047 L"Your client's version is not supported.\n"
2048 L"Server version is ")
2049 + narrow_to_wide(VERSION_STRING) + L",\n"
2050 + L"server's PROTOCOL_VERSION is "
2051 + narrow_to_wide(itos(PROTOCOL_VERSION))
2052 + L", client's PROTOCOL_VERSION is "
2053 + narrow_to_wide(itos(net_proto_version))
2064 char playername[PLAYERNAME_SIZE];
2065 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
2067 playername[i] = data[3+i];
2069 playername[PLAYERNAME_SIZE-1] = 0;
2071 if(playername[0]=='\0')
2073 actionstream<<"Server: Player with an empty name "
2074 <<"tried to connect from "<<addr_s<<std::endl;
2075 SendAccessDenied(m_con, peer_id,
2080 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
2082 actionstream<<"Server: Player with an invalid name "
2083 <<"tried to connect from "<<addr_s<<std::endl;
2084 SendAccessDenied(m_con, peer_id,
2085 L"Name contains unallowed characters");
2089 infostream<<"Server: New connection: \""<<playername<<"\" from "
2090 <<m_con.GetPeerAddress(peer_id).serializeString()<<std::endl;
2093 char given_password[PASSWORD_SIZE];
2094 if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
2096 // old version - assume blank password
2097 given_password[0] = 0;
2101 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2103 given_password[i] = data[23+i];
2105 given_password[PASSWORD_SIZE-1] = 0;
2108 if(!base64_is_valid(given_password)){
2109 infostream<<"Server: "<<playername
2110 <<" supplied invalid password hash"<<std::endl;
2111 SendAccessDenied(m_con, peer_id, L"Invalid password hash");
2115 std::string checkpwd; // Password hash to check against
2116 bool has_auth = scriptapi_get_auth(m_lua, playername, &checkpwd, NULL);
2118 // If no authentication info exists for user, create it
2120 if(!isSingleplayer() &&
2121 g_settings->getBool("disallow_empty_password") &&
2122 std::string(given_password) == ""){
2123 SendAccessDenied(m_con, peer_id, L"Empty passwords are "
2124 L"disallowed. Set a password and try again.");
2127 std::wstring raw_default_password =
2128 narrow_to_wide(g_settings->get("default_password"));
2129 std::string initial_password =
2130 translatePassword(playername, raw_default_password);
2132 // If default_password is empty, allow any initial password
2133 if (raw_default_password.length() == 0)
2134 initial_password = given_password;
2136 scriptapi_create_auth(m_lua, playername, initial_password);
2139 has_auth = scriptapi_get_auth(m_lua, playername, &checkpwd, NULL);
2142 SendAccessDenied(m_con, peer_id, L"Not allowed to login");
2146 if(given_password != checkpwd){
2147 infostream<<"Server: peer_id="<<peer_id
2148 <<": supplied invalid password for "
2149 <<playername<<std::endl;
2150 SendAccessDenied(m_con, peer_id, L"Invalid password");
2154 // Do not allow multiple players in simple singleplayer mode.
2155 // This isn't a perfect way to do it, but will suffice for now.
2156 if(m_simple_singleplayer_mode && m_clients.size() > 1){
2157 infostream<<"Server: Not allowing another client to connect in"
2158 <<" simple singleplayer mode"<<std::endl;
2159 SendAccessDenied(m_con, peer_id,
2160 L"Running in simple singleplayer mode.");
2164 // Enforce user limit.
2165 // Don't enforce for users that have some admin right
2166 if(m_clients.size() >= g_settings->getU16("max_users") &&
2167 !checkPriv(playername, "server") &&
2168 !checkPriv(playername, "ban") &&
2169 !checkPriv(playername, "privs") &&
2170 !checkPriv(playername, "password") &&
2171 playername != g_settings->get("name"))
2173 actionstream<<"Server: "<<playername<<" tried to join, but there"
2174 <<" are already max_users="
2175 <<g_settings->getU16("max_users")<<" players."<<std::endl;
2176 SendAccessDenied(m_con, peer_id, L"Too many users.");
2181 PlayerSAO *playersao = emergePlayer(playername, peer_id);
2183 // If failed, cancel
2184 if(playersao == NULL)
2186 errorstream<<"Server: peer_id="<<peer_id
2187 <<": failed to emerge player"<<std::endl;
2192 Answer with a TOCLIENT_INIT
2195 SharedBuffer<u8> reply(2+1+6+8);
2196 writeU16(&reply[0], TOCLIENT_INIT);
2197 writeU8(&reply[2], deployed);
2198 writeV3S16(&reply[2+1], floatToInt(playersao->getPlayer()->getPosition()+v3f(0,BS/2,0), BS));
2199 writeU64(&reply[2+1+6], m_env->getServerMap().getSeed());
2202 m_con.Send(peer_id, 0, reply, true);
2206 Send complete position information
2208 SendMovePlayer(peer_id);
2213 if(command == TOSERVER_INIT2)
2215 verbosestream<<"Server: Got TOSERVER_INIT2 from "
2216 <<peer_id<<std::endl;
2218 Player *player = m_env->getPlayer(peer_id);
2220 verbosestream<<"Server: TOSERVER_INIT2: "
2221 <<"Player not found; ignoring."<<std::endl;
2225 getClient(peer_id)->serialization_version
2226 = getClient(peer_id)->pending_serialization_version;
2229 Send some initialization data
2232 infostream<<"Server: Sending content to "
2233 <<getPlayerName(peer_id)<<std::endl;
2235 // Send item definitions
2236 SendItemDef(m_con, peer_id, m_itemdef);
2238 // Send node definitions
2239 SendNodeDef(m_con, peer_id, m_nodedef);
2241 // Send media announcement
2242 sendMediaAnnouncement(peer_id);
2245 SendPlayerPrivileges(peer_id);
2247 // Send inventory formspec
2248 SendPlayerInventoryFormspec(peer_id);
2251 UpdateCrafting(peer_id);
2252 SendInventory(peer_id);
2255 SendPlayerHP(peer_id);
2257 // Show death screen if necessary
2259 SendDeathscreen(m_con, peer_id, false, v3f(0,0,0));
2263 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2264 m_env->getTimeOfDay(), g_settings->getFloat("time_speed"));
2265 m_con.Send(peer_id, 0, data, true);
2268 // Note things in chat if not in simple singleplayer mode
2269 if(!m_simple_singleplayer_mode)
2271 // Send information about server to player in chat
2272 SendChatMessage(peer_id, getStatusString());
2274 // Send information about joining in chat
2276 std::wstring name = L"unknown";
2277 Player *player = m_env->getPlayer(peer_id);
2279 name = narrow_to_wide(player->getName());
2281 std::wstring message;
2284 message += L" joined the game.";
2285 BroadcastChatMessage(message);
2289 // Warnings about protocol version can be issued here
2290 if(getClient(peer_id)->net_proto_version < PROTOCOL_VERSION)
2292 SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT IS OLD AND MAY WORK PROPERLY WITH THIS SERVER!");
2299 std::ostringstream os(std::ios_base::binary);
2300 for(core::map<u16, RemoteClient*>::Iterator
2301 i = m_clients.getIterator();
2302 i.atEnd() == false; i++)
2304 RemoteClient *client = i.getNode()->getValue();
2305 assert(client->peer_id == i.getNode()->getKey());
2306 if(client->serialization_version == SER_FMT_VER_INVALID)
2309 Player *player = m_env->getPlayer(client->peer_id);
2312 // Get name of player
2313 os<<player->getName()<<" ";
2316 actionstream<<player->getName()<<" joins game. List of players: "
2317 <<os.str()<<std::endl;
2323 if(peer_ser_ver == SER_FMT_VER_INVALID)
2325 infostream<<"Server::ProcessData(): Cancelling: Peer"
2326 " serialization format invalid or not initialized."
2327 " Skipping incoming command="<<command<<std::endl;
2331 Player *player = m_env->getPlayer(peer_id);
2333 infostream<<"Server::ProcessData(): Cancelling: "
2334 "No player for peer_id="<<peer_id
2339 PlayerSAO *playersao = player->getPlayerSAO();
2340 if(playersao == NULL){
2341 infostream<<"Server::ProcessData(): Cancelling: "
2342 "No player object for peer_id="<<peer_id
2347 if(command == TOSERVER_PLAYERPOS)
2349 if(datasize < 2+12+12+4+4)
2353 v3s32 ps = readV3S32(&data[start+2]);
2354 v3s32 ss = readV3S32(&data[start+2+12]);
2355 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2356 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2357 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2358 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2359 pitch = wrapDegrees(pitch);
2360 yaw = wrapDegrees(yaw);
2362 player->setPosition(position);
2363 player->setSpeed(speed);
2364 player->setPitch(pitch);
2365 player->setYaw(yaw);
2367 /*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2368 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2369 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2371 else if(command == TOSERVER_GOTBLOCKS)
2384 u16 count = data[2];
2385 for(u16 i=0; i<count; i++)
2387 if((s16)datasize < 2+1+(i+1)*6)
2388 throw con::InvalidIncomingDataException
2389 ("GOTBLOCKS length is too short");
2390 v3s16 p = readV3S16(&data[2+1+i*6]);
2391 /*infostream<<"Server: GOTBLOCKS ("
2392 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2393 RemoteClient *client = getClient(peer_id);
2394 client->GotBlock(p);
2397 else if(command == TOSERVER_DELETEDBLOCKS)
2410 u16 count = data[2];
2411 for(u16 i=0; i<count; i++)
2413 if((s16)datasize < 2+1+(i+1)*6)
2414 throw con::InvalidIncomingDataException
2415 ("DELETEDBLOCKS length is too short");
2416 v3s16 p = readV3S16(&data[2+1+i*6]);
2417 /*infostream<<"Server: DELETEDBLOCKS ("
2418 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2419 RemoteClient *client = getClient(peer_id);
2420 client->SetBlockNotSent(p);
2423 else if(command == TOSERVER_CLICK_OBJECT)
2425 infostream<<"Server: CLICK_OBJECT not supported anymore"<<std::endl;
2428 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2430 infostream<<"Server: CLICK_ACTIVEOBJECT not supported anymore"<<std::endl;
2433 else if(command == TOSERVER_GROUND_ACTION)
2435 infostream<<"Server: GROUND_ACTION not supported anymore"<<std::endl;
2439 else if(command == TOSERVER_RELEASE)
2441 infostream<<"Server: RELEASE not supported anymore"<<std::endl;
2444 else if(command == TOSERVER_SIGNTEXT)
2446 infostream<<"Server: SIGNTEXT not supported anymore"
2450 else if(command == TOSERVER_SIGNNODETEXT)
2452 infostream<<"Server: SIGNNODETEXT not supported anymore"
2456 else if(command == TOSERVER_INVENTORY_ACTION)
2458 // Strip command and create a stream
2459 std::string datastring((char*)&data[2], datasize-2);
2460 verbosestream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2461 std::istringstream is(datastring, std::ios_base::binary);
2463 InventoryAction *a = InventoryAction::deSerialize(is);
2466 infostream<<"TOSERVER_INVENTORY_ACTION: "
2467 <<"InventoryAction::deSerialize() returned NULL"
2473 Note: Always set inventory not sent, to repair cases
2474 where the client made a bad prediction.
2478 Handle restrictions and special cases of the move action
2480 if(a->getType() == IACTION_MOVE)
2482 IMoveAction *ma = (IMoveAction*)a;
2484 ma->from_inv.applyCurrentPlayer(player->getName());
2485 ma->to_inv.applyCurrentPlayer(player->getName());
2487 setInventoryModified(ma->from_inv);
2488 setInventoryModified(ma->to_inv);
2490 bool from_inv_is_current_player =
2491 (ma->from_inv.type == InventoryLocation::PLAYER) &&
2492 (ma->from_inv.name == player->getName());
2494 bool to_inv_is_current_player =
2495 (ma->to_inv.type == InventoryLocation::PLAYER) &&
2496 (ma->to_inv.name == player->getName());
2499 Disable moving items out of craftpreview
2501 if(ma->from_list == "craftpreview")
2503 infostream<<"Ignoring IMoveAction from "
2504 <<(ma->from_inv.dump())<<":"<<ma->from_list
2505 <<" to "<<(ma->to_inv.dump())<<":"<<ma->to_list
2506 <<" because src is "<<ma->from_list<<std::endl;
2512 Disable moving items into craftresult and craftpreview
2514 if(ma->to_list == "craftpreview" || ma->to_list == "craftresult")
2516 infostream<<"Ignoring IMoveAction from "
2517 <<(ma->from_inv.dump())<<":"<<ma->from_list
2518 <<" to "<<(ma->to_inv.dump())<<":"<<ma->to_list
2519 <<" because dst is "<<ma->to_list<<std::endl;
2524 // Disallow moving items in elsewhere than player's inventory
2525 // if not allowed to interact
2526 if(!checkPriv(player->getName(), "interact") &&
2527 (!from_inv_is_current_player ||
2528 !to_inv_is_current_player))
2530 infostream<<"Cannot move outside of player's inventory: "
2531 <<"No interact privilege"<<std::endl;
2536 // If player is not an admin, check for ownership of src and dst
2537 /*if(!checkPriv(player->getName(), "server"))
2539 std::string owner_from = getInventoryOwner(ma->from_inv);
2540 if(owner_from != "" && owner_from != player->getName())
2542 infostream<<"WARNING: "<<player->getName()
2543 <<" tried to access an inventory that"
2544 <<" belongs to "<<owner_from<<std::endl;
2549 std::string owner_to = getInventoryOwner(ma->to_inv);
2550 if(owner_to != "" && owner_to != player->getName())
2552 infostream<<"WARNING: "<<player->getName()
2553 <<" tried to access an inventory that"
2554 <<" belongs to "<<owner_to<<std::endl;
2561 Handle restrictions and special cases of the drop action
2563 else if(a->getType() == IACTION_DROP)
2565 IDropAction *da = (IDropAction*)a;
2567 da->from_inv.applyCurrentPlayer(player->getName());
2569 setInventoryModified(da->from_inv);
2571 // Disallow dropping items if not allowed to interact
2572 if(!checkPriv(player->getName(), "interact"))
2577 // If player is not an admin, check for ownership
2578 /*else if(!checkPriv(player->getName(), "server"))
2580 std::string owner_from = getInventoryOwner(da->from_inv);
2581 if(owner_from != "" && owner_from != player->getName())
2583 infostream<<"WARNING: "<<player->getName()
2584 <<" tried to access an inventory that"
2585 <<" belongs to "<<owner_from<<std::endl;
2592 Handle restrictions and special cases of the craft action
2594 else if(a->getType() == IACTION_CRAFT)
2596 ICraftAction *ca = (ICraftAction*)a;
2598 ca->craft_inv.applyCurrentPlayer(player->getName());
2600 setInventoryModified(ca->craft_inv);
2602 //bool craft_inv_is_current_player =
2603 // (ca->craft_inv.type == InventoryLocation::PLAYER) &&
2604 // (ca->craft_inv.name == player->getName());
2606 // Disallow crafting if not allowed to interact
2607 if(!checkPriv(player->getName(), "interact"))
2609 infostream<<"Cannot craft: "
2610 <<"No interact privilege"<<std::endl;
2615 // If player is not an admin, check for ownership of inventory
2616 /*if(!checkPriv(player->getName(), "server"))
2618 std::string owner_craft = getInventoryOwner(ca->craft_inv);
2619 if(owner_craft != "" && owner_craft != player->getName())
2621 infostream<<"WARNING: "<<player->getName()
2622 <<" tried to access an inventory that"
2623 <<" belongs to "<<owner_craft<<std::endl;
2631 a->apply(this, playersao, this);
2635 else if(command == TOSERVER_CHAT_MESSAGE)
2643 std::string datastring((char*)&data[2], datasize-2);
2644 std::istringstream is(datastring, std::ios_base::binary);
2647 is.read((char*)buf, 2);
2648 u16 len = readU16(buf);
2650 std::wstring message;
2651 for(u16 i=0; i<len; i++)
2653 is.read((char*)buf, 2);
2654 message += (wchar_t)readU16(buf);
2657 // Get player name of this client
2658 std::wstring name = narrow_to_wide(player->getName());
2661 bool ate = scriptapi_on_chat_message(m_lua, player->getName(),
2662 wide_to_narrow(message));
2663 // If script ate the message, don't proceed
2667 // Line to send to players
2669 // Whether to send to the player that sent the line
2670 bool send_to_sender = false;
2671 // Whether to send to other players
2672 bool send_to_others = false;
2675 if(message[0] == L'/')
2677 size_t strip_size = 1;
2678 if (message[1] == L'#') // support old-style commans
2680 message = message.substr(strip_size);
2682 WStrfnd f1(message);
2683 f1.next(L" "); // Skip over /#whatever
2684 std::wstring paramstring = f1.next(L"");
2686 ServerCommandContext *ctx = new ServerCommandContext(
2687 str_split(message, L' '),
2693 std::wstring reply(processServerCommand(ctx));
2694 send_to_sender = ctx->flags & SEND_TO_SENDER;
2695 send_to_others = ctx->flags & SEND_TO_OTHERS;
2697 if (ctx->flags & SEND_NO_PREFIX)
2700 line += L"Server: " + reply;
2707 if(checkPriv(player->getName(), "shout")){
2712 send_to_others = true;
2714 line += L"-!- You don't have permission to shout.";
2715 send_to_sender = true;
2722 actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2725 Send the message to clients
2727 for(core::map<u16, RemoteClient*>::Iterator
2728 i = m_clients.getIterator();
2729 i.atEnd() == false; i++)
2731 // Get client and check that it is valid
2732 RemoteClient *client = i.getNode()->getValue();
2733 assert(client->peer_id == i.getNode()->getKey());
2734 if(client->serialization_version == SER_FMT_VER_INVALID)
2738 bool sender_selected = (peer_id == client->peer_id);
2739 if(sender_selected == true && send_to_sender == false)
2741 if(sender_selected == false && send_to_others == false)
2744 SendChatMessage(client->peer_id, line);
2748 else if(command == TOSERVER_DAMAGE)
2750 std::string datastring((char*)&data[2], datasize-2);
2751 std::istringstream is(datastring, std::ios_base::binary);
2752 u8 damage = readU8(is);
2754 actionstream<<player->getName()<<" damaged by "
2755 <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
2758 playersao->setHP(playersao->getHP() - damage);
2760 if(playersao->getHP() == 0 && playersao->m_hp_not_sent)
2763 if(playersao->m_hp_not_sent)
2764 SendPlayerHP(peer_id);
2766 else if(command == TOSERVER_PASSWORD)
2769 [0] u16 TOSERVER_PASSWORD
2770 [2] u8[28] old password
2771 [30] u8[28] new password
2774 if(datasize != 2+PASSWORD_SIZE*2)
2776 /*char password[PASSWORD_SIZE];
2777 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2778 password[i] = data[2+i];
2779 password[PASSWORD_SIZE-1] = 0;*/
2781 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2789 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2791 char c = data[2+PASSWORD_SIZE+i];
2797 if(!base64_is_valid(newpwd)){
2798 infostream<<"Server: "<<player->getName()<<" supplied invalid password hash"<<std::endl;
2799 // Wrong old password supplied!!
2800 SendChatMessage(peer_id, L"Invalid new password hash supplied. Password NOT changed.");
2804 infostream<<"Server: Client requests a password change from "
2805 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
2807 std::string playername = player->getName();
2809 std::string checkpwd;
2810 scriptapi_get_auth(m_lua, playername, &checkpwd, NULL);
2812 if(oldpwd != checkpwd)
2814 infostream<<"Server: invalid old password"<<std::endl;
2815 // Wrong old password supplied!!
2816 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
2820 bool success = scriptapi_set_password(m_lua, playername, newpwd);
2822 actionstream<<player->getName()<<" changes password"<<std::endl;
2823 SendChatMessage(peer_id, L"Password change successful.");
2825 actionstream<<player->getName()<<" tries to change password but "
2826 <<"it fails"<<std::endl;
2827 SendChatMessage(peer_id, L"Password change failed or inavailable.");
2830 else if(command == TOSERVER_PLAYERITEM)
2835 u16 item = readU16(&data[2]);
2836 playersao->setWieldIndex(item);
2838 else if(command == TOSERVER_RESPAWN)
2843 RespawnPlayer(peer_id);
2845 actionstream<<player->getName()<<" respawns at "
2846 <<PP(player->getPosition()/BS)<<std::endl;
2848 // ActiveObject is added to environment in AsyncRunStep after
2849 // the previous addition has been succesfully removed
2851 else if(command == TOSERVER_REQUEST_MEDIA) {
2852 std::string datastring((char*)&data[2], datasize-2);
2853 std::istringstream is(datastring, std::ios_base::binary);
2855 core::list<MediaRequest> tosend;
2856 u16 numfiles = readU16(is);
2858 infostream<<"Sending "<<numfiles<<" files to "
2859 <<getPlayerName(peer_id)<<std::endl;
2860 verbosestream<<"TOSERVER_REQUEST_MEDIA: "<<std::endl;
2862 for(int i = 0; i < numfiles; i++) {
2863 std::string name = deSerializeString(is);
2864 tosend.push_back(MediaRequest(name));
2865 verbosestream<<"TOSERVER_REQUEST_MEDIA: requested file "
2869 sendRequestedMedia(peer_id, tosend);
2871 // Now the client should know about everything
2872 // (definitions and files)
2873 getClient(peer_id)->definitions_sent = true;
2875 else if(command == TOSERVER_INTERACT)
2877 std::string datastring((char*)&data[2], datasize-2);
2878 std::istringstream is(datastring, std::ios_base::binary);
2884 [5] u32 length of the next item
2885 [9] serialized PointedThing
2887 0: start digging (from undersurface) or use
2888 1: stop digging (all parameters ignored)
2889 2: digging completed
2890 3: place block or item (to abovesurface)
2893 u8 action = readU8(is);
2894 u16 item_i = readU16(is);
2895 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
2896 PointedThing pointed;
2897 pointed.deSerialize(tmp_is);
2899 verbosestream<<"TOSERVER_INTERACT: action="<<(int)action<<", item="
2900 <<item_i<<", pointed="<<pointed.dump()<<std::endl;
2904 verbosestream<<"TOSERVER_INTERACT: "<<player->getName()
2905 <<" tried to interact, but is dead!"<<std::endl;
2909 v3f player_pos = playersao->getLastGoodPosition();
2911 // Update wielded item
2912 playersao->setWieldIndex(item_i);
2914 // Get pointed to node (undefined if not POINTEDTYPE_NODE)
2915 v3s16 p_under = pointed.node_undersurface;
2916 v3s16 p_above = pointed.node_abovesurface;
2918 // Get pointed to object (NULL if not POINTEDTYPE_OBJECT)
2919 ServerActiveObject *pointed_object = NULL;
2920 if(pointed.type == POINTEDTHING_OBJECT)
2922 pointed_object = m_env->getActiveObject(pointed.object_id);
2923 if(pointed_object == NULL)
2925 verbosestream<<"TOSERVER_INTERACT: "
2926 "pointed object is NULL"<<std::endl;
2932 v3f pointed_pos_under = player_pos;
2933 v3f pointed_pos_above = player_pos;
2934 if(pointed.type == POINTEDTHING_NODE)
2936 pointed_pos_under = intToFloat(p_under, BS);
2937 pointed_pos_above = intToFloat(p_above, BS);
2939 else if(pointed.type == POINTEDTHING_OBJECT)
2941 pointed_pos_under = pointed_object->getBasePosition();
2942 pointed_pos_above = pointed_pos_under;
2946 Check that target is reasonably close
2947 (only when digging or placing things)
2949 if(action == 0 || action == 2 || action == 3)
2951 float d = player_pos.getDistanceFrom(pointed_pos_under);
2952 float max_d = BS * 14; // Just some large enough value
2954 actionstream<<"Player "<<player->getName()
2955 <<" tried to access "<<pointed.dump()
2957 <<"d="<<d<<", max_d="<<max_d
2958 <<". ignoring."<<std::endl;
2959 // Re-send block to revert change on client-side
2960 RemoteClient *client = getClient(peer_id);
2961 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2962 client->SetBlockNotSent(blockpos);
2969 Make sure the player is allowed to do it
2971 if(!checkPriv(player->getName(), "interact"))
2973 actionstream<<player->getName()<<" attempted to interact with "
2974 <<pointed.dump()<<" without 'interact' privilege"
2976 // Re-send block to revert change on client-side
2977 RemoteClient *client = getClient(peer_id);
2978 // Digging completed -> under
2980 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2981 client->SetBlockNotSent(blockpos);
2983 // Placement -> above
2985 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
2986 client->SetBlockNotSent(blockpos);
2992 0: start digging or punch object
2996 if(pointed.type == POINTEDTHING_NODE)
2999 NOTE: This can be used in the future to check if
3000 somebody is cheating, by checking the timing.
3002 MapNode n(CONTENT_IGNORE);
3005 n = m_env->getMap().getNode(p_under);
3007 catch(InvalidPositionException &e)
3009 infostream<<"Server: Not punching: Node not found."
3010 <<" Adding block to emerge queue."
3012 m_emerge_queue.addBlock(peer_id,
3013 getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
3015 if(n.getContent() != CONTENT_IGNORE)
3016 scriptapi_node_on_punch(m_lua, p_under, n, playersao);
3018 else if(pointed.type == POINTEDTHING_OBJECT)
3020 // Skip if object has been removed
3021 if(pointed_object->m_removed)
3024 actionstream<<player->getName()<<" punches object "
3025 <<pointed.object_id<<": "
3026 <<pointed_object->getDescription()<<std::endl;
3028 ItemStack punchitem = playersao->getWieldedItem();
3029 ToolCapabilities toolcap =
3030 punchitem.getToolCapabilities(m_itemdef);
3031 v3f dir = (pointed_object->getBasePosition() -
3032 (player->getPosition() + player->getEyeOffset())
3034 float time_from_last_punch =
3035 playersao->resetTimeFromLastPunch();
3036 pointed_object->punch(dir, &toolcap, playersao,
3037 time_from_last_punch);
3045 else if(action == 1)
3050 2: Digging completed
3052 else if(action == 2)
3054 // Only complete digging of nodes
3055 if(pointed.type == POINTEDTHING_NODE)
3057 MapNode n(CONTENT_IGNORE);
3060 n = m_env->getMap().getNode(p_under);
3062 catch(InvalidPositionException &e)
3064 infostream<<"Server: Not finishing digging: Node not found."
3065 <<" Adding block to emerge queue."
3067 m_emerge_queue.addBlock(peer_id,
3068 getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
3070 if(n.getContent() != CONTENT_IGNORE)
3071 scriptapi_node_on_dig(m_lua, p_under, n, playersao);
3073 if (m_env->getMap().getNodeNoEx(p_under).getContent() != CONTENT_AIR)
3075 // Re-send block to revert change on client-side
3076 RemoteClient *client = getClient(peer_id);
3077 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
3078 client->SetBlockNotSent(blockpos);
3084 3: place block or right-click object
3086 else if(action == 3)
3088 ItemStack item = playersao->getWieldedItem();
3090 // Reset build time counter
3091 if(pointed.type == POINTEDTHING_NODE &&
3092 item.getDefinition(m_itemdef).type == ITEM_NODE)
3093 getClient(peer_id)->m_time_from_building = 0.0;
3095 if(pointed.type == POINTEDTHING_OBJECT)
3097 // Right click object
3099 // Skip if object has been removed
3100 if(pointed_object->m_removed)
3103 actionstream<<player->getName()<<" right-clicks object "
3104 <<pointed.object_id<<": "
3105 <<pointed_object->getDescription()<<std::endl;
3108 pointed_object->rightClick(playersao);
3110 else if(scriptapi_item_on_place(m_lua,
3111 item, playersao, pointed))
3113 // Placement was handled in lua
3115 // Apply returned ItemStack
3116 if(g_settings->getBool("creative_mode") == false)
3117 playersao->setWieldedItem(item);
3120 // If item has node placement prediction, always send the above
3121 // node to make sure the client knows what exactly happened
3122 if(item.getDefinition(m_itemdef).node_placement_prediction != ""){
3123 RemoteClient *client = getClient(peer_id);
3124 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
3125 client->SetBlockNotSent(blockpos);
3132 else if(action == 4)
3134 ItemStack item = playersao->getWieldedItem();
3136 actionstream<<player->getName()<<" uses "<<item.name
3137 <<", pointing at "<<pointed.dump()<<std::endl;
3139 if(scriptapi_item_on_use(m_lua,
3140 item, playersao, pointed))
3142 // Apply returned ItemStack
3143 if(g_settings->getBool("creative_mode") == false)
3144 playersao->setWieldedItem(item);
3150 Catch invalid actions
3154 infostream<<"WARNING: Server: Invalid action "
3155 <<action<<std::endl;
3158 else if(command == TOSERVER_REMOVED_SOUNDS)
3160 std::string datastring((char*)&data[2], datasize-2);
3161 std::istringstream is(datastring, std::ios_base::binary);
3163 int num = readU16(is);
3164 for(int k=0; k<num; k++){
3165 s32 id = readS32(is);
3166 std::map<s32, ServerPlayingSound>::iterator i =
3167 m_playing_sounds.find(id);
3168 if(i == m_playing_sounds.end())
3170 ServerPlayingSound &psound = i->second;
3171 psound.clients.erase(peer_id);
3172 if(psound.clients.size() == 0)
3173 m_playing_sounds.erase(i++);
3176 else if(command == TOSERVER_NODEMETA_FIELDS)
3178 std::string datastring((char*)&data[2], datasize-2);
3179 std::istringstream is(datastring, std::ios_base::binary);
3181 v3s16 p = readV3S16(is);
3182 std::string formname = deSerializeString(is);
3183 int num = readU16(is);
3184 std::map<std::string, std::string> fields;
3185 for(int k=0; k<num; k++){
3186 std::string fieldname = deSerializeString(is);
3187 std::string fieldvalue = deSerializeLongString(is);
3188 fields[fieldname] = fieldvalue;
3191 scriptapi_node_on_receive_fields(m_lua, p, formname, fields,
3196 infostream<<"Server::ProcessData(): Ignoring "
3197 "unknown command "<<command<<std::endl;
3201 catch(SendFailedException &e)
3203 errorstream<<"Server::ProcessData(): SendFailedException: "
3209 void Server::onMapEditEvent(MapEditEvent *event)
3211 //infostream<<"Server::onMapEditEvent()"<<std::endl;
3212 if(m_ignore_map_edit_events)
3214 if(m_ignore_map_edit_events_area.contains(event->getArea()))
3216 MapEditEvent *e = event->clone();
3217 m_unsent_map_edit_queue.push_back(e);
3220 Inventory* Server::getInventory(const InventoryLocation &loc)
3223 case InventoryLocation::UNDEFINED:
3226 case InventoryLocation::CURRENT_PLAYER:
3229 case InventoryLocation::PLAYER:
3231 Player *player = m_env->getPlayer(loc.name.c_str());
3234 PlayerSAO *playersao = player->getPlayerSAO();
3237 return playersao->getInventory();
3240 case InventoryLocation::NODEMETA:
3242 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
3245 return meta->getInventory();
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 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3277 block->raiseModified(MOD_STATE_WRITE_NEEDED);
3279 setBlockNotSent(blockpos);
3287 core::list<PlayerInfo> Server::getPlayerInfo()
3289 DSTACK(__FUNCTION_NAME);
3290 JMutexAutoLock envlock(m_env_mutex);
3291 JMutexAutoLock conlock(m_con_mutex);
3293 core::list<PlayerInfo> list;
3295 core::list<Player*> players = m_env->getPlayers();
3297 core::list<Player*>::Iterator i;
3298 for(i = players.begin();
3299 i != players.end(); i++)
3303 Player *player = *i;
3306 // Copy info from connection to info struct
3307 info.id = player->peer_id;
3308 info.address = m_con.GetPeerAddress(player->peer_id);
3309 info.avg_rtt = m_con.GetPeerAvgRTT(player->peer_id);
3311 catch(con::PeerNotFoundException &e)
3313 // Set dummy peer info
3315 info.address = Address(0,0,0,0,0);
3319 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3320 info.position = player->getPosition();
3322 list.push_back(info);
3329 void Server::peerAdded(con::Peer *peer)
3331 DSTACK(__FUNCTION_NAME);
3332 verbosestream<<"Server::peerAdded(): peer->id="
3333 <<peer->id<<std::endl;
3336 c.type = PEER_ADDED;
3337 c.peer_id = peer->id;
3339 m_peer_change_queue.push_back(c);
3342 void Server::deletingPeer(con::Peer *peer, bool timeout)
3344 DSTACK(__FUNCTION_NAME);
3345 verbosestream<<"Server::deletingPeer(): peer->id="
3346 <<peer->id<<", timeout="<<timeout<<std::endl;
3349 c.type = PEER_REMOVED;
3350 c.peer_id = peer->id;
3351 c.timeout = timeout;
3352 m_peer_change_queue.push_back(c);
3359 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3361 DSTACK(__FUNCTION_NAME);
3362 std::ostringstream os(std::ios_base::binary);
3364 writeU16(os, TOCLIENT_HP);
3368 std::string s = os.str();
3369 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3371 con.Send(peer_id, 0, data, true);
3374 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3375 const std::wstring &reason)
3377 DSTACK(__FUNCTION_NAME);
3378 std::ostringstream os(std::ios_base::binary);
3380 writeU16(os, TOCLIENT_ACCESS_DENIED);
3381 os<<serializeWideString(reason);
3384 std::string s = os.str();
3385 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3387 con.Send(peer_id, 0, data, true);
3390 void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
3391 bool set_camera_point_target, v3f camera_point_target)
3393 DSTACK(__FUNCTION_NAME);
3394 std::ostringstream os(std::ios_base::binary);
3396 writeU16(os, TOCLIENT_DEATHSCREEN);
3397 writeU8(os, set_camera_point_target);
3398 writeV3F1000(os, camera_point_target);
3401 std::string s = os.str();
3402 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3404 con.Send(peer_id, 0, data, true);
3407 void Server::SendItemDef(con::Connection &con, u16 peer_id,
3408 IItemDefManager *itemdef)
3410 DSTACK(__FUNCTION_NAME);
3411 std::ostringstream os(std::ios_base::binary);
3415 u32 length of the next item
3416 zlib-compressed serialized ItemDefManager
3418 writeU16(os, TOCLIENT_ITEMDEF);
3419 std::ostringstream tmp_os(std::ios::binary);
3420 itemdef->serialize(tmp_os);
3421 std::ostringstream tmp_os2(std::ios::binary);
3422 compressZlib(tmp_os.str(), tmp_os2);
3423 os<<serializeLongString(tmp_os2.str());
3426 std::string s = os.str();
3427 verbosestream<<"Server: Sending item definitions to id("<<peer_id
3428 <<"): size="<<s.size()<<std::endl;
3429 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3431 con.Send(peer_id, 0, data, true);
3434 void Server::SendNodeDef(con::Connection &con, u16 peer_id,
3435 INodeDefManager *nodedef)
3437 DSTACK(__FUNCTION_NAME);
3438 std::ostringstream os(std::ios_base::binary);
3442 u32 length of the next item
3443 zlib-compressed serialized NodeDefManager
3445 writeU16(os, TOCLIENT_NODEDEF);
3446 std::ostringstream tmp_os(std::ios::binary);
3447 nodedef->serialize(tmp_os);
3448 std::ostringstream tmp_os2(std::ios::binary);
3449 compressZlib(tmp_os.str(), tmp_os2);
3450 os<<serializeLongString(tmp_os2.str());
3453 std::string s = os.str();
3454 verbosestream<<"Server: Sending node definitions to id("<<peer_id
3455 <<"): size="<<s.size()<<std::endl;
3456 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3458 con.Send(peer_id, 0, data, true);
3462 Non-static send methods
3465 void Server::SendInventory(u16 peer_id)
3467 DSTACK(__FUNCTION_NAME);
3469 PlayerSAO *playersao = getPlayerSAO(peer_id);
3472 playersao->m_inventory_not_sent = false;
3478 std::ostringstream os;
3479 playersao->getInventory()->serialize(os);
3481 std::string s = os.str();
3483 SharedBuffer<u8> data(s.size()+2);
3484 writeU16(&data[0], TOCLIENT_INVENTORY);
3485 memcpy(&data[2], s.c_str(), s.size());
3488 m_con.Send(peer_id, 0, data, true);
3491 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3493 DSTACK(__FUNCTION_NAME);
3495 std::ostringstream os(std::ios_base::binary);
3499 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3500 os.write((char*)buf, 2);
3503 writeU16(buf, message.size());
3504 os.write((char*)buf, 2);
3507 for(u32 i=0; i<message.size(); i++)
3511 os.write((char*)buf, 2);
3515 std::string s = os.str();
3516 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3518 m_con.Send(peer_id, 0, data, true);
3521 void Server::BroadcastChatMessage(const std::wstring &message)
3523 for(core::map<u16, RemoteClient*>::Iterator
3524 i = m_clients.getIterator();
3525 i.atEnd() == false; i++)
3527 // Get client and check that it is valid
3528 RemoteClient *client = i.getNode()->getValue();
3529 assert(client->peer_id == i.getNode()->getKey());
3530 if(client->serialization_version == SER_FMT_VER_INVALID)
3533 SendChatMessage(client->peer_id, message);
3537 void Server::SendPlayerHP(u16 peer_id)
3539 DSTACK(__FUNCTION_NAME);
3540 PlayerSAO *playersao = getPlayerSAO(peer_id);
3542 playersao->m_hp_not_sent = false;
3543 SendHP(m_con, peer_id, playersao->getHP());
3546 void Server::SendMovePlayer(u16 peer_id)
3548 DSTACK(__FUNCTION_NAME);
3549 Player *player = m_env->getPlayer(peer_id);
3552 std::ostringstream os(std::ios_base::binary);
3553 writeU16(os, TOCLIENT_MOVE_PLAYER);
3554 writeV3F1000(os, player->getPosition());
3555 writeF1000(os, player->getPitch());
3556 writeF1000(os, player->getYaw());
3559 v3f pos = player->getPosition();
3560 f32 pitch = player->getPitch();
3561 f32 yaw = player->getYaw();
3562 verbosestream<<"Server: Sending TOCLIENT_MOVE_PLAYER"
3563 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3570 std::string s = os.str();
3571 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3573 m_con.Send(peer_id, 0, data, true);
3576 void Server::SendPlayerPrivileges(u16 peer_id)
3578 Player *player = m_env->getPlayer(peer_id);
3580 if(player->peer_id == PEER_ID_INEXISTENT)
3583 std::set<std::string> privs;
3584 scriptapi_get_auth(m_lua, player->getName(), NULL, &privs);
3586 std::ostringstream os(std::ios_base::binary);
3587 writeU16(os, TOCLIENT_PRIVILEGES);
3588 writeU16(os, privs.size());
3589 for(std::set<std::string>::const_iterator i = privs.begin();
3590 i != privs.end(); i++){
3591 os<<serializeString(*i);
3595 std::string s = os.str();
3596 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3598 m_con.Send(peer_id, 0, data, true);
3601 void Server::SendPlayerInventoryFormspec(u16 peer_id)
3603 Player *player = m_env->getPlayer(peer_id);
3605 if(player->peer_id == PEER_ID_INEXISTENT)
3608 std::ostringstream os(std::ios_base::binary);
3609 writeU16(os, TOCLIENT_INVENTORY_FORMSPEC);
3610 os<<serializeLongString(player->inventory_formspec);
3613 std::string s = os.str();
3614 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3616 m_con.Send(peer_id, 0, data, true);
3619 s32 Server::playSound(const SimpleSoundSpec &spec,
3620 const ServerSoundParams ¶ms)
3622 // Find out initial position of sound
3623 bool pos_exists = false;
3624 v3f pos = params.getPos(m_env, &pos_exists);
3625 // If position is not found while it should be, cancel sound
3626 if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
3628 // Filter destination clients
3629 std::set<RemoteClient*> dst_clients;
3630 if(params.to_player != "")
3632 Player *player = m_env->getPlayer(params.to_player.c_str());
3634 infostream<<"Server::playSound: Player \""<<params.to_player
3635 <<"\" not found"<<std::endl;
3638 if(player->peer_id == PEER_ID_INEXISTENT){
3639 infostream<<"Server::playSound: Player \""<<params.to_player
3640 <<"\" not connected"<<std::endl;
3643 RemoteClient *client = getClient(player->peer_id);
3644 dst_clients.insert(client);
3648 for(core::map<u16, RemoteClient*>::Iterator
3649 i = m_clients.getIterator(); i.atEnd() == false; i++)
3651 RemoteClient *client = i.getNode()->getValue();
3652 Player *player = m_env->getPlayer(client->peer_id);
3656 if(player->getPosition().getDistanceFrom(pos) >
3657 params.max_hear_distance)
3660 dst_clients.insert(client);
3663 if(dst_clients.size() == 0)
3666 s32 id = m_next_sound_id++;
3667 // The sound will exist as a reference in m_playing_sounds
3668 m_playing_sounds[id] = ServerPlayingSound();
3669 ServerPlayingSound &psound = m_playing_sounds[id];
3670 psound.params = params;
3671 for(std::set<RemoteClient*>::iterator i = dst_clients.begin();
3672 i != dst_clients.end(); i++)
3673 psound.clients.insert((*i)->peer_id);
3675 std::ostringstream os(std::ios_base::binary);
3676 writeU16(os, TOCLIENT_PLAY_SOUND);
3678 os<<serializeString(spec.name);
3679 writeF1000(os, spec.gain * params.gain);
3680 writeU8(os, params.type);
3681 writeV3F1000(os, pos);
3682 writeU16(os, params.object);
3683 writeU8(os, params.loop);
3685 std::string s = os.str();
3686 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3688 for(std::set<RemoteClient*>::iterator i = dst_clients.begin();
3689 i != dst_clients.end(); i++){
3691 m_con.Send((*i)->peer_id, 0, data, true);
3695 void Server::stopSound(s32 handle)
3697 // Get sound reference
3698 std::map<s32, ServerPlayingSound>::iterator i =
3699 m_playing_sounds.find(handle);
3700 if(i == m_playing_sounds.end())
3702 ServerPlayingSound &psound = i->second;
3704 std::ostringstream os(std::ios_base::binary);
3705 writeU16(os, TOCLIENT_STOP_SOUND);
3706 writeS32(os, handle);
3708 std::string s = os.str();
3709 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3711 for(std::set<u16>::iterator i = psound.clients.begin();
3712 i != psound.clients.end(); i++){
3714 m_con.Send(*i, 0, data, true);
3716 // Remove sound reference
3717 m_playing_sounds.erase(i);
3720 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3721 core::list<u16> *far_players, float far_d_nodes)
3723 float maxd = far_d_nodes*BS;
3724 v3f p_f = intToFloat(p, BS);
3728 SharedBuffer<u8> reply(replysize);
3729 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3730 writeS16(&reply[2], p.X);
3731 writeS16(&reply[4], p.Y);
3732 writeS16(&reply[6], p.Z);
3734 for(core::map<u16, RemoteClient*>::Iterator
3735 i = m_clients.getIterator();
3736 i.atEnd() == false; i++)
3738 // Get client and check that it is valid
3739 RemoteClient *client = i.getNode()->getValue();
3740 assert(client->peer_id == i.getNode()->getKey());
3741 if(client->serialization_version == SER_FMT_VER_INVALID)
3744 // Don't send if it's the same one
3745 if(client->peer_id == ignore_id)
3751 Player *player = m_env->getPlayer(client->peer_id);
3754 // If player is far away, only set modified blocks not sent
3755 v3f player_pos = player->getPosition();
3756 if(player_pos.getDistanceFrom(p_f) > maxd)
3758 far_players->push_back(client->peer_id);
3765 m_con.Send(client->peer_id, 0, reply, true);
3769 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3770 core::list<u16> *far_players, float far_d_nodes)
3772 float maxd = far_d_nodes*BS;
3773 v3f p_f = intToFloat(p, BS);
3775 for(core::map<u16, RemoteClient*>::Iterator
3776 i = m_clients.getIterator();
3777 i.atEnd() == false; i++)
3779 // Get client and check that it is valid
3780 RemoteClient *client = i.getNode()->getValue();
3781 assert(client->peer_id == i.getNode()->getKey());
3782 if(client->serialization_version == SER_FMT_VER_INVALID)
3785 // Don't send if it's the same one
3786 if(client->peer_id == ignore_id)
3792 Player *player = m_env->getPlayer(client->peer_id);
3795 // If player is far away, only set modified blocks not sent
3796 v3f player_pos = player->getPosition();
3797 if(player_pos.getDistanceFrom(p_f) > maxd)
3799 far_players->push_back(client->peer_id);
3806 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3807 SharedBuffer<u8> reply(replysize);
3808 writeU16(&reply[0], TOCLIENT_ADDNODE);
3809 writeS16(&reply[2], p.X);
3810 writeS16(&reply[4], p.Y);
3811 writeS16(&reply[6], p.Z);
3812 n.serialize(&reply[8], client->serialization_version);
3815 m_con.Send(client->peer_id, 0, reply, true);
3819 void Server::setBlockNotSent(v3s16 p)
3821 for(core::map<u16, RemoteClient*>::Iterator
3822 i = m_clients.getIterator();
3823 i.atEnd()==false; i++)
3825 RemoteClient *client = i.getNode()->getValue();
3826 client->SetBlockNotSent(p);
3830 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3832 DSTACK(__FUNCTION_NAME);
3834 v3s16 p = block->getPos();
3838 bool completely_air = true;
3839 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3840 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3841 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3843 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
3845 completely_air = false;
3846 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
3851 infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
3853 infostream<<"[completely air] ";
3854 infostream<<std::endl;
3858 Create a packet with the block in the right format
3861 std::ostringstream os(std::ios_base::binary);
3862 block->serialize(os, ver, false);
3863 std::string s = os.str();
3864 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3866 u32 replysize = 8 + blockdata.getSize();
3867 SharedBuffer<u8> reply(replysize);
3868 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3869 writeS16(&reply[2], p.X);
3870 writeS16(&reply[4], p.Y);
3871 writeS16(&reply[6], p.Z);
3872 memcpy(&reply[8], *blockdata, blockdata.getSize());
3874 /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3875 <<": \tpacket size: "<<replysize<<std::endl;*/
3880 m_con.Send(peer_id, 1, reply, true);
3883 void Server::SendBlocks(float dtime)
3885 DSTACK(__FUNCTION_NAME);
3887 JMutexAutoLock envlock(m_env_mutex);
3888 JMutexAutoLock conlock(m_con_mutex);
3890 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
3892 core::array<PrioritySortedBlockTransfer> queue;
3894 s32 total_sending = 0;
3897 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
3899 for(core::map<u16, RemoteClient*>::Iterator
3900 i = m_clients.getIterator();
3901 i.atEnd() == false; i++)
3903 RemoteClient *client = i.getNode()->getValue();
3904 assert(client->peer_id == i.getNode()->getKey());
3906 // If definitions and textures have not been sent, don't
3907 // send MapBlocks either
3908 if(!client->definitions_sent)
3911 total_sending += client->SendingCount();
3913 if(client->serialization_version == SER_FMT_VER_INVALID)
3916 client->GetNextBlocks(this, dtime, queue);
3921 // Lowest priority number comes first.
3922 // Lowest is most important.
3925 for(u32 i=0; i<queue.size(); i++)
3927 //TODO: Calculate limit dynamically
3928 if(total_sending >= g_settings->getS32
3929 ("max_simultaneous_block_sends_server_total"))
3932 PrioritySortedBlockTransfer q = queue[i];
3934 MapBlock *block = NULL;
3937 block = m_env->getMap().getBlockNoCreate(q.pos);
3939 catch(InvalidPositionException &e)
3944 RemoteClient *client = getClient(q.peer_id);
3946 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3948 client->SentBlock(q.pos);
3954 void Server::fillMediaCache()
3956 DSTACK(__FUNCTION_NAME);
3958 infostream<<"Server: Calculating media file checksums"<<std::endl;
3960 // Collect all media file paths
3961 std::list<std::string> paths;
3962 for(core::list<ModSpec>::Iterator i = m_mods.begin();
3963 i != m_mods.end(); i++){
3964 const ModSpec &mod = *i;
3965 paths.push_back(mod.path + DIR_DELIM + "textures");
3966 paths.push_back(mod.path + DIR_DELIM + "sounds");
3967 paths.push_back(mod.path + DIR_DELIM + "media");
3969 std::string path_all = "textures";
3970 paths.push_back(path_all + DIR_DELIM + "all");
3972 // Collect media file information from paths into cache
3973 for(std::list<std::string>::iterator i = paths.begin();
3974 i != paths.end(); i++)
3976 std::string mediapath = *i;
3977 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
3978 for(u32 j=0; j<dirlist.size(); j++){
3979 if(dirlist[j].dir) // Ignode dirs
3981 std::string filename = dirlist[j].name;
3982 // If name contains illegal characters, ignore the file
3983 if(!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)){
3984 infostream<<"Server: ignoring illegal file name: \""
3985 <<filename<<"\""<<std::endl;
3988 // If name is not in a supported format, ignore it
3989 const char *supported_ext[] = {
3990 ".png", ".jpg", ".bmp", ".tga",
3991 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
3995 if(removeStringEnd(filename, supported_ext) == ""){
3996 infostream<<"Server: ignoring unsupported file extension: \""
3997 <<filename<<"\""<<std::endl;
4000 // Ok, attempt to load the file and add to cache
4001 std::string filepath = mediapath + DIR_DELIM + filename;
4003 std::ifstream fis(filepath.c_str(), std::ios_base::binary);
4004 if(fis.good() == false){
4005 errorstream<<"Server::fillMediaCache(): Could not open \""
4006 <<filename<<"\" for reading"<<std::endl;
4009 std::ostringstream tmp_os(std::ios_base::binary);
4013 fis.read(buf, 1024);
4014 std::streamsize len = fis.gcount();
4015 tmp_os.write(buf, len);
4024 errorstream<<"Server::fillMediaCache(): Failed to read \""
4025 <<filename<<"\""<<std::endl;
4028 if(tmp_os.str().length() == 0){
4029 errorstream<<"Server::fillMediaCache(): Empty file \""
4030 <<filepath<<"\""<<std::endl;
4035 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
4037 unsigned char *digest = sha1.getDigest();
4038 std::string sha1_base64 = base64_encode(digest, 20);
4039 std::string sha1_hex = hex_encode((char*)digest, 20);
4043 this->m_media[filename] = MediaInfo(filepath, sha1_base64);
4044 verbosestream<<"Server: "<<sha1_hex<<" is "<<filename<<std::endl;
4049 struct SendableMediaAnnouncement
4052 std::string sha1_digest;
4054 SendableMediaAnnouncement(const std::string name_="",
4055 const std::string sha1_digest_=""):
4057 sha1_digest(sha1_digest_)
4061 void Server::sendMediaAnnouncement(u16 peer_id)
4063 DSTACK(__FUNCTION_NAME);
4065 verbosestream<<"Server: Announcing files to id("<<peer_id<<")"
4068 core::list<SendableMediaAnnouncement> file_announcements;
4070 for(std::map<std::string, MediaInfo>::iterator i = m_media.begin();
4071 i != m_media.end(); i++){
4073 file_announcements.push_back(
4074 SendableMediaAnnouncement(i->first, i->second.sha1_digest));
4078 std::ostringstream os(std::ios_base::binary);
4086 u16 length of sha1_digest
4091 writeU16(os, TOCLIENT_ANNOUNCE_MEDIA);
4092 writeU16(os, file_announcements.size());
4094 for(core::list<SendableMediaAnnouncement>::Iterator
4095 j = file_announcements.begin();
4096 j != file_announcements.end(); j++){
4097 os<<serializeString(j->name);
4098 os<<serializeString(j->sha1_digest);
4102 std::string s = os.str();
4103 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4106 m_con.Send(peer_id, 0, data, true);
4110 struct SendableMedia
4116 SendableMedia(const std::string &name_="", const std::string path_="",
4117 const std::string &data_=""):
4124 void Server::sendRequestedMedia(u16 peer_id,
4125 const core::list<MediaRequest> &tosend)
4127 DSTACK(__FUNCTION_NAME);
4129 verbosestream<<"Server::sendRequestedMedia(): "
4130 <<"Sending files to client"<<std::endl;
4134 // Put 5kB in one bunch (this is not accurate)
4135 u32 bytes_per_bunch = 5000;
4137 core::array< core::list<SendableMedia> > file_bunches;
4138 file_bunches.push_back(core::list<SendableMedia>());
4140 u32 file_size_bunch_total = 0;
4142 for(core::list<MediaRequest>::ConstIterator i = tosend.begin();
4143 i != tosend.end(); i++)
4145 if(m_media.find(i->name) == m_media.end()){
4146 errorstream<<"Server::sendRequestedMedia(): Client asked for "
4147 <<"unknown file \""<<(i->name)<<"\""<<std::endl;
4151 //TODO get path + name
4152 std::string tpath = m_media[(*i).name].path;
4155 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
4156 if(fis.good() == false){
4157 errorstream<<"Server::sendRequestedMedia(): Could not open \""
4158 <<tpath<<"\" for reading"<<std::endl;
4161 std::ostringstream tmp_os(std::ios_base::binary);
4165 fis.read(buf, 1024);
4166 std::streamsize len = fis.gcount();
4167 tmp_os.write(buf, len);
4168 file_size_bunch_total += len;
4177 errorstream<<"Server::sendRequestedMedia(): Failed to read \""
4178 <<(*i).name<<"\""<<std::endl;
4181 /*infostream<<"Server::sendRequestedMedia(): Loaded \""
4182 <<tname<<"\""<<std::endl;*/
4184 file_bunches[file_bunches.size()-1].push_back(
4185 SendableMedia((*i).name, tpath, tmp_os.str()));
4187 // Start next bunch if got enough data
4188 if(file_size_bunch_total >= bytes_per_bunch){
4189 file_bunches.push_back(core::list<SendableMedia>());
4190 file_size_bunch_total = 0;
4195 /* Create and send packets */
4197 u32 num_bunches = file_bunches.size();
4198 for(u32 i=0; i<num_bunches; i++)
4200 std::ostringstream os(std::ios_base::binary);
4204 u16 total number of texture bunches
4205 u16 index of this bunch
4206 u32 number of files in this bunch
4215 writeU16(os, TOCLIENT_MEDIA);
4216 writeU16(os, num_bunches);
4218 writeU32(os, file_bunches[i].size());
4220 for(core::list<SendableMedia>::Iterator
4221 j = file_bunches[i].begin();
4222 j != file_bunches[i].end(); j++){
4223 os<<serializeString(j->name);
4224 os<<serializeLongString(j->data);
4228 std::string s = os.str();
4229 verbosestream<<"Server::sendRequestedMedia(): bunch "
4230 <<i<<"/"<<num_bunches
4231 <<" files="<<file_bunches[i].size()
4232 <<" size=" <<s.size()<<std::endl;
4233 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4235 m_con.Send(peer_id, 0, data, true);
4243 void Server::DiePlayer(u16 peer_id)
4245 DSTACK(__FUNCTION_NAME);
4247 PlayerSAO *playersao = getPlayerSAO(peer_id);
4250 infostream<<"Server::DiePlayer(): Player "
4251 <<playersao->getPlayer()->getName()
4252 <<" dies"<<std::endl;
4254 playersao->setHP(0);
4256 // Trigger scripted stuff
4257 scriptapi_on_dieplayer(m_lua, playersao);
4259 SendPlayerHP(peer_id);
4260 SendDeathscreen(m_con, peer_id, false, v3f(0,0,0));
4263 void Server::RespawnPlayer(u16 peer_id)
4265 DSTACK(__FUNCTION_NAME);
4267 PlayerSAO *playersao = getPlayerSAO(peer_id);
4270 infostream<<"Server::RespawnPlayer(): Player "
4271 <<playersao->getPlayer()->getName()
4272 <<" respawns"<<std::endl;
4274 playersao->setHP(PLAYER_MAX_HP);
4276 bool repositioned = scriptapi_on_respawnplayer(m_lua, playersao);
4278 v3f pos = findSpawnPos(m_env->getServerMap());
4279 playersao->setPos(pos);
4283 void Server::UpdateCrafting(u16 peer_id)
4285 DSTACK(__FUNCTION_NAME);
4287 Player* player = m_env->getPlayer(peer_id);
4290 // Get a preview for crafting
4292 // No crafting in creative mode
4293 if(g_settings->getBool("creative_mode") == false)
4294 getCraftingResult(&player->inventory, preview, false, this);
4296 // Put the new preview in
4297 InventoryList *plist = player->inventory.getList("craftpreview");
4299 assert(plist->getSize() >= 1);
4300 plist->changeItem(0, preview);
4303 RemoteClient* Server::getClient(u16 peer_id)
4305 DSTACK(__FUNCTION_NAME);
4306 //JMutexAutoLock lock(m_con_mutex);
4307 core::map<u16, RemoteClient*>::Node *n;
4308 n = m_clients.find(peer_id);
4309 // A client should exist for all peers
4311 return n->getValue();
4314 std::wstring Server::getStatusString()
4316 std::wostringstream os(std::ios_base::binary);
4319 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4321 os<<L", uptime="<<m_uptime.get();
4322 // Information about clients
4324 for(core::map<u16, RemoteClient*>::Iterator
4325 i = m_clients.getIterator();
4326 i.atEnd() == false; i++)
4328 // Get client and check that it is valid
4329 RemoteClient *client = i.getNode()->getValue();
4330 assert(client->peer_id == i.getNode()->getKey());
4331 if(client->serialization_version == SER_FMT_VER_INVALID)
4334 Player *player = m_env->getPlayer(client->peer_id);
4335 // Get name of player
4336 std::wstring name = L"unknown";
4338 name = narrow_to_wide(player->getName());
4339 // Add name to information string
4343 if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
4344 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4345 if(g_settings->get("motd") != "")
4346 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4350 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
4352 std::set<std::string> privs;
4353 scriptapi_get_auth(m_lua, name, NULL, &privs);
4357 bool Server::checkPriv(const std::string &name, const std::string &priv)
4359 std::set<std::string> privs = getPlayerEffectivePrivs(name);
4360 return (privs.count(priv) != 0);
4363 void Server::reportPrivsModified(const std::string &name)
4366 for(core::map<u16, RemoteClient*>::Iterator
4367 i = m_clients.getIterator();
4368 i.atEnd() == false; i++){
4369 RemoteClient *client = i.getNode()->getValue();
4370 Player *player = m_env->getPlayer(client->peer_id);
4371 reportPrivsModified(player->getName());
4374 Player *player = m_env->getPlayer(name.c_str());
4377 SendPlayerPrivileges(player->peer_id);
4378 PlayerSAO *sao = player->getPlayerSAO();
4381 sao->updatePrivileges(
4382 getPlayerEffectivePrivs(name),
4387 void Server::reportInventoryFormspecModified(const std::string &name)
4389 Player *player = m_env->getPlayer(name.c_str());
4392 SendPlayerInventoryFormspec(player->peer_id);
4395 // Saves g_settings to configpath given at initialization
4396 void Server::saveConfig()
4398 if(m_path_config != "")
4399 g_settings->updateConfigFile(m_path_config.c_str());
4402 void Server::notifyPlayer(const char *name, const std::wstring msg)
4404 Player *player = m_env->getPlayer(name);
4407 SendChatMessage(player->peer_id, std::wstring(L"Server: -!- ")+msg);
4410 void Server::notifyPlayers(const std::wstring msg)
4412 BroadcastChatMessage(msg);
4415 void Server::queueBlockEmerge(v3s16 blockpos, bool allow_generate)
4419 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
4420 m_emerge_queue.addBlock(PEER_ID_INEXISTENT, blockpos, flags);
4423 // IGameDef interface
4425 IItemDefManager* Server::getItemDefManager()
4429 INodeDefManager* Server::getNodeDefManager()
4433 ICraftDefManager* Server::getCraftDefManager()
4437 ITextureSource* Server::getTextureSource()
4441 u16 Server::allocateUnknownNodeId(const std::string &name)
4443 return m_nodedef->allocateDummy(name);
4445 ISoundManager* Server::getSoundManager()
4447 return &dummySoundManager;
4449 MtEventManager* Server::getEventManager()
4454 IWritableItemDefManager* Server::getWritableItemDefManager()
4458 IWritableNodeDefManager* Server::getWritableNodeDefManager()
4462 IWritableCraftDefManager* Server::getWritableCraftDefManager()
4467 const ModSpec* Server::getModSpec(const std::string &modname)
4469 for(core::list<ModSpec>::Iterator i = m_mods.begin();
4470 i != m_mods.end(); i++){
4471 const ModSpec &mod = *i;
4472 if(mod.name == modname)
4477 std::string Server::getBuiltinLuaPath()
4479 return porting::path_share + DIR_DELIM + "builtin";
4482 v3f findSpawnPos(ServerMap &map)
4484 //return v3f(50,50,50)*BS;
4489 nodepos = v2s16(0,0);
4494 // Try to find a good place a few times
4495 for(s32 i=0; i<1000; i++)
4498 // We're going to try to throw the player to this position
4499 v2s16 nodepos2d = v2s16(-range + (myrand()%(range*2)),
4500 -range + (myrand()%(range*2)));
4501 //v2s16 sectorpos = getNodeSectorPos(nodepos2d);
4502 // Get ground height at point (fallbacks to heightmap function)
4503 s16 groundheight = map.findGroundLevel(nodepos2d);
4504 // Don't go underwater
4505 if(groundheight < WATER_LEVEL)
4507 //infostream<<"-> Underwater"<<std::endl;
4510 // Don't go to high places
4511 if(groundheight > WATER_LEVEL + 4)
4513 //infostream<<"-> Underwater"<<std::endl;
4517 nodepos = v3s16(nodepos2d.X, groundheight-2, nodepos2d.Y);
4518 bool is_good = false;
4520 for(s32 i=0; i<10; i++){
4521 v3s16 blockpos = getNodeBlockPos(nodepos);
4522 map.emergeBlock(blockpos, true);
4523 MapNode n = map.getNodeNoEx(nodepos);
4524 if(n.getContent() == CONTENT_AIR){
4535 // Found a good place
4536 //infostream<<"Searched through "<<i<<" places."<<std::endl;
4542 return intToFloat(nodepos, BS);
4545 PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id)
4547 RemotePlayer *player = NULL;
4548 bool newplayer = false;
4551 Try to get an existing player
4553 player = static_cast<RemotePlayer*>(m_env->getPlayer(name));
4555 // If player is already connected, cancel
4556 if(player != NULL && player->peer_id != 0)
4558 infostream<<"emergePlayer(): Player already connected"<<std::endl;
4563 If player with the wanted peer_id already exists, cancel.
4565 if(m_env->getPlayer(peer_id) != NULL)
4567 infostream<<"emergePlayer(): Player with wrong name but same"
4568 " peer_id already exists"<<std::endl;
4573 Create a new player if it doesn't exist yet
4578 player = new RemotePlayer(this);
4579 player->updateName(name);
4581 /* Set player position */
4582 infostream<<"Server: Finding spawn place for player \""
4583 <<name<<"\""<<std::endl;
4584 v3f pos = findSpawnPos(m_env->getServerMap());
4585 player->setPosition(pos);
4587 /* Add player to environment */
4588 m_env->addPlayer(player);
4592 Create a new player active object
4594 PlayerSAO *playersao = new PlayerSAO(m_env, player, peer_id,
4595 getPlayerEffectivePrivs(player->getName()),
4598 /* Add object to environment */
4599 m_env->addActiveObject(playersao);
4603 scriptapi_on_newplayer(m_lua, playersao);
4605 scriptapi_on_joinplayer(m_lua, playersao);
4608 if(g_settings->getBool("creative_mode"))
4609 playersao->createCreativeInventory();
4614 void Server::handlePeerChange(PeerChange &c)
4616 JMutexAutoLock envlock(m_env_mutex);
4617 JMutexAutoLock conlock(m_con_mutex);
4619 if(c.type == PEER_ADDED)
4626 core::map<u16, RemoteClient*>::Node *n;
4627 n = m_clients.find(c.peer_id);
4628 // The client shouldn't already exist
4632 RemoteClient *client = new RemoteClient();
4633 client->peer_id = c.peer_id;
4634 m_clients.insert(client->peer_id, client);
4637 else if(c.type == PEER_REMOVED)
4644 core::map<u16, RemoteClient*>::Node *n;
4645 n = m_clients.find(c.peer_id);
4646 // The client should exist
4650 Mark objects to be not known by the client
4652 RemoteClient *client = n->getValue();
4654 for(core::map<u16, bool>::Iterator
4655 i = client->m_known_objects.getIterator();
4656 i.atEnd()==false; i++)
4659 u16 id = i.getNode()->getKey();
4660 ServerActiveObject* obj = m_env->getActiveObject(id);
4662 if(obj && obj->m_known_by_count > 0)
4663 obj->m_known_by_count--;
4667 Clear references to playing sounds
4669 for(std::map<s32, ServerPlayingSound>::iterator
4670 i = m_playing_sounds.begin();
4671 i != m_playing_sounds.end();)
4673 ServerPlayingSound &psound = i->second;
4674 psound.clients.erase(c.peer_id);
4675 if(psound.clients.size() == 0)
4676 m_playing_sounds.erase(i++);
4681 Player *player = m_env->getPlayer(c.peer_id);
4683 // Collect information about leaving in chat
4684 std::wstring message;
4688 std::wstring name = narrow_to_wide(player->getName());
4691 message += L" left the game.";
4693 message += L" (timed out)";
4697 /* Run scripts and remove from environment */
4701 PlayerSAO *playersao = player->getPlayerSAO();
4704 scriptapi_on_leaveplayer(m_lua, playersao);
4706 playersao->disconnected();
4716 std::ostringstream os(std::ios_base::binary);
4717 for(core::map<u16, RemoteClient*>::Iterator
4718 i = m_clients.getIterator();
4719 i.atEnd() == false; i++)
4721 RemoteClient *client = i.getNode()->getValue();
4722 assert(client->peer_id == i.getNode()->getKey());
4723 if(client->serialization_version == SER_FMT_VER_INVALID)
4726 Player *player = m_env->getPlayer(client->peer_id);
4729 // Get name of player
4730 os<<player->getName()<<" ";
4733 actionstream<<player->getName()<<" "
4734 <<(c.timeout?"times out.":"leaves game.")
4735 <<" List of players: "
4736 <<os.str()<<std::endl;
4741 delete m_clients[c.peer_id];
4742 m_clients.remove(c.peer_id);
4744 // Send player info to all remaining clients
4745 //SendPlayerInfos();
4747 // Send leave chat message to all remaining clients
4748 if(message.length() != 0)
4749 BroadcastChatMessage(message);
4758 void Server::handlePeerChanges()
4760 while(m_peer_change_queue.size() > 0)
4762 PeerChange c = m_peer_change_queue.pop_front();
4764 verbosestream<<"Server: Handling peer change: "
4765 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4768 handlePeerChange(c);
4772 void dedicated_server_loop(Server &server, bool &kill)
4774 DSTACK(__FUNCTION_NAME);
4776 verbosestream<<"dedicated_server_loop()"<<std::endl;
4778 IntervalLimiter m_profiler_interval;
4782 float steplen = g_settings->getFloat("dedicated_server_step");
4783 // This is kind of a hack but can be done like this
4784 // because server.step() is very light
4786 ScopeProfiler sp(g_profiler, "dedicated server sleep");
4787 sleep_ms((int)(steplen*1000.0));
4789 server.step(steplen);
4791 if(server.getShutdownRequested() || kill)
4793 infostream<<"Dedicated server quitting"<<std::endl;
4800 float profiler_print_interval =
4801 g_settings->getFloat("profiler_print_interval");
4802 if(profiler_print_interval != 0)
4804 if(m_profiler_interval.step(steplen, profiler_print_interval))
4806 infostream<<"Profiler:"<<std::endl;
4807 g_profiler->print(infostream);
4808 g_profiler->clear();