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 if(g_settings->getBool("creative_mode") == false)
3135 playersao->setWieldedItem(item);
3138 // If item has node placement prediction, always send the above
3139 // node to make sure the client knows what exactly happened
3140 if(item.getDefinition(m_itemdef).node_placement_prediction != ""){
3141 RemoteClient *client = getClient(peer_id);
3142 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
3143 client->SetBlockNotSent(blockpos);
3150 else if(action == 4)
3152 ItemStack item = playersao->getWieldedItem();
3154 actionstream<<player->getName()<<" uses "<<item.name
3155 <<", pointing at "<<pointed.dump()<<std::endl;
3157 if(scriptapi_item_on_use(m_lua,
3158 item, playersao, pointed))
3160 // Apply returned ItemStack
3161 if(g_settings->getBool("creative_mode") == false)
3162 playersao->setWieldedItem(item);
3168 Catch invalid actions
3172 infostream<<"WARNING: Server: Invalid action "
3173 <<action<<std::endl;
3176 else if(command == TOSERVER_REMOVED_SOUNDS)
3178 std::string datastring((char*)&data[2], datasize-2);
3179 std::istringstream is(datastring, std::ios_base::binary);
3181 int num = readU16(is);
3182 for(int k=0; k<num; k++){
3183 s32 id = readS32(is);
3184 std::map<s32, ServerPlayingSound>::iterator i =
3185 m_playing_sounds.find(id);
3186 if(i == m_playing_sounds.end())
3188 ServerPlayingSound &psound = i->second;
3189 psound.clients.erase(peer_id);
3190 if(psound.clients.size() == 0)
3191 m_playing_sounds.erase(i++);
3194 else if(command == TOSERVER_NODEMETA_FIELDS)
3196 std::string datastring((char*)&data[2], datasize-2);
3197 std::istringstream is(datastring, std::ios_base::binary);
3199 v3s16 p = readV3S16(is);
3200 std::string formname = deSerializeString(is);
3201 int num = readU16(is);
3202 std::map<std::string, std::string> fields;
3203 for(int k=0; k<num; k++){
3204 std::string fieldname = deSerializeString(is);
3205 std::string fieldvalue = deSerializeLongString(is);
3206 fields[fieldname] = fieldvalue;
3209 scriptapi_node_on_receive_fields(m_lua, p, formname, fields,
3212 else if(command == TOSERVER_INVENTORY_FIELDS)
3214 std::string datastring((char*)&data[2], datasize-2);
3215 std::istringstream is(datastring, std::ios_base::binary);
3217 std::string formname = deSerializeString(is);
3218 int num = readU16(is);
3219 std::map<std::string, std::string> fields;
3220 for(int k=0; k<num; k++){
3221 std::string fieldname = deSerializeString(is);
3222 std::string fieldvalue = deSerializeLongString(is);
3223 fields[fieldname] = fieldvalue;
3226 scriptapi_on_player_receive_fields(m_lua, playersao, formname, fields);
3230 infostream<<"Server::ProcessData(): Ignoring "
3231 "unknown command "<<command<<std::endl;
3235 catch(SendFailedException &e)
3237 errorstream<<"Server::ProcessData(): SendFailedException: "
3243 void Server::onMapEditEvent(MapEditEvent *event)
3245 //infostream<<"Server::onMapEditEvent()"<<std::endl;
3246 if(m_ignore_map_edit_events)
3248 if(m_ignore_map_edit_events_area.contains(event->getArea()))
3250 MapEditEvent *e = event->clone();
3251 m_unsent_map_edit_queue.push_back(e);
3254 Inventory* Server::getInventory(const InventoryLocation &loc)
3257 case InventoryLocation::UNDEFINED:
3260 case InventoryLocation::CURRENT_PLAYER:
3263 case InventoryLocation::PLAYER:
3265 Player *player = m_env->getPlayer(loc.name.c_str());
3268 PlayerSAO *playersao = player->getPlayerSAO();
3271 return playersao->getInventory();
3274 case InventoryLocation::NODEMETA:
3276 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
3279 return meta->getInventory();
3282 case InventoryLocation::DETACHED:
3284 if(m_detached_inventories.count(loc.name) == 0)
3286 return m_detached_inventories[loc.name];
3294 void Server::setInventoryModified(const InventoryLocation &loc)
3297 case InventoryLocation::UNDEFINED:
3300 case InventoryLocation::PLAYER:
3302 Player *player = m_env->getPlayer(loc.name.c_str());
3305 PlayerSAO *playersao = player->getPlayerSAO();
3308 playersao->m_inventory_not_sent = true;
3309 playersao->m_wielded_item_not_sent = true;
3312 case InventoryLocation::NODEMETA:
3314 v3s16 blockpos = getNodeBlockPos(loc.p);
3316 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3318 block->raiseModified(MOD_STATE_WRITE_NEEDED);
3320 setBlockNotSent(blockpos);
3323 case InventoryLocation::DETACHED:
3325 sendDetachedInventoryToAll(loc.name);
3333 core::list<PlayerInfo> Server::getPlayerInfo()
3335 DSTACK(__FUNCTION_NAME);
3336 JMutexAutoLock envlock(m_env_mutex);
3337 JMutexAutoLock conlock(m_con_mutex);
3339 core::list<PlayerInfo> list;
3341 core::list<Player*> players = m_env->getPlayers();
3343 core::list<Player*>::Iterator i;
3344 for(i = players.begin();
3345 i != players.end(); i++)
3349 Player *player = *i;
3352 // Copy info from connection to info struct
3353 info.id = player->peer_id;
3354 info.address = m_con.GetPeerAddress(player->peer_id);
3355 info.avg_rtt = m_con.GetPeerAvgRTT(player->peer_id);
3357 catch(con::PeerNotFoundException &e)
3359 // Set dummy peer info
3361 info.address = Address(0,0,0,0,0);
3365 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3366 info.position = player->getPosition();
3368 list.push_back(info);
3375 void Server::peerAdded(con::Peer *peer)
3377 DSTACK(__FUNCTION_NAME);
3378 verbosestream<<"Server::peerAdded(): peer->id="
3379 <<peer->id<<std::endl;
3382 c.type = PEER_ADDED;
3383 c.peer_id = peer->id;
3385 m_peer_change_queue.push_back(c);
3388 void Server::deletingPeer(con::Peer *peer, bool timeout)
3390 DSTACK(__FUNCTION_NAME);
3391 verbosestream<<"Server::deletingPeer(): peer->id="
3392 <<peer->id<<", timeout="<<timeout<<std::endl;
3395 c.type = PEER_REMOVED;
3396 c.peer_id = peer->id;
3397 c.timeout = timeout;
3398 m_peer_change_queue.push_back(c);
3405 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3407 DSTACK(__FUNCTION_NAME);
3408 std::ostringstream os(std::ios_base::binary);
3410 writeU16(os, TOCLIENT_HP);
3414 std::string s = os.str();
3415 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3417 con.Send(peer_id, 0, data, true);
3420 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3421 const std::wstring &reason)
3423 DSTACK(__FUNCTION_NAME);
3424 std::ostringstream os(std::ios_base::binary);
3426 writeU16(os, TOCLIENT_ACCESS_DENIED);
3427 os<<serializeWideString(reason);
3430 std::string s = os.str();
3431 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3433 con.Send(peer_id, 0, data, true);
3436 void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
3437 bool set_camera_point_target, v3f camera_point_target)
3439 DSTACK(__FUNCTION_NAME);
3440 std::ostringstream os(std::ios_base::binary);
3442 writeU16(os, TOCLIENT_DEATHSCREEN);
3443 writeU8(os, set_camera_point_target);
3444 writeV3F1000(os, camera_point_target);
3447 std::string s = os.str();
3448 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3450 con.Send(peer_id, 0, data, true);
3453 void Server::SendItemDef(con::Connection &con, u16 peer_id,
3454 IItemDefManager *itemdef)
3456 DSTACK(__FUNCTION_NAME);
3457 std::ostringstream os(std::ios_base::binary);
3461 u32 length of the next item
3462 zlib-compressed serialized ItemDefManager
3464 writeU16(os, TOCLIENT_ITEMDEF);
3465 std::ostringstream tmp_os(std::ios::binary);
3466 itemdef->serialize(tmp_os);
3467 std::ostringstream tmp_os2(std::ios::binary);
3468 compressZlib(tmp_os.str(), tmp_os2);
3469 os<<serializeLongString(tmp_os2.str());
3472 std::string s = os.str();
3473 verbosestream<<"Server: Sending item definitions to id("<<peer_id
3474 <<"): size="<<s.size()<<std::endl;
3475 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3477 con.Send(peer_id, 0, data, true);
3480 void Server::SendNodeDef(con::Connection &con, u16 peer_id,
3481 INodeDefManager *nodedef)
3483 DSTACK(__FUNCTION_NAME);
3484 std::ostringstream os(std::ios_base::binary);
3488 u32 length of the next item
3489 zlib-compressed serialized NodeDefManager
3491 writeU16(os, TOCLIENT_NODEDEF);
3492 std::ostringstream tmp_os(std::ios::binary);
3493 nodedef->serialize(tmp_os);
3494 std::ostringstream tmp_os2(std::ios::binary);
3495 compressZlib(tmp_os.str(), tmp_os2);
3496 os<<serializeLongString(tmp_os2.str());
3499 std::string s = os.str();
3500 verbosestream<<"Server: Sending node definitions to id("<<peer_id
3501 <<"): size="<<s.size()<<std::endl;
3502 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3504 con.Send(peer_id, 0, data, true);
3508 Non-static send methods
3511 void Server::SendInventory(u16 peer_id)
3513 DSTACK(__FUNCTION_NAME);
3515 PlayerSAO *playersao = getPlayerSAO(peer_id);
3518 playersao->m_inventory_not_sent = false;
3524 std::ostringstream os;
3525 playersao->getInventory()->serialize(os);
3527 std::string s = os.str();
3529 SharedBuffer<u8> data(s.size()+2);
3530 writeU16(&data[0], TOCLIENT_INVENTORY);
3531 memcpy(&data[2], s.c_str(), s.size());
3534 m_con.Send(peer_id, 0, data, true);
3537 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3539 DSTACK(__FUNCTION_NAME);
3541 std::ostringstream os(std::ios_base::binary);
3545 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3546 os.write((char*)buf, 2);
3549 writeU16(buf, message.size());
3550 os.write((char*)buf, 2);
3553 for(u32 i=0; i<message.size(); i++)
3557 os.write((char*)buf, 2);
3561 std::string s = os.str();
3562 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3564 m_con.Send(peer_id, 0, data, true);
3567 void Server::BroadcastChatMessage(const std::wstring &message)
3569 for(core::map<u16, RemoteClient*>::Iterator
3570 i = m_clients.getIterator();
3571 i.atEnd() == false; i++)
3573 // Get client and check that it is valid
3574 RemoteClient *client = i.getNode()->getValue();
3575 assert(client->peer_id == i.getNode()->getKey());
3576 if(client->serialization_version == SER_FMT_VER_INVALID)
3579 SendChatMessage(client->peer_id, message);
3583 void Server::SendPlayerHP(u16 peer_id)
3585 DSTACK(__FUNCTION_NAME);
3586 PlayerSAO *playersao = getPlayerSAO(peer_id);
3588 playersao->m_hp_not_sent = false;
3589 SendHP(m_con, peer_id, playersao->getHP());
3592 void Server::SendMovePlayer(u16 peer_id)
3594 DSTACK(__FUNCTION_NAME);
3595 Player *player = m_env->getPlayer(peer_id);
3598 std::ostringstream os(std::ios_base::binary);
3599 writeU16(os, TOCLIENT_MOVE_PLAYER);
3600 writeV3F1000(os, player->getPosition());
3601 writeF1000(os, player->getPitch());
3602 writeF1000(os, player->getYaw());
3605 v3f pos = player->getPosition();
3606 f32 pitch = player->getPitch();
3607 f32 yaw = player->getYaw();
3608 verbosestream<<"Server: Sending TOCLIENT_MOVE_PLAYER"
3609 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3616 std::string s = os.str();
3617 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3619 m_con.Send(peer_id, 0, data, true);
3622 void Server::SendPlayerPrivileges(u16 peer_id)
3624 Player *player = m_env->getPlayer(peer_id);
3626 if(player->peer_id == PEER_ID_INEXISTENT)
3629 std::set<std::string> privs;
3630 scriptapi_get_auth(m_lua, player->getName(), NULL, &privs);
3632 std::ostringstream os(std::ios_base::binary);
3633 writeU16(os, TOCLIENT_PRIVILEGES);
3634 writeU16(os, privs.size());
3635 for(std::set<std::string>::const_iterator i = privs.begin();
3636 i != privs.end(); i++){
3637 os<<serializeString(*i);
3641 std::string s = os.str();
3642 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3644 m_con.Send(peer_id, 0, data, true);
3647 void Server::SendPlayerInventoryFormspec(u16 peer_id)
3649 Player *player = m_env->getPlayer(peer_id);
3651 if(player->peer_id == PEER_ID_INEXISTENT)
3654 std::ostringstream os(std::ios_base::binary);
3655 writeU16(os, TOCLIENT_INVENTORY_FORMSPEC);
3656 os<<serializeLongString(player->inventory_formspec);
3659 std::string s = os.str();
3660 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3662 m_con.Send(peer_id, 0, data, true);
3665 s32 Server::playSound(const SimpleSoundSpec &spec,
3666 const ServerSoundParams ¶ms)
3668 // Find out initial position of sound
3669 bool pos_exists = false;
3670 v3f pos = params.getPos(m_env, &pos_exists);
3671 // If position is not found while it should be, cancel sound
3672 if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
3674 // Filter destination clients
3675 std::set<RemoteClient*> dst_clients;
3676 if(params.to_player != "")
3678 Player *player = m_env->getPlayer(params.to_player.c_str());
3680 infostream<<"Server::playSound: Player \""<<params.to_player
3681 <<"\" not found"<<std::endl;
3684 if(player->peer_id == PEER_ID_INEXISTENT){
3685 infostream<<"Server::playSound: Player \""<<params.to_player
3686 <<"\" not connected"<<std::endl;
3689 RemoteClient *client = getClient(player->peer_id);
3690 dst_clients.insert(client);
3694 for(core::map<u16, RemoteClient*>::Iterator
3695 i = m_clients.getIterator(); i.atEnd() == false; i++)
3697 RemoteClient *client = i.getNode()->getValue();
3698 Player *player = m_env->getPlayer(client->peer_id);
3702 if(player->getPosition().getDistanceFrom(pos) >
3703 params.max_hear_distance)
3706 dst_clients.insert(client);
3709 if(dst_clients.size() == 0)
3712 s32 id = m_next_sound_id++;
3713 // The sound will exist as a reference in m_playing_sounds
3714 m_playing_sounds[id] = ServerPlayingSound();
3715 ServerPlayingSound &psound = m_playing_sounds[id];
3716 psound.params = params;
3717 for(std::set<RemoteClient*>::iterator i = dst_clients.begin();
3718 i != dst_clients.end(); i++)
3719 psound.clients.insert((*i)->peer_id);
3721 std::ostringstream os(std::ios_base::binary);
3722 writeU16(os, TOCLIENT_PLAY_SOUND);
3724 os<<serializeString(spec.name);
3725 writeF1000(os, spec.gain * params.gain);
3726 writeU8(os, params.type);
3727 writeV3F1000(os, pos);
3728 writeU16(os, params.object);
3729 writeU8(os, params.loop);
3731 std::string s = os.str();
3732 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3734 for(std::set<RemoteClient*>::iterator i = dst_clients.begin();
3735 i != dst_clients.end(); i++){
3737 m_con.Send((*i)->peer_id, 0, data, true);
3741 void Server::stopSound(s32 handle)
3743 // Get sound reference
3744 std::map<s32, ServerPlayingSound>::iterator i =
3745 m_playing_sounds.find(handle);
3746 if(i == m_playing_sounds.end())
3748 ServerPlayingSound &psound = i->second;
3750 std::ostringstream os(std::ios_base::binary);
3751 writeU16(os, TOCLIENT_STOP_SOUND);
3752 writeS32(os, handle);
3754 std::string s = os.str();
3755 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3757 for(std::set<u16>::iterator i = psound.clients.begin();
3758 i != psound.clients.end(); i++){
3760 m_con.Send(*i, 0, data, true);
3762 // Remove sound reference
3763 m_playing_sounds.erase(i);
3766 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3767 core::list<u16> *far_players, float far_d_nodes)
3769 float maxd = far_d_nodes*BS;
3770 v3f p_f = intToFloat(p, BS);
3774 SharedBuffer<u8> reply(replysize);
3775 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3776 writeS16(&reply[2], p.X);
3777 writeS16(&reply[4], p.Y);
3778 writeS16(&reply[6], p.Z);
3780 for(core::map<u16, RemoteClient*>::Iterator
3781 i = m_clients.getIterator();
3782 i.atEnd() == false; i++)
3784 // Get client and check that it is valid
3785 RemoteClient *client = i.getNode()->getValue();
3786 assert(client->peer_id == i.getNode()->getKey());
3787 if(client->serialization_version == SER_FMT_VER_INVALID)
3790 // Don't send if it's the same one
3791 if(client->peer_id == ignore_id)
3797 Player *player = m_env->getPlayer(client->peer_id);
3800 // If player is far away, only set modified blocks not sent
3801 v3f player_pos = player->getPosition();
3802 if(player_pos.getDistanceFrom(p_f) > maxd)
3804 far_players->push_back(client->peer_id);
3811 m_con.Send(client->peer_id, 0, reply, true);
3815 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3816 core::list<u16> *far_players, float far_d_nodes)
3818 float maxd = far_d_nodes*BS;
3819 v3f p_f = intToFloat(p, BS);
3821 for(core::map<u16, RemoteClient*>::Iterator
3822 i = m_clients.getIterator();
3823 i.atEnd() == false; i++)
3825 // Get client and check that it is valid
3826 RemoteClient *client = i.getNode()->getValue();
3827 assert(client->peer_id == i.getNode()->getKey());
3828 if(client->serialization_version == SER_FMT_VER_INVALID)
3831 // Don't send if it's the same one
3832 if(client->peer_id == ignore_id)
3838 Player *player = m_env->getPlayer(client->peer_id);
3841 // If player is far away, only set modified blocks not sent
3842 v3f player_pos = player->getPosition();
3843 if(player_pos.getDistanceFrom(p_f) > maxd)
3845 far_players->push_back(client->peer_id);
3852 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3853 SharedBuffer<u8> reply(replysize);
3854 writeU16(&reply[0], TOCLIENT_ADDNODE);
3855 writeS16(&reply[2], p.X);
3856 writeS16(&reply[4], p.Y);
3857 writeS16(&reply[6], p.Z);
3858 n.serialize(&reply[8], client->serialization_version);
3861 m_con.Send(client->peer_id, 0, reply, true);
3865 void Server::setBlockNotSent(v3s16 p)
3867 for(core::map<u16, RemoteClient*>::Iterator
3868 i = m_clients.getIterator();
3869 i.atEnd()==false; i++)
3871 RemoteClient *client = i.getNode()->getValue();
3872 client->SetBlockNotSent(p);
3876 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3878 DSTACK(__FUNCTION_NAME);
3880 v3s16 p = block->getPos();
3884 bool completely_air = true;
3885 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3886 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3887 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3889 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
3891 completely_air = false;
3892 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
3897 infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
3899 infostream<<"[completely air] ";
3900 infostream<<std::endl;
3904 Create a packet with the block in the right format
3907 std::ostringstream os(std::ios_base::binary);
3908 block->serialize(os, ver, false);
3909 std::string s = os.str();
3910 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3912 u32 replysize = 8 + blockdata.getSize();
3913 SharedBuffer<u8> reply(replysize);
3914 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3915 writeS16(&reply[2], p.X);
3916 writeS16(&reply[4], p.Y);
3917 writeS16(&reply[6], p.Z);
3918 memcpy(&reply[8], *blockdata, blockdata.getSize());
3920 /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3921 <<": \tpacket size: "<<replysize<<std::endl;*/
3926 m_con.Send(peer_id, 1, reply, true);
3929 void Server::SendBlocks(float dtime)
3931 DSTACK(__FUNCTION_NAME);
3933 JMutexAutoLock envlock(m_env_mutex);
3934 JMutexAutoLock conlock(m_con_mutex);
3936 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
3938 core::array<PrioritySortedBlockTransfer> queue;
3940 s32 total_sending = 0;
3943 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
3945 for(core::map<u16, RemoteClient*>::Iterator
3946 i = m_clients.getIterator();
3947 i.atEnd() == false; i++)
3949 RemoteClient *client = i.getNode()->getValue();
3950 assert(client->peer_id == i.getNode()->getKey());
3952 // If definitions and textures have not been sent, don't
3953 // send MapBlocks either
3954 if(!client->definitions_sent)
3957 total_sending += client->SendingCount();
3959 if(client->serialization_version == SER_FMT_VER_INVALID)
3962 client->GetNextBlocks(this, dtime, queue);
3967 // Lowest priority number comes first.
3968 // Lowest is most important.
3971 for(u32 i=0; i<queue.size(); i++)
3973 //TODO: Calculate limit dynamically
3974 if(total_sending >= g_settings->getS32
3975 ("max_simultaneous_block_sends_server_total"))
3978 PrioritySortedBlockTransfer q = queue[i];
3980 MapBlock *block = NULL;
3983 block = m_env->getMap().getBlockNoCreate(q.pos);
3985 catch(InvalidPositionException &e)
3990 RemoteClient *client = getClient(q.peer_id);
3992 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3994 client->SentBlock(q.pos);
4000 void Server::fillMediaCache()
4002 DSTACK(__FUNCTION_NAME);
4004 infostream<<"Server: Calculating media file checksums"<<std::endl;
4006 // Collect all media file paths
4007 std::list<std::string> paths;
4008 for(core::list<ModSpec>::Iterator i = m_mods.begin();
4009 i != m_mods.end(); i++){
4010 const ModSpec &mod = *i;
4011 paths.push_back(mod.path + DIR_DELIM + "textures");
4012 paths.push_back(mod.path + DIR_DELIM + "sounds");
4013 paths.push_back(mod.path + DIR_DELIM + "media");
4015 std::string path_all = "textures";
4016 paths.push_back(path_all + DIR_DELIM + "all");
4018 // Collect media file information from paths into cache
4019 for(std::list<std::string>::iterator i = paths.begin();
4020 i != paths.end(); i++)
4022 std::string mediapath = *i;
4023 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
4024 for(u32 j=0; j<dirlist.size(); j++){
4025 if(dirlist[j].dir) // Ignode dirs
4027 std::string filename = dirlist[j].name;
4028 // If name contains illegal characters, ignore the file
4029 if(!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)){
4030 infostream<<"Server: ignoring illegal file name: \""
4031 <<filename<<"\""<<std::endl;
4034 // If name is not in a supported format, ignore it
4035 const char *supported_ext[] = {
4036 ".png", ".jpg", ".bmp", ".tga",
4037 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
4041 if(removeStringEnd(filename, supported_ext) == ""){
4042 infostream<<"Server: ignoring unsupported file extension: \""
4043 <<filename<<"\""<<std::endl;
4046 // Ok, attempt to load the file and add to cache
4047 std::string filepath = mediapath + DIR_DELIM + filename;
4049 std::ifstream fis(filepath.c_str(), std::ios_base::binary);
4050 if(fis.good() == false){
4051 errorstream<<"Server::fillMediaCache(): Could not open \""
4052 <<filename<<"\" for reading"<<std::endl;
4055 std::ostringstream tmp_os(std::ios_base::binary);
4059 fis.read(buf, 1024);
4060 std::streamsize len = fis.gcount();
4061 tmp_os.write(buf, len);
4070 errorstream<<"Server::fillMediaCache(): Failed to read \""
4071 <<filename<<"\""<<std::endl;
4074 if(tmp_os.str().length() == 0){
4075 errorstream<<"Server::fillMediaCache(): Empty file \""
4076 <<filepath<<"\""<<std::endl;
4081 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
4083 unsigned char *digest = sha1.getDigest();
4084 std::string sha1_base64 = base64_encode(digest, 20);
4085 std::string sha1_hex = hex_encode((char*)digest, 20);
4089 this->m_media[filename] = MediaInfo(filepath, sha1_base64);
4090 verbosestream<<"Server: "<<sha1_hex<<" is "<<filename<<std::endl;
4095 struct SendableMediaAnnouncement
4098 std::string sha1_digest;
4100 SendableMediaAnnouncement(const std::string name_="",
4101 const std::string sha1_digest_=""):
4103 sha1_digest(sha1_digest_)
4107 void Server::sendMediaAnnouncement(u16 peer_id)
4109 DSTACK(__FUNCTION_NAME);
4111 verbosestream<<"Server: Announcing files to id("<<peer_id<<")"
4114 core::list<SendableMediaAnnouncement> file_announcements;
4116 for(std::map<std::string, MediaInfo>::iterator i = m_media.begin();
4117 i != m_media.end(); i++){
4119 file_announcements.push_back(
4120 SendableMediaAnnouncement(i->first, i->second.sha1_digest));
4124 std::ostringstream os(std::ios_base::binary);
4132 u16 length of sha1_digest
4137 writeU16(os, TOCLIENT_ANNOUNCE_MEDIA);
4138 writeU16(os, file_announcements.size());
4140 for(core::list<SendableMediaAnnouncement>::Iterator
4141 j = file_announcements.begin();
4142 j != file_announcements.end(); j++){
4143 os<<serializeString(j->name);
4144 os<<serializeString(j->sha1_digest);
4148 std::string s = os.str();
4149 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4152 m_con.Send(peer_id, 0, data, true);
4156 struct SendableMedia
4162 SendableMedia(const std::string &name_="", const std::string path_="",
4163 const std::string &data_=""):
4170 void Server::sendRequestedMedia(u16 peer_id,
4171 const core::list<MediaRequest> &tosend)
4173 DSTACK(__FUNCTION_NAME);
4175 verbosestream<<"Server::sendRequestedMedia(): "
4176 <<"Sending files to client"<<std::endl;
4180 // Put 5kB in one bunch (this is not accurate)
4181 u32 bytes_per_bunch = 5000;
4183 core::array< core::list<SendableMedia> > file_bunches;
4184 file_bunches.push_back(core::list<SendableMedia>());
4186 u32 file_size_bunch_total = 0;
4188 for(core::list<MediaRequest>::ConstIterator i = tosend.begin();
4189 i != tosend.end(); i++)
4191 if(m_media.find(i->name) == m_media.end()){
4192 errorstream<<"Server::sendRequestedMedia(): Client asked for "
4193 <<"unknown file \""<<(i->name)<<"\""<<std::endl;
4197 //TODO get path + name
4198 std::string tpath = m_media[(*i).name].path;
4201 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
4202 if(fis.good() == false){
4203 errorstream<<"Server::sendRequestedMedia(): Could not open \""
4204 <<tpath<<"\" for reading"<<std::endl;
4207 std::ostringstream tmp_os(std::ios_base::binary);
4211 fis.read(buf, 1024);
4212 std::streamsize len = fis.gcount();
4213 tmp_os.write(buf, len);
4214 file_size_bunch_total += len;
4223 errorstream<<"Server::sendRequestedMedia(): Failed to read \""
4224 <<(*i).name<<"\""<<std::endl;
4227 /*infostream<<"Server::sendRequestedMedia(): Loaded \""
4228 <<tname<<"\""<<std::endl;*/
4230 file_bunches[file_bunches.size()-1].push_back(
4231 SendableMedia((*i).name, tpath, tmp_os.str()));
4233 // Start next bunch if got enough data
4234 if(file_size_bunch_total >= bytes_per_bunch){
4235 file_bunches.push_back(core::list<SendableMedia>());
4236 file_size_bunch_total = 0;
4241 /* Create and send packets */
4243 u32 num_bunches = file_bunches.size();
4244 for(u32 i=0; i<num_bunches; i++)
4246 std::ostringstream os(std::ios_base::binary);
4250 u16 total number of texture bunches
4251 u16 index of this bunch
4252 u32 number of files in this bunch
4261 writeU16(os, TOCLIENT_MEDIA);
4262 writeU16(os, num_bunches);
4264 writeU32(os, file_bunches[i].size());
4266 for(core::list<SendableMedia>::Iterator
4267 j = file_bunches[i].begin();
4268 j != file_bunches[i].end(); j++){
4269 os<<serializeString(j->name);
4270 os<<serializeLongString(j->data);
4274 std::string s = os.str();
4275 verbosestream<<"Server::sendRequestedMedia(): bunch "
4276 <<i<<"/"<<num_bunches
4277 <<" files="<<file_bunches[i].size()
4278 <<" size=" <<s.size()<<std::endl;
4279 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4281 m_con.Send(peer_id, 0, data, true);
4285 void Server::sendDetachedInventory(const std::string &name, u16 peer_id)
4287 if(m_detached_inventories.count(name) == 0){
4288 errorstream<<__FUNCTION_NAME<<": \""<<name<<"\" not found"<<std::endl;
4291 Inventory *inv = m_detached_inventories[name];
4293 std::ostringstream os(std::ios_base::binary);
4294 writeU16(os, TOCLIENT_DETACHED_INVENTORY);
4295 os<<serializeString(name);
4299 std::string s = os.str();
4300 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4302 m_con.Send(peer_id, 0, data, true);
4305 void Server::sendDetachedInventoryToAll(const std::string &name)
4307 DSTACK(__FUNCTION_NAME);
4309 for(core::map<u16, RemoteClient*>::Iterator
4310 i = m_clients.getIterator();
4311 i.atEnd() == false; i++){
4312 RemoteClient *client = i.getNode()->getValue();
4313 sendDetachedInventory(name, client->peer_id);
4317 void Server::sendDetachedInventories(u16 peer_id)
4319 DSTACK(__FUNCTION_NAME);
4321 for(std::map<std::string, Inventory*>::iterator
4322 i = m_detached_inventories.begin();
4323 i != m_detached_inventories.end(); i++){
4324 const std::string &name = i->first;
4325 //Inventory *inv = i->second;
4326 sendDetachedInventory(name, peer_id);
4334 void Server::DiePlayer(u16 peer_id)
4336 DSTACK(__FUNCTION_NAME);
4338 PlayerSAO *playersao = getPlayerSAO(peer_id);
4341 infostream<<"Server::DiePlayer(): Player "
4342 <<playersao->getPlayer()->getName()
4343 <<" dies"<<std::endl;
4345 playersao->setHP(0);
4347 // Trigger scripted stuff
4348 scriptapi_on_dieplayer(m_lua, playersao);
4350 SendPlayerHP(peer_id);
4351 SendDeathscreen(m_con, peer_id, false, v3f(0,0,0));
4354 void Server::RespawnPlayer(u16 peer_id)
4356 DSTACK(__FUNCTION_NAME);
4358 PlayerSAO *playersao = getPlayerSAO(peer_id);
4361 infostream<<"Server::RespawnPlayer(): Player "
4362 <<playersao->getPlayer()->getName()
4363 <<" respawns"<<std::endl;
4365 playersao->setHP(PLAYER_MAX_HP);
4367 bool repositioned = scriptapi_on_respawnplayer(m_lua, playersao);
4369 v3f pos = findSpawnPos(m_env->getServerMap());
4370 playersao->setPos(pos);
4374 void Server::UpdateCrafting(u16 peer_id)
4376 DSTACK(__FUNCTION_NAME);
4378 Player* player = m_env->getPlayer(peer_id);
4381 // Get a preview for crafting
4383 // No crafting in creative mode
4384 if(g_settings->getBool("creative_mode") == false)
4385 getCraftingResult(&player->inventory, preview, false, this);
4387 // Put the new preview in
4388 InventoryList *plist = player->inventory.getList("craftpreview");
4390 assert(plist->getSize() >= 1);
4391 plist->changeItem(0, preview);
4394 RemoteClient* Server::getClient(u16 peer_id)
4396 DSTACK(__FUNCTION_NAME);
4397 //JMutexAutoLock lock(m_con_mutex);
4398 core::map<u16, RemoteClient*>::Node *n;
4399 n = m_clients.find(peer_id);
4400 // A client should exist for all peers
4402 return n->getValue();
4405 std::wstring Server::getStatusString()
4407 std::wostringstream os(std::ios_base::binary);
4410 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4412 os<<L", uptime="<<m_uptime.get();
4413 // Information about clients
4415 for(core::map<u16, RemoteClient*>::Iterator
4416 i = m_clients.getIterator();
4417 i.atEnd() == false; i++)
4419 // Get client and check that it is valid
4420 RemoteClient *client = i.getNode()->getValue();
4421 assert(client->peer_id == i.getNode()->getKey());
4422 if(client->serialization_version == SER_FMT_VER_INVALID)
4425 Player *player = m_env->getPlayer(client->peer_id);
4426 // Get name of player
4427 std::wstring name = L"unknown";
4429 name = narrow_to_wide(player->getName());
4430 // Add name to information string
4434 if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
4435 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4436 if(g_settings->get("motd") != "")
4437 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4441 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
4443 std::set<std::string> privs;
4444 scriptapi_get_auth(m_lua, name, NULL, &privs);
4448 bool Server::checkPriv(const std::string &name, const std::string &priv)
4450 std::set<std::string> privs = getPlayerEffectivePrivs(name);
4451 return (privs.count(priv) != 0);
4454 void Server::reportPrivsModified(const std::string &name)
4457 for(core::map<u16, RemoteClient*>::Iterator
4458 i = m_clients.getIterator();
4459 i.atEnd() == false; i++){
4460 RemoteClient *client = i.getNode()->getValue();
4461 Player *player = m_env->getPlayer(client->peer_id);
4462 reportPrivsModified(player->getName());
4465 Player *player = m_env->getPlayer(name.c_str());
4468 SendPlayerPrivileges(player->peer_id);
4469 PlayerSAO *sao = player->getPlayerSAO();
4472 sao->updatePrivileges(
4473 getPlayerEffectivePrivs(name),
4478 void Server::reportInventoryFormspecModified(const std::string &name)
4480 Player *player = m_env->getPlayer(name.c_str());
4483 SendPlayerInventoryFormspec(player->peer_id);
4486 // Saves g_settings to configpath given at initialization
4487 void Server::saveConfig()
4489 if(m_path_config != "")
4490 g_settings->updateConfigFile(m_path_config.c_str());
4493 void Server::notifyPlayer(const char *name, const std::wstring msg)
4495 Player *player = m_env->getPlayer(name);
4498 SendChatMessage(player->peer_id, std::wstring(L"Server: -!- ")+msg);
4501 void Server::notifyPlayers(const std::wstring msg)
4503 BroadcastChatMessage(msg);
4506 void Server::queueBlockEmerge(v3s16 blockpos, bool allow_generate)
4510 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
4511 m_emerge_queue.addBlock(PEER_ID_INEXISTENT, blockpos, flags);
4514 Inventory* Server::createDetachedInventory(const std::string &name)
4516 if(m_detached_inventories.count(name) > 0){
4517 infostream<<"Server clearing detached inventory \""<<name<<"\""<<std::endl;
4518 delete m_detached_inventories[name];
4520 infostream<<"Server creating detached inventory \""<<name<<"\""<<std::endl;
4522 Inventory *inv = new Inventory(m_itemdef);
4524 m_detached_inventories[name] = inv;
4525 sendDetachedInventoryToAll(name);
4529 // IGameDef interface
4531 IItemDefManager* Server::getItemDefManager()
4535 INodeDefManager* Server::getNodeDefManager()
4539 ICraftDefManager* Server::getCraftDefManager()
4543 ITextureSource* Server::getTextureSource()
4547 u16 Server::allocateUnknownNodeId(const std::string &name)
4549 return m_nodedef->allocateDummy(name);
4551 ISoundManager* Server::getSoundManager()
4553 return &dummySoundManager;
4555 MtEventManager* Server::getEventManager()
4560 IWritableItemDefManager* Server::getWritableItemDefManager()
4564 IWritableNodeDefManager* Server::getWritableNodeDefManager()
4568 IWritableCraftDefManager* Server::getWritableCraftDefManager()
4573 const ModSpec* Server::getModSpec(const std::string &modname)
4575 for(core::list<ModSpec>::Iterator i = m_mods.begin();
4576 i != m_mods.end(); i++){
4577 const ModSpec &mod = *i;
4578 if(mod.name == modname)
4583 void Server::getModNames(core::list<std::string> &modlist)
4585 for(core::list<ModSpec>::Iterator i = m_mods.begin(); i != m_mods.end(); i++)
4587 modlist.push_back((*i).name);
4590 std::string Server::getBuiltinLuaPath()
4592 return porting::path_share + DIR_DELIM + "builtin";
4595 v3f findSpawnPos(ServerMap &map)
4597 //return v3f(50,50,50)*BS;
4602 nodepos = v2s16(0,0);
4607 // Try to find a good place a few times
4608 for(s32 i=0; i<1000; i++)
4611 // We're going to try to throw the player to this position
4612 v2s16 nodepos2d = v2s16(-range + (myrand()%(range*2)),
4613 -range + (myrand()%(range*2)));
4614 //v2s16 sectorpos = getNodeSectorPos(nodepos2d);
4615 // Get ground height at point (fallbacks to heightmap function)
4616 s16 groundheight = map.findGroundLevel(nodepos2d);
4617 // Don't go underwater
4618 if(groundheight < WATER_LEVEL)
4620 //infostream<<"-> Underwater"<<std::endl;
4623 // Don't go to high places
4624 if(groundheight > WATER_LEVEL + 4)
4626 //infostream<<"-> Underwater"<<std::endl;
4630 nodepos = v3s16(nodepos2d.X, groundheight-2, nodepos2d.Y);
4631 bool is_good = false;
4633 for(s32 i=0; i<10; i++){
4634 v3s16 blockpos = getNodeBlockPos(nodepos);
4635 map.emergeBlock(blockpos, true);
4636 MapNode n = map.getNodeNoEx(nodepos);
4637 if(n.getContent() == CONTENT_AIR){
4648 // Found a good place
4649 //infostream<<"Searched through "<<i<<" places."<<std::endl;
4655 return intToFloat(nodepos, BS);
4658 PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id)
4660 RemotePlayer *player = NULL;
4661 bool newplayer = false;
4664 Try to get an existing player
4666 player = static_cast<RemotePlayer*>(m_env->getPlayer(name));
4668 // If player is already connected, cancel
4669 if(player != NULL && player->peer_id != 0)
4671 infostream<<"emergePlayer(): Player already connected"<<std::endl;
4676 If player with the wanted peer_id already exists, cancel.
4678 if(m_env->getPlayer(peer_id) != NULL)
4680 infostream<<"emergePlayer(): Player with wrong name but same"
4681 " peer_id already exists"<<std::endl;
4686 Create a new player if it doesn't exist yet
4691 player = new RemotePlayer(this);
4692 player->updateName(name);
4694 /* Set player position */
4695 infostream<<"Server: Finding spawn place for player \""
4696 <<name<<"\""<<std::endl;
4697 v3f pos = findSpawnPos(m_env->getServerMap());
4698 player->setPosition(pos);
4700 /* Add player to environment */
4701 m_env->addPlayer(player);
4705 Create a new player active object
4707 PlayerSAO *playersao = new PlayerSAO(m_env, player, peer_id,
4708 getPlayerEffectivePrivs(player->getName()),
4711 /* Add object to environment */
4712 m_env->addActiveObject(playersao);
4716 scriptapi_on_newplayer(m_lua, playersao);
4718 scriptapi_on_joinplayer(m_lua, playersao);
4721 if(g_settings->getBool("creative_mode"))
4722 playersao->createCreativeInventory();
4727 void Server::handlePeerChange(PeerChange &c)
4729 JMutexAutoLock envlock(m_env_mutex);
4730 JMutexAutoLock conlock(m_con_mutex);
4732 if(c.type == PEER_ADDED)
4739 core::map<u16, RemoteClient*>::Node *n;
4740 n = m_clients.find(c.peer_id);
4741 // The client shouldn't already exist
4745 RemoteClient *client = new RemoteClient();
4746 client->peer_id = c.peer_id;
4747 m_clients.insert(client->peer_id, client);
4750 else if(c.type == PEER_REMOVED)
4757 core::map<u16, RemoteClient*>::Node *n;
4758 n = m_clients.find(c.peer_id);
4759 // The client should exist
4763 Mark objects to be not known by the client
4765 RemoteClient *client = n->getValue();
4767 for(core::map<u16, bool>::Iterator
4768 i = client->m_known_objects.getIterator();
4769 i.atEnd()==false; i++)
4772 u16 id = i.getNode()->getKey();
4773 ServerActiveObject* obj = m_env->getActiveObject(id);
4775 if(obj && obj->m_known_by_count > 0)
4776 obj->m_known_by_count--;
4780 Clear references to playing sounds
4782 for(std::map<s32, ServerPlayingSound>::iterator
4783 i = m_playing_sounds.begin();
4784 i != m_playing_sounds.end();)
4786 ServerPlayingSound &psound = i->second;
4787 psound.clients.erase(c.peer_id);
4788 if(psound.clients.size() == 0)
4789 m_playing_sounds.erase(i++);
4794 Player *player = m_env->getPlayer(c.peer_id);
4796 // Collect information about leaving in chat
4797 std::wstring message;
4801 std::wstring name = narrow_to_wide(player->getName());
4804 message += L" left the game.";
4806 message += L" (timed out)";
4810 /* Run scripts and remove from environment */
4814 PlayerSAO *playersao = player->getPlayerSAO();
4817 scriptapi_on_leaveplayer(m_lua, playersao);
4819 playersao->disconnected();
4829 std::ostringstream os(std::ios_base::binary);
4830 for(core::map<u16, RemoteClient*>::Iterator
4831 i = m_clients.getIterator();
4832 i.atEnd() == false; i++)
4834 RemoteClient *client = i.getNode()->getValue();
4835 assert(client->peer_id == i.getNode()->getKey());
4836 if(client->serialization_version == SER_FMT_VER_INVALID)
4839 Player *player = m_env->getPlayer(client->peer_id);
4842 // Get name of player
4843 os<<player->getName()<<" ";
4846 actionstream<<player->getName()<<" "
4847 <<(c.timeout?"times out.":"leaves game.")
4848 <<" List of players: "
4849 <<os.str()<<std::endl;
4854 delete m_clients[c.peer_id];
4855 m_clients.remove(c.peer_id);
4857 // Send player info to all remaining clients
4858 //SendPlayerInfos();
4860 // Send leave chat message to all remaining clients
4861 if(message.length() != 0)
4862 BroadcastChatMessage(message);
4871 void Server::handlePeerChanges()
4873 while(m_peer_change_queue.size() > 0)
4875 PeerChange c = m_peer_change_queue.pop_front();
4877 verbosestream<<"Server: Handling peer change: "
4878 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4881 handlePeerChange(c);
4885 void dedicated_server_loop(Server &server, bool &kill)
4887 DSTACK(__FUNCTION_NAME);
4889 verbosestream<<"dedicated_server_loop()"<<std::endl;
4891 IntervalLimiter m_profiler_interval;
4895 float steplen = g_settings->getFloat("dedicated_server_step");
4896 // This is kind of a hack but can be done like this
4897 // because server.step() is very light
4899 ScopeProfiler sp(g_profiler, "dedicated server sleep");
4900 sleep_ms((int)(steplen*1000.0));
4902 server.step(steplen);
4904 if(server.getShutdownRequested() || kill)
4906 infostream<<"Dedicated server quitting"<<std::endl;
4913 float profiler_print_interval =
4914 g_settings->getFloat("profiler_print_interval");
4915 if(profiler_print_interval != 0)
4917 if(m_profiler_interval.step(steplen, profiler_print_interval))
4919 infostream<<"Profiler:"<<std::endl;
4920 g_profiler->print(infostream);
4921 g_profiler->clear();