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 timeout: "<<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);
1164 // Delete detached inventories
1166 for(std::map<std::string, Inventory*>::iterator
1167 i = m_detached_inventories.begin();
1168 i != m_detached_inventories.end(); i++){
1174 void Server::start(unsigned short port)
1176 DSTACK(__FUNCTION_NAME);
1177 infostream<<"Starting server on port "<<port<<"..."<<std::endl;
1179 // Stop thread if already running
1182 // Initialize connection
1183 m_con.SetTimeoutMs(30);
1187 m_thread.setRun(true);
1190 // ASCII art for the win!
1192 <<" .__ __ __ "<<std::endl
1193 <<" _____ |__| ____ _____/ |_ ____ _______/ |_ "<<std::endl
1194 <<" / \\| |/ \\_/ __ \\ __\\/ __ \\ / ___/\\ __\\"<<std::endl
1195 <<"| Y Y \\ | | \\ ___/| | \\ ___/ \\___ \\ | | "<<std::endl
1196 <<"|__|_| /__|___| /\\___ >__| \\___ >____ > |__| "<<std::endl
1197 <<" \\/ \\/ \\/ \\/ \\/ "<<std::endl;
1198 actionstream<<"World at ["<<m_path_world<<"]"<<std::endl;
1199 actionstream<<"Server for gameid=\""<<m_gamespec.id
1200 <<"\" listening on port "<<port<<"."<<std::endl;
1205 DSTACK(__FUNCTION_NAME);
1207 infostream<<"Server: Stopping and waiting threads"<<std::endl;
1209 // Stop threads (set run=false first so both start stopping)
1210 m_thread.setRun(false);
1211 m_emergethread.setRun(false);
1213 m_emergethread.stop();
1215 infostream<<"Server: Threads stopped"<<std::endl;
1218 void Server::step(float dtime)
1220 DSTACK(__FUNCTION_NAME);
1225 JMutexAutoLock lock(m_step_dtime_mutex);
1226 m_step_dtime += dtime;
1228 // Throw if fatal error occurred in thread
1229 std::string async_err = m_async_fatal_error.get();
1230 if(async_err != ""){
1231 throw ServerError(async_err);
1235 void Server::AsyncRunStep()
1237 DSTACK(__FUNCTION_NAME);
1239 g_profiler->add("Server::AsyncRunStep (num)", 1);
1243 JMutexAutoLock lock1(m_step_dtime_mutex);
1244 dtime = m_step_dtime;
1248 // Send blocks to clients
1255 g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
1257 //infostream<<"Server steps "<<dtime<<std::endl;
1258 //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1261 JMutexAutoLock lock1(m_step_dtime_mutex);
1262 m_step_dtime -= dtime;
1269 m_uptime.set(m_uptime.get() + dtime);
1273 // Process connection's timeouts
1274 JMutexAutoLock lock2(m_con_mutex);
1275 ScopeProfiler sp(g_profiler, "Server: connection timeout processing");
1276 m_con.RunTimeouts(dtime);
1280 // This has to be called so that the client list gets synced
1281 // with the peer list of the connection
1282 handlePeerChanges();
1286 Update time of day and overall game time
1289 JMutexAutoLock envlock(m_env_mutex);
1291 m_env->setTimeOfDaySpeed(g_settings->getFloat("time_speed"));
1294 Send to clients at constant intervals
1297 m_time_of_day_send_timer -= dtime;
1298 if(m_time_of_day_send_timer < 0.0)
1300 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
1302 //JMutexAutoLock envlock(m_env_mutex);
1303 JMutexAutoLock conlock(m_con_mutex);
1305 for(core::map<u16, RemoteClient*>::Iterator
1306 i = m_clients.getIterator();
1307 i.atEnd() == false; i++)
1309 RemoteClient *client = i.getNode()->getValue();
1310 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1311 m_env->getTimeOfDay(), g_settings->getFloat("time_speed"));
1313 m_con.Send(client->peer_id, 0, data, true);
1319 JMutexAutoLock lock(m_env_mutex);
1321 ScopeProfiler sp(g_profiler, "SEnv step");
1322 ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
1326 const float map_timer_and_unload_dtime = 2.92;
1327 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
1329 JMutexAutoLock lock(m_env_mutex);
1330 // Run Map's timers and unload unused data
1331 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
1332 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
1333 g_settings->getFloat("server_unload_unused_data_timeout"));
1344 JMutexAutoLock lock(m_env_mutex);
1345 JMutexAutoLock lock2(m_con_mutex);
1347 ScopeProfiler sp(g_profiler, "Server: handle players");
1349 for(core::map<u16, RemoteClient*>::Iterator
1350 i = m_clients.getIterator();
1351 i.atEnd() == false; i++)
1353 RemoteClient *client = i.getNode()->getValue();
1354 PlayerSAO *playersao = getPlayerSAO(client->peer_id);
1355 if(playersao == NULL)
1359 Handle player HPs (die if hp=0)
1361 if(playersao->getHP() == 0 && playersao->m_hp_not_sent)
1362 DiePlayer(client->peer_id);
1365 Send player inventories and HPs if necessary
1367 if(playersao->m_teleported){
1368 SendMovePlayer(client->peer_id);
1369 playersao->m_teleported = false;
1371 if(playersao->m_inventory_not_sent){
1372 UpdateCrafting(client->peer_id);
1373 SendInventory(client->peer_id);
1375 if(playersao->m_hp_not_sent){
1376 SendPlayerHP(client->peer_id);
1381 /* Transform liquids */
1382 m_liquid_transform_timer += dtime;
1383 if(m_liquid_transform_timer >= 1.00)
1385 m_liquid_transform_timer -= 1.00;
1387 JMutexAutoLock lock(m_env_mutex);
1389 ScopeProfiler sp(g_profiler, "Server: liquid transform");
1391 core::map<v3s16, MapBlock*> modified_blocks;
1392 m_env->getMap().transformLiquids(modified_blocks);
1397 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1398 ServerMap &map = ((ServerMap&)m_env->getMap());
1399 map.updateLighting(modified_blocks, lighting_modified_blocks);
1401 // Add blocks modified by lighting to modified_blocks
1402 for(core::map<v3s16, MapBlock*>::Iterator
1403 i = lighting_modified_blocks.getIterator();
1404 i.atEnd() == false; i++)
1406 MapBlock *block = i.getNode()->getValue();
1407 modified_blocks.insert(block->getPos(), block);
1411 Set the modified blocks unsent for all the clients
1414 JMutexAutoLock lock2(m_con_mutex);
1416 for(core::map<u16, RemoteClient*>::Iterator
1417 i = m_clients.getIterator();
1418 i.atEnd() == false; i++)
1420 RemoteClient *client = i.getNode()->getValue();
1422 if(modified_blocks.size() > 0)
1424 // Remove block from sent history
1425 client->SetBlocksNotSent(modified_blocks);
1430 // Periodically print some info
1432 float &counter = m_print_info_timer;
1438 JMutexAutoLock lock2(m_con_mutex);
1440 if(m_clients.size() != 0)
1441 infostream<<"Players:"<<std::endl;
1442 for(core::map<u16, RemoteClient*>::Iterator
1443 i = m_clients.getIterator();
1444 i.atEnd() == false; i++)
1446 //u16 peer_id = i.getNode()->getKey();
1447 RemoteClient *client = i.getNode()->getValue();
1448 Player *player = m_env->getPlayer(client->peer_id);
1451 infostream<<"* "<<player->getName()<<"\t";
1452 client->PrintInfo(infostream);
1457 //if(g_settings->getBool("enable_experimental"))
1461 Check added and deleted active objects
1464 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
1465 JMutexAutoLock envlock(m_env_mutex);
1466 JMutexAutoLock conlock(m_con_mutex);
1468 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
1470 // Radius inside which objects are active
1471 s16 radius = g_settings->getS16("active_object_send_range_blocks");
1472 radius *= MAP_BLOCKSIZE;
1474 for(core::map<u16, RemoteClient*>::Iterator
1475 i = m_clients.getIterator();
1476 i.atEnd() == false; i++)
1478 RemoteClient *client = i.getNode()->getValue();
1480 // If definitions and textures have not been sent, don't
1481 // send objects either
1482 if(!client->definitions_sent)
1485 Player *player = m_env->getPlayer(client->peer_id);
1488 // This can happen if the client timeouts somehow
1489 /*infostream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
1491 <<" has no associated player"<<std::endl;*/
1494 v3s16 pos = floatToInt(player->getPosition(), BS);
1496 core::map<u16, bool> removed_objects;
1497 core::map<u16, bool> added_objects;
1498 m_env->getRemovedActiveObjects(pos, radius,
1499 client->m_known_objects, removed_objects);
1500 m_env->getAddedActiveObjects(pos, radius,
1501 client->m_known_objects, added_objects);
1503 // Ignore if nothing happened
1504 if(removed_objects.size() == 0 && added_objects.size() == 0)
1506 //infostream<<"active objects: none changed"<<std::endl;
1510 std::string data_buffer;
1514 // Handle removed objects
1515 writeU16((u8*)buf, removed_objects.size());
1516 data_buffer.append(buf, 2);
1517 for(core::map<u16, bool>::Iterator
1518 i = removed_objects.getIterator();
1519 i.atEnd()==false; i++)
1522 u16 id = i.getNode()->getKey();
1523 ServerActiveObject* obj = m_env->getActiveObject(id);
1525 // Add to data buffer for sending
1526 writeU16((u8*)buf, i.getNode()->getKey());
1527 data_buffer.append(buf, 2);
1529 // Remove from known objects
1530 client->m_known_objects.remove(i.getNode()->getKey());
1532 if(obj && obj->m_known_by_count > 0)
1533 obj->m_known_by_count--;
1536 // Handle added objects
1537 writeU16((u8*)buf, added_objects.size());
1538 data_buffer.append(buf, 2);
1539 for(core::map<u16, bool>::Iterator
1540 i = added_objects.getIterator();
1541 i.atEnd()==false; i++)
1544 u16 id = i.getNode()->getKey();
1545 ServerActiveObject* obj = m_env->getActiveObject(id);
1548 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1550 infostream<<"WARNING: "<<__FUNCTION_NAME
1551 <<": NULL object"<<std::endl;
1553 type = obj->getSendType();
1555 // Add to data buffer for sending
1556 writeU16((u8*)buf, id);
1557 data_buffer.append(buf, 2);
1558 writeU8((u8*)buf, type);
1559 data_buffer.append(buf, 1);
1562 data_buffer.append(serializeLongString(
1563 obj->getClientInitializationData()));
1565 data_buffer.append(serializeLongString(""));
1567 // Add to known objects
1568 client->m_known_objects.insert(i.getNode()->getKey(), false);
1571 obj->m_known_by_count++;
1575 SharedBuffer<u8> reply(2 + data_buffer.size());
1576 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1577 memcpy((char*)&reply[2], data_buffer.c_str(),
1578 data_buffer.size());
1580 m_con.Send(client->peer_id, 0, reply, true);
1582 verbosestream<<"Server: Sent object remove/add: "
1583 <<removed_objects.size()<<" removed, "
1584 <<added_objects.size()<<" added, "
1585 <<"packet size is "<<reply.getSize()<<std::endl;
1590 Collect a list of all the objects known by the clients
1591 and report it back to the environment.
1594 core::map<u16, bool> all_known_objects;
1596 for(core::map<u16, RemoteClient*>::Iterator
1597 i = m_clients.getIterator();
1598 i.atEnd() == false; i++)
1600 RemoteClient *client = i.getNode()->getValue();
1601 // Go through all known objects of client
1602 for(core::map<u16, bool>::Iterator
1603 i = client->m_known_objects.getIterator();
1604 i.atEnd()==false; i++)
1606 u16 id = i.getNode()->getKey();
1607 all_known_objects[id] = true;
1611 m_env->setKnownActiveObjects(whatever);
1617 Send object messages
1620 JMutexAutoLock envlock(m_env_mutex);
1621 JMutexAutoLock conlock(m_con_mutex);
1623 ScopeProfiler sp(g_profiler, "Server: sending object messages");
1626 // Value = data sent by object
1627 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1629 // Get active object messages from environment
1632 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
1636 core::list<ActiveObjectMessage>* message_list = NULL;
1637 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1638 n = buffered_messages.find(aom.id);
1641 message_list = new core::list<ActiveObjectMessage>;
1642 buffered_messages.insert(aom.id, message_list);
1646 message_list = n->getValue();
1648 message_list->push_back(aom);
1651 // Route data to every client
1652 for(core::map<u16, RemoteClient*>::Iterator
1653 i = m_clients.getIterator();
1654 i.atEnd()==false; i++)
1656 RemoteClient *client = i.getNode()->getValue();
1657 std::string reliable_data;
1658 std::string unreliable_data;
1659 // Go through all objects in message buffer
1660 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1661 j = buffered_messages.getIterator();
1662 j.atEnd()==false; j++)
1664 // If object is not known by client, skip it
1665 u16 id = j.getNode()->getKey();
1666 if(client->m_known_objects.find(id) == NULL)
1668 // Get message list of object
1669 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1670 // Go through every message
1671 for(core::list<ActiveObjectMessage>::Iterator
1672 k = list->begin(); k != list->end(); k++)
1674 // Compose the full new data with header
1675 ActiveObjectMessage aom = *k;
1676 std::string new_data;
1679 writeU16((u8*)&buf[0], aom.id);
1680 new_data.append(buf, 2);
1682 new_data += serializeString(aom.datastring);
1683 // Add data to buffer
1685 reliable_data += new_data;
1687 unreliable_data += new_data;
1691 reliable_data and unreliable_data are now ready.
1694 if(reliable_data.size() > 0)
1696 SharedBuffer<u8> reply(2 + reliable_data.size());
1697 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1698 memcpy((char*)&reply[2], reliable_data.c_str(),
1699 reliable_data.size());
1701 m_con.Send(client->peer_id, 0, reply, true);
1703 if(unreliable_data.size() > 0)
1705 SharedBuffer<u8> reply(2 + unreliable_data.size());
1706 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1707 memcpy((char*)&reply[2], unreliable_data.c_str(),
1708 unreliable_data.size());
1709 // Send as unreliable
1710 m_con.Send(client->peer_id, 0, reply, false);
1713 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1715 infostream<<"Server: Size of object message data: "
1716 <<"reliable: "<<reliable_data.size()
1717 <<", unreliable: "<<unreliable_data.size()
1722 // Clear buffered_messages
1723 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1724 i = buffered_messages.getIterator();
1725 i.atEnd()==false; i++)
1727 delete i.getNode()->getValue();
1731 } // enable_experimental
1734 Send queued-for-sending map edit events.
1737 // We will be accessing the environment and the connection
1738 JMutexAutoLock lock(m_env_mutex);
1739 JMutexAutoLock conlock(m_con_mutex);
1741 // Don't send too many at a time
1744 // Single change sending is disabled if queue size is not small
1745 bool disable_single_change_sending = false;
1746 if(m_unsent_map_edit_queue.size() >= 4)
1747 disable_single_change_sending = true;
1749 int event_count = m_unsent_map_edit_queue.size();
1751 // We'll log the amount of each
1754 while(m_unsent_map_edit_queue.size() != 0)
1756 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1758 // Players far away from the change are stored here.
1759 // Instead of sending the changes, MapBlocks are set not sent
1761 core::list<u16> far_players;
1763 if(event->type == MEET_ADDNODE)
1765 //infostream<<"Server: MEET_ADDNODE"<<std::endl;
1766 prof.add("MEET_ADDNODE", 1);
1767 if(disable_single_change_sending)
1768 sendAddNode(event->p, event->n, event->already_known_by_peer,
1771 sendAddNode(event->p, event->n, event->already_known_by_peer,
1774 else if(event->type == MEET_REMOVENODE)
1776 //infostream<<"Server: MEET_REMOVENODE"<<std::endl;
1777 prof.add("MEET_REMOVENODE", 1);
1778 if(disable_single_change_sending)
1779 sendRemoveNode(event->p, event->already_known_by_peer,
1782 sendRemoveNode(event->p, event->already_known_by_peer,
1785 else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
1787 infostream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
1788 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
1789 setBlockNotSent(event->p);
1791 else if(event->type == MEET_OTHER)
1793 infostream<<"Server: MEET_OTHER"<<std::endl;
1794 prof.add("MEET_OTHER", 1);
1795 for(core::map<v3s16, bool>::Iterator
1796 i = event->modified_blocks.getIterator();
1797 i.atEnd()==false; i++)
1799 v3s16 p = i.getNode()->getKey();
1805 prof.add("unknown", 1);
1806 infostream<<"WARNING: Server: Unknown MapEditEvent "
1807 <<((u32)event->type)<<std::endl;
1811 Set blocks not sent to far players
1813 if(far_players.size() > 0)
1815 // Convert list format to that wanted by SetBlocksNotSent
1816 core::map<v3s16, MapBlock*> modified_blocks2;
1817 for(core::map<v3s16, bool>::Iterator
1818 i = event->modified_blocks.getIterator();
1819 i.atEnd()==false; i++)
1821 v3s16 p = i.getNode()->getKey();
1822 modified_blocks2.insert(p,
1823 m_env->getMap().getBlockNoCreateNoEx(p));
1825 // Set blocks not sent
1826 for(core::list<u16>::Iterator
1827 i = far_players.begin();
1828 i != far_players.end(); i++)
1831 RemoteClient *client = getClient(peer_id);
1834 client->SetBlocksNotSent(modified_blocks2);
1840 /*// Don't send too many at a time
1842 if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
1846 if(event_count >= 5){
1847 infostream<<"Server: MapEditEvents:"<<std::endl;
1848 prof.print(infostream);
1849 } else if(event_count != 0){
1850 verbosestream<<"Server: MapEditEvents:"<<std::endl;
1851 prof.print(verbosestream);
1857 Trigger emergethread (it somehow gets to a non-triggered but
1858 bysy state sometimes)
1861 float &counter = m_emergethread_trigger_timer;
1867 m_emergethread.trigger();
1871 // Save map, players and auth stuff
1873 float &counter = m_savemap_timer;
1875 if(counter >= g_settings->getFloat("server_map_save_interval"))
1878 JMutexAutoLock lock(m_env_mutex);
1880 ScopeProfiler sp(g_profiler, "Server: saving stuff");
1883 if(m_banmanager.isModified())
1884 m_banmanager.save();
1886 // Save changed parts of map
1887 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
1890 m_env->serializePlayers(m_path_world);
1892 // Save environment metadata
1893 m_env->saveMeta(m_path_world);
1898 void Server::Receive()
1900 DSTACK(__FUNCTION_NAME);
1901 SharedBuffer<u8> data;
1906 JMutexAutoLock conlock(m_con_mutex);
1907 datasize = m_con.Receive(peer_id, data);
1910 // This has to be called so that the client list gets synced
1911 // with the peer list of the connection
1912 handlePeerChanges();
1914 ProcessData(*data, datasize, peer_id);
1916 catch(con::InvalidIncomingDataException &e)
1918 infostream<<"Server::Receive(): "
1919 "InvalidIncomingDataException: what()="
1920 <<e.what()<<std::endl;
1922 catch(con::PeerNotFoundException &e)
1924 //NOTE: This is not needed anymore
1926 // The peer has been disconnected.
1927 // Find the associated player and remove it.
1929 /*JMutexAutoLock envlock(m_env_mutex);
1931 infostream<<"ServerThread: peer_id="<<peer_id
1932 <<" has apparently closed connection. "
1933 <<"Removing player."<<std::endl;
1935 m_env->removePlayer(peer_id);*/
1939 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1941 DSTACK(__FUNCTION_NAME);
1942 // Environment is locked first.
1943 JMutexAutoLock envlock(m_env_mutex);
1944 JMutexAutoLock conlock(m_con_mutex);
1946 ScopeProfiler sp(g_profiler, "Server::ProcessData");
1949 Address address = m_con.GetPeerAddress(peer_id);
1950 std::string addr_s = address.serializeString();
1952 // drop player if is ip is banned
1953 if(m_banmanager.isIpBanned(addr_s)){
1954 infostream<<"Server: A banned client tried to connect from "
1955 <<addr_s<<"; banned name was "
1956 <<m_banmanager.getBanName(addr_s)<<std::endl;
1957 // This actually doesn't seem to transfer to the client
1958 SendAccessDenied(m_con, peer_id,
1959 L"Your ip is banned. Banned name was "
1960 +narrow_to_wide(m_banmanager.getBanName(addr_s)));
1961 m_con.DeletePeer(peer_id);
1965 catch(con::PeerNotFoundException &e)
1967 infostream<<"Server::ProcessData(): Cancelling: peer "
1968 <<peer_id<<" not found"<<std::endl;
1972 std::string addr_s = m_con.GetPeerAddress(peer_id).serializeString();
1974 u8 peer_ser_ver = getClient(peer_id)->serialization_version;
1982 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1984 if(command == TOSERVER_INIT)
1986 // [0] u16 TOSERVER_INIT
1987 // [2] u8 SER_FMT_VER_HIGHEST
1988 // [3] u8[20] player_name
1989 // [23] u8[28] password <--- can be sent without this, from old versions
1991 if(datasize < 2+1+PLAYERNAME_SIZE)
1994 verbosestream<<"Server: Got TOSERVER_INIT from "
1995 <<peer_id<<std::endl;
1997 // First byte after command is maximum supported
1998 // serialization version
1999 u8 client_max = data[2];
2000 u8 our_max = SER_FMT_VER_HIGHEST;
2001 // Use the highest version supported by both
2002 u8 deployed = core::min_(client_max, our_max);
2003 // If it's lower than the lowest supported, give up.
2004 if(deployed < SER_FMT_VER_LOWEST)
2005 deployed = SER_FMT_VER_INVALID;
2007 //peer->serialization_version = deployed;
2008 getClient(peer_id)->pending_serialization_version = deployed;
2010 if(deployed == SER_FMT_VER_INVALID)
2012 actionstream<<"Server: A mismatched client tried to connect from "
2013 <<addr_s<<std::endl;
2014 infostream<<"Server: Cannot negotiate "
2015 "serialization version with peer "
2016 <<peer_id<<std::endl;
2017 SendAccessDenied(m_con, peer_id, std::wstring(
2018 L"Your client's version is not supported.\n"
2019 L"Server version is ")
2020 + narrow_to_wide(VERSION_STRING) + L"."
2026 Read and check network protocol version
2029 u16 net_proto_version = 0;
2030 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
2032 net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
2035 getClient(peer_id)->net_proto_version = net_proto_version;
2037 if(net_proto_version == 0)
2039 actionstream<<"Server: An old tried to connect from "<<addr_s
2041 SendAccessDenied(m_con, peer_id, std::wstring(
2042 L"Your client's version is not supported.\n"
2043 L"Server version is ")
2044 + narrow_to_wide(VERSION_STRING) + L"."
2049 if(g_settings->getBool("strict_protocol_version_checking"))
2051 if(net_proto_version != PROTOCOL_VERSION)
2053 actionstream<<"Server: A mismatched client tried to connect"
2054 <<" from "<<addr_s<<std::endl;
2055 SendAccessDenied(m_con, peer_id, std::wstring(
2056 L"Your client's version is not supported.\n"
2057 L"Server version is ")
2058 + narrow_to_wide(VERSION_STRING) + L",\n"
2059 + L"server's PROTOCOL_VERSION is "
2060 + narrow_to_wide(itos(PROTOCOL_VERSION))
2061 + L", client's PROTOCOL_VERSION is "
2062 + narrow_to_wide(itos(net_proto_version))
2073 char playername[PLAYERNAME_SIZE];
2074 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
2076 playername[i] = data[3+i];
2078 playername[PLAYERNAME_SIZE-1] = 0;
2080 if(playername[0]=='\0')
2082 actionstream<<"Server: Player with an empty name "
2083 <<"tried to connect from "<<addr_s<<std::endl;
2084 SendAccessDenied(m_con, peer_id,
2089 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
2091 actionstream<<"Server: Player with an invalid name "
2092 <<"tried to connect from "<<addr_s<<std::endl;
2093 SendAccessDenied(m_con, peer_id,
2094 L"Name contains unallowed characters");
2098 infostream<<"Server: New connection: \""<<playername<<"\" from "
2099 <<m_con.GetPeerAddress(peer_id).serializeString()<<std::endl;
2102 char given_password[PASSWORD_SIZE];
2103 if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
2105 // old version - assume blank password
2106 given_password[0] = 0;
2110 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2112 given_password[i] = data[23+i];
2114 given_password[PASSWORD_SIZE-1] = 0;
2117 if(!base64_is_valid(given_password)){
2118 infostream<<"Server: "<<playername
2119 <<" supplied invalid password hash"<<std::endl;
2120 SendAccessDenied(m_con, peer_id, L"Invalid password hash");
2124 std::string checkpwd; // Password hash to check against
2125 bool has_auth = scriptapi_get_auth(m_lua, playername, &checkpwd, NULL);
2127 // If no authentication info exists for user, create it
2129 if(!isSingleplayer() &&
2130 g_settings->getBool("disallow_empty_password") &&
2131 std::string(given_password) == ""){
2132 SendAccessDenied(m_con, peer_id, L"Empty passwords are "
2133 L"disallowed. Set a password and try again.");
2136 std::wstring raw_default_password =
2137 narrow_to_wide(g_settings->get("default_password"));
2138 std::string initial_password =
2139 translatePassword(playername, raw_default_password);
2141 // If default_password is empty, allow any initial password
2142 if (raw_default_password.length() == 0)
2143 initial_password = given_password;
2145 scriptapi_create_auth(m_lua, playername, initial_password);
2148 has_auth = scriptapi_get_auth(m_lua, playername, &checkpwd, NULL);
2151 SendAccessDenied(m_con, peer_id, L"Not allowed to login");
2155 if(given_password != checkpwd){
2156 infostream<<"Server: peer_id="<<peer_id
2157 <<": supplied invalid password for "
2158 <<playername<<std::endl;
2159 SendAccessDenied(m_con, peer_id, L"Invalid password");
2163 // Do not allow multiple players in simple singleplayer mode.
2164 // This isn't a perfect way to do it, but will suffice for now.
2165 if(m_simple_singleplayer_mode && m_clients.size() > 1){
2166 infostream<<"Server: Not allowing another client to connect in"
2167 <<" simple singleplayer mode"<<std::endl;
2168 SendAccessDenied(m_con, peer_id,
2169 L"Running in simple singleplayer mode.");
2173 // Enforce user limit.
2174 // Don't enforce for users that have some admin right
2175 if(m_clients.size() >= g_settings->getU16("max_users") &&
2176 !checkPriv(playername, "server") &&
2177 !checkPriv(playername, "ban") &&
2178 !checkPriv(playername, "privs") &&
2179 !checkPriv(playername, "password") &&
2180 playername != g_settings->get("name"))
2182 actionstream<<"Server: "<<playername<<" tried to join, but there"
2183 <<" are already max_users="
2184 <<g_settings->getU16("max_users")<<" players."<<std::endl;
2185 SendAccessDenied(m_con, peer_id, L"Too many users.");
2190 PlayerSAO *playersao = emergePlayer(playername, peer_id);
2192 // If failed, cancel
2193 if(playersao == NULL)
2195 errorstream<<"Server: peer_id="<<peer_id
2196 <<": failed to emerge player"<<std::endl;
2201 Answer with a TOCLIENT_INIT
2204 SharedBuffer<u8> reply(2+1+6+8);
2205 writeU16(&reply[0], TOCLIENT_INIT);
2206 writeU8(&reply[2], deployed);
2207 writeV3S16(&reply[2+1], floatToInt(playersao->getPlayer()->getPosition()+v3f(0,BS/2,0), BS));
2208 writeU64(&reply[2+1+6], m_env->getServerMap().getSeed());
2211 m_con.Send(peer_id, 0, reply, true);
2215 Send complete position information
2217 SendMovePlayer(peer_id);
2222 if(command == TOSERVER_INIT2)
2224 verbosestream<<"Server: Got TOSERVER_INIT2 from "
2225 <<peer_id<<std::endl;
2227 Player *player = m_env->getPlayer(peer_id);
2229 verbosestream<<"Server: TOSERVER_INIT2: "
2230 <<"Player not found; ignoring."<<std::endl;
2234 getClient(peer_id)->serialization_version
2235 = getClient(peer_id)->pending_serialization_version;
2238 Send some initialization data
2241 infostream<<"Server: Sending content to "
2242 <<getPlayerName(peer_id)<<std::endl;
2244 // Send item definitions
2245 SendItemDef(m_con, peer_id, m_itemdef);
2247 // Send node definitions
2248 SendNodeDef(m_con, peer_id, m_nodedef);
2250 // Send media announcement
2251 sendMediaAnnouncement(peer_id);
2254 SendPlayerPrivileges(peer_id);
2256 // Send inventory formspec
2257 SendPlayerInventoryFormspec(peer_id);
2260 UpdateCrafting(peer_id);
2261 SendInventory(peer_id);
2264 SendPlayerHP(peer_id);
2266 // Send detached inventories
2267 sendDetachedInventories(peer_id);
2269 // Show death screen if necessary
2271 SendDeathscreen(m_con, peer_id, false, v3f(0,0,0));
2275 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2276 m_env->getTimeOfDay(), g_settings->getFloat("time_speed"));
2277 m_con.Send(peer_id, 0, data, true);
2280 // Note things in chat if not in simple singleplayer mode
2281 if(!m_simple_singleplayer_mode)
2283 // Send information about server to player in chat
2284 SendChatMessage(peer_id, getStatusString());
2286 // Send information about joining in chat
2288 std::wstring name = L"unknown";
2289 Player *player = m_env->getPlayer(peer_id);
2291 name = narrow_to_wide(player->getName());
2293 std::wstring message;
2296 message += L" joined the game.";
2297 BroadcastChatMessage(message);
2301 // Warnings about protocol version can be issued here
2302 if(getClient(peer_id)->net_proto_version < PROTOCOL_VERSION)
2304 SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT IS OLD AND MAY WORK PROPERLY WITH THIS SERVER!");
2311 std::ostringstream os(std::ios_base::binary);
2312 for(core::map<u16, RemoteClient*>::Iterator
2313 i = m_clients.getIterator();
2314 i.atEnd() == false; i++)
2316 RemoteClient *client = i.getNode()->getValue();
2317 assert(client->peer_id == i.getNode()->getKey());
2318 if(client->serialization_version == SER_FMT_VER_INVALID)
2321 Player *player = m_env->getPlayer(client->peer_id);
2324 // Get name of player
2325 os<<player->getName()<<" ";
2328 actionstream<<player->getName()<<" joins game. List of players: "
2329 <<os.str()<<std::endl;
2335 if(peer_ser_ver == SER_FMT_VER_INVALID)
2337 infostream<<"Server::ProcessData(): Cancelling: Peer"
2338 " serialization format invalid or not initialized."
2339 " Skipping incoming command="<<command<<std::endl;
2343 Player *player = m_env->getPlayer(peer_id);
2345 infostream<<"Server::ProcessData(): Cancelling: "
2346 "No player for peer_id="<<peer_id
2351 PlayerSAO *playersao = player->getPlayerSAO();
2352 if(playersao == NULL){
2353 infostream<<"Server::ProcessData(): Cancelling: "
2354 "No player object for peer_id="<<peer_id
2359 if(command == TOSERVER_PLAYERPOS)
2361 if(datasize < 2+12+12+4+4)
2365 v3s32 ps = readV3S32(&data[start+2]);
2366 v3s32 ss = readV3S32(&data[start+2+12]);
2367 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2368 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2369 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2370 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2371 pitch = wrapDegrees(pitch);
2372 yaw = wrapDegrees(yaw);
2374 player->setPosition(position);
2375 player->setSpeed(speed);
2376 player->setPitch(pitch);
2377 player->setYaw(yaw);
2379 /*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2380 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2381 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2383 else if(command == TOSERVER_GOTBLOCKS)
2396 u16 count = data[2];
2397 for(u16 i=0; i<count; i++)
2399 if((s16)datasize < 2+1+(i+1)*6)
2400 throw con::InvalidIncomingDataException
2401 ("GOTBLOCKS length is too short");
2402 v3s16 p = readV3S16(&data[2+1+i*6]);
2403 /*infostream<<"Server: GOTBLOCKS ("
2404 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2405 RemoteClient *client = getClient(peer_id);
2406 client->GotBlock(p);
2409 else if(command == TOSERVER_DELETEDBLOCKS)
2422 u16 count = data[2];
2423 for(u16 i=0; i<count; i++)
2425 if((s16)datasize < 2+1+(i+1)*6)
2426 throw con::InvalidIncomingDataException
2427 ("DELETEDBLOCKS length is too short");
2428 v3s16 p = readV3S16(&data[2+1+i*6]);
2429 /*infostream<<"Server: DELETEDBLOCKS ("
2430 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2431 RemoteClient *client = getClient(peer_id);
2432 client->SetBlockNotSent(p);
2435 else if(command == TOSERVER_CLICK_OBJECT)
2437 infostream<<"Server: CLICK_OBJECT not supported anymore"<<std::endl;
2440 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2442 infostream<<"Server: CLICK_ACTIVEOBJECT not supported anymore"<<std::endl;
2445 else if(command == TOSERVER_GROUND_ACTION)
2447 infostream<<"Server: GROUND_ACTION not supported anymore"<<std::endl;
2451 else if(command == TOSERVER_RELEASE)
2453 infostream<<"Server: RELEASE not supported anymore"<<std::endl;
2456 else if(command == TOSERVER_SIGNTEXT)
2458 infostream<<"Server: SIGNTEXT not supported anymore"
2462 else if(command == TOSERVER_SIGNNODETEXT)
2464 infostream<<"Server: SIGNNODETEXT not supported anymore"
2468 else if(command == TOSERVER_INVENTORY_ACTION)
2470 // Strip command and create a stream
2471 std::string datastring((char*)&data[2], datasize-2);
2472 verbosestream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2473 std::istringstream is(datastring, std::ios_base::binary);
2475 InventoryAction *a = InventoryAction::deSerialize(is);
2478 infostream<<"TOSERVER_INVENTORY_ACTION: "
2479 <<"InventoryAction::deSerialize() returned NULL"
2485 Note: Always set inventory not sent, to repair cases
2486 where the client made a bad prediction.
2490 Handle restrictions and special cases of the move action
2492 if(a->getType() == IACTION_MOVE)
2494 IMoveAction *ma = (IMoveAction*)a;
2496 ma->from_inv.applyCurrentPlayer(player->getName());
2497 ma->to_inv.applyCurrentPlayer(player->getName());
2499 setInventoryModified(ma->from_inv);
2500 setInventoryModified(ma->to_inv);
2502 bool from_inv_is_current_player =
2503 (ma->from_inv.type == InventoryLocation::PLAYER) &&
2504 (ma->from_inv.name == player->getName());
2506 bool to_inv_is_current_player =
2507 (ma->to_inv.type == InventoryLocation::PLAYER) &&
2508 (ma->to_inv.name == player->getName());
2511 Disable moving items out of craftpreview
2513 if(ma->from_list == "craftpreview")
2515 infostream<<"Ignoring IMoveAction from "
2516 <<(ma->from_inv.dump())<<":"<<ma->from_list
2517 <<" to "<<(ma->to_inv.dump())<<":"<<ma->to_list
2518 <<" because src is "<<ma->from_list<<std::endl;
2524 Disable moving items into craftresult and craftpreview
2526 if(ma->to_list == "craftpreview" || ma->to_list == "craftresult")
2528 infostream<<"Ignoring IMoveAction from "
2529 <<(ma->from_inv.dump())<<":"<<ma->from_list
2530 <<" to "<<(ma->to_inv.dump())<<":"<<ma->to_list
2531 <<" because dst is "<<ma->to_list<<std::endl;
2536 // Disallow moving items in elsewhere than player's inventory
2537 // if not allowed to interact
2538 if(!checkPriv(player->getName(), "interact") &&
2539 (!from_inv_is_current_player ||
2540 !to_inv_is_current_player))
2542 infostream<<"Cannot move outside of player's inventory: "
2543 <<"No interact privilege"<<std::endl;
2549 Handle restrictions and special cases of the drop action
2551 else if(a->getType() == IACTION_DROP)
2553 IDropAction *da = (IDropAction*)a;
2555 da->from_inv.applyCurrentPlayer(player->getName());
2557 setInventoryModified(da->from_inv);
2559 // Disallow dropping items if not allowed to interact
2560 if(!checkPriv(player->getName(), "interact"))
2567 Handle restrictions and special cases of the craft action
2569 else if(a->getType() == IACTION_CRAFT)
2571 ICraftAction *ca = (ICraftAction*)a;
2573 ca->craft_inv.applyCurrentPlayer(player->getName());
2575 setInventoryModified(ca->craft_inv);
2577 //bool craft_inv_is_current_player =
2578 // (ca->craft_inv.type == InventoryLocation::PLAYER) &&
2579 // (ca->craft_inv.name == player->getName());
2581 // Disallow crafting if not allowed to interact
2582 if(!checkPriv(player->getName(), "interact"))
2584 infostream<<"Cannot craft: "
2585 <<"No interact privilege"<<std::endl;
2592 a->apply(this, playersao, this);
2596 else if(command == TOSERVER_CHAT_MESSAGE)
2604 std::string datastring((char*)&data[2], datasize-2);
2605 std::istringstream is(datastring, std::ios_base::binary);
2608 is.read((char*)buf, 2);
2609 u16 len = readU16(buf);
2611 std::wstring message;
2612 for(u16 i=0; i<len; i++)
2614 is.read((char*)buf, 2);
2615 message += (wchar_t)readU16(buf);
2618 // Get player name of this client
2619 std::wstring name = narrow_to_wide(player->getName());
2622 bool ate = scriptapi_on_chat_message(m_lua, player->getName(),
2623 wide_to_narrow(message));
2624 // If script ate the message, don't proceed
2628 // Line to send to players
2630 // Whether to send to the player that sent the line
2631 bool send_to_sender = false;
2632 // Whether to send to other players
2633 bool send_to_others = false;
2636 if(message[0] == L'/')
2638 size_t strip_size = 1;
2639 if (message[1] == L'#') // support old-style commans
2641 message = message.substr(strip_size);
2643 WStrfnd f1(message);
2644 f1.next(L" "); // Skip over /#whatever
2645 std::wstring paramstring = f1.next(L"");
2647 ServerCommandContext *ctx = new ServerCommandContext(
2648 str_split(message, L' '),
2654 std::wstring reply(processServerCommand(ctx));
2655 send_to_sender = ctx->flags & SEND_TO_SENDER;
2656 send_to_others = ctx->flags & SEND_TO_OTHERS;
2658 if (ctx->flags & SEND_NO_PREFIX)
2661 line += L"Server: " + reply;
2668 if(checkPriv(player->getName(), "shout")){
2673 send_to_others = true;
2675 line += L"-!- You don't have permission to shout.";
2676 send_to_sender = true;
2683 actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2686 Send the message to clients
2688 for(core::map<u16, RemoteClient*>::Iterator
2689 i = m_clients.getIterator();
2690 i.atEnd() == false; i++)
2692 // Get client and check that it is valid
2693 RemoteClient *client = i.getNode()->getValue();
2694 assert(client->peer_id == i.getNode()->getKey());
2695 if(client->serialization_version == SER_FMT_VER_INVALID)
2699 bool sender_selected = (peer_id == client->peer_id);
2700 if(sender_selected == true && send_to_sender == false)
2702 if(sender_selected == false && send_to_others == false)
2705 SendChatMessage(client->peer_id, line);
2709 else if(command == TOSERVER_DAMAGE)
2711 std::string datastring((char*)&data[2], datasize-2);
2712 std::istringstream is(datastring, std::ios_base::binary);
2713 u8 damage = readU8(is);
2715 actionstream<<player->getName()<<" damaged by "
2716 <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
2719 playersao->setHP(playersao->getHP() - damage);
2721 if(playersao->getHP() == 0 && playersao->m_hp_not_sent)
2724 if(playersao->m_hp_not_sent)
2725 SendPlayerHP(peer_id);
2727 else if(command == TOSERVER_PASSWORD)
2730 [0] u16 TOSERVER_PASSWORD
2731 [2] u8[28] old password
2732 [30] u8[28] new password
2735 if(datasize != 2+PASSWORD_SIZE*2)
2737 /*char password[PASSWORD_SIZE];
2738 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2739 password[i] = data[2+i];
2740 password[PASSWORD_SIZE-1] = 0;*/
2742 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2750 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2752 char c = data[2+PASSWORD_SIZE+i];
2758 if(!base64_is_valid(newpwd)){
2759 infostream<<"Server: "<<player->getName()<<" supplied invalid password hash"<<std::endl;
2760 // Wrong old password supplied!!
2761 SendChatMessage(peer_id, L"Invalid new password hash supplied. Password NOT changed.");
2765 infostream<<"Server: Client requests a password change from "
2766 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
2768 std::string playername = player->getName();
2770 std::string checkpwd;
2771 scriptapi_get_auth(m_lua, playername, &checkpwd, NULL);
2773 if(oldpwd != checkpwd)
2775 infostream<<"Server: invalid old password"<<std::endl;
2776 // Wrong old password supplied!!
2777 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
2781 bool success = scriptapi_set_password(m_lua, playername, newpwd);
2783 actionstream<<player->getName()<<" changes password"<<std::endl;
2784 SendChatMessage(peer_id, L"Password change successful.");
2786 actionstream<<player->getName()<<" tries to change password but "
2787 <<"it fails"<<std::endl;
2788 SendChatMessage(peer_id, L"Password change failed or inavailable.");
2791 else if(command == TOSERVER_PLAYERITEM)
2796 u16 item = readU16(&data[2]);
2797 playersao->setWieldIndex(item);
2799 else if(command == TOSERVER_RESPAWN)
2804 RespawnPlayer(peer_id);
2806 actionstream<<player->getName()<<" respawns at "
2807 <<PP(player->getPosition()/BS)<<std::endl;
2809 // ActiveObject is added to environment in AsyncRunStep after
2810 // the previous addition has been succesfully removed
2812 else if(command == TOSERVER_REQUEST_MEDIA) {
2813 std::string datastring((char*)&data[2], datasize-2);
2814 std::istringstream is(datastring, std::ios_base::binary);
2816 core::list<MediaRequest> tosend;
2817 u16 numfiles = readU16(is);
2819 infostream<<"Sending "<<numfiles<<" files to "
2820 <<getPlayerName(peer_id)<<std::endl;
2821 verbosestream<<"TOSERVER_REQUEST_MEDIA: "<<std::endl;
2823 for(int i = 0; i < numfiles; i++) {
2824 std::string name = deSerializeString(is);
2825 tosend.push_back(MediaRequest(name));
2826 verbosestream<<"TOSERVER_REQUEST_MEDIA: requested file "
2830 sendRequestedMedia(peer_id, tosend);
2832 // Now the client should know about everything
2833 // (definitions and files)
2834 getClient(peer_id)->definitions_sent = true;
2836 else if(command == TOSERVER_INTERACT)
2838 std::string datastring((char*)&data[2], datasize-2);
2839 std::istringstream is(datastring, std::ios_base::binary);
2845 [5] u32 length of the next item
2846 [9] serialized PointedThing
2848 0: start digging (from undersurface) or use
2849 1: stop digging (all parameters ignored)
2850 2: digging completed
2851 3: place block or item (to abovesurface)
2854 u8 action = readU8(is);
2855 u16 item_i = readU16(is);
2856 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
2857 PointedThing pointed;
2858 pointed.deSerialize(tmp_is);
2860 verbosestream<<"TOSERVER_INTERACT: action="<<(int)action<<", item="
2861 <<item_i<<", pointed="<<pointed.dump()<<std::endl;
2865 verbosestream<<"TOSERVER_INTERACT: "<<player->getName()
2866 <<" tried to interact, but is dead!"<<std::endl;
2870 v3f player_pos = playersao->getLastGoodPosition();
2872 // Update wielded item
2873 playersao->setWieldIndex(item_i);
2875 // Get pointed to node (undefined if not POINTEDTYPE_NODE)
2876 v3s16 p_under = pointed.node_undersurface;
2877 v3s16 p_above = pointed.node_abovesurface;
2879 // Get pointed to object (NULL if not POINTEDTYPE_OBJECT)
2880 ServerActiveObject *pointed_object = NULL;
2881 if(pointed.type == POINTEDTHING_OBJECT)
2883 pointed_object = m_env->getActiveObject(pointed.object_id);
2884 if(pointed_object == NULL)
2886 verbosestream<<"TOSERVER_INTERACT: "
2887 "pointed object is NULL"<<std::endl;
2893 v3f pointed_pos_under = player_pos;
2894 v3f pointed_pos_above = player_pos;
2895 if(pointed.type == POINTEDTHING_NODE)
2897 pointed_pos_under = intToFloat(p_under, BS);
2898 pointed_pos_above = intToFloat(p_above, BS);
2900 else if(pointed.type == POINTEDTHING_OBJECT)
2902 pointed_pos_under = pointed_object->getBasePosition();
2903 pointed_pos_above = pointed_pos_under;
2907 Check that target is reasonably close
2908 (only when digging or placing things)
2910 if(action == 0 || action == 2 || action == 3)
2912 float d = player_pos.getDistanceFrom(pointed_pos_under);
2913 float max_d = BS * 14; // Just some large enough value
2915 actionstream<<"Player "<<player->getName()
2916 <<" tried to access "<<pointed.dump()
2918 <<"d="<<d<<", max_d="<<max_d
2919 <<". ignoring."<<std::endl;
2920 // Re-send block to revert change on client-side
2921 RemoteClient *client = getClient(peer_id);
2922 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2923 client->SetBlockNotSent(blockpos);
2930 Make sure the player is allowed to do it
2932 if(!checkPriv(player->getName(), "interact"))
2934 actionstream<<player->getName()<<" attempted to interact with "
2935 <<pointed.dump()<<" without 'interact' privilege"
2937 // Re-send block to revert change on client-side
2938 RemoteClient *client = getClient(peer_id);
2939 // Digging completed -> under
2941 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2942 client->SetBlockNotSent(blockpos);
2944 // Placement -> above
2946 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
2947 client->SetBlockNotSent(blockpos);
2953 0: start digging or punch object
2957 if(pointed.type == POINTEDTHING_NODE)
2960 NOTE: This can be used in the future to check if
2961 somebody is cheating, by checking the timing.
2963 MapNode n(CONTENT_IGNORE);
2966 n = m_env->getMap().getNode(p_under);
2968 catch(InvalidPositionException &e)
2970 infostream<<"Server: Not punching: Node not found."
2971 <<" Adding block to emerge queue."
2973 m_emerge_queue.addBlock(peer_id,
2974 getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
2976 if(n.getContent() != CONTENT_IGNORE)
2977 scriptapi_node_on_punch(m_lua, p_under, n, playersao);
2979 playersao->noCheatDigStart(p_under);
2981 else if(pointed.type == POINTEDTHING_OBJECT)
2983 // Skip if object has been removed
2984 if(pointed_object->m_removed)
2987 actionstream<<player->getName()<<" punches object "
2988 <<pointed.object_id<<": "
2989 <<pointed_object->getDescription()<<std::endl;
2991 ItemStack punchitem = playersao->getWieldedItem();
2992 ToolCapabilities toolcap =
2993 punchitem.getToolCapabilities(m_itemdef);
2994 v3f dir = (pointed_object->getBasePosition() -
2995 (player->getPosition() + player->getEyeOffset())
2997 float time_from_last_punch =
2998 playersao->resetTimeFromLastPunch();
2999 pointed_object->punch(dir, &toolcap, playersao,
3000 time_from_last_punch);
3008 else if(action == 1)
3013 2: Digging completed
3015 else if(action == 2)
3017 // Only digging of nodes
3018 if(pointed.type == POINTEDTHING_NODE)
3020 MapNode n(CONTENT_IGNORE);
3023 n = m_env->getMap().getNode(p_under);
3025 catch(InvalidPositionException &e)
3027 infostream<<"Server: Not finishing digging: Node not found."
3028 <<" Adding block to emerge queue."
3030 m_emerge_queue.addBlock(peer_id,
3031 getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
3034 /* Cheat prevention */
3035 bool is_valid_dig = true;
3036 if(!isSingleplayer() && !g_settings->getBool("disable_anticheat"))
3038 v3s16 nocheat_p = playersao->getNoCheatDigPos();
3039 float nocheat_t = playersao->getNoCheatDigTime();
3040 playersao->noCheatDigEnd();
3041 // If player didn't start digging this, ignore dig
3042 if(nocheat_p != p_under){
3043 infostream<<"Server: NoCheat: "<<player->getName()
3044 <<" started digging "
3045 <<PP(nocheat_p)<<" and completed digging "
3046 <<PP(p_under)<<"; not digging."<<std::endl;
3047 is_valid_dig = false;
3049 // Get player's wielded item
3050 ItemStack playeritem;
3051 InventoryList *mlist = playersao->getInventory()->getList("main");
3053 playeritem = mlist->getItem(playersao->getWieldIndex());
3054 ToolCapabilities playeritem_toolcap =
3055 playeritem.getToolCapabilities(m_itemdef);
3056 // Get diggability and expected digging time
3057 DigParams params = getDigParams(m_nodedef->get(n).groups,
3058 &playeritem_toolcap);
3059 // If can't dig, try hand
3060 if(!params.diggable){
3061 const ItemDefinition &hand = m_itemdef->get("");
3062 const ToolCapabilities *tp = hand.tool_capabilities;
3064 params = getDigParams(m_nodedef->get(n).groups, tp);
3066 // If can't dig, ignore dig
3067 if(!params.diggable){
3068 infostream<<"Server: NoCheat: "<<player->getName()
3069 <<" completed digging "<<PP(p_under)
3070 <<", which is not diggable with tool. not digging."
3072 is_valid_dig = false;
3074 // If time is considerably too short, ignore dig
3075 // Check time only for medium and slow timed digs
3076 if(params.diggable && params.time > 0.3 && nocheat_t < 0.5 * params.time){
3077 infostream<<"Server: NoCheat: "<<player->getName()
3078 <<" completed digging "
3079 <<PP(p_under)<<" in "<<nocheat_t<<"s; expected "
3080 <<params.time<<"s; not digging."<<std::endl;
3081 is_valid_dig = false;
3085 /* Actually dig node */
3087 if(is_valid_dig && n.getContent() != CONTENT_IGNORE)
3088 scriptapi_node_on_dig(m_lua, p_under, n, playersao);
3090 // Send unusual result (that is, node not being removed)
3091 if(m_env->getMap().getNodeNoEx(p_under).getContent() != CONTENT_AIR)
3093 // Re-send block to revert change on client-side
3094 RemoteClient *client = getClient(peer_id);
3095 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
3096 client->SetBlockNotSent(blockpos);
3102 3: place block or right-click object
3104 else if(action == 3)
3106 ItemStack item = playersao->getWieldedItem();
3108 // Reset build time counter
3109 if(pointed.type == POINTEDTHING_NODE &&
3110 item.getDefinition(m_itemdef).type == ITEM_NODE)
3111 getClient(peer_id)->m_time_from_building = 0.0;
3113 if(pointed.type == POINTEDTHING_OBJECT)
3115 // Right click object
3117 // Skip if object has been removed
3118 if(pointed_object->m_removed)
3121 actionstream<<player->getName()<<" right-clicks object "
3122 <<pointed.object_id<<": "
3123 <<pointed_object->getDescription()<<std::endl;
3126 pointed_object->rightClick(playersao);
3128 else if(scriptapi_item_on_place(m_lua,
3129 item, playersao, pointed))
3131 // Placement was handled in lua
3133 // Apply returned ItemStack
3134 playersao->setWieldedItem(item);
3137 // If item has node placement prediction, always send the above
3138 // node to make sure the client knows what exactly happened
3139 if(item.getDefinition(m_itemdef).node_placement_prediction != ""){
3140 RemoteClient *client = getClient(peer_id);
3141 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
3142 client->SetBlockNotSent(blockpos);
3149 else if(action == 4)
3151 ItemStack item = playersao->getWieldedItem();
3153 actionstream<<player->getName()<<" uses "<<item.name
3154 <<", pointing at "<<pointed.dump()<<std::endl;
3156 if(scriptapi_item_on_use(m_lua,
3157 item, playersao, pointed))
3159 // Apply returned ItemStack
3160 playersao->setWieldedItem(item);
3166 Catch invalid actions
3170 infostream<<"WARNING: Server: Invalid action "
3171 <<action<<std::endl;
3174 else if(command == TOSERVER_REMOVED_SOUNDS)
3176 std::string datastring((char*)&data[2], datasize-2);
3177 std::istringstream is(datastring, std::ios_base::binary);
3179 int num = readU16(is);
3180 for(int k=0; k<num; k++){
3181 s32 id = readS32(is);
3182 std::map<s32, ServerPlayingSound>::iterator i =
3183 m_playing_sounds.find(id);
3184 if(i == m_playing_sounds.end())
3186 ServerPlayingSound &psound = i->second;
3187 psound.clients.erase(peer_id);
3188 if(psound.clients.size() == 0)
3189 m_playing_sounds.erase(i++);
3192 else if(command == TOSERVER_NODEMETA_FIELDS)
3194 std::string datastring((char*)&data[2], datasize-2);
3195 std::istringstream is(datastring, std::ios_base::binary);
3197 v3s16 p = readV3S16(is);
3198 std::string formname = deSerializeString(is);
3199 int num = readU16(is);
3200 std::map<std::string, std::string> fields;
3201 for(int k=0; k<num; k++){
3202 std::string fieldname = deSerializeString(is);
3203 std::string fieldvalue = deSerializeLongString(is);
3204 fields[fieldname] = fieldvalue;
3207 scriptapi_node_on_receive_fields(m_lua, p, formname, fields,
3210 else if(command == TOSERVER_INVENTORY_FIELDS)
3212 std::string datastring((char*)&data[2], datasize-2);
3213 std::istringstream is(datastring, std::ios_base::binary);
3215 std::string formname = deSerializeString(is);
3216 int num = readU16(is);
3217 std::map<std::string, std::string> fields;
3218 for(int k=0; k<num; k++){
3219 std::string fieldname = deSerializeString(is);
3220 std::string fieldvalue = deSerializeLongString(is);
3221 fields[fieldname] = fieldvalue;
3224 scriptapi_on_player_receive_fields(m_lua, playersao, formname, fields);
3228 infostream<<"Server::ProcessData(): Ignoring "
3229 "unknown command "<<command<<std::endl;
3233 catch(SendFailedException &e)
3235 errorstream<<"Server::ProcessData(): SendFailedException: "
3241 void Server::onMapEditEvent(MapEditEvent *event)
3243 //infostream<<"Server::onMapEditEvent()"<<std::endl;
3244 if(m_ignore_map_edit_events)
3246 if(m_ignore_map_edit_events_area.contains(event->getArea()))
3248 MapEditEvent *e = event->clone();
3249 m_unsent_map_edit_queue.push_back(e);
3252 Inventory* Server::getInventory(const InventoryLocation &loc)
3255 case InventoryLocation::UNDEFINED:
3258 case InventoryLocation::CURRENT_PLAYER:
3261 case InventoryLocation::PLAYER:
3263 Player *player = m_env->getPlayer(loc.name.c_str());
3266 PlayerSAO *playersao = player->getPlayerSAO();
3269 return playersao->getInventory();
3272 case InventoryLocation::NODEMETA:
3274 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
3277 return meta->getInventory();
3280 case InventoryLocation::DETACHED:
3282 if(m_detached_inventories.count(loc.name) == 0)
3284 return m_detached_inventories[loc.name];
3292 void Server::setInventoryModified(const InventoryLocation &loc)
3295 case InventoryLocation::UNDEFINED:
3298 case InventoryLocation::PLAYER:
3300 Player *player = m_env->getPlayer(loc.name.c_str());
3303 PlayerSAO *playersao = player->getPlayerSAO();
3306 playersao->m_inventory_not_sent = true;
3307 playersao->m_wielded_item_not_sent = true;
3310 case InventoryLocation::NODEMETA:
3312 v3s16 blockpos = getNodeBlockPos(loc.p);
3314 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3316 block->raiseModified(MOD_STATE_WRITE_NEEDED);
3318 setBlockNotSent(blockpos);
3321 case InventoryLocation::DETACHED:
3323 sendDetachedInventoryToAll(loc.name);
3331 core::list<PlayerInfo> Server::getPlayerInfo()
3333 DSTACK(__FUNCTION_NAME);
3334 JMutexAutoLock envlock(m_env_mutex);
3335 JMutexAutoLock conlock(m_con_mutex);
3337 core::list<PlayerInfo> list;
3339 core::list<Player*> players = m_env->getPlayers();
3341 core::list<Player*>::Iterator i;
3342 for(i = players.begin();
3343 i != players.end(); i++)
3347 Player *player = *i;
3350 // Copy info from connection to info struct
3351 info.id = player->peer_id;
3352 info.address = m_con.GetPeerAddress(player->peer_id);
3353 info.avg_rtt = m_con.GetPeerAvgRTT(player->peer_id);
3355 catch(con::PeerNotFoundException &e)
3357 // Set dummy peer info
3359 info.address = Address(0,0,0,0,0);
3363 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3364 info.position = player->getPosition();
3366 list.push_back(info);
3373 void Server::peerAdded(con::Peer *peer)
3375 DSTACK(__FUNCTION_NAME);
3376 verbosestream<<"Server::peerAdded(): peer->id="
3377 <<peer->id<<std::endl;
3380 c.type = PEER_ADDED;
3381 c.peer_id = peer->id;
3383 m_peer_change_queue.push_back(c);
3386 void Server::deletingPeer(con::Peer *peer, bool timeout)
3388 DSTACK(__FUNCTION_NAME);
3389 verbosestream<<"Server::deletingPeer(): peer->id="
3390 <<peer->id<<", timeout="<<timeout<<std::endl;
3393 c.type = PEER_REMOVED;
3394 c.peer_id = peer->id;
3395 c.timeout = timeout;
3396 m_peer_change_queue.push_back(c);
3403 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3405 DSTACK(__FUNCTION_NAME);
3406 std::ostringstream os(std::ios_base::binary);
3408 writeU16(os, TOCLIENT_HP);
3412 std::string s = os.str();
3413 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3415 con.Send(peer_id, 0, data, true);
3418 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3419 const std::wstring &reason)
3421 DSTACK(__FUNCTION_NAME);
3422 std::ostringstream os(std::ios_base::binary);
3424 writeU16(os, TOCLIENT_ACCESS_DENIED);
3425 os<<serializeWideString(reason);
3428 std::string s = os.str();
3429 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3431 con.Send(peer_id, 0, data, true);
3434 void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
3435 bool set_camera_point_target, v3f camera_point_target)
3437 DSTACK(__FUNCTION_NAME);
3438 std::ostringstream os(std::ios_base::binary);
3440 writeU16(os, TOCLIENT_DEATHSCREEN);
3441 writeU8(os, set_camera_point_target);
3442 writeV3F1000(os, camera_point_target);
3445 std::string s = os.str();
3446 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3448 con.Send(peer_id, 0, data, true);
3451 void Server::SendItemDef(con::Connection &con, u16 peer_id,
3452 IItemDefManager *itemdef)
3454 DSTACK(__FUNCTION_NAME);
3455 std::ostringstream os(std::ios_base::binary);
3459 u32 length of the next item
3460 zlib-compressed serialized ItemDefManager
3462 writeU16(os, TOCLIENT_ITEMDEF);
3463 std::ostringstream tmp_os(std::ios::binary);
3464 itemdef->serialize(tmp_os);
3465 std::ostringstream tmp_os2(std::ios::binary);
3466 compressZlib(tmp_os.str(), tmp_os2);
3467 os<<serializeLongString(tmp_os2.str());
3470 std::string s = os.str();
3471 verbosestream<<"Server: Sending item definitions to id("<<peer_id
3472 <<"): size="<<s.size()<<std::endl;
3473 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3475 con.Send(peer_id, 0, data, true);
3478 void Server::SendNodeDef(con::Connection &con, u16 peer_id,
3479 INodeDefManager *nodedef)
3481 DSTACK(__FUNCTION_NAME);
3482 std::ostringstream os(std::ios_base::binary);
3486 u32 length of the next item
3487 zlib-compressed serialized NodeDefManager
3489 writeU16(os, TOCLIENT_NODEDEF);
3490 std::ostringstream tmp_os(std::ios::binary);
3491 nodedef->serialize(tmp_os);
3492 std::ostringstream tmp_os2(std::ios::binary);
3493 compressZlib(tmp_os.str(), tmp_os2);
3494 os<<serializeLongString(tmp_os2.str());
3497 std::string s = os.str();
3498 verbosestream<<"Server: Sending node definitions to id("<<peer_id
3499 <<"): size="<<s.size()<<std::endl;
3500 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3502 con.Send(peer_id, 0, data, true);
3506 Non-static send methods
3509 void Server::SendInventory(u16 peer_id)
3511 DSTACK(__FUNCTION_NAME);
3513 PlayerSAO *playersao = getPlayerSAO(peer_id);
3516 playersao->m_inventory_not_sent = false;
3522 std::ostringstream os;
3523 playersao->getInventory()->serialize(os);
3525 std::string s = os.str();
3527 SharedBuffer<u8> data(s.size()+2);
3528 writeU16(&data[0], TOCLIENT_INVENTORY);
3529 memcpy(&data[2], s.c_str(), s.size());
3532 m_con.Send(peer_id, 0, data, true);
3535 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3537 DSTACK(__FUNCTION_NAME);
3539 std::ostringstream os(std::ios_base::binary);
3543 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3544 os.write((char*)buf, 2);
3547 writeU16(buf, message.size());
3548 os.write((char*)buf, 2);
3551 for(u32 i=0; i<message.size(); i++)
3555 os.write((char*)buf, 2);
3559 std::string s = os.str();
3560 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3562 m_con.Send(peer_id, 0, data, true);
3565 void Server::BroadcastChatMessage(const std::wstring &message)
3567 for(core::map<u16, RemoteClient*>::Iterator
3568 i = m_clients.getIterator();
3569 i.atEnd() == false; i++)
3571 // Get client and check that it is valid
3572 RemoteClient *client = i.getNode()->getValue();
3573 assert(client->peer_id == i.getNode()->getKey());
3574 if(client->serialization_version == SER_FMT_VER_INVALID)
3577 SendChatMessage(client->peer_id, message);
3581 void Server::SendPlayerHP(u16 peer_id)
3583 DSTACK(__FUNCTION_NAME);
3584 PlayerSAO *playersao = getPlayerSAO(peer_id);
3586 playersao->m_hp_not_sent = false;
3587 SendHP(m_con, peer_id, playersao->getHP());
3590 void Server::SendMovePlayer(u16 peer_id)
3592 DSTACK(__FUNCTION_NAME);
3593 Player *player = m_env->getPlayer(peer_id);
3596 std::ostringstream os(std::ios_base::binary);
3597 writeU16(os, TOCLIENT_MOVE_PLAYER);
3598 writeV3F1000(os, player->getPosition());
3599 writeF1000(os, player->getPitch());
3600 writeF1000(os, player->getYaw());
3603 v3f pos = player->getPosition();
3604 f32 pitch = player->getPitch();
3605 f32 yaw = player->getYaw();
3606 verbosestream<<"Server: Sending TOCLIENT_MOVE_PLAYER"
3607 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3614 std::string s = os.str();
3615 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3617 m_con.Send(peer_id, 0, data, true);
3620 void Server::SendPlayerPrivileges(u16 peer_id)
3622 Player *player = m_env->getPlayer(peer_id);
3624 if(player->peer_id == PEER_ID_INEXISTENT)
3627 std::set<std::string> privs;
3628 scriptapi_get_auth(m_lua, player->getName(), NULL, &privs);
3630 std::ostringstream os(std::ios_base::binary);
3631 writeU16(os, TOCLIENT_PRIVILEGES);
3632 writeU16(os, privs.size());
3633 for(std::set<std::string>::const_iterator i = privs.begin();
3634 i != privs.end(); i++){
3635 os<<serializeString(*i);
3639 std::string s = os.str();
3640 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3642 m_con.Send(peer_id, 0, data, true);
3645 void Server::SendPlayerInventoryFormspec(u16 peer_id)
3647 Player *player = m_env->getPlayer(peer_id);
3649 if(player->peer_id == PEER_ID_INEXISTENT)
3652 std::ostringstream os(std::ios_base::binary);
3653 writeU16(os, TOCLIENT_INVENTORY_FORMSPEC);
3654 os<<serializeLongString(player->inventory_formspec);
3657 std::string s = os.str();
3658 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3660 m_con.Send(peer_id, 0, data, true);
3663 s32 Server::playSound(const SimpleSoundSpec &spec,
3664 const ServerSoundParams ¶ms)
3666 // Find out initial position of sound
3667 bool pos_exists = false;
3668 v3f pos = params.getPos(m_env, &pos_exists);
3669 // If position is not found while it should be, cancel sound
3670 if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
3672 // Filter destination clients
3673 std::set<RemoteClient*> dst_clients;
3674 if(params.to_player != "")
3676 Player *player = m_env->getPlayer(params.to_player.c_str());
3678 infostream<<"Server::playSound: Player \""<<params.to_player
3679 <<"\" not found"<<std::endl;
3682 if(player->peer_id == PEER_ID_INEXISTENT){
3683 infostream<<"Server::playSound: Player \""<<params.to_player
3684 <<"\" not connected"<<std::endl;
3687 RemoteClient *client = getClient(player->peer_id);
3688 dst_clients.insert(client);
3692 for(core::map<u16, RemoteClient*>::Iterator
3693 i = m_clients.getIterator(); i.atEnd() == false; i++)
3695 RemoteClient *client = i.getNode()->getValue();
3696 Player *player = m_env->getPlayer(client->peer_id);
3700 if(player->getPosition().getDistanceFrom(pos) >
3701 params.max_hear_distance)
3704 dst_clients.insert(client);
3707 if(dst_clients.size() == 0)
3710 s32 id = m_next_sound_id++;
3711 // The sound will exist as a reference in m_playing_sounds
3712 m_playing_sounds[id] = ServerPlayingSound();
3713 ServerPlayingSound &psound = m_playing_sounds[id];
3714 psound.params = params;
3715 for(std::set<RemoteClient*>::iterator i = dst_clients.begin();
3716 i != dst_clients.end(); i++)
3717 psound.clients.insert((*i)->peer_id);
3719 std::ostringstream os(std::ios_base::binary);
3720 writeU16(os, TOCLIENT_PLAY_SOUND);
3722 os<<serializeString(spec.name);
3723 writeF1000(os, spec.gain * params.gain);
3724 writeU8(os, params.type);
3725 writeV3F1000(os, pos);
3726 writeU16(os, params.object);
3727 writeU8(os, params.loop);
3729 std::string s = os.str();
3730 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3732 for(std::set<RemoteClient*>::iterator i = dst_clients.begin();
3733 i != dst_clients.end(); i++){
3735 m_con.Send((*i)->peer_id, 0, data, true);
3739 void Server::stopSound(s32 handle)
3741 // Get sound reference
3742 std::map<s32, ServerPlayingSound>::iterator i =
3743 m_playing_sounds.find(handle);
3744 if(i == m_playing_sounds.end())
3746 ServerPlayingSound &psound = i->second;
3748 std::ostringstream os(std::ios_base::binary);
3749 writeU16(os, TOCLIENT_STOP_SOUND);
3750 writeS32(os, handle);
3752 std::string s = os.str();
3753 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3755 for(std::set<u16>::iterator i = psound.clients.begin();
3756 i != psound.clients.end(); i++){
3758 m_con.Send(*i, 0, data, true);
3760 // Remove sound reference
3761 m_playing_sounds.erase(i);
3764 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3765 core::list<u16> *far_players, float far_d_nodes)
3767 float maxd = far_d_nodes*BS;
3768 v3f p_f = intToFloat(p, BS);
3772 SharedBuffer<u8> reply(replysize);
3773 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3774 writeS16(&reply[2], p.X);
3775 writeS16(&reply[4], p.Y);
3776 writeS16(&reply[6], p.Z);
3778 for(core::map<u16, RemoteClient*>::Iterator
3779 i = m_clients.getIterator();
3780 i.atEnd() == false; i++)
3782 // Get client and check that it is valid
3783 RemoteClient *client = i.getNode()->getValue();
3784 assert(client->peer_id == i.getNode()->getKey());
3785 if(client->serialization_version == SER_FMT_VER_INVALID)
3788 // Don't send if it's the same one
3789 if(client->peer_id == ignore_id)
3795 Player *player = m_env->getPlayer(client->peer_id);
3798 // If player is far away, only set modified blocks not sent
3799 v3f player_pos = player->getPosition();
3800 if(player_pos.getDistanceFrom(p_f) > maxd)
3802 far_players->push_back(client->peer_id);
3809 m_con.Send(client->peer_id, 0, reply, true);
3813 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3814 core::list<u16> *far_players, float far_d_nodes)
3816 float maxd = far_d_nodes*BS;
3817 v3f p_f = intToFloat(p, BS);
3819 for(core::map<u16, RemoteClient*>::Iterator
3820 i = m_clients.getIterator();
3821 i.atEnd() == false; i++)
3823 // Get client and check that it is valid
3824 RemoteClient *client = i.getNode()->getValue();
3825 assert(client->peer_id == i.getNode()->getKey());
3826 if(client->serialization_version == SER_FMT_VER_INVALID)
3829 // Don't send if it's the same one
3830 if(client->peer_id == ignore_id)
3836 Player *player = m_env->getPlayer(client->peer_id);
3839 // If player is far away, only set modified blocks not sent
3840 v3f player_pos = player->getPosition();
3841 if(player_pos.getDistanceFrom(p_f) > maxd)
3843 far_players->push_back(client->peer_id);
3850 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3851 SharedBuffer<u8> reply(replysize);
3852 writeU16(&reply[0], TOCLIENT_ADDNODE);
3853 writeS16(&reply[2], p.X);
3854 writeS16(&reply[4], p.Y);
3855 writeS16(&reply[6], p.Z);
3856 n.serialize(&reply[8], client->serialization_version);
3859 m_con.Send(client->peer_id, 0, reply, true);
3863 void Server::setBlockNotSent(v3s16 p)
3865 for(core::map<u16, RemoteClient*>::Iterator
3866 i = m_clients.getIterator();
3867 i.atEnd()==false; i++)
3869 RemoteClient *client = i.getNode()->getValue();
3870 client->SetBlockNotSent(p);
3874 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3876 DSTACK(__FUNCTION_NAME);
3878 v3s16 p = block->getPos();
3882 bool completely_air = true;
3883 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3884 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3885 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3887 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
3889 completely_air = false;
3890 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
3895 infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
3897 infostream<<"[completely air] ";
3898 infostream<<std::endl;
3902 Create a packet with the block in the right format
3905 std::ostringstream os(std::ios_base::binary);
3906 block->serialize(os, ver, false);
3907 std::string s = os.str();
3908 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3910 u32 replysize = 8 + blockdata.getSize();
3911 SharedBuffer<u8> reply(replysize);
3912 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3913 writeS16(&reply[2], p.X);
3914 writeS16(&reply[4], p.Y);
3915 writeS16(&reply[6], p.Z);
3916 memcpy(&reply[8], *blockdata, blockdata.getSize());
3918 /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3919 <<": \tpacket size: "<<replysize<<std::endl;*/
3924 m_con.Send(peer_id, 1, reply, true);
3927 void Server::SendBlocks(float dtime)
3929 DSTACK(__FUNCTION_NAME);
3931 JMutexAutoLock envlock(m_env_mutex);
3932 JMutexAutoLock conlock(m_con_mutex);
3934 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
3936 core::array<PrioritySortedBlockTransfer> queue;
3938 s32 total_sending = 0;
3941 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
3943 for(core::map<u16, RemoteClient*>::Iterator
3944 i = m_clients.getIterator();
3945 i.atEnd() == false; i++)
3947 RemoteClient *client = i.getNode()->getValue();
3948 assert(client->peer_id == i.getNode()->getKey());
3950 // If definitions and textures have not been sent, don't
3951 // send MapBlocks either
3952 if(!client->definitions_sent)
3955 total_sending += client->SendingCount();
3957 if(client->serialization_version == SER_FMT_VER_INVALID)
3960 client->GetNextBlocks(this, dtime, queue);
3965 // Lowest priority number comes first.
3966 // Lowest is most important.
3969 for(u32 i=0; i<queue.size(); i++)
3971 //TODO: Calculate limit dynamically
3972 if(total_sending >= g_settings->getS32
3973 ("max_simultaneous_block_sends_server_total"))
3976 PrioritySortedBlockTransfer q = queue[i];
3978 MapBlock *block = NULL;
3981 block = m_env->getMap().getBlockNoCreate(q.pos);
3983 catch(InvalidPositionException &e)
3988 RemoteClient *client = getClient(q.peer_id);
3990 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3992 client->SentBlock(q.pos);
3998 void Server::fillMediaCache()
4000 DSTACK(__FUNCTION_NAME);
4002 infostream<<"Server: Calculating media file checksums"<<std::endl;
4004 // Collect all media file paths
4005 std::list<std::string> paths;
4006 for(core::list<ModSpec>::Iterator i = m_mods.begin();
4007 i != m_mods.end(); i++){
4008 const ModSpec &mod = *i;
4009 paths.push_back(mod.path + DIR_DELIM + "textures");
4010 paths.push_back(mod.path + DIR_DELIM + "sounds");
4011 paths.push_back(mod.path + DIR_DELIM + "media");
4013 std::string path_all = "textures";
4014 paths.push_back(path_all + DIR_DELIM + "all");
4016 // Collect media file information from paths into cache
4017 for(std::list<std::string>::iterator i = paths.begin();
4018 i != paths.end(); i++)
4020 std::string mediapath = *i;
4021 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
4022 for(u32 j=0; j<dirlist.size(); j++){
4023 if(dirlist[j].dir) // Ignode dirs
4025 std::string filename = dirlist[j].name;
4026 // If name contains illegal characters, ignore the file
4027 if(!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)){
4028 infostream<<"Server: ignoring illegal file name: \""
4029 <<filename<<"\""<<std::endl;
4032 // If name is not in a supported format, ignore it
4033 const char *supported_ext[] = {
4034 ".png", ".jpg", ".bmp", ".tga",
4035 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
4039 if(removeStringEnd(filename, supported_ext) == ""){
4040 infostream<<"Server: ignoring unsupported file extension: \""
4041 <<filename<<"\""<<std::endl;
4044 // Ok, attempt to load the file and add to cache
4045 std::string filepath = mediapath + DIR_DELIM + filename;
4047 std::ifstream fis(filepath.c_str(), std::ios_base::binary);
4048 if(fis.good() == false){
4049 errorstream<<"Server::fillMediaCache(): Could not open \""
4050 <<filename<<"\" for reading"<<std::endl;
4053 std::ostringstream tmp_os(std::ios_base::binary);
4057 fis.read(buf, 1024);
4058 std::streamsize len = fis.gcount();
4059 tmp_os.write(buf, len);
4068 errorstream<<"Server::fillMediaCache(): Failed to read \""
4069 <<filename<<"\""<<std::endl;
4072 if(tmp_os.str().length() == 0){
4073 errorstream<<"Server::fillMediaCache(): Empty file \""
4074 <<filepath<<"\""<<std::endl;
4079 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
4081 unsigned char *digest = sha1.getDigest();
4082 std::string sha1_base64 = base64_encode(digest, 20);
4083 std::string sha1_hex = hex_encode((char*)digest, 20);
4087 this->m_media[filename] = MediaInfo(filepath, sha1_base64);
4088 verbosestream<<"Server: "<<sha1_hex<<" is "<<filename<<std::endl;
4093 struct SendableMediaAnnouncement
4096 std::string sha1_digest;
4098 SendableMediaAnnouncement(const std::string name_="",
4099 const std::string sha1_digest_=""):
4101 sha1_digest(sha1_digest_)
4105 void Server::sendMediaAnnouncement(u16 peer_id)
4107 DSTACK(__FUNCTION_NAME);
4109 verbosestream<<"Server: Announcing files to id("<<peer_id<<")"
4112 core::list<SendableMediaAnnouncement> file_announcements;
4114 for(std::map<std::string, MediaInfo>::iterator i = m_media.begin();
4115 i != m_media.end(); i++){
4117 file_announcements.push_back(
4118 SendableMediaAnnouncement(i->first, i->second.sha1_digest));
4122 std::ostringstream os(std::ios_base::binary);
4130 u16 length of sha1_digest
4135 writeU16(os, TOCLIENT_ANNOUNCE_MEDIA);
4136 writeU16(os, file_announcements.size());
4138 for(core::list<SendableMediaAnnouncement>::Iterator
4139 j = file_announcements.begin();
4140 j != file_announcements.end(); j++){
4141 os<<serializeString(j->name);
4142 os<<serializeString(j->sha1_digest);
4146 std::string s = os.str();
4147 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4150 m_con.Send(peer_id, 0, data, true);
4154 struct SendableMedia
4160 SendableMedia(const std::string &name_="", const std::string path_="",
4161 const std::string &data_=""):
4168 void Server::sendRequestedMedia(u16 peer_id,
4169 const core::list<MediaRequest> &tosend)
4171 DSTACK(__FUNCTION_NAME);
4173 verbosestream<<"Server::sendRequestedMedia(): "
4174 <<"Sending files to client"<<std::endl;
4178 // Put 5kB in one bunch (this is not accurate)
4179 u32 bytes_per_bunch = 5000;
4181 core::array< core::list<SendableMedia> > file_bunches;
4182 file_bunches.push_back(core::list<SendableMedia>());
4184 u32 file_size_bunch_total = 0;
4186 for(core::list<MediaRequest>::ConstIterator i = tosend.begin();
4187 i != tosend.end(); i++)
4189 if(m_media.find(i->name) == m_media.end()){
4190 errorstream<<"Server::sendRequestedMedia(): Client asked for "
4191 <<"unknown file \""<<(i->name)<<"\""<<std::endl;
4195 //TODO get path + name
4196 std::string tpath = m_media[(*i).name].path;
4199 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
4200 if(fis.good() == false){
4201 errorstream<<"Server::sendRequestedMedia(): Could not open \""
4202 <<tpath<<"\" for reading"<<std::endl;
4205 std::ostringstream tmp_os(std::ios_base::binary);
4209 fis.read(buf, 1024);
4210 std::streamsize len = fis.gcount();
4211 tmp_os.write(buf, len);
4212 file_size_bunch_total += len;
4221 errorstream<<"Server::sendRequestedMedia(): Failed to read \""
4222 <<(*i).name<<"\""<<std::endl;
4225 /*infostream<<"Server::sendRequestedMedia(): Loaded \""
4226 <<tname<<"\""<<std::endl;*/
4228 file_bunches[file_bunches.size()-1].push_back(
4229 SendableMedia((*i).name, tpath, tmp_os.str()));
4231 // Start next bunch if got enough data
4232 if(file_size_bunch_total >= bytes_per_bunch){
4233 file_bunches.push_back(core::list<SendableMedia>());
4234 file_size_bunch_total = 0;
4239 /* Create and send packets */
4241 u32 num_bunches = file_bunches.size();
4242 for(u32 i=0; i<num_bunches; i++)
4244 std::ostringstream os(std::ios_base::binary);
4248 u16 total number of texture bunches
4249 u16 index of this bunch
4250 u32 number of files in this bunch
4259 writeU16(os, TOCLIENT_MEDIA);
4260 writeU16(os, num_bunches);
4262 writeU32(os, file_bunches[i].size());
4264 for(core::list<SendableMedia>::Iterator
4265 j = file_bunches[i].begin();
4266 j != file_bunches[i].end(); j++){
4267 os<<serializeString(j->name);
4268 os<<serializeLongString(j->data);
4272 std::string s = os.str();
4273 verbosestream<<"Server::sendRequestedMedia(): bunch "
4274 <<i<<"/"<<num_bunches
4275 <<" files="<<file_bunches[i].size()
4276 <<" size=" <<s.size()<<std::endl;
4277 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4279 m_con.Send(peer_id, 0, data, true);
4283 void Server::sendDetachedInventory(const std::string &name, u16 peer_id)
4285 if(m_detached_inventories.count(name) == 0){
4286 errorstream<<__FUNCTION_NAME<<": \""<<name<<"\" not found"<<std::endl;
4289 Inventory *inv = m_detached_inventories[name];
4291 std::ostringstream os(std::ios_base::binary);
4292 writeU16(os, TOCLIENT_DETACHED_INVENTORY);
4293 os<<serializeString(name);
4297 std::string s = os.str();
4298 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4300 m_con.Send(peer_id, 0, data, true);
4303 void Server::sendDetachedInventoryToAll(const std::string &name)
4305 DSTACK(__FUNCTION_NAME);
4307 for(core::map<u16, RemoteClient*>::Iterator
4308 i = m_clients.getIterator();
4309 i.atEnd() == false; i++){
4310 RemoteClient *client = i.getNode()->getValue();
4311 sendDetachedInventory(name, client->peer_id);
4315 void Server::sendDetachedInventories(u16 peer_id)
4317 DSTACK(__FUNCTION_NAME);
4319 for(std::map<std::string, Inventory*>::iterator
4320 i = m_detached_inventories.begin();
4321 i != m_detached_inventories.end(); i++){
4322 const std::string &name = i->first;
4323 //Inventory *inv = i->second;
4324 sendDetachedInventory(name, peer_id);
4332 void Server::DiePlayer(u16 peer_id)
4334 DSTACK(__FUNCTION_NAME);
4336 PlayerSAO *playersao = getPlayerSAO(peer_id);
4339 infostream<<"Server::DiePlayer(): Player "
4340 <<playersao->getPlayer()->getName()
4341 <<" dies"<<std::endl;
4343 playersao->setHP(0);
4345 // Trigger scripted stuff
4346 scriptapi_on_dieplayer(m_lua, playersao);
4348 SendPlayerHP(peer_id);
4349 SendDeathscreen(m_con, peer_id, false, v3f(0,0,0));
4352 void Server::RespawnPlayer(u16 peer_id)
4354 DSTACK(__FUNCTION_NAME);
4356 PlayerSAO *playersao = getPlayerSAO(peer_id);
4359 infostream<<"Server::RespawnPlayer(): Player "
4360 <<playersao->getPlayer()->getName()
4361 <<" respawns"<<std::endl;
4363 playersao->setHP(PLAYER_MAX_HP);
4365 bool repositioned = scriptapi_on_respawnplayer(m_lua, playersao);
4367 v3f pos = findSpawnPos(m_env->getServerMap());
4368 playersao->setPos(pos);
4372 void Server::UpdateCrafting(u16 peer_id)
4374 DSTACK(__FUNCTION_NAME);
4376 Player* player = m_env->getPlayer(peer_id);
4379 // Get a preview for crafting
4381 getCraftingResult(&player->inventory, preview, false, this);
4383 // Put the new preview in
4384 InventoryList *plist = player->inventory.getList("craftpreview");
4386 assert(plist->getSize() >= 1);
4387 plist->changeItem(0, preview);
4390 RemoteClient* Server::getClient(u16 peer_id)
4392 DSTACK(__FUNCTION_NAME);
4393 //JMutexAutoLock lock(m_con_mutex);
4394 core::map<u16, RemoteClient*>::Node *n;
4395 n = m_clients.find(peer_id);
4396 // A client should exist for all peers
4398 return n->getValue();
4401 std::wstring Server::getStatusString()
4403 std::wostringstream os(std::ios_base::binary);
4406 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4408 os<<L", uptime="<<m_uptime.get();
4409 // Information about clients
4411 for(core::map<u16, RemoteClient*>::Iterator
4412 i = m_clients.getIterator();
4413 i.atEnd() == false; i++)
4415 // Get client and check that it is valid
4416 RemoteClient *client = i.getNode()->getValue();
4417 assert(client->peer_id == i.getNode()->getKey());
4418 if(client->serialization_version == SER_FMT_VER_INVALID)
4421 Player *player = m_env->getPlayer(client->peer_id);
4422 // Get name of player
4423 std::wstring name = L"unknown";
4425 name = narrow_to_wide(player->getName());
4426 // Add name to information string
4430 if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
4431 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4432 if(g_settings->get("motd") != "")
4433 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4437 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
4439 std::set<std::string> privs;
4440 scriptapi_get_auth(m_lua, name, NULL, &privs);
4444 bool Server::checkPriv(const std::string &name, const std::string &priv)
4446 std::set<std::string> privs = getPlayerEffectivePrivs(name);
4447 return (privs.count(priv) != 0);
4450 void Server::reportPrivsModified(const std::string &name)
4453 for(core::map<u16, RemoteClient*>::Iterator
4454 i = m_clients.getIterator();
4455 i.atEnd() == false; i++){
4456 RemoteClient *client = i.getNode()->getValue();
4457 Player *player = m_env->getPlayer(client->peer_id);
4458 reportPrivsModified(player->getName());
4461 Player *player = m_env->getPlayer(name.c_str());
4464 SendPlayerPrivileges(player->peer_id);
4465 PlayerSAO *sao = player->getPlayerSAO();
4468 sao->updatePrivileges(
4469 getPlayerEffectivePrivs(name),
4474 void Server::reportInventoryFormspecModified(const std::string &name)
4476 Player *player = m_env->getPlayer(name.c_str());
4479 SendPlayerInventoryFormspec(player->peer_id);
4482 // Saves g_settings to configpath given at initialization
4483 void Server::saveConfig()
4485 if(m_path_config != "")
4486 g_settings->updateConfigFile(m_path_config.c_str());
4489 void Server::notifyPlayer(const char *name, const std::wstring msg)
4491 Player *player = m_env->getPlayer(name);
4494 SendChatMessage(player->peer_id, std::wstring(L"Server: -!- ")+msg);
4497 void Server::notifyPlayers(const std::wstring msg)
4499 BroadcastChatMessage(msg);
4502 void Server::queueBlockEmerge(v3s16 blockpos, bool allow_generate)
4506 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
4507 m_emerge_queue.addBlock(PEER_ID_INEXISTENT, blockpos, flags);
4510 Inventory* Server::createDetachedInventory(const std::string &name)
4512 if(m_detached_inventories.count(name) > 0){
4513 infostream<<"Server clearing detached inventory \""<<name<<"\""<<std::endl;
4514 delete m_detached_inventories[name];
4516 infostream<<"Server creating detached inventory \""<<name<<"\""<<std::endl;
4518 Inventory *inv = new Inventory(m_itemdef);
4520 m_detached_inventories[name] = inv;
4521 sendDetachedInventoryToAll(name);
4525 // IGameDef interface
4527 IItemDefManager* Server::getItemDefManager()
4531 INodeDefManager* Server::getNodeDefManager()
4535 ICraftDefManager* Server::getCraftDefManager()
4539 ITextureSource* Server::getTextureSource()
4543 u16 Server::allocateUnknownNodeId(const std::string &name)
4545 return m_nodedef->allocateDummy(name);
4547 ISoundManager* Server::getSoundManager()
4549 return &dummySoundManager;
4551 MtEventManager* Server::getEventManager()
4556 IWritableItemDefManager* Server::getWritableItemDefManager()
4560 IWritableNodeDefManager* Server::getWritableNodeDefManager()
4564 IWritableCraftDefManager* Server::getWritableCraftDefManager()
4569 const ModSpec* Server::getModSpec(const std::string &modname)
4571 for(core::list<ModSpec>::Iterator i = m_mods.begin();
4572 i != m_mods.end(); i++){
4573 const ModSpec &mod = *i;
4574 if(mod.name == modname)
4579 void Server::getModNames(core::list<std::string> &modlist)
4581 for(core::list<ModSpec>::Iterator i = m_mods.begin(); i != m_mods.end(); i++)
4583 modlist.push_back((*i).name);
4586 std::string Server::getBuiltinLuaPath()
4588 return porting::path_share + DIR_DELIM + "builtin";
4591 v3f findSpawnPos(ServerMap &map)
4593 //return v3f(50,50,50)*BS;
4598 nodepos = v2s16(0,0);
4603 // Try to find a good place a few times
4604 for(s32 i=0; i<1000; i++)
4607 // We're going to try to throw the player to this position
4608 v2s16 nodepos2d = v2s16(-range + (myrand()%(range*2)),
4609 -range + (myrand()%(range*2)));
4610 //v2s16 sectorpos = getNodeSectorPos(nodepos2d);
4611 // Get ground height at point (fallbacks to heightmap function)
4612 s16 groundheight = map.findGroundLevel(nodepos2d);
4613 // Don't go underwater
4614 if(groundheight < WATER_LEVEL)
4616 //infostream<<"-> Underwater"<<std::endl;
4619 // Don't go to high places
4620 if(groundheight > WATER_LEVEL + 4)
4622 //infostream<<"-> Underwater"<<std::endl;
4626 nodepos = v3s16(nodepos2d.X, groundheight-2, nodepos2d.Y);
4627 bool is_good = false;
4629 for(s32 i=0; i<10; i++){
4630 v3s16 blockpos = getNodeBlockPos(nodepos);
4631 map.emergeBlock(blockpos, true);
4632 MapNode n = map.getNodeNoEx(nodepos);
4633 if(n.getContent() == CONTENT_AIR){
4644 // Found a good place
4645 //infostream<<"Searched through "<<i<<" places."<<std::endl;
4651 return intToFloat(nodepos, BS);
4654 PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id)
4656 RemotePlayer *player = NULL;
4657 bool newplayer = false;
4660 Try to get an existing player
4662 player = static_cast<RemotePlayer*>(m_env->getPlayer(name));
4664 // If player is already connected, cancel
4665 if(player != NULL && player->peer_id != 0)
4667 infostream<<"emergePlayer(): Player already connected"<<std::endl;
4672 If player with the wanted peer_id already exists, cancel.
4674 if(m_env->getPlayer(peer_id) != NULL)
4676 infostream<<"emergePlayer(): Player with wrong name but same"
4677 " peer_id already exists"<<std::endl;
4682 Create a new player if it doesn't exist yet
4687 player = new RemotePlayer(this);
4688 player->updateName(name);
4690 /* Set player position */
4691 infostream<<"Server: Finding spawn place for player \""
4692 <<name<<"\""<<std::endl;
4693 v3f pos = findSpawnPos(m_env->getServerMap());
4694 player->setPosition(pos);
4696 /* Add player to environment */
4697 m_env->addPlayer(player);
4701 Create a new player active object
4703 PlayerSAO *playersao = new PlayerSAO(m_env, player, peer_id,
4704 getPlayerEffectivePrivs(player->getName()),
4707 /* Add object to environment */
4708 m_env->addActiveObject(playersao);
4712 scriptapi_on_newplayer(m_lua, playersao);
4714 scriptapi_on_joinplayer(m_lua, playersao);
4719 void Server::handlePeerChange(PeerChange &c)
4721 JMutexAutoLock envlock(m_env_mutex);
4722 JMutexAutoLock conlock(m_con_mutex);
4724 if(c.type == PEER_ADDED)
4731 core::map<u16, RemoteClient*>::Node *n;
4732 n = m_clients.find(c.peer_id);
4733 // The client shouldn't already exist
4737 RemoteClient *client = new RemoteClient();
4738 client->peer_id = c.peer_id;
4739 m_clients.insert(client->peer_id, client);
4742 else if(c.type == PEER_REMOVED)
4749 core::map<u16, RemoteClient*>::Node *n;
4750 n = m_clients.find(c.peer_id);
4751 // The client should exist
4755 Mark objects to be not known by the client
4757 RemoteClient *client = n->getValue();
4759 for(core::map<u16, bool>::Iterator
4760 i = client->m_known_objects.getIterator();
4761 i.atEnd()==false; i++)
4764 u16 id = i.getNode()->getKey();
4765 ServerActiveObject* obj = m_env->getActiveObject(id);
4767 if(obj && obj->m_known_by_count > 0)
4768 obj->m_known_by_count--;
4772 Clear references to playing sounds
4774 for(std::map<s32, ServerPlayingSound>::iterator
4775 i = m_playing_sounds.begin();
4776 i != m_playing_sounds.end();)
4778 ServerPlayingSound &psound = i->second;
4779 psound.clients.erase(c.peer_id);
4780 if(psound.clients.size() == 0)
4781 m_playing_sounds.erase(i++);
4786 Player *player = m_env->getPlayer(c.peer_id);
4788 // Collect information about leaving in chat
4789 std::wstring message;
4793 std::wstring name = narrow_to_wide(player->getName());
4796 message += L" left the game.";
4798 message += L" (timed out)";
4802 /* Run scripts and remove from environment */
4806 PlayerSAO *playersao = player->getPlayerSAO();
4809 scriptapi_on_leaveplayer(m_lua, playersao);
4811 playersao->disconnected();
4821 std::ostringstream os(std::ios_base::binary);
4822 for(core::map<u16, RemoteClient*>::Iterator
4823 i = m_clients.getIterator();
4824 i.atEnd() == false; i++)
4826 RemoteClient *client = i.getNode()->getValue();
4827 assert(client->peer_id == i.getNode()->getKey());
4828 if(client->serialization_version == SER_FMT_VER_INVALID)
4831 Player *player = m_env->getPlayer(client->peer_id);
4834 // Get name of player
4835 os<<player->getName()<<" ";
4838 actionstream<<player->getName()<<" "
4839 <<(c.timeout?"times out.":"leaves game.")
4840 <<" List of players: "
4841 <<os.str()<<std::endl;
4846 delete m_clients[c.peer_id];
4847 m_clients.remove(c.peer_id);
4849 // Send player info to all remaining clients
4850 //SendPlayerInfos();
4852 // Send leave chat message to all remaining clients
4853 if(message.length() != 0)
4854 BroadcastChatMessage(message);
4863 void Server::handlePeerChanges()
4865 while(m_peer_change_queue.size() > 0)
4867 PeerChange c = m_peer_change_queue.pop_front();
4869 verbosestream<<"Server: Handling peer change: "
4870 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4873 handlePeerChange(c);
4877 void dedicated_server_loop(Server &server, bool &kill)
4879 DSTACK(__FUNCTION_NAME);
4881 verbosestream<<"dedicated_server_loop()"<<std::endl;
4883 IntervalLimiter m_profiler_interval;
4887 float steplen = g_settings->getFloat("dedicated_server_step");
4888 // This is kind of a hack but can be done like this
4889 // because server.step() is very light
4891 ScopeProfiler sp(g_profiler, "dedicated server sleep");
4892 sleep_ms((int)(steplen*1000.0));
4894 server.step(steplen);
4896 if(server.getShutdownRequested() || kill)
4898 infostream<<"Dedicated server quitting"<<std::endl;
4905 float profiler_print_interval =
4906 g_settings->getFloat("profiler_print_interval");
4907 if(profiler_print_interval != 0)
4909 if(m_profiler_interval.step(steplen, profiler_print_interval))
4911 infostream<<"Profiler:"<<std::endl;
4912 g_profiler->print(infostream);
4913 g_profiler->clear();