3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include "clientserver.h"
25 #include "jmutexautolock.h"
27 #include "constants.h"
30 #include "servercommand.h"
33 #include "serverobject.h"
38 #include "scriptapi.h"
43 #include "content_mapnode.h"
44 #include "content_nodemeta.h"
45 #include "content_abm.h"
46 #include "content_sao.h"
51 #include "sound.h" // dummySoundManager
52 #include "event_manager.h"
54 #include "util/string.h"
55 #include "util/pointedthing.h"
56 #include "util/mathconstants.h"
58 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
60 #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
62 class MapEditEventIgnorer
65 MapEditEventIgnorer(bool *flag):
74 ~MapEditEventIgnorer()
87 class MapEditEventAreaIgnorer
90 MapEditEventAreaIgnorer(VoxelArea *ignorevariable, const VoxelArea &a):
91 m_ignorevariable(ignorevariable)
93 if(m_ignorevariable->getVolume() == 0)
94 *m_ignorevariable = a;
96 m_ignorevariable = NULL;
99 ~MapEditEventAreaIgnorer()
103 assert(m_ignorevariable->getVolume() != 0);
104 *m_ignorevariable = VoxelArea();
109 VoxelArea *m_ignorevariable;
112 void * ServerThread::Thread()
116 log_register_thread("ServerThread");
118 DSTACK(__FUNCTION_NAME);
120 BEGIN_DEBUG_EXCEPTION_HANDLER
125 //TimeTaker timer("AsyncRunStep() + Receive()");
128 //TimeTaker timer("AsyncRunStep()");
129 m_server->AsyncRunStep();
132 //infostream<<"Running m_server->Receive()"<<std::endl;
135 catch(con::NoIncomingDataException &e)
138 catch(con::PeerNotFoundException &e)
140 infostream<<"Server: PeerNotFoundException"<<std::endl;
142 catch(con::ConnectionBindFailed &e)
144 m_server->setAsyncFatalError(e.what());
148 m_server->setAsyncFatalError(e.what());
152 END_DEBUG_EXCEPTION_HANDLER(errorstream)
157 void * EmergeThread::Thread()
161 log_register_thread("EmergeThread");
163 DSTACK(__FUNCTION_NAME);
165 BEGIN_DEBUG_EXCEPTION_HANDLER
167 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
169 v3s16 last_tried_pos(-32768,-32768,-32768); // For error output
172 Get block info from queue, emerge them and send them
175 After queue is empty, exit.
179 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
183 SharedPtr<QueuedBlockEmerge> q(qptr);
191 Do not generate over-limit
193 if(blockpos_over_limit(p))
196 //infostream<<"EmergeThread::Thread(): running"<<std::endl;
198 //TimeTaker timer("block emerge");
201 Try to emerge it from somewhere.
203 If it is only wanted as optional, only loading from disk
208 Check if any peer wants it as non-optional. In that case it
211 Also decrement the emerge queue count in clients.
214 bool only_from_disk = true;
217 core::map<u16, u8>::Iterator i;
218 for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
220 //u16 peer_id = i.getNode()->getKey();
223 u8 flags = i.getNode()->getValue();
224 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
225 only_from_disk = false;
230 if(enable_mapgen_debug_info)
231 infostream<<"EmergeThread: p="
232 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
233 <<"only_from_disk="<<only_from_disk<<std::endl;
235 ServerMap &map = ((ServerMap&)m_server->m_env->getMap());
237 MapBlock *block = NULL;
238 bool got_block = true;
239 core::map<v3s16, MapBlock*> modified_blocks;
242 Try to fetch block from memory or disk.
243 If not found and asked to generate, initialize generator.
246 bool started_generate = false;
247 mapgen::BlockMakeData data;
250 JMutexAutoLock envlock(m_server->m_env_mutex);
252 // Load sector if it isn't loaded
253 if(map.getSectorNoGenerateNoEx(p2d) == NULL)
254 map.loadSectorMeta(p2d);
256 // Attempt to load block
257 block = map.getBlockNoCreateNoEx(p);
258 if(!block || block->isDummy() || !block->isGenerated())
260 if(enable_mapgen_debug_info)
261 infostream<<"EmergeThread: not in memory, "
262 <<"attempting to load from disk"<<std::endl;
264 block = map.loadBlock(p);
267 // If could not load and allowed to generate, start generation
268 // inside this same envlock
269 if(only_from_disk == false &&
270 (block == NULL || block->isGenerated() == false)){
271 if(enable_mapgen_debug_info)
272 infostream<<"EmergeThread: generating"<<std::endl;
273 started_generate = true;
275 map.initBlockMake(&data, p);
280 If generator was initialized, generate now when envlock is free.
285 ScopeProfiler sp(g_profiler, "EmergeThread: mapgen::make_block",
287 TimeTaker t("mapgen::make_block()");
289 mapgen::make_block(&data);
291 if(enable_mapgen_debug_info == false)
292 t.stop(true); // Hide output
296 // Lock environment again to access the map
297 JMutexAutoLock envlock(m_server->m_env_mutex);
299 ScopeProfiler sp(g_profiler, "EmergeThread: after "
300 "mapgen::make_block (envlock)", SPT_AVG);
302 // Blit data back on map, update lighting, add mobs and
303 // whatever this does
304 map.finishBlockMake(&data, modified_blocks);
307 block = map.getBlockNoCreateNoEx(p);
309 // If block doesn't exist, don't try doing anything with it
310 // This happens if the block is not in generation boundaries
315 Do some post-generate stuff
318 v3s16 minp = data.blockpos_min*MAP_BLOCKSIZE;
319 v3s16 maxp = data.blockpos_max*MAP_BLOCKSIZE +
320 v3s16(1,1,1)*(MAP_BLOCKSIZE-1);
323 Ignore map edit events, they will not need to be
324 sent to anybody because the block hasn't been sent
327 //MapEditEventIgnorer ign(&m_server->m_ignore_map_edit_events);
328 MapEditEventAreaIgnorer ign(
329 &m_server->m_ignore_map_edit_events_area,
330 VoxelArea(minp, maxp));
332 TimeTaker timer("on_generated");
333 scriptapi_environment_on_generated(m_server->m_lua,
334 minp, maxp, mapgen::get_blockseed(data.seed, minp));
335 /*int t = timer.stop(true);
336 dstream<<"on_generated took "<<t<<"ms"<<std::endl;*/
339 if(enable_mapgen_debug_info)
340 infostream<<"EmergeThread: ended up with: "
341 <<analyze_block(block)<<std::endl;
343 // Activate objects and stuff
344 m_server->m_env->activateBlock(block, 0);
352 Set sent status of modified blocks on clients
355 // NOTE: Server's clients are also behind the connection mutex
356 JMutexAutoLock lock(m_server->m_con_mutex);
359 Add the originally fetched block to the modified list
363 modified_blocks.insert(p, block);
367 Set the modified blocks unsent for all the clients
370 for(core::map<u16, RemoteClient*>::Iterator
371 i = m_server->m_clients.getIterator();
372 i.atEnd() == false; i++)
374 RemoteClient *client = i.getNode()->getValue();
376 if(modified_blocks.size() > 0)
378 // Remove block from sent history
379 client->SetBlocksNotSent(modified_blocks);
383 catch(VersionMismatchException &e)
385 std::ostringstream err;
386 err<<"World data version mismatch in MapBlock "<<PP(last_tried_pos)<<std::endl;
387 err<<"----"<<std::endl;
388 err<<"\""<<e.what()<<"\""<<std::endl;
389 err<<"See debug.txt."<<std::endl;
390 err<<"World probably saved by a newer version of Minetest."<<std::endl;
391 m_server->setAsyncFatalError(err.str());
393 catch(SerializationError &e)
395 std::ostringstream err;
396 err<<"Invalid data in MapBlock "<<PP(last_tried_pos)<<std::endl;
397 err<<"----"<<std::endl;
398 err<<"\""<<e.what()<<"\""<<std::endl;
399 err<<"See debug.txt."<<std::endl;
400 err<<"You can ignore this using [ignore_world_load_errors = true]."<<std::endl;
401 m_server->setAsyncFatalError(err.str());
404 END_DEBUG_EXCEPTION_HANDLER(errorstream)
406 log_deregister_thread();
411 v3f ServerSoundParams::getPos(ServerEnvironment *env, bool *pos_exists) const
413 if(pos_exists) *pos_exists = false;
418 if(pos_exists) *pos_exists = true;
423 ServerActiveObject *sao = env->getActiveObject(object);
426 if(pos_exists) *pos_exists = true;
427 return sao->getBasePosition(); }
432 void RemoteClient::GetNextBlocks(Server *server, float dtime,
433 core::array<PrioritySortedBlockTransfer> &dest)
435 DSTACK(__FUNCTION_NAME);
438 TimeTaker timer("RemoteClient::GetNextBlocks", &timer_result);*/
441 m_nothing_to_send_pause_timer -= dtime;
442 m_nearest_unsent_reset_timer += dtime;
444 if(m_nothing_to_send_pause_timer >= 0)
449 // Won't send anything if already sending
450 if(m_blocks_sending.size() >= g_settings->getU16
451 ("max_simultaneous_block_sends_per_client"))
453 //infostream<<"Not sending any blocks, Queue full."<<std::endl;
457 //TimeTaker timer("RemoteClient::GetNextBlocks");
459 Player *player = server->m_env->getPlayer(peer_id);
461 assert(player != NULL);
463 v3f playerpos = player->getPosition();
464 v3f playerspeed = player->getSpeed();
465 v3f playerspeeddir(0,0,0);
466 if(playerspeed.getLength() > 1.0*BS)
467 playerspeeddir = playerspeed / playerspeed.getLength();
468 // Predict to next block
469 v3f playerpos_predicted = playerpos + playerspeeddir*MAP_BLOCKSIZE*BS;
471 v3s16 center_nodepos = floatToInt(playerpos_predicted, BS);
473 v3s16 center = getNodeBlockPos(center_nodepos);
475 // Camera position and direction
476 v3f camera_pos = player->getEyePosition();
477 v3f camera_dir = v3f(0,0,1);
478 camera_dir.rotateYZBy(player->getPitch());
479 camera_dir.rotateXZBy(player->getYaw());
481 /*infostream<<"camera_dir=("<<camera_dir.X<<","<<camera_dir.Y<<","
482 <<camera_dir.Z<<")"<<std::endl;*/
485 Get the starting value of the block finder radius.
488 if(m_last_center != center)
490 m_nearest_unsent_d = 0;
491 m_last_center = center;
494 /*infostream<<"m_nearest_unsent_reset_timer="
495 <<m_nearest_unsent_reset_timer<<std::endl;*/
497 // Reset periodically to workaround for some bugs or stuff
498 if(m_nearest_unsent_reset_timer > 20.0)
500 m_nearest_unsent_reset_timer = 0;
501 m_nearest_unsent_d = 0;
502 //infostream<<"Resetting m_nearest_unsent_d for "
503 // <<server->getPlayerName(peer_id)<<std::endl;
506 //s16 last_nearest_unsent_d = m_nearest_unsent_d;
507 s16 d_start = m_nearest_unsent_d;
509 //infostream<<"d_start="<<d_start<<std::endl;
511 u16 max_simul_sends_setting = g_settings->getU16
512 ("max_simultaneous_block_sends_per_client");
513 u16 max_simul_sends_usually = max_simul_sends_setting;
516 Check the time from last addNode/removeNode.
518 Decrease send rate if player is building stuff.
520 m_time_from_building += dtime;
521 if(m_time_from_building < g_settings->getFloat(
522 "full_block_send_enable_min_time_from_building"))
524 max_simul_sends_usually
525 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
529 Number of blocks sending + number of blocks selected for sending
531 u32 num_blocks_selected = m_blocks_sending.size();
534 next time d will be continued from the d from which the nearest
535 unsent block was found this time.
537 This is because not necessarily any of the blocks found this
538 time are actually sent.
540 s32 new_nearest_unsent_d = -1;
542 s16 d_max = g_settings->getS16("max_block_send_distance");
543 s16 d_max_gen = g_settings->getS16("max_block_generate_distance");
545 // Don't loop very much at a time
546 s16 max_d_increment_at_time = 2;
547 if(d_max > d_start + max_d_increment_at_time)
548 d_max = d_start + max_d_increment_at_time;
549 /*if(d_max_gen > d_start+2)
550 d_max_gen = d_start+2;*/
552 //infostream<<"Starting from "<<d_start<<std::endl;
554 s32 nearest_emerged_d = -1;
555 s32 nearest_emergefull_d = -1;
556 s32 nearest_sent_d = -1;
557 bool queue_is_full = false;
560 for(d = d_start; d <= d_max; d++)
562 /*errorstream<<"checking d="<<d<<" for "
563 <<server->getPlayerName(peer_id)<<std::endl;*/
564 //infostream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
567 If m_nearest_unsent_d was changed by the EmergeThread
568 (it can change it to 0 through SetBlockNotSent),
570 Else update m_nearest_unsent_d
572 /*if(m_nearest_unsent_d != last_nearest_unsent_d)
574 d = m_nearest_unsent_d;
575 last_nearest_unsent_d = m_nearest_unsent_d;
579 Get the border/face dot coordinates of a "d-radiused"
582 core::list<v3s16> list;
583 getFacePositions(list, d);
585 core::list<v3s16>::Iterator li;
586 for(li=list.begin(); li!=list.end(); li++)
588 v3s16 p = *li + center;
592 - Don't allow too many simultaneous transfers
593 - EXCEPT when the blocks are very close
595 Also, don't send blocks that are already flying.
598 // Start with the usual maximum
599 u16 max_simul_dynamic = max_simul_sends_usually;
601 // If block is very close, allow full maximum
602 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
603 max_simul_dynamic = max_simul_sends_setting;
605 // Don't select too many blocks for sending
606 if(num_blocks_selected >= max_simul_dynamic)
608 queue_is_full = true;
609 goto queue_full_break;
612 // Don't send blocks that are currently being transferred
613 if(m_blocks_sending.find(p) != NULL)
619 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
620 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
621 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
622 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
623 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
624 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
627 // If this is true, inexistent block will be made from scratch
628 bool generate = d <= d_max_gen;
631 /*// Limit the generating area vertically to 2/3
632 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
635 // Limit the send area vertically to 1/2
636 if(abs(p.Y - center.Y) > d_max / 2)
642 If block is far away, don't generate it unless it is
648 // Block center y in nodes
649 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
650 // Don't generate if it's very high or very low
651 if(y < -64 || y > 64)
655 v2s16 p2d_nodes_center(
659 // Get ground height in nodes
660 s16 gh = server->m_env->getServerMap().findGroundLevel(
663 // If differs a lot, don't generate
664 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
666 // Actually, don't even send it
672 //infostream<<"d="<<d<<std::endl;
675 Don't generate or send if not in sight
676 FIXME This only works if the client uses a small enough
677 FOV setting. The default of 72 degrees is fine.
680 float camera_fov = (72.0*M_PI/180) * 4./3.;
681 if(isBlockInSight(p, camera_pos, camera_dir, camera_fov, 10000*BS) == false)
687 Don't send already sent blocks
690 if(m_blocks_sent.find(p) != NULL)
697 Check if map has this block
699 MapBlock *block = server->m_env->getMap().getBlockNoCreateNoEx(p);
701 bool surely_not_found_on_disk = false;
702 bool block_is_invalid = false;
705 // Reset usage timer, this block will be of use in the future.
706 block->resetUsageTimer();
708 // Block is dummy if data doesn't exist.
709 // It means it has been not found from disk and not generated
712 surely_not_found_on_disk = true;
715 // Block is valid if lighting is up-to-date and data exists
716 if(block->isValid() == false)
718 block_is_invalid = true;
721 /*if(block->isFullyGenerated() == false)
723 block_is_invalid = true;
728 ServerMap *map = (ServerMap*)(&server->m_env->getMap());
729 v2s16 chunkpos = map->sector_to_chunk(p2d);
730 if(map->chunkNonVolatile(chunkpos) == false)
731 block_is_invalid = true;
733 if(block->isGenerated() == false)
734 block_is_invalid = true;
737 If block is not close, don't send it unless it is near
740 Block is near ground level if night-time mesh
741 differs from day-time mesh.
745 if(block->getDayNightDiff() == false)
752 If block has been marked to not exist on disk (dummy)
753 and generating new ones is not wanted, skip block.
755 if(generate == false && surely_not_found_on_disk == true)
762 Add inexistent block to emerge queue.
764 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
766 //TODO: Get value from somewhere
767 // Allow only one block in emerge queue
768 //if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
769 // Allow two blocks in queue per client
770 //if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
772 // Make it more responsive when needing to generate stuff
773 if(surely_not_found_on_disk)
775 if(server->m_emerge_queue.peerItemCount(peer_id) < max_emerge)
777 //infostream<<"Adding block to emerge queue"<<std::endl;
779 // Add it to the emerge queue and trigger the thread
782 if(generate == false)
783 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
785 server->m_emerge_queue.addBlock(peer_id, p, flags);
786 server->m_emergethread.trigger();
788 if(nearest_emerged_d == -1)
789 nearest_emerged_d = d;
791 if(nearest_emergefull_d == -1)
792 nearest_emergefull_d = d;
799 if(nearest_sent_d == -1)
803 Add block to send queue
806 /*errorstream<<"sending from d="<<d<<" to "
807 <<server->getPlayerName(peer_id)<<std::endl;*/
809 PrioritySortedBlockTransfer q((float)d, p, peer_id);
813 num_blocks_selected += 1;
818 //infostream<<"Stopped at "<<d<<std::endl;
820 // If nothing was found for sending and nothing was queued for
821 // emerging, continue next time browsing from here
822 if(nearest_emerged_d != -1){
823 new_nearest_unsent_d = nearest_emerged_d;
824 } else if(nearest_emergefull_d != -1){
825 new_nearest_unsent_d = nearest_emergefull_d;
827 if(d > g_settings->getS16("max_block_send_distance")){
828 new_nearest_unsent_d = 0;
829 m_nothing_to_send_pause_timer = 2.0;
830 /*infostream<<"GetNextBlocks(): d wrapped around for "
831 <<server->getPlayerName(peer_id)
832 <<"; setting to 0 and pausing"<<std::endl;*/
834 if(nearest_sent_d != -1)
835 new_nearest_unsent_d = nearest_sent_d;
837 new_nearest_unsent_d = d;
841 if(new_nearest_unsent_d != -1)
842 m_nearest_unsent_d = new_nearest_unsent_d;
844 /*timer_result = timer.stop(true);
845 if(timer_result != 0)
846 infostream<<"GetNextBlocks duration: "<<timer_result<<" (!=0)"<<std::endl;*/
849 void RemoteClient::GotBlock(v3s16 p)
851 if(m_blocks_sending.find(p) != NULL)
852 m_blocks_sending.remove(p);
855 /*infostream<<"RemoteClient::GotBlock(): Didn't find in"
856 " m_blocks_sending"<<std::endl;*/
857 m_excess_gotblocks++;
859 m_blocks_sent.insert(p, true);
862 void RemoteClient::SentBlock(v3s16 p)
864 if(m_blocks_sending.find(p) == NULL)
865 m_blocks_sending.insert(p, 0.0);
867 infostream<<"RemoteClient::SentBlock(): Sent block"
868 " already in m_blocks_sending"<<std::endl;
871 void RemoteClient::SetBlockNotSent(v3s16 p)
873 m_nearest_unsent_d = 0;
875 if(m_blocks_sending.find(p) != NULL)
876 m_blocks_sending.remove(p);
877 if(m_blocks_sent.find(p) != NULL)
878 m_blocks_sent.remove(p);
881 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
883 m_nearest_unsent_d = 0;
885 for(core::map<v3s16, MapBlock*>::Iterator
886 i = blocks.getIterator();
887 i.atEnd()==false; i++)
889 v3s16 p = i.getNode()->getKey();
891 if(m_blocks_sending.find(p) != NULL)
892 m_blocks_sending.remove(p);
893 if(m_blocks_sent.find(p) != NULL)
894 m_blocks_sent.remove(p);
902 PlayerInfo::PlayerInfo()
908 void PlayerInfo::PrintLine(std::ostream *s)
911 (*s)<<"\""<<name<<"\" ("
912 <<(position.X/10)<<","<<(position.Y/10)
913 <<","<<(position.Z/10)<<") ";
915 (*s)<<" avg_rtt="<<avg_rtt;
924 const std::string &path_world,
925 const std::string &path_config,
926 const SubgameSpec &gamespec,
927 bool simple_singleplayer_mode
929 m_path_world(path_world),
930 m_path_config(path_config),
931 m_gamespec(gamespec),
932 m_simple_singleplayer_mode(simple_singleplayer_mode),
933 m_async_fatal_error(""),
935 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
936 m_banmanager(path_world+DIR_DELIM+"ipban.txt"),
938 m_itemdef(createItemDefManager()),
939 m_nodedef(createNodeDefManager()),
940 m_craftdef(createCraftDefManager()),
941 m_event(new EventManager()),
943 m_emergethread(this),
944 m_time_of_day_send_timer(0),
946 m_shutdown_requested(false),
947 m_ignore_map_edit_events(false),
948 m_ignore_map_edit_events_peer_id(0)
950 m_liquid_transform_timer = 0.0;
951 m_print_info_timer = 0.0;
952 m_objectdata_timer = 0.0;
953 m_emergethread_trigger_timer = 0.0;
954 m_savemap_timer = 0.0;
958 m_step_dtime_mutex.Init();
962 throw ServerError("Supplied empty world path");
964 if(!gamespec.isValid())
965 throw ServerError("Supplied invalid gamespec");
967 infostream<<"Server created for gameid \""<<m_gamespec.id<<"\"";
968 if(m_simple_singleplayer_mode)
969 infostream<<" in simple singleplayer mode"<<std::endl;
971 infostream<<std::endl;
972 infostream<<"- world: "<<m_path_world<<std::endl;
973 infostream<<"- config: "<<m_path_config<<std::endl;
974 infostream<<"- game: "<<m_gamespec.path<<std::endl;
976 // Add world mod search path
977 m_modspaths.push_front(m_path_world + DIR_DELIM + "worldmods");
978 // Add addon mod search path
979 for(std::set<std::string>::const_iterator i = m_gamespec.mods_paths.begin();
980 i != m_gamespec.mods_paths.end(); i++)
981 m_modspaths.push_front((*i));
983 // Print out mod search paths
984 for(core::list<std::string>::Iterator i = m_modspaths.begin();
985 i != m_modspaths.end(); i++){
986 std::string modspath = *i;
987 infostream<<"- mods: "<<modspath<<std::endl;
990 // Path to builtin.lua
991 std::string builtinpath = getBuiltinLuaPath() + DIR_DELIM + "builtin.lua";
993 // Create world if it doesn't exist
994 if(!initializeWorld(m_path_world, m_gamespec.id))
995 throw ServerError("Failed to initialize world");
998 JMutexAutoLock envlock(m_env_mutex);
999 JMutexAutoLock conlock(m_con_mutex);
1001 // Initialize scripting
1003 infostream<<"Server: Initializing Lua"<<std::endl;
1004 m_lua = script_init();
1007 scriptapi_export(m_lua, this);
1008 // Load and run builtin.lua
1009 infostream<<"Server: Loading builtin.lua [\""
1010 <<builtinpath<<"\"]"<<std::endl;
1011 bool success = scriptapi_loadmod(m_lua, builtinpath, "__builtin");
1013 errorstream<<"Server: Failed to load and run "
1014 <<builtinpath<<std::endl;
1015 throw ModError("Failed to load and run "+builtinpath);
1017 // Find mods in mod search paths
1018 m_mods = getMods(m_modspaths);
1020 infostream<<"Server: Loading mods: ";
1021 for(core::list<ModSpec>::Iterator i = m_mods.begin();
1022 i != m_mods.end(); i++){
1023 const ModSpec &mod = *i;
1024 infostream<<mod.name<<" ";
1026 infostream<<std::endl;
1027 // Load and run "mod" scripts
1028 for(core::list<ModSpec>::Iterator i = m_mods.begin();
1029 i != m_mods.end(); i++){
1030 const ModSpec &mod = *i;
1031 std::string scriptpath = mod.path + DIR_DELIM + "init.lua";
1032 infostream<<" ["<<padStringRight(mod.name, 12)<<"] [\""
1033 <<scriptpath<<"\"]"<<std::endl;
1034 bool success = scriptapi_loadmod(m_lua, scriptpath, mod.name);
1036 errorstream<<"Server: Failed to load and run "
1037 <<scriptpath<<std::endl;
1038 throw ModError("Failed to load and run "+scriptpath);
1042 // Read Textures and calculate sha1 sums
1045 // Apply item aliases in the node definition manager
1046 m_nodedef->updateAliases(m_itemdef);
1048 // Initialize Environment
1050 m_env = new ServerEnvironment(new ServerMap(path_world, this), m_lua,
1053 // Give environment reference to scripting api
1054 scriptapi_add_environment(m_lua, m_env);
1056 // Register us to receive map edit events
1057 m_env->getMap().addEventReceiver(this);
1059 // If file exists, load environment metadata
1060 if(fs::PathExists(m_path_world+DIR_DELIM+"env_meta.txt"))
1062 infostream<<"Server: Loading environment metadata"<<std::endl;
1063 m_env->loadMeta(m_path_world);
1067 infostream<<"Server: Loading players"<<std::endl;
1068 m_env->deSerializePlayers(m_path_world);
1071 Add some test ActiveBlockModifiers to environment
1073 add_legacy_abms(m_env, m_nodedef);
1078 infostream<<"Server destructing"<<std::endl;
1081 Send shutdown message
1084 JMutexAutoLock conlock(m_con_mutex);
1086 std::wstring line = L"*** Server shutting down";
1089 Send the message to clients
1091 for(core::map<u16, RemoteClient*>::Iterator
1092 i = m_clients.getIterator();
1093 i.atEnd() == false; i++)
1095 // Get client and check that it is valid
1096 RemoteClient *client = i.getNode()->getValue();
1097 assert(client->peer_id == i.getNode()->getKey());
1098 if(client->serialization_version == SER_FMT_VER_INVALID)
1102 SendChatMessage(client->peer_id, line);
1104 catch(con::PeerNotFoundException &e)
1110 JMutexAutoLock envlock(m_env_mutex);
1115 infostream<<"Server: Saving players"<<std::endl;
1116 m_env->serializePlayers(m_path_world);
1119 Save environment metadata
1121 infostream<<"Server: Saving environment metadata"<<std::endl;
1122 m_env->saveMeta(m_path_world);
1134 JMutexAutoLock clientslock(m_con_mutex);
1136 for(core::map<u16, RemoteClient*>::Iterator
1137 i = m_clients.getIterator();
1138 i.atEnd() == false; i++)
1141 // NOTE: These are removed by env destructor
1143 u16 peer_id = i.getNode()->getKey();
1144 JMutexAutoLock envlock(m_env_mutex);
1145 m_env->removePlayer(peer_id);
1149 delete i.getNode()->getValue();
1153 // Delete things in the reverse order of creation
1160 // Deinitialize scripting
1161 infostream<<"Server: Deinitializing scripting"<<std::endl;
1162 script_deinit(m_lua);
1165 void Server::start(unsigned short port)
1167 DSTACK(__FUNCTION_NAME);
1168 infostream<<"Starting server on port "<<port<<"..."<<std::endl;
1170 // Stop thread if already running
1173 // Initialize connection
1174 m_con.SetTimeoutMs(30);
1178 m_thread.setRun(true);
1181 // ASCII art for the win!
1183 <<" .__ __ __ "<<std::endl
1184 <<" _____ |__| ____ _____/ |_ ____ _______/ |_ "<<std::endl
1185 <<" / \\| |/ \\_/ __ \\ __\\/ __ \\ / ___/\\ __\\"<<std::endl
1186 <<"| Y Y \\ | | \\ ___/| | \\ ___/ \\___ \\ | | "<<std::endl
1187 <<"|__|_| /__|___| /\\___ >__| \\___ >____ > |__| "<<std::endl
1188 <<" \\/ \\/ \\/ \\/ \\/ "<<std::endl;
1189 actionstream<<"World at ["<<m_path_world<<"]"<<std::endl;
1190 actionstream<<"Server for gameid=\""<<m_gamespec.id
1191 <<"\" listening on port "<<port<<"."<<std::endl;
1196 DSTACK(__FUNCTION_NAME);
1198 infostream<<"Server: Stopping and waiting threads"<<std::endl;
1200 // Stop threads (set run=false first so both start stopping)
1201 m_thread.setRun(false);
1202 m_emergethread.setRun(false);
1204 m_emergethread.stop();
1206 infostream<<"Server: Threads stopped"<<std::endl;
1209 void Server::step(float dtime)
1211 DSTACK(__FUNCTION_NAME);
1216 JMutexAutoLock lock(m_step_dtime_mutex);
1217 m_step_dtime += dtime;
1219 // Throw if fatal error occurred in thread
1220 std::string async_err = m_async_fatal_error.get();
1221 if(async_err != ""){
1222 throw ServerError(async_err);
1226 void Server::AsyncRunStep()
1228 DSTACK(__FUNCTION_NAME);
1230 g_profiler->add("Server::AsyncRunStep (num)", 1);
1234 JMutexAutoLock lock1(m_step_dtime_mutex);
1235 dtime = m_step_dtime;
1239 // Send blocks to clients
1246 g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
1248 //infostream<<"Server steps "<<dtime<<std::endl;
1249 //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1252 JMutexAutoLock lock1(m_step_dtime_mutex);
1253 m_step_dtime -= dtime;
1260 m_uptime.set(m_uptime.get() + dtime);
1264 // Process connection's timeouts
1265 JMutexAutoLock lock2(m_con_mutex);
1266 ScopeProfiler sp(g_profiler, "Server: connection timeout processing");
1267 m_con.RunTimeouts(dtime);
1271 // This has to be called so that the client list gets synced
1272 // with the peer list of the connection
1273 handlePeerChanges();
1277 Update time of day and overall game time
1280 JMutexAutoLock envlock(m_env_mutex);
1282 m_env->setTimeOfDaySpeed(g_settings->getFloat("time_speed"));
1285 Send to clients at constant intervals
1288 m_time_of_day_send_timer -= dtime;
1289 if(m_time_of_day_send_timer < 0.0)
1291 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
1293 //JMutexAutoLock envlock(m_env_mutex);
1294 JMutexAutoLock conlock(m_con_mutex);
1296 for(core::map<u16, RemoteClient*>::Iterator
1297 i = m_clients.getIterator();
1298 i.atEnd() == false; i++)
1300 RemoteClient *client = i.getNode()->getValue();
1301 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1302 m_env->getTimeOfDay(), g_settings->getFloat("time_speed"));
1304 m_con.Send(client->peer_id, 0, data, true);
1310 JMutexAutoLock lock(m_env_mutex);
1312 ScopeProfiler sp(g_profiler, "SEnv step");
1313 ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
1317 const float map_timer_and_unload_dtime = 2.92;
1318 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
1320 JMutexAutoLock lock(m_env_mutex);
1321 // Run Map's timers and unload unused data
1322 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
1323 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
1324 g_settings->getFloat("server_unload_unused_data_timeout"));
1335 JMutexAutoLock lock(m_env_mutex);
1336 JMutexAutoLock lock2(m_con_mutex);
1338 ScopeProfiler sp(g_profiler, "Server: handle players");
1340 for(core::map<u16, RemoteClient*>::Iterator
1341 i = m_clients.getIterator();
1342 i.atEnd() == false; i++)
1344 RemoteClient *client = i.getNode()->getValue();
1345 PlayerSAO *playersao = getPlayerSAO(client->peer_id);
1346 if(playersao == NULL)
1350 Handle player HPs (die if hp=0)
1352 if(playersao->getHP() == 0 && playersao->m_hp_not_sent)
1353 DiePlayer(client->peer_id);
1356 Send player inventories and HPs if necessary
1358 if(playersao->m_teleported){
1359 SendMovePlayer(client->peer_id);
1360 playersao->m_teleported = false;
1362 if(playersao->m_inventory_not_sent){
1363 UpdateCrafting(client->peer_id);
1364 SendInventory(client->peer_id);
1366 if(playersao->m_hp_not_sent){
1367 SendPlayerHP(client->peer_id);
1372 /* Transform liquids */
1373 m_liquid_transform_timer += dtime;
1374 if(m_liquid_transform_timer >= 1.00)
1376 m_liquid_transform_timer -= 1.00;
1378 JMutexAutoLock lock(m_env_mutex);
1380 ScopeProfiler sp(g_profiler, "Server: liquid transform");
1382 core::map<v3s16, MapBlock*> modified_blocks;
1383 m_env->getMap().transformLiquids(modified_blocks);
1388 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1389 ServerMap &map = ((ServerMap&)m_env->getMap());
1390 map.updateLighting(modified_blocks, lighting_modified_blocks);
1392 // Add blocks modified by lighting to modified_blocks
1393 for(core::map<v3s16, MapBlock*>::Iterator
1394 i = lighting_modified_blocks.getIterator();
1395 i.atEnd() == false; i++)
1397 MapBlock *block = i.getNode()->getValue();
1398 modified_blocks.insert(block->getPos(), block);
1402 Set the modified blocks unsent for all the clients
1405 JMutexAutoLock lock2(m_con_mutex);
1407 for(core::map<u16, RemoteClient*>::Iterator
1408 i = m_clients.getIterator();
1409 i.atEnd() == false; i++)
1411 RemoteClient *client = i.getNode()->getValue();
1413 if(modified_blocks.size() > 0)
1415 // Remove block from sent history
1416 client->SetBlocksNotSent(modified_blocks);
1421 // Periodically print some info
1423 float &counter = m_print_info_timer;
1429 JMutexAutoLock lock2(m_con_mutex);
1431 if(m_clients.size() != 0)
1432 infostream<<"Players:"<<std::endl;
1433 for(core::map<u16, RemoteClient*>::Iterator
1434 i = m_clients.getIterator();
1435 i.atEnd() == false; i++)
1437 //u16 peer_id = i.getNode()->getKey();
1438 RemoteClient *client = i.getNode()->getValue();
1439 Player *player = m_env->getPlayer(client->peer_id);
1442 infostream<<"* "<<player->getName()<<"\t";
1443 client->PrintInfo(infostream);
1448 //if(g_settings->getBool("enable_experimental"))
1452 Check added and deleted active objects
1455 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
1456 JMutexAutoLock envlock(m_env_mutex);
1457 JMutexAutoLock conlock(m_con_mutex);
1459 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
1461 // Radius inside which objects are active
1462 s16 radius = g_settings->getS16("active_object_send_range_blocks");
1463 radius *= MAP_BLOCKSIZE;
1465 for(core::map<u16, RemoteClient*>::Iterator
1466 i = m_clients.getIterator();
1467 i.atEnd() == false; i++)
1469 RemoteClient *client = i.getNode()->getValue();
1471 // If definitions and textures have not been sent, don't
1472 // send objects either
1473 if(!client->definitions_sent)
1476 Player *player = m_env->getPlayer(client->peer_id);
1479 // This can happen if the client timeouts somehow
1480 /*infostream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
1482 <<" has no associated player"<<std::endl;*/
1485 v3s16 pos = floatToInt(player->getPosition(), BS);
1487 core::map<u16, bool> removed_objects;
1488 core::map<u16, bool> added_objects;
1489 m_env->getRemovedActiveObjects(pos, radius,
1490 client->m_known_objects, removed_objects);
1491 m_env->getAddedActiveObjects(pos, radius,
1492 client->m_known_objects, added_objects);
1494 // Ignore if nothing happened
1495 if(removed_objects.size() == 0 && added_objects.size() == 0)
1497 //infostream<<"active objects: none changed"<<std::endl;
1501 std::string data_buffer;
1505 // Handle removed objects
1506 writeU16((u8*)buf, removed_objects.size());
1507 data_buffer.append(buf, 2);
1508 for(core::map<u16, bool>::Iterator
1509 i = removed_objects.getIterator();
1510 i.atEnd()==false; i++)
1513 u16 id = i.getNode()->getKey();
1514 ServerActiveObject* obj = m_env->getActiveObject(id);
1516 // Add to data buffer for sending
1517 writeU16((u8*)buf, i.getNode()->getKey());
1518 data_buffer.append(buf, 2);
1520 // Remove from known objects
1521 client->m_known_objects.remove(i.getNode()->getKey());
1523 if(obj && obj->m_known_by_count > 0)
1524 obj->m_known_by_count--;
1527 // Handle added objects
1528 writeU16((u8*)buf, added_objects.size());
1529 data_buffer.append(buf, 2);
1530 for(core::map<u16, bool>::Iterator
1531 i = added_objects.getIterator();
1532 i.atEnd()==false; i++)
1535 u16 id = i.getNode()->getKey();
1536 ServerActiveObject* obj = m_env->getActiveObject(id);
1539 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1541 infostream<<"WARNING: "<<__FUNCTION_NAME
1542 <<": NULL object"<<std::endl;
1544 type = obj->getSendType();
1546 // Add to data buffer for sending
1547 writeU16((u8*)buf, id);
1548 data_buffer.append(buf, 2);
1549 writeU8((u8*)buf, type);
1550 data_buffer.append(buf, 1);
1553 data_buffer.append(serializeLongString(
1554 obj->getClientInitializationData()));
1556 data_buffer.append(serializeLongString(""));
1558 // Add to known objects
1559 client->m_known_objects.insert(i.getNode()->getKey(), false);
1562 obj->m_known_by_count++;
1566 SharedBuffer<u8> reply(2 + data_buffer.size());
1567 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1568 memcpy((char*)&reply[2], data_buffer.c_str(),
1569 data_buffer.size());
1571 m_con.Send(client->peer_id, 0, reply, true);
1573 verbosestream<<"Server: Sent object remove/add: "
1574 <<removed_objects.size()<<" removed, "
1575 <<added_objects.size()<<" added, "
1576 <<"packet size is "<<reply.getSize()<<std::endl;
1581 Collect a list of all the objects known by the clients
1582 and report it back to the environment.
1585 core::map<u16, bool> all_known_objects;
1587 for(core::map<u16, RemoteClient*>::Iterator
1588 i = m_clients.getIterator();
1589 i.atEnd() == false; i++)
1591 RemoteClient *client = i.getNode()->getValue();
1592 // Go through all known objects of client
1593 for(core::map<u16, bool>::Iterator
1594 i = client->m_known_objects.getIterator();
1595 i.atEnd()==false; i++)
1597 u16 id = i.getNode()->getKey();
1598 all_known_objects[id] = true;
1602 m_env->setKnownActiveObjects(whatever);
1608 Send object messages
1611 JMutexAutoLock envlock(m_env_mutex);
1612 JMutexAutoLock conlock(m_con_mutex);
1614 ScopeProfiler sp(g_profiler, "Server: sending object messages");
1617 // Value = data sent by object
1618 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1620 // Get active object messages from environment
1623 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
1627 core::list<ActiveObjectMessage>* message_list = NULL;
1628 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1629 n = buffered_messages.find(aom.id);
1632 message_list = new core::list<ActiveObjectMessage>;
1633 buffered_messages.insert(aom.id, message_list);
1637 message_list = n->getValue();
1639 message_list->push_back(aom);
1642 // Route data to every client
1643 for(core::map<u16, RemoteClient*>::Iterator
1644 i = m_clients.getIterator();
1645 i.atEnd()==false; i++)
1647 RemoteClient *client = i.getNode()->getValue();
1648 std::string reliable_data;
1649 std::string unreliable_data;
1650 // Go through all objects in message buffer
1651 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1652 j = buffered_messages.getIterator();
1653 j.atEnd()==false; j++)
1655 // If object is not known by client, skip it
1656 u16 id = j.getNode()->getKey();
1657 if(client->m_known_objects.find(id) == NULL)
1659 // Get message list of object
1660 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1661 // Go through every message
1662 for(core::list<ActiveObjectMessage>::Iterator
1663 k = list->begin(); k != list->end(); k++)
1665 // Compose the full new data with header
1666 ActiveObjectMessage aom = *k;
1667 std::string new_data;
1670 writeU16((u8*)&buf[0], aom.id);
1671 new_data.append(buf, 2);
1673 new_data += serializeString(aom.datastring);
1674 // Add data to buffer
1676 reliable_data += new_data;
1678 unreliable_data += new_data;
1682 reliable_data and unreliable_data are now ready.
1685 if(reliable_data.size() > 0)
1687 SharedBuffer<u8> reply(2 + reliable_data.size());
1688 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1689 memcpy((char*)&reply[2], reliable_data.c_str(),
1690 reliable_data.size());
1692 m_con.Send(client->peer_id, 0, reply, true);
1694 if(unreliable_data.size() > 0)
1696 SharedBuffer<u8> reply(2 + unreliable_data.size());
1697 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1698 memcpy((char*)&reply[2], unreliable_data.c_str(),
1699 unreliable_data.size());
1700 // Send as unreliable
1701 m_con.Send(client->peer_id, 0, reply, false);
1704 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1706 infostream<<"Server: Size of object message data: "
1707 <<"reliable: "<<reliable_data.size()
1708 <<", unreliable: "<<unreliable_data.size()
1713 // Clear buffered_messages
1714 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1715 i = buffered_messages.getIterator();
1716 i.atEnd()==false; i++)
1718 delete i.getNode()->getValue();
1722 } // enable_experimental
1725 Send queued-for-sending map edit events.
1728 // We will be accessing the environment and the connection
1729 JMutexAutoLock lock(m_env_mutex);
1730 JMutexAutoLock conlock(m_con_mutex);
1732 // Don't send too many at a time
1735 // Single change sending is disabled if queue size is not small
1736 bool disable_single_change_sending = false;
1737 if(m_unsent_map_edit_queue.size() >= 4)
1738 disable_single_change_sending = true;
1740 int event_count = m_unsent_map_edit_queue.size();
1742 // We'll log the amount of each
1745 while(m_unsent_map_edit_queue.size() != 0)
1747 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1749 // Players far away from the change are stored here.
1750 // Instead of sending the changes, MapBlocks are set not sent
1752 core::list<u16> far_players;
1754 if(event->type == MEET_ADDNODE)
1756 //infostream<<"Server: MEET_ADDNODE"<<std::endl;
1757 prof.add("MEET_ADDNODE", 1);
1758 if(disable_single_change_sending)
1759 sendAddNode(event->p, event->n, event->already_known_by_peer,
1762 sendAddNode(event->p, event->n, event->already_known_by_peer,
1765 else if(event->type == MEET_REMOVENODE)
1767 //infostream<<"Server: MEET_REMOVENODE"<<std::endl;
1768 prof.add("MEET_REMOVENODE", 1);
1769 if(disable_single_change_sending)
1770 sendRemoveNode(event->p, event->already_known_by_peer,
1773 sendRemoveNode(event->p, event->already_known_by_peer,
1776 else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
1778 infostream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
1779 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
1780 setBlockNotSent(event->p);
1782 else if(event->type == MEET_OTHER)
1784 infostream<<"Server: MEET_OTHER"<<std::endl;
1785 prof.add("MEET_OTHER", 1);
1786 for(core::map<v3s16, bool>::Iterator
1787 i = event->modified_blocks.getIterator();
1788 i.atEnd()==false; i++)
1790 v3s16 p = i.getNode()->getKey();
1796 prof.add("unknown", 1);
1797 infostream<<"WARNING: Server: Unknown MapEditEvent "
1798 <<((u32)event->type)<<std::endl;
1802 Set blocks not sent to far players
1804 if(far_players.size() > 0)
1806 // Convert list format to that wanted by SetBlocksNotSent
1807 core::map<v3s16, MapBlock*> modified_blocks2;
1808 for(core::map<v3s16, bool>::Iterator
1809 i = event->modified_blocks.getIterator();
1810 i.atEnd()==false; i++)
1812 v3s16 p = i.getNode()->getKey();
1813 modified_blocks2.insert(p,
1814 m_env->getMap().getBlockNoCreateNoEx(p));
1816 // Set blocks not sent
1817 for(core::list<u16>::Iterator
1818 i = far_players.begin();
1819 i != far_players.end(); i++)
1822 RemoteClient *client = getClient(peer_id);
1825 client->SetBlocksNotSent(modified_blocks2);
1831 /*// Don't send too many at a time
1833 if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
1837 if(event_count >= 5){
1838 infostream<<"Server: MapEditEvents:"<<std::endl;
1839 prof.print(infostream);
1840 } else if(event_count != 0){
1841 verbosestream<<"Server: MapEditEvents:"<<std::endl;
1842 prof.print(verbosestream);
1848 Trigger emergethread (it somehow gets to a non-triggered but
1849 bysy state sometimes)
1852 float &counter = m_emergethread_trigger_timer;
1858 m_emergethread.trigger();
1862 // Save map, players and auth stuff
1864 float &counter = m_savemap_timer;
1866 if(counter >= g_settings->getFloat("server_map_save_interval"))
1869 JMutexAutoLock lock(m_env_mutex);
1871 ScopeProfiler sp(g_profiler, "Server: saving stuff");
1874 if(m_banmanager.isModified())
1875 m_banmanager.save();
1877 // Save changed parts of map
1878 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
1881 m_env->serializePlayers(m_path_world);
1883 // Save environment metadata
1884 m_env->saveMeta(m_path_world);
1889 void Server::Receive()
1891 DSTACK(__FUNCTION_NAME);
1892 SharedBuffer<u8> data;
1897 JMutexAutoLock conlock(m_con_mutex);
1898 datasize = m_con.Receive(peer_id, data);
1901 // This has to be called so that the client list gets synced
1902 // with the peer list of the connection
1903 handlePeerChanges();
1905 ProcessData(*data, datasize, peer_id);
1907 catch(con::InvalidIncomingDataException &e)
1909 infostream<<"Server::Receive(): "
1910 "InvalidIncomingDataException: what()="
1911 <<e.what()<<std::endl;
1913 catch(con::PeerNotFoundException &e)
1915 //NOTE: This is not needed anymore
1917 // The peer has been disconnected.
1918 // Find the associated player and remove it.
1920 /*JMutexAutoLock envlock(m_env_mutex);
1922 infostream<<"ServerThread: peer_id="<<peer_id
1923 <<" has apparently closed connection. "
1924 <<"Removing player."<<std::endl;
1926 m_env->removePlayer(peer_id);*/
1930 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1932 DSTACK(__FUNCTION_NAME);
1933 // Environment is locked first.
1934 JMutexAutoLock envlock(m_env_mutex);
1935 JMutexAutoLock conlock(m_con_mutex);
1937 ScopeProfiler sp(g_profiler, "Server::ProcessData");
1940 Address address = m_con.GetPeerAddress(peer_id);
1941 std::string addr_s = address.serializeString();
1943 // drop player if is ip is banned
1944 if(m_banmanager.isIpBanned(addr_s)){
1945 infostream<<"Server: A banned client tried to connect from "
1946 <<addr_s<<"; banned name was "
1947 <<m_banmanager.getBanName(addr_s)<<std::endl;
1948 // This actually doesn't seem to transfer to the client
1949 SendAccessDenied(m_con, peer_id,
1950 L"Your ip is banned. Banned name was "
1951 +narrow_to_wide(m_banmanager.getBanName(addr_s)));
1952 m_con.DeletePeer(peer_id);
1956 catch(con::PeerNotFoundException &e)
1958 infostream<<"Server::ProcessData(): Cancelling: peer "
1959 <<peer_id<<" not found"<<std::endl;
1963 std::string addr_s = m_con.GetPeerAddress(peer_id).serializeString();
1965 u8 peer_ser_ver = getClient(peer_id)->serialization_version;
1973 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1975 if(command == TOSERVER_INIT)
1977 // [0] u16 TOSERVER_INIT
1978 // [2] u8 SER_FMT_VER_HIGHEST
1979 // [3] u8[20] player_name
1980 // [23] u8[28] password <--- can be sent without this, from old versions
1982 if(datasize < 2+1+PLAYERNAME_SIZE)
1985 verbosestream<<"Server: Got TOSERVER_INIT from "
1986 <<peer_id<<std::endl;
1988 // First byte after command is maximum supported
1989 // serialization version
1990 u8 client_max = data[2];
1991 u8 our_max = SER_FMT_VER_HIGHEST;
1992 // Use the highest version supported by both
1993 u8 deployed = core::min_(client_max, our_max);
1994 // If it's lower than the lowest supported, give up.
1995 if(deployed < SER_FMT_VER_LOWEST)
1996 deployed = SER_FMT_VER_INVALID;
1998 //peer->serialization_version = deployed;
1999 getClient(peer_id)->pending_serialization_version = deployed;
2001 if(deployed == SER_FMT_VER_INVALID)
2003 actionstream<<"Server: A mismatched client tried to connect from "
2004 <<addr_s<<std::endl;
2005 infostream<<"Server: Cannot negotiate "
2006 "serialization version with peer "
2007 <<peer_id<<std::endl;
2008 SendAccessDenied(m_con, peer_id, std::wstring(
2009 L"Your client's version is not supported.\n"
2010 L"Server version is ")
2011 + narrow_to_wide(VERSION_STRING) + L"."
2017 Read and check network protocol version
2020 u16 net_proto_version = 0;
2021 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
2023 net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
2026 getClient(peer_id)->net_proto_version = net_proto_version;
2028 if(net_proto_version == 0)
2030 actionstream<<"Server: An old tried to connect from "<<addr_s
2032 SendAccessDenied(m_con, peer_id, std::wstring(
2033 L"Your client's version is not supported.\n"
2034 L"Server version is ")
2035 + narrow_to_wide(VERSION_STRING) + L"."
2040 if(g_settings->getBool("strict_protocol_version_checking"))
2042 if(net_proto_version != PROTOCOL_VERSION)
2044 actionstream<<"Server: A mismatched client tried to connect"
2045 <<" from "<<addr_s<<std::endl;
2046 SendAccessDenied(m_con, peer_id, std::wstring(
2047 L"Your client's version is not supported.\n"
2048 L"Server version is ")
2049 + narrow_to_wide(VERSION_STRING) + L",\n"
2050 + L"server's PROTOCOL_VERSION is "
2051 + narrow_to_wide(itos(PROTOCOL_VERSION))
2052 + L", client's PROTOCOL_VERSION is "
2053 + narrow_to_wide(itos(net_proto_version))
2064 char playername[PLAYERNAME_SIZE];
2065 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
2067 playername[i] = data[3+i];
2069 playername[PLAYERNAME_SIZE-1] = 0;
2071 if(playername[0]=='\0')
2073 actionstream<<"Server: Player with an empty name "
2074 <<"tried to connect from "<<addr_s<<std::endl;
2075 SendAccessDenied(m_con, peer_id,
2080 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
2082 actionstream<<"Server: Player with an invalid name "
2083 <<"tried to connect from "<<addr_s<<std::endl;
2084 SendAccessDenied(m_con, peer_id,
2085 L"Name contains unallowed characters");
2089 infostream<<"Server: New connection: \""<<playername<<"\" from "
2090 <<m_con.GetPeerAddress(peer_id).serializeString()<<std::endl;
2093 char given_password[PASSWORD_SIZE];
2094 if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
2096 // old version - assume blank password
2097 given_password[0] = 0;
2101 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2103 given_password[i] = data[23+i];
2105 given_password[PASSWORD_SIZE-1] = 0;
2108 if(!base64_is_valid(given_password)){
2109 infostream<<"Server: "<<playername
2110 <<" supplied invalid password hash"<<std::endl;
2111 SendAccessDenied(m_con, peer_id, L"Invalid password hash");
2115 std::string checkpwd; // Password hash to check against
2116 bool has_auth = scriptapi_get_auth(m_lua, playername, &checkpwd, NULL);
2118 // If no authentication info exists for user, create it
2120 if(!isSingleplayer() &&
2121 g_settings->getBool("disallow_empty_password") &&
2122 std::string(given_password) == ""){
2123 SendAccessDenied(m_con, peer_id, L"Empty passwords are "
2124 L"disallowed. Set a password and try again.");
2127 std::wstring raw_default_password =
2128 narrow_to_wide(g_settings->get("default_password"));
2129 std::string initial_password =
2130 translatePassword(playername, raw_default_password);
2132 // If default_password is empty, allow any initial password
2133 if (raw_default_password.length() == 0)
2134 initial_password = given_password;
2136 scriptapi_create_auth(m_lua, playername, initial_password);
2139 has_auth = scriptapi_get_auth(m_lua, playername, &checkpwd, NULL);
2142 SendAccessDenied(m_con, peer_id, L"Not allowed to login");
2146 if(given_password != checkpwd){
2147 infostream<<"Server: peer_id="<<peer_id
2148 <<": supplied invalid password for "
2149 <<playername<<std::endl;
2150 SendAccessDenied(m_con, peer_id, L"Invalid password");
2154 // Do not allow multiple players in simple singleplayer mode.
2155 // This isn't a perfect way to do it, but will suffice for now.
2156 if(m_simple_singleplayer_mode && m_clients.size() > 1){
2157 infostream<<"Server: Not allowing another client to connect in"
2158 <<" simple singleplayer mode"<<std::endl;
2159 SendAccessDenied(m_con, peer_id,
2160 L"Running in simple singleplayer mode.");
2164 // Enforce user limit.
2165 // Don't enforce for users that have some admin right
2166 if(m_clients.size() >= g_settings->getU16("max_users") &&
2167 !checkPriv(playername, "server") &&
2168 !checkPriv(playername, "ban") &&
2169 !checkPriv(playername, "privs") &&
2170 !checkPriv(playername, "password") &&
2171 playername != g_settings->get("name"))
2173 actionstream<<"Server: "<<playername<<" tried to join, but there"
2174 <<" are already max_users="
2175 <<g_settings->getU16("max_users")<<" players."<<std::endl;
2176 SendAccessDenied(m_con, peer_id, L"Too many users.");
2181 PlayerSAO *playersao = emergePlayer(playername, peer_id);
2183 // If failed, cancel
2184 if(playersao == NULL)
2186 errorstream<<"Server: peer_id="<<peer_id
2187 <<": failed to emerge player"<<std::endl;
2192 Answer with a TOCLIENT_INIT
2195 SharedBuffer<u8> reply(2+1+6+8);
2196 writeU16(&reply[0], TOCLIENT_INIT);
2197 writeU8(&reply[2], deployed);
2198 writeV3S16(&reply[2+1], floatToInt(playersao->getPlayer()->getPosition()+v3f(0,BS/2,0), BS));
2199 writeU64(&reply[2+1+6], m_env->getServerMap().getSeed());
2202 m_con.Send(peer_id, 0, reply, true);
2206 Send complete position information
2208 SendMovePlayer(peer_id);
2213 if(command == TOSERVER_INIT2)
2215 verbosestream<<"Server: Got TOSERVER_INIT2 from "
2216 <<peer_id<<std::endl;
2218 Player *player = m_env->getPlayer(peer_id);
2220 verbosestream<<"Server: TOSERVER_INIT2: "
2221 <<"Player not found; ignoring."<<std::endl;
2225 getClient(peer_id)->serialization_version
2226 = getClient(peer_id)->pending_serialization_version;
2229 Send some initialization data
2232 infostream<<"Server: Sending content to "
2233 <<getPlayerName(peer_id)<<std::endl;
2235 // Send item definitions
2236 SendItemDef(m_con, peer_id, m_itemdef);
2238 // Send node definitions
2239 SendNodeDef(m_con, peer_id, m_nodedef);
2241 // Send media announcement
2242 sendMediaAnnouncement(peer_id);
2245 SendPlayerPrivileges(peer_id);
2247 // Send inventory formspec
2248 SendPlayerInventoryFormspec(peer_id);
2251 UpdateCrafting(peer_id);
2252 SendInventory(peer_id);
2255 SendPlayerHP(peer_id);
2257 // Show death screen if necessary
2259 SendDeathscreen(m_con, peer_id, false, v3f(0,0,0));
2263 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2264 m_env->getTimeOfDay(), g_settings->getFloat("time_speed"));
2265 m_con.Send(peer_id, 0, data, true);
2268 // Note things in chat if not in simple singleplayer mode
2269 if(!m_simple_singleplayer_mode)
2271 // Send information about server to player in chat
2272 SendChatMessage(peer_id, getStatusString());
2274 // Send information about joining in chat
2276 std::wstring name = L"unknown";
2277 Player *player = m_env->getPlayer(peer_id);
2279 name = narrow_to_wide(player->getName());
2281 std::wstring message;
2284 message += L" joined the game.";
2285 BroadcastChatMessage(message);
2289 // Warnings about protocol version can be issued here
2290 if(getClient(peer_id)->net_proto_version < PROTOCOL_VERSION)
2292 SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT IS OLD AND MAY WORK PROPERLY WITH THIS SERVER!");
2299 std::ostringstream os(std::ios_base::binary);
2300 for(core::map<u16, RemoteClient*>::Iterator
2301 i = m_clients.getIterator();
2302 i.atEnd() == false; i++)
2304 RemoteClient *client = i.getNode()->getValue();
2305 assert(client->peer_id == i.getNode()->getKey());
2306 if(client->serialization_version == SER_FMT_VER_INVALID)
2309 Player *player = m_env->getPlayer(client->peer_id);
2312 // Get name of player
2313 os<<player->getName()<<" ";
2316 actionstream<<player->getName()<<" joins game. List of players: "
2317 <<os.str()<<std::endl;
2323 if(peer_ser_ver == SER_FMT_VER_INVALID)
2325 infostream<<"Server::ProcessData(): Cancelling: Peer"
2326 " serialization format invalid or not initialized."
2327 " Skipping incoming command="<<command<<std::endl;
2331 Player *player = m_env->getPlayer(peer_id);
2333 infostream<<"Server::ProcessData(): Cancelling: "
2334 "No player for peer_id="<<peer_id
2339 PlayerSAO *playersao = player->getPlayerSAO();
2340 if(playersao == NULL){
2341 infostream<<"Server::ProcessData(): Cancelling: "
2342 "No player object for peer_id="<<peer_id
2347 if(command == TOSERVER_PLAYERPOS)
2349 if(datasize < 2+12+12+4+4)
2353 v3s32 ps = readV3S32(&data[start+2]);
2354 v3s32 ss = readV3S32(&data[start+2+12]);
2355 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2356 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2357 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2358 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2359 pitch = wrapDegrees(pitch);
2360 yaw = wrapDegrees(yaw);
2362 player->setPosition(position);
2363 player->setSpeed(speed);
2364 player->setPitch(pitch);
2365 player->setYaw(yaw);
2367 /*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2368 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2369 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2371 else if(command == TOSERVER_GOTBLOCKS)
2384 u16 count = data[2];
2385 for(u16 i=0; i<count; i++)
2387 if((s16)datasize < 2+1+(i+1)*6)
2388 throw con::InvalidIncomingDataException
2389 ("GOTBLOCKS length is too short");
2390 v3s16 p = readV3S16(&data[2+1+i*6]);
2391 /*infostream<<"Server: GOTBLOCKS ("
2392 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2393 RemoteClient *client = getClient(peer_id);
2394 client->GotBlock(p);
2397 else if(command == TOSERVER_DELETEDBLOCKS)
2410 u16 count = data[2];
2411 for(u16 i=0; i<count; i++)
2413 if((s16)datasize < 2+1+(i+1)*6)
2414 throw con::InvalidIncomingDataException
2415 ("DELETEDBLOCKS length is too short");
2416 v3s16 p = readV3S16(&data[2+1+i*6]);
2417 /*infostream<<"Server: DELETEDBLOCKS ("
2418 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2419 RemoteClient *client = getClient(peer_id);
2420 client->SetBlockNotSent(p);
2423 else if(command == TOSERVER_CLICK_OBJECT)
2425 infostream<<"Server: CLICK_OBJECT not supported anymore"<<std::endl;
2428 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2430 infostream<<"Server: CLICK_ACTIVEOBJECT not supported anymore"<<std::endl;
2433 else if(command == TOSERVER_GROUND_ACTION)
2435 infostream<<"Server: GROUND_ACTION not supported anymore"<<std::endl;
2439 else if(command == TOSERVER_RELEASE)
2441 infostream<<"Server: RELEASE not supported anymore"<<std::endl;
2444 else if(command == TOSERVER_SIGNTEXT)
2446 infostream<<"Server: SIGNTEXT not supported anymore"
2450 else if(command == TOSERVER_SIGNNODETEXT)
2452 infostream<<"Server: SIGNNODETEXT not supported anymore"
2456 else if(command == TOSERVER_INVENTORY_ACTION)
2458 // Strip command and create a stream
2459 std::string datastring((char*)&data[2], datasize-2);
2460 verbosestream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2461 std::istringstream is(datastring, std::ios_base::binary);
2463 InventoryAction *a = InventoryAction::deSerialize(is);
2466 infostream<<"TOSERVER_INVENTORY_ACTION: "
2467 <<"InventoryAction::deSerialize() returned NULL"
2473 Note: Always set inventory not sent, to repair cases
2474 where the client made a bad prediction.
2478 Handle restrictions and special cases of the move action
2480 if(a->getType() == IACTION_MOVE)
2482 IMoveAction *ma = (IMoveAction*)a;
2484 ma->from_inv.applyCurrentPlayer(player->getName());
2485 ma->to_inv.applyCurrentPlayer(player->getName());
2487 setInventoryModified(ma->from_inv);
2488 setInventoryModified(ma->to_inv);
2490 bool from_inv_is_current_player =
2491 (ma->from_inv.type == InventoryLocation::PLAYER) &&
2492 (ma->from_inv.name == player->getName());
2494 bool to_inv_is_current_player =
2495 (ma->to_inv.type == InventoryLocation::PLAYER) &&
2496 (ma->to_inv.name == player->getName());
2499 Disable moving items out of craftpreview
2501 if(ma->from_list == "craftpreview")
2503 infostream<<"Ignoring IMoveAction from "
2504 <<(ma->from_inv.dump())<<":"<<ma->from_list
2505 <<" to "<<(ma->to_inv.dump())<<":"<<ma->to_list
2506 <<" because src is "<<ma->from_list<<std::endl;
2512 Disable moving items into craftresult and craftpreview
2514 if(ma->to_list == "craftpreview" || ma->to_list == "craftresult")
2516 infostream<<"Ignoring IMoveAction from "
2517 <<(ma->from_inv.dump())<<":"<<ma->from_list
2518 <<" to "<<(ma->to_inv.dump())<<":"<<ma->to_list
2519 <<" because dst is "<<ma->to_list<<std::endl;
2524 // Disallow moving items in elsewhere than player's inventory
2525 // if not allowed to interact
2526 if(!checkPriv(player->getName(), "interact") &&
2527 (!from_inv_is_current_player ||
2528 !to_inv_is_current_player))
2530 infostream<<"Cannot move outside of player's inventory: "
2531 <<"No interact privilege"<<std::endl;
2536 // If player is not an admin, check for ownership of src and dst
2537 /*if(!checkPriv(player->getName(), "server"))
2539 std::string owner_from = getInventoryOwner(ma->from_inv);
2540 if(owner_from != "" && owner_from != player->getName())
2542 infostream<<"WARNING: "<<player->getName()
2543 <<" tried to access an inventory that"
2544 <<" belongs to "<<owner_from<<std::endl;
2549 std::string owner_to = getInventoryOwner(ma->to_inv);
2550 if(owner_to != "" && owner_to != player->getName())
2552 infostream<<"WARNING: "<<player->getName()
2553 <<" tried to access an inventory that"
2554 <<" belongs to "<<owner_to<<std::endl;
2561 Handle restrictions and special cases of the drop action
2563 else if(a->getType() == IACTION_DROP)
2565 IDropAction *da = (IDropAction*)a;
2567 da->from_inv.applyCurrentPlayer(player->getName());
2569 setInventoryModified(da->from_inv);
2571 // Disallow dropping items if not allowed to interact
2572 if(!checkPriv(player->getName(), "interact"))
2577 // If player is not an admin, check for ownership
2578 /*else if(!checkPriv(player->getName(), "server"))
2580 std::string owner_from = getInventoryOwner(da->from_inv);
2581 if(owner_from != "" && owner_from != player->getName())
2583 infostream<<"WARNING: "<<player->getName()
2584 <<" tried to access an inventory that"
2585 <<" belongs to "<<owner_from<<std::endl;
2592 Handle restrictions and special cases of the craft action
2594 else if(a->getType() == IACTION_CRAFT)
2596 ICraftAction *ca = (ICraftAction*)a;
2598 ca->craft_inv.applyCurrentPlayer(player->getName());
2600 setInventoryModified(ca->craft_inv);
2602 //bool craft_inv_is_current_player =
2603 // (ca->craft_inv.type == InventoryLocation::PLAYER) &&
2604 // (ca->craft_inv.name == player->getName());
2606 // Disallow crafting if not allowed to interact
2607 if(!checkPriv(player->getName(), "interact"))
2609 infostream<<"Cannot craft: "
2610 <<"No interact privilege"<<std::endl;
2615 // If player is not an admin, check for ownership of inventory
2616 /*if(!checkPriv(player->getName(), "server"))
2618 std::string owner_craft = getInventoryOwner(ca->craft_inv);
2619 if(owner_craft != "" && owner_craft != player->getName())
2621 infostream<<"WARNING: "<<player->getName()
2622 <<" tried to access an inventory that"
2623 <<" belongs to "<<owner_craft<<std::endl;
2631 a->apply(this, playersao, this);
2635 else if(command == TOSERVER_CHAT_MESSAGE)
2643 std::string datastring((char*)&data[2], datasize-2);
2644 std::istringstream is(datastring, std::ios_base::binary);
2647 is.read((char*)buf, 2);
2648 u16 len = readU16(buf);
2650 std::wstring message;
2651 for(u16 i=0; i<len; i++)
2653 is.read((char*)buf, 2);
2654 message += (wchar_t)readU16(buf);
2657 // Get player name of this client
2658 std::wstring name = narrow_to_wide(player->getName());
2661 bool ate = scriptapi_on_chat_message(m_lua, player->getName(),
2662 wide_to_narrow(message));
2663 // If script ate the message, don't proceed
2667 // Line to send to players
2669 // Whether to send to the player that sent the line
2670 bool send_to_sender = false;
2671 // Whether to send to other players
2672 bool send_to_others = false;
2675 if(message[0] == L'/')
2677 size_t strip_size = 1;
2678 if (message[1] == L'#') // support old-style commans
2680 message = message.substr(strip_size);
2682 WStrfnd f1(message);
2683 f1.next(L" "); // Skip over /#whatever
2684 std::wstring paramstring = f1.next(L"");
2686 ServerCommandContext *ctx = new ServerCommandContext(
2687 str_split(message, L' '),
2693 std::wstring reply(processServerCommand(ctx));
2694 send_to_sender = ctx->flags & SEND_TO_SENDER;
2695 send_to_others = ctx->flags & SEND_TO_OTHERS;
2697 if (ctx->flags & SEND_NO_PREFIX)
2700 line += L"Server: " + reply;
2707 if(checkPriv(player->getName(), "shout")){
2712 send_to_others = true;
2714 line += L"-!- You don't have permission to shout.";
2715 send_to_sender = true;
2722 actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2725 Send the message to clients
2727 for(core::map<u16, RemoteClient*>::Iterator
2728 i = m_clients.getIterator();
2729 i.atEnd() == false; i++)
2731 // Get client and check that it is valid
2732 RemoteClient *client = i.getNode()->getValue();
2733 assert(client->peer_id == i.getNode()->getKey());
2734 if(client->serialization_version == SER_FMT_VER_INVALID)
2738 bool sender_selected = (peer_id == client->peer_id);
2739 if(sender_selected == true && send_to_sender == false)
2741 if(sender_selected == false && send_to_others == false)
2744 SendChatMessage(client->peer_id, line);
2748 else if(command == TOSERVER_DAMAGE)
2750 std::string datastring((char*)&data[2], datasize-2);
2751 std::istringstream is(datastring, std::ios_base::binary);
2752 u8 damage = readU8(is);
2754 actionstream<<player->getName()<<" damaged by "
2755 <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
2758 playersao->setHP(playersao->getHP() - damage);
2760 if(playersao->getHP() == 0 && playersao->m_hp_not_sent)
2763 if(playersao->m_hp_not_sent)
2764 SendPlayerHP(peer_id);
2766 else if(command == TOSERVER_PASSWORD)
2769 [0] u16 TOSERVER_PASSWORD
2770 [2] u8[28] old password
2771 [30] u8[28] new password
2774 if(datasize != 2+PASSWORD_SIZE*2)
2776 /*char password[PASSWORD_SIZE];
2777 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2778 password[i] = data[2+i];
2779 password[PASSWORD_SIZE-1] = 0;*/
2781 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2789 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2791 char c = data[2+PASSWORD_SIZE+i];
2797 if(!base64_is_valid(newpwd)){
2798 infostream<<"Server: "<<player->getName()<<" supplied invalid password hash"<<std::endl;
2799 // Wrong old password supplied!!
2800 SendChatMessage(peer_id, L"Invalid new password hash supplied. Password NOT changed.");
2804 infostream<<"Server: Client requests a password change from "
2805 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
2807 std::string playername = player->getName();
2809 std::string checkpwd;
2810 scriptapi_get_auth(m_lua, playername, &checkpwd, NULL);
2812 if(oldpwd != checkpwd)
2814 infostream<<"Server: invalid old password"<<std::endl;
2815 // Wrong old password supplied!!
2816 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
2820 bool success = scriptapi_set_password(m_lua, playername, newpwd);
2822 actionstream<<player->getName()<<" changes password"<<std::endl;
2823 SendChatMessage(peer_id, L"Password change successful.");
2825 actionstream<<player->getName()<<" tries to change password but "
2826 <<"it fails"<<std::endl;
2827 SendChatMessage(peer_id, L"Password change failed or inavailable.");
2830 else if(command == TOSERVER_PLAYERITEM)
2835 u16 item = readU16(&data[2]);
2836 playersao->setWieldIndex(item);
2838 else if(command == TOSERVER_RESPAWN)
2843 RespawnPlayer(peer_id);
2845 actionstream<<player->getName()<<" respawns at "
2846 <<PP(player->getPosition()/BS)<<std::endl;
2848 // ActiveObject is added to environment in AsyncRunStep after
2849 // the previous addition has been succesfully removed
2851 else if(command == TOSERVER_REQUEST_MEDIA) {
2852 std::string datastring((char*)&data[2], datasize-2);
2853 std::istringstream is(datastring, std::ios_base::binary);
2855 core::list<MediaRequest> tosend;
2856 u16 numfiles = readU16(is);
2858 infostream<<"Sending "<<numfiles<<" files to "
2859 <<getPlayerName(peer_id)<<std::endl;
2860 verbosestream<<"TOSERVER_REQUEST_MEDIA: "<<std::endl;
2862 for(int i = 0; i < numfiles; i++) {
2863 std::string name = deSerializeString(is);
2864 tosend.push_back(MediaRequest(name));
2865 verbosestream<<"TOSERVER_REQUEST_MEDIA: requested file "
2869 sendRequestedMedia(peer_id, tosend);
2871 // Now the client should know about everything
2872 // (definitions and files)
2873 getClient(peer_id)->definitions_sent = true;
2875 else if(command == TOSERVER_INTERACT)
2877 std::string datastring((char*)&data[2], datasize-2);
2878 std::istringstream is(datastring, std::ios_base::binary);
2884 [5] u32 length of the next item
2885 [9] serialized PointedThing
2887 0: start digging (from undersurface) or use
2888 1: stop digging (all parameters ignored)
2889 2: digging completed
2890 3: place block or item (to abovesurface)
2893 u8 action = readU8(is);
2894 u16 item_i = readU16(is);
2895 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
2896 PointedThing pointed;
2897 pointed.deSerialize(tmp_is);
2899 verbosestream<<"TOSERVER_INTERACT: action="<<(int)action<<", item="
2900 <<item_i<<", pointed="<<pointed.dump()<<std::endl;
2904 verbosestream<<"TOSERVER_INTERACT: "<<player->getName()
2905 <<" tried to interact, but is dead!"<<std::endl;
2909 v3f player_pos = playersao->getLastGoodPosition();
2911 // Update wielded item
2912 playersao->setWieldIndex(item_i);
2914 // Get pointed to node (undefined if not POINTEDTYPE_NODE)
2915 v3s16 p_under = pointed.node_undersurface;
2916 v3s16 p_above = pointed.node_abovesurface;
2918 // Get pointed to object (NULL if not POINTEDTYPE_OBJECT)
2919 ServerActiveObject *pointed_object = NULL;
2920 if(pointed.type == POINTEDTHING_OBJECT)
2922 pointed_object = m_env->getActiveObject(pointed.object_id);
2923 if(pointed_object == NULL)
2925 verbosestream<<"TOSERVER_INTERACT: "
2926 "pointed object is NULL"<<std::endl;
2932 v3f pointed_pos_under = player_pos;
2933 v3f pointed_pos_above = player_pos;
2934 if(pointed.type == POINTEDTHING_NODE)
2936 pointed_pos_under = intToFloat(p_under, BS);
2937 pointed_pos_above = intToFloat(p_above, BS);
2939 else if(pointed.type == POINTEDTHING_OBJECT)
2941 pointed_pos_under = pointed_object->getBasePosition();
2942 pointed_pos_above = pointed_pos_under;
2946 Check that target is reasonably close
2947 (only when digging or placing things)
2949 if(action == 0 || action == 2 || action == 3)
2951 float d = player_pos.getDistanceFrom(pointed_pos_under);
2952 float max_d = BS * 14; // Just some large enough value
2954 actionstream<<"Player "<<player->getName()
2955 <<" tried to access "<<pointed.dump()
2957 <<"d="<<d<<", max_d="<<max_d
2958 <<". ignoring."<<std::endl;
2959 // Re-send block to revert change on client-side
2960 RemoteClient *client = getClient(peer_id);
2961 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2962 client->SetBlockNotSent(blockpos);
2969 Make sure the player is allowed to do it
2971 if(!checkPriv(player->getName(), "interact"))
2973 actionstream<<player->getName()<<" attempted to interact with "
2974 <<pointed.dump()<<" without 'interact' privilege"
2976 // Re-send block to revert change on client-side
2977 RemoteClient *client = getClient(peer_id);
2978 // Digging completed -> under
2980 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2981 client->SetBlockNotSent(blockpos);
2983 // Placement -> above
2985 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
2986 client->SetBlockNotSent(blockpos);
2992 0: start digging or punch object
2996 if(pointed.type == POINTEDTHING_NODE)
2999 NOTE: This can be used in the future to check if
3000 somebody is cheating, by checking the timing.
3002 MapNode n(CONTENT_IGNORE);
3005 n = m_env->getMap().getNode(p_under);
3007 catch(InvalidPositionException &e)
3009 infostream<<"Server: Not punching: Node not found."
3010 <<" Adding block to emerge queue."
3012 m_emerge_queue.addBlock(peer_id,
3013 getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
3015 if(n.getContent() != CONTENT_IGNORE)
3016 scriptapi_node_on_punch(m_lua, p_under, n, playersao);
3018 playersao->noCheatDigStart(p_under);
3020 else if(pointed.type == POINTEDTHING_OBJECT)
3022 // Skip if object has been removed
3023 if(pointed_object->m_removed)
3026 actionstream<<player->getName()<<" punches object "
3027 <<pointed.object_id<<": "
3028 <<pointed_object->getDescription()<<std::endl;
3030 ItemStack punchitem = playersao->getWieldedItem();
3031 ToolCapabilities toolcap =
3032 punchitem.getToolCapabilities(m_itemdef);
3033 v3f dir = (pointed_object->getBasePosition() -
3034 (player->getPosition() + player->getEyeOffset())
3036 float time_from_last_punch =
3037 playersao->resetTimeFromLastPunch();
3038 pointed_object->punch(dir, &toolcap, playersao,
3039 time_from_last_punch);
3047 else if(action == 1)
3052 2: Digging completed
3054 else if(action == 2)
3056 // Only digging of nodes
3057 if(pointed.type == POINTEDTHING_NODE)
3059 MapNode n(CONTENT_IGNORE);
3062 n = m_env->getMap().getNode(p_under);
3064 catch(InvalidPositionException &e)
3066 infostream<<"Server: Not finishing digging: Node not found."
3067 <<" Adding block to emerge queue."
3069 m_emerge_queue.addBlock(peer_id,
3070 getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
3073 /* Cheat prevention */
3074 bool is_valid_dig = true;
3075 if(!isSingleplayer() && !g_settings->getBool("disable_anticheat"))
3077 v3s16 nocheat_p = playersao->getNoCheatDigPos();
3078 float nocheat_t = playersao->getNoCheatDigTime();
3079 playersao->noCheatDigEnd();
3080 // If player didn't start digging this, ignore dig
3081 if(nocheat_p != p_under){
3082 infostream<<"Server: NoCheat: "<<player->getName()
3083 <<" started digging "
3084 <<PP(nocheat_p)<<" and completed digging "
3085 <<PP(p_under)<<"; not digging."<<std::endl;
3086 is_valid_dig = false;
3088 // Get player's wielded item
3089 ItemStack playeritem;
3090 InventoryList *mlist = playersao->getInventory()->getList("main");
3092 playeritem = mlist->getItem(playersao->getWieldIndex());
3093 ToolCapabilities playeritem_toolcap =
3094 playeritem.getToolCapabilities(m_itemdef);
3095 // Get diggability and expected digging time
3096 DigParams params = getDigParams(m_nodedef->get(n).groups,
3097 &playeritem_toolcap);
3098 // If can't dig, try hand
3099 if(!params.diggable){
3100 const ItemDefinition &hand = m_itemdef->get("");
3101 const ToolCapabilities *tp = hand.tool_capabilities;
3103 params = getDigParams(m_nodedef->get(n).groups, tp);
3105 // If can't dig, ignore dig
3106 if(!params.diggable){
3107 infostream<<"Server: NoCheat: "<<player->getName()
3108 <<" completed digging "<<PP(p_under)
3109 <<", which is not diggable with tool. not digging."
3111 is_valid_dig = false;
3113 // If time is considerably too short, ignore dig
3114 // Check time only for medium and slow timed digs
3115 if(params.diggable && params.time > 0.3 && nocheat_t < 0.5 * params.time){
3116 infostream<<"Server: NoCheat: "<<player->getName()
3117 <<" completed digging "
3118 <<PP(p_under)<<" in "<<nocheat_t<<"s; expected "
3119 <<params.time<<"s; not digging."<<std::endl;
3120 is_valid_dig = false;
3124 /* Actually dig node */
3126 if(is_valid_dig && n.getContent() != CONTENT_IGNORE)
3127 scriptapi_node_on_dig(m_lua, p_under, n, playersao);
3129 // Send unusual result (that is, node not being removed)
3130 if(m_env->getMap().getNodeNoEx(p_under).getContent() != CONTENT_AIR)
3132 // Re-send block to revert change on client-side
3133 RemoteClient *client = getClient(peer_id);
3134 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
3135 client->SetBlockNotSent(blockpos);
3141 3: place block or right-click object
3143 else if(action == 3)
3145 ItemStack item = playersao->getWieldedItem();
3147 // Reset build time counter
3148 if(pointed.type == POINTEDTHING_NODE &&
3149 item.getDefinition(m_itemdef).type == ITEM_NODE)
3150 getClient(peer_id)->m_time_from_building = 0.0;
3152 if(pointed.type == POINTEDTHING_OBJECT)
3154 // Right click object
3156 // Skip if object has been removed
3157 if(pointed_object->m_removed)
3160 actionstream<<player->getName()<<" right-clicks object "
3161 <<pointed.object_id<<": "
3162 <<pointed_object->getDescription()<<std::endl;
3165 pointed_object->rightClick(playersao);
3167 else if(scriptapi_item_on_place(m_lua,
3168 item, playersao, pointed))
3170 // Placement was handled in lua
3172 // Apply returned ItemStack
3173 if(g_settings->getBool("creative_mode") == false)
3174 playersao->setWieldedItem(item);
3177 // If item has node placement prediction, always send the above
3178 // node to make sure the client knows what exactly happened
3179 if(item.getDefinition(m_itemdef).node_placement_prediction != ""){
3180 RemoteClient *client = getClient(peer_id);
3181 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
3182 client->SetBlockNotSent(blockpos);
3189 else if(action == 4)
3191 ItemStack item = playersao->getWieldedItem();
3193 actionstream<<player->getName()<<" uses "<<item.name
3194 <<", pointing at "<<pointed.dump()<<std::endl;
3196 if(scriptapi_item_on_use(m_lua,
3197 item, playersao, pointed))
3199 // Apply returned ItemStack
3200 if(g_settings->getBool("creative_mode") == false)
3201 playersao->setWieldedItem(item);
3207 Catch invalid actions
3211 infostream<<"WARNING: Server: Invalid action "
3212 <<action<<std::endl;
3215 else if(command == TOSERVER_REMOVED_SOUNDS)
3217 std::string datastring((char*)&data[2], datasize-2);
3218 std::istringstream is(datastring, std::ios_base::binary);
3220 int num = readU16(is);
3221 for(int k=0; k<num; k++){
3222 s32 id = readS32(is);
3223 std::map<s32, ServerPlayingSound>::iterator i =
3224 m_playing_sounds.find(id);
3225 if(i == m_playing_sounds.end())
3227 ServerPlayingSound &psound = i->second;
3228 psound.clients.erase(peer_id);
3229 if(psound.clients.size() == 0)
3230 m_playing_sounds.erase(i++);
3233 else if(command == TOSERVER_NODEMETA_FIELDS)
3235 std::string datastring((char*)&data[2], datasize-2);
3236 std::istringstream is(datastring, std::ios_base::binary);
3238 v3s16 p = readV3S16(is);
3239 std::string formname = deSerializeString(is);
3240 int num = readU16(is);
3241 std::map<std::string, std::string> fields;
3242 for(int k=0; k<num; k++){
3243 std::string fieldname = deSerializeString(is);
3244 std::string fieldvalue = deSerializeLongString(is);
3245 fields[fieldname] = fieldvalue;
3248 scriptapi_node_on_receive_fields(m_lua, p, formname, fields,
3253 infostream<<"Server::ProcessData(): Ignoring "
3254 "unknown command "<<command<<std::endl;
3258 catch(SendFailedException &e)
3260 errorstream<<"Server::ProcessData(): SendFailedException: "
3266 void Server::onMapEditEvent(MapEditEvent *event)
3268 //infostream<<"Server::onMapEditEvent()"<<std::endl;
3269 if(m_ignore_map_edit_events)
3271 if(m_ignore_map_edit_events_area.contains(event->getArea()))
3273 MapEditEvent *e = event->clone();
3274 m_unsent_map_edit_queue.push_back(e);
3277 Inventory* Server::getInventory(const InventoryLocation &loc)
3280 case InventoryLocation::UNDEFINED:
3283 case InventoryLocation::CURRENT_PLAYER:
3286 case InventoryLocation::PLAYER:
3288 Player *player = m_env->getPlayer(loc.name.c_str());
3291 PlayerSAO *playersao = player->getPlayerSAO();
3294 return playersao->getInventory();
3297 case InventoryLocation::NODEMETA:
3299 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
3302 return meta->getInventory();
3310 void Server::setInventoryModified(const InventoryLocation &loc)
3313 case InventoryLocation::UNDEFINED:
3316 case InventoryLocation::PLAYER:
3318 Player *player = m_env->getPlayer(loc.name.c_str());
3321 PlayerSAO *playersao = player->getPlayerSAO();
3324 playersao->m_inventory_not_sent = true;
3325 playersao->m_wielded_item_not_sent = true;
3328 case InventoryLocation::NODEMETA:
3330 v3s16 blockpos = getNodeBlockPos(loc.p);
3332 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3334 block->raiseModified(MOD_STATE_WRITE_NEEDED);
3336 setBlockNotSent(blockpos);
3344 core::list<PlayerInfo> Server::getPlayerInfo()
3346 DSTACK(__FUNCTION_NAME);
3347 JMutexAutoLock envlock(m_env_mutex);
3348 JMutexAutoLock conlock(m_con_mutex);
3350 core::list<PlayerInfo> list;
3352 core::list<Player*> players = m_env->getPlayers();
3354 core::list<Player*>::Iterator i;
3355 for(i = players.begin();
3356 i != players.end(); i++)
3360 Player *player = *i;
3363 // Copy info from connection to info struct
3364 info.id = player->peer_id;
3365 info.address = m_con.GetPeerAddress(player->peer_id);
3366 info.avg_rtt = m_con.GetPeerAvgRTT(player->peer_id);
3368 catch(con::PeerNotFoundException &e)
3370 // Set dummy peer info
3372 info.address = Address(0,0,0,0,0);
3376 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3377 info.position = player->getPosition();
3379 list.push_back(info);
3386 void Server::peerAdded(con::Peer *peer)
3388 DSTACK(__FUNCTION_NAME);
3389 verbosestream<<"Server::peerAdded(): peer->id="
3390 <<peer->id<<std::endl;
3393 c.type = PEER_ADDED;
3394 c.peer_id = peer->id;
3396 m_peer_change_queue.push_back(c);
3399 void Server::deletingPeer(con::Peer *peer, bool timeout)
3401 DSTACK(__FUNCTION_NAME);
3402 verbosestream<<"Server::deletingPeer(): peer->id="
3403 <<peer->id<<", timeout="<<timeout<<std::endl;
3406 c.type = PEER_REMOVED;
3407 c.peer_id = peer->id;
3408 c.timeout = timeout;
3409 m_peer_change_queue.push_back(c);
3416 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3418 DSTACK(__FUNCTION_NAME);
3419 std::ostringstream os(std::ios_base::binary);
3421 writeU16(os, TOCLIENT_HP);
3425 std::string s = os.str();
3426 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3428 con.Send(peer_id, 0, data, true);
3431 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3432 const std::wstring &reason)
3434 DSTACK(__FUNCTION_NAME);
3435 std::ostringstream os(std::ios_base::binary);
3437 writeU16(os, TOCLIENT_ACCESS_DENIED);
3438 os<<serializeWideString(reason);
3441 std::string s = os.str();
3442 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3444 con.Send(peer_id, 0, data, true);
3447 void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
3448 bool set_camera_point_target, v3f camera_point_target)
3450 DSTACK(__FUNCTION_NAME);
3451 std::ostringstream os(std::ios_base::binary);
3453 writeU16(os, TOCLIENT_DEATHSCREEN);
3454 writeU8(os, set_camera_point_target);
3455 writeV3F1000(os, camera_point_target);
3458 std::string s = os.str();
3459 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3461 con.Send(peer_id, 0, data, true);
3464 void Server::SendItemDef(con::Connection &con, u16 peer_id,
3465 IItemDefManager *itemdef)
3467 DSTACK(__FUNCTION_NAME);
3468 std::ostringstream os(std::ios_base::binary);
3472 u32 length of the next item
3473 zlib-compressed serialized ItemDefManager
3475 writeU16(os, TOCLIENT_ITEMDEF);
3476 std::ostringstream tmp_os(std::ios::binary);
3477 itemdef->serialize(tmp_os);
3478 std::ostringstream tmp_os2(std::ios::binary);
3479 compressZlib(tmp_os.str(), tmp_os2);
3480 os<<serializeLongString(tmp_os2.str());
3483 std::string s = os.str();
3484 verbosestream<<"Server: Sending item definitions to id("<<peer_id
3485 <<"): size="<<s.size()<<std::endl;
3486 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3488 con.Send(peer_id, 0, data, true);
3491 void Server::SendNodeDef(con::Connection &con, u16 peer_id,
3492 INodeDefManager *nodedef)
3494 DSTACK(__FUNCTION_NAME);
3495 std::ostringstream os(std::ios_base::binary);
3499 u32 length of the next item
3500 zlib-compressed serialized NodeDefManager
3502 writeU16(os, TOCLIENT_NODEDEF);
3503 std::ostringstream tmp_os(std::ios::binary);
3504 nodedef->serialize(tmp_os);
3505 std::ostringstream tmp_os2(std::ios::binary);
3506 compressZlib(tmp_os.str(), tmp_os2);
3507 os<<serializeLongString(tmp_os2.str());
3510 std::string s = os.str();
3511 verbosestream<<"Server: Sending node definitions to id("<<peer_id
3512 <<"): size="<<s.size()<<std::endl;
3513 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3515 con.Send(peer_id, 0, data, true);
3519 Non-static send methods
3522 void Server::SendInventory(u16 peer_id)
3524 DSTACK(__FUNCTION_NAME);
3526 PlayerSAO *playersao = getPlayerSAO(peer_id);
3529 playersao->m_inventory_not_sent = false;
3535 std::ostringstream os;
3536 playersao->getInventory()->serialize(os);
3538 std::string s = os.str();
3540 SharedBuffer<u8> data(s.size()+2);
3541 writeU16(&data[0], TOCLIENT_INVENTORY);
3542 memcpy(&data[2], s.c_str(), s.size());
3545 m_con.Send(peer_id, 0, data, true);
3548 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3550 DSTACK(__FUNCTION_NAME);
3552 std::ostringstream os(std::ios_base::binary);
3556 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3557 os.write((char*)buf, 2);
3560 writeU16(buf, message.size());
3561 os.write((char*)buf, 2);
3564 for(u32 i=0; i<message.size(); i++)
3568 os.write((char*)buf, 2);
3572 std::string s = os.str();
3573 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3575 m_con.Send(peer_id, 0, data, true);
3578 void Server::BroadcastChatMessage(const std::wstring &message)
3580 for(core::map<u16, RemoteClient*>::Iterator
3581 i = m_clients.getIterator();
3582 i.atEnd() == false; i++)
3584 // Get client and check that it is valid
3585 RemoteClient *client = i.getNode()->getValue();
3586 assert(client->peer_id == i.getNode()->getKey());
3587 if(client->serialization_version == SER_FMT_VER_INVALID)
3590 SendChatMessage(client->peer_id, message);
3594 void Server::SendPlayerHP(u16 peer_id)
3596 DSTACK(__FUNCTION_NAME);
3597 PlayerSAO *playersao = getPlayerSAO(peer_id);
3599 playersao->m_hp_not_sent = false;
3600 SendHP(m_con, peer_id, playersao->getHP());
3603 void Server::SendMovePlayer(u16 peer_id)
3605 DSTACK(__FUNCTION_NAME);
3606 Player *player = m_env->getPlayer(peer_id);
3609 std::ostringstream os(std::ios_base::binary);
3610 writeU16(os, TOCLIENT_MOVE_PLAYER);
3611 writeV3F1000(os, player->getPosition());
3612 writeF1000(os, player->getPitch());
3613 writeF1000(os, player->getYaw());
3616 v3f pos = player->getPosition();
3617 f32 pitch = player->getPitch();
3618 f32 yaw = player->getYaw();
3619 verbosestream<<"Server: Sending TOCLIENT_MOVE_PLAYER"
3620 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3627 std::string s = os.str();
3628 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3630 m_con.Send(peer_id, 0, data, true);
3633 void Server::SendPlayerPrivileges(u16 peer_id)
3635 Player *player = m_env->getPlayer(peer_id);
3637 if(player->peer_id == PEER_ID_INEXISTENT)
3640 std::set<std::string> privs;
3641 scriptapi_get_auth(m_lua, player->getName(), NULL, &privs);
3643 std::ostringstream os(std::ios_base::binary);
3644 writeU16(os, TOCLIENT_PRIVILEGES);
3645 writeU16(os, privs.size());
3646 for(std::set<std::string>::const_iterator i = privs.begin();
3647 i != privs.end(); i++){
3648 os<<serializeString(*i);
3652 std::string s = os.str();
3653 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3655 m_con.Send(peer_id, 0, data, true);
3658 void Server::SendPlayerInventoryFormspec(u16 peer_id)
3660 Player *player = m_env->getPlayer(peer_id);
3662 if(player->peer_id == PEER_ID_INEXISTENT)
3665 std::ostringstream os(std::ios_base::binary);
3666 writeU16(os, TOCLIENT_INVENTORY_FORMSPEC);
3667 os<<serializeLongString(player->inventory_formspec);
3670 std::string s = os.str();
3671 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3673 m_con.Send(peer_id, 0, data, true);
3676 s32 Server::playSound(const SimpleSoundSpec &spec,
3677 const ServerSoundParams ¶ms)
3679 // Find out initial position of sound
3680 bool pos_exists = false;
3681 v3f pos = params.getPos(m_env, &pos_exists);
3682 // If position is not found while it should be, cancel sound
3683 if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
3685 // Filter destination clients
3686 std::set<RemoteClient*> dst_clients;
3687 if(params.to_player != "")
3689 Player *player = m_env->getPlayer(params.to_player.c_str());
3691 infostream<<"Server::playSound: Player \""<<params.to_player
3692 <<"\" not found"<<std::endl;
3695 if(player->peer_id == PEER_ID_INEXISTENT){
3696 infostream<<"Server::playSound: Player \""<<params.to_player
3697 <<"\" not connected"<<std::endl;
3700 RemoteClient *client = getClient(player->peer_id);
3701 dst_clients.insert(client);
3705 for(core::map<u16, RemoteClient*>::Iterator
3706 i = m_clients.getIterator(); i.atEnd() == false; i++)
3708 RemoteClient *client = i.getNode()->getValue();
3709 Player *player = m_env->getPlayer(client->peer_id);
3713 if(player->getPosition().getDistanceFrom(pos) >
3714 params.max_hear_distance)
3717 dst_clients.insert(client);
3720 if(dst_clients.size() == 0)
3723 s32 id = m_next_sound_id++;
3724 // The sound will exist as a reference in m_playing_sounds
3725 m_playing_sounds[id] = ServerPlayingSound();
3726 ServerPlayingSound &psound = m_playing_sounds[id];
3727 psound.params = params;
3728 for(std::set<RemoteClient*>::iterator i = dst_clients.begin();
3729 i != dst_clients.end(); i++)
3730 psound.clients.insert((*i)->peer_id);
3732 std::ostringstream os(std::ios_base::binary);
3733 writeU16(os, TOCLIENT_PLAY_SOUND);
3735 os<<serializeString(spec.name);
3736 writeF1000(os, spec.gain * params.gain);
3737 writeU8(os, params.type);
3738 writeV3F1000(os, pos);
3739 writeU16(os, params.object);
3740 writeU8(os, params.loop);
3742 std::string s = os.str();
3743 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3745 for(std::set<RemoteClient*>::iterator i = dst_clients.begin();
3746 i != dst_clients.end(); i++){
3748 m_con.Send((*i)->peer_id, 0, data, true);
3752 void Server::stopSound(s32 handle)
3754 // Get sound reference
3755 std::map<s32, ServerPlayingSound>::iterator i =
3756 m_playing_sounds.find(handle);
3757 if(i == m_playing_sounds.end())
3759 ServerPlayingSound &psound = i->second;
3761 std::ostringstream os(std::ios_base::binary);
3762 writeU16(os, TOCLIENT_STOP_SOUND);
3763 writeS32(os, handle);
3765 std::string s = os.str();
3766 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3768 for(std::set<u16>::iterator i = psound.clients.begin();
3769 i != psound.clients.end(); i++){
3771 m_con.Send(*i, 0, data, true);
3773 // Remove sound reference
3774 m_playing_sounds.erase(i);
3777 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3778 core::list<u16> *far_players, float far_d_nodes)
3780 float maxd = far_d_nodes*BS;
3781 v3f p_f = intToFloat(p, BS);
3785 SharedBuffer<u8> reply(replysize);
3786 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3787 writeS16(&reply[2], p.X);
3788 writeS16(&reply[4], p.Y);
3789 writeS16(&reply[6], p.Z);
3791 for(core::map<u16, RemoteClient*>::Iterator
3792 i = m_clients.getIterator();
3793 i.atEnd() == false; i++)
3795 // Get client and check that it is valid
3796 RemoteClient *client = i.getNode()->getValue();
3797 assert(client->peer_id == i.getNode()->getKey());
3798 if(client->serialization_version == SER_FMT_VER_INVALID)
3801 // Don't send if it's the same one
3802 if(client->peer_id == ignore_id)
3808 Player *player = m_env->getPlayer(client->peer_id);
3811 // If player is far away, only set modified blocks not sent
3812 v3f player_pos = player->getPosition();
3813 if(player_pos.getDistanceFrom(p_f) > maxd)
3815 far_players->push_back(client->peer_id);
3822 m_con.Send(client->peer_id, 0, reply, true);
3826 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3827 core::list<u16> *far_players, float far_d_nodes)
3829 float maxd = far_d_nodes*BS;
3830 v3f p_f = intToFloat(p, BS);
3832 for(core::map<u16, RemoteClient*>::Iterator
3833 i = m_clients.getIterator();
3834 i.atEnd() == false; i++)
3836 // Get client and check that it is valid
3837 RemoteClient *client = i.getNode()->getValue();
3838 assert(client->peer_id == i.getNode()->getKey());
3839 if(client->serialization_version == SER_FMT_VER_INVALID)
3842 // Don't send if it's the same one
3843 if(client->peer_id == ignore_id)
3849 Player *player = m_env->getPlayer(client->peer_id);
3852 // If player is far away, only set modified blocks not sent
3853 v3f player_pos = player->getPosition();
3854 if(player_pos.getDistanceFrom(p_f) > maxd)
3856 far_players->push_back(client->peer_id);
3863 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3864 SharedBuffer<u8> reply(replysize);
3865 writeU16(&reply[0], TOCLIENT_ADDNODE);
3866 writeS16(&reply[2], p.X);
3867 writeS16(&reply[4], p.Y);
3868 writeS16(&reply[6], p.Z);
3869 n.serialize(&reply[8], client->serialization_version);
3872 m_con.Send(client->peer_id, 0, reply, true);
3876 void Server::setBlockNotSent(v3s16 p)
3878 for(core::map<u16, RemoteClient*>::Iterator
3879 i = m_clients.getIterator();
3880 i.atEnd()==false; i++)
3882 RemoteClient *client = i.getNode()->getValue();
3883 client->SetBlockNotSent(p);
3887 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3889 DSTACK(__FUNCTION_NAME);
3891 v3s16 p = block->getPos();
3895 bool completely_air = true;
3896 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3897 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3898 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3900 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
3902 completely_air = false;
3903 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
3908 infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
3910 infostream<<"[completely air] ";
3911 infostream<<std::endl;
3915 Create a packet with the block in the right format
3918 std::ostringstream os(std::ios_base::binary);
3919 block->serialize(os, ver, false);
3920 std::string s = os.str();
3921 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3923 u32 replysize = 8 + blockdata.getSize();
3924 SharedBuffer<u8> reply(replysize);
3925 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3926 writeS16(&reply[2], p.X);
3927 writeS16(&reply[4], p.Y);
3928 writeS16(&reply[6], p.Z);
3929 memcpy(&reply[8], *blockdata, blockdata.getSize());
3931 /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3932 <<": \tpacket size: "<<replysize<<std::endl;*/
3937 m_con.Send(peer_id, 1, reply, true);
3940 void Server::SendBlocks(float dtime)
3942 DSTACK(__FUNCTION_NAME);
3944 JMutexAutoLock envlock(m_env_mutex);
3945 JMutexAutoLock conlock(m_con_mutex);
3947 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
3949 core::array<PrioritySortedBlockTransfer> queue;
3951 s32 total_sending = 0;
3954 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
3956 for(core::map<u16, RemoteClient*>::Iterator
3957 i = m_clients.getIterator();
3958 i.atEnd() == false; i++)
3960 RemoteClient *client = i.getNode()->getValue();
3961 assert(client->peer_id == i.getNode()->getKey());
3963 // If definitions and textures have not been sent, don't
3964 // send MapBlocks either
3965 if(!client->definitions_sent)
3968 total_sending += client->SendingCount();
3970 if(client->serialization_version == SER_FMT_VER_INVALID)
3973 client->GetNextBlocks(this, dtime, queue);
3978 // Lowest priority number comes first.
3979 // Lowest is most important.
3982 for(u32 i=0; i<queue.size(); i++)
3984 //TODO: Calculate limit dynamically
3985 if(total_sending >= g_settings->getS32
3986 ("max_simultaneous_block_sends_server_total"))
3989 PrioritySortedBlockTransfer q = queue[i];
3991 MapBlock *block = NULL;
3994 block = m_env->getMap().getBlockNoCreate(q.pos);
3996 catch(InvalidPositionException &e)
4001 RemoteClient *client = getClient(q.peer_id);
4003 SendBlockNoLock(q.peer_id, block, client->serialization_version);
4005 client->SentBlock(q.pos);
4011 void Server::fillMediaCache()
4013 DSTACK(__FUNCTION_NAME);
4015 infostream<<"Server: Calculating media file checksums"<<std::endl;
4017 // Collect all media file paths
4018 std::list<std::string> paths;
4019 for(core::list<ModSpec>::Iterator i = m_mods.begin();
4020 i != m_mods.end(); i++){
4021 const ModSpec &mod = *i;
4022 paths.push_back(mod.path + DIR_DELIM + "textures");
4023 paths.push_back(mod.path + DIR_DELIM + "sounds");
4024 paths.push_back(mod.path + DIR_DELIM + "media");
4026 std::string path_all = "textures";
4027 paths.push_back(path_all + DIR_DELIM + "all");
4029 // Collect media file information from paths into cache
4030 for(std::list<std::string>::iterator i = paths.begin();
4031 i != paths.end(); i++)
4033 std::string mediapath = *i;
4034 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
4035 for(u32 j=0; j<dirlist.size(); j++){
4036 if(dirlist[j].dir) // Ignode dirs
4038 std::string filename = dirlist[j].name;
4039 // If name contains illegal characters, ignore the file
4040 if(!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)){
4041 infostream<<"Server: ignoring illegal file name: \""
4042 <<filename<<"\""<<std::endl;
4045 // If name is not in a supported format, ignore it
4046 const char *supported_ext[] = {
4047 ".png", ".jpg", ".bmp", ".tga",
4048 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
4052 if(removeStringEnd(filename, supported_ext) == ""){
4053 infostream<<"Server: ignoring unsupported file extension: \""
4054 <<filename<<"\""<<std::endl;
4057 // Ok, attempt to load the file and add to cache
4058 std::string filepath = mediapath + DIR_DELIM + filename;
4060 std::ifstream fis(filepath.c_str(), std::ios_base::binary);
4061 if(fis.good() == false){
4062 errorstream<<"Server::fillMediaCache(): Could not open \""
4063 <<filename<<"\" for reading"<<std::endl;
4066 std::ostringstream tmp_os(std::ios_base::binary);
4070 fis.read(buf, 1024);
4071 std::streamsize len = fis.gcount();
4072 tmp_os.write(buf, len);
4081 errorstream<<"Server::fillMediaCache(): Failed to read \""
4082 <<filename<<"\""<<std::endl;
4085 if(tmp_os.str().length() == 0){
4086 errorstream<<"Server::fillMediaCache(): Empty file \""
4087 <<filepath<<"\""<<std::endl;
4092 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
4094 unsigned char *digest = sha1.getDigest();
4095 std::string sha1_base64 = base64_encode(digest, 20);
4096 std::string sha1_hex = hex_encode((char*)digest, 20);
4100 this->m_media[filename] = MediaInfo(filepath, sha1_base64);
4101 verbosestream<<"Server: "<<sha1_hex<<" is "<<filename<<std::endl;
4106 struct SendableMediaAnnouncement
4109 std::string sha1_digest;
4111 SendableMediaAnnouncement(const std::string name_="",
4112 const std::string sha1_digest_=""):
4114 sha1_digest(sha1_digest_)
4118 void Server::sendMediaAnnouncement(u16 peer_id)
4120 DSTACK(__FUNCTION_NAME);
4122 verbosestream<<"Server: Announcing files to id("<<peer_id<<")"
4125 core::list<SendableMediaAnnouncement> file_announcements;
4127 for(std::map<std::string, MediaInfo>::iterator i = m_media.begin();
4128 i != m_media.end(); i++){
4130 file_announcements.push_back(
4131 SendableMediaAnnouncement(i->first, i->second.sha1_digest));
4135 std::ostringstream os(std::ios_base::binary);
4143 u16 length of sha1_digest
4148 writeU16(os, TOCLIENT_ANNOUNCE_MEDIA);
4149 writeU16(os, file_announcements.size());
4151 for(core::list<SendableMediaAnnouncement>::Iterator
4152 j = file_announcements.begin();
4153 j != file_announcements.end(); j++){
4154 os<<serializeString(j->name);
4155 os<<serializeString(j->sha1_digest);
4159 std::string s = os.str();
4160 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4163 m_con.Send(peer_id, 0, data, true);
4167 struct SendableMedia
4173 SendableMedia(const std::string &name_="", const std::string path_="",
4174 const std::string &data_=""):
4181 void Server::sendRequestedMedia(u16 peer_id,
4182 const core::list<MediaRequest> &tosend)
4184 DSTACK(__FUNCTION_NAME);
4186 verbosestream<<"Server::sendRequestedMedia(): "
4187 <<"Sending files to client"<<std::endl;
4191 // Put 5kB in one bunch (this is not accurate)
4192 u32 bytes_per_bunch = 5000;
4194 core::array< core::list<SendableMedia> > file_bunches;
4195 file_bunches.push_back(core::list<SendableMedia>());
4197 u32 file_size_bunch_total = 0;
4199 for(core::list<MediaRequest>::ConstIterator i = tosend.begin();
4200 i != tosend.end(); i++)
4202 if(m_media.find(i->name) == m_media.end()){
4203 errorstream<<"Server::sendRequestedMedia(): Client asked for "
4204 <<"unknown file \""<<(i->name)<<"\""<<std::endl;
4208 //TODO get path + name
4209 std::string tpath = m_media[(*i).name].path;
4212 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
4213 if(fis.good() == false){
4214 errorstream<<"Server::sendRequestedMedia(): Could not open \""
4215 <<tpath<<"\" for reading"<<std::endl;
4218 std::ostringstream tmp_os(std::ios_base::binary);
4222 fis.read(buf, 1024);
4223 std::streamsize len = fis.gcount();
4224 tmp_os.write(buf, len);
4225 file_size_bunch_total += len;
4234 errorstream<<"Server::sendRequestedMedia(): Failed to read \""
4235 <<(*i).name<<"\""<<std::endl;
4238 /*infostream<<"Server::sendRequestedMedia(): Loaded \""
4239 <<tname<<"\""<<std::endl;*/
4241 file_bunches[file_bunches.size()-1].push_back(
4242 SendableMedia((*i).name, tpath, tmp_os.str()));
4244 // Start next bunch if got enough data
4245 if(file_size_bunch_total >= bytes_per_bunch){
4246 file_bunches.push_back(core::list<SendableMedia>());
4247 file_size_bunch_total = 0;
4252 /* Create and send packets */
4254 u32 num_bunches = file_bunches.size();
4255 for(u32 i=0; i<num_bunches; i++)
4257 std::ostringstream os(std::ios_base::binary);
4261 u16 total number of texture bunches
4262 u16 index of this bunch
4263 u32 number of files in this bunch
4272 writeU16(os, TOCLIENT_MEDIA);
4273 writeU16(os, num_bunches);
4275 writeU32(os, file_bunches[i].size());
4277 for(core::list<SendableMedia>::Iterator
4278 j = file_bunches[i].begin();
4279 j != file_bunches[i].end(); j++){
4280 os<<serializeString(j->name);
4281 os<<serializeLongString(j->data);
4285 std::string s = os.str();
4286 verbosestream<<"Server::sendRequestedMedia(): bunch "
4287 <<i<<"/"<<num_bunches
4288 <<" files="<<file_bunches[i].size()
4289 <<" size=" <<s.size()<<std::endl;
4290 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4292 m_con.Send(peer_id, 0, data, true);
4300 void Server::DiePlayer(u16 peer_id)
4302 DSTACK(__FUNCTION_NAME);
4304 PlayerSAO *playersao = getPlayerSAO(peer_id);
4307 infostream<<"Server::DiePlayer(): Player "
4308 <<playersao->getPlayer()->getName()
4309 <<" dies"<<std::endl;
4311 playersao->setHP(0);
4313 // Trigger scripted stuff
4314 scriptapi_on_dieplayer(m_lua, playersao);
4316 SendPlayerHP(peer_id);
4317 SendDeathscreen(m_con, peer_id, false, v3f(0,0,0));
4320 void Server::RespawnPlayer(u16 peer_id)
4322 DSTACK(__FUNCTION_NAME);
4324 PlayerSAO *playersao = getPlayerSAO(peer_id);
4327 infostream<<"Server::RespawnPlayer(): Player "
4328 <<playersao->getPlayer()->getName()
4329 <<" respawns"<<std::endl;
4331 playersao->setHP(PLAYER_MAX_HP);
4333 bool repositioned = scriptapi_on_respawnplayer(m_lua, playersao);
4335 v3f pos = findSpawnPos(m_env->getServerMap());
4336 playersao->setPos(pos);
4340 void Server::UpdateCrafting(u16 peer_id)
4342 DSTACK(__FUNCTION_NAME);
4344 Player* player = m_env->getPlayer(peer_id);
4347 // Get a preview for crafting
4349 // No crafting in creative mode
4350 if(g_settings->getBool("creative_mode") == false)
4351 getCraftingResult(&player->inventory, preview, false, this);
4353 // Put the new preview in
4354 InventoryList *plist = player->inventory.getList("craftpreview");
4356 assert(plist->getSize() >= 1);
4357 plist->changeItem(0, preview);
4360 RemoteClient* Server::getClient(u16 peer_id)
4362 DSTACK(__FUNCTION_NAME);
4363 //JMutexAutoLock lock(m_con_mutex);
4364 core::map<u16, RemoteClient*>::Node *n;
4365 n = m_clients.find(peer_id);
4366 // A client should exist for all peers
4368 return n->getValue();
4371 std::wstring Server::getStatusString()
4373 std::wostringstream os(std::ios_base::binary);
4376 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4378 os<<L", uptime="<<m_uptime.get();
4379 // Information about clients
4381 for(core::map<u16, RemoteClient*>::Iterator
4382 i = m_clients.getIterator();
4383 i.atEnd() == false; i++)
4385 // Get client and check that it is valid
4386 RemoteClient *client = i.getNode()->getValue();
4387 assert(client->peer_id == i.getNode()->getKey());
4388 if(client->serialization_version == SER_FMT_VER_INVALID)
4391 Player *player = m_env->getPlayer(client->peer_id);
4392 // Get name of player
4393 std::wstring name = L"unknown";
4395 name = narrow_to_wide(player->getName());
4396 // Add name to information string
4400 if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
4401 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4402 if(g_settings->get("motd") != "")
4403 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4407 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
4409 std::set<std::string> privs;
4410 scriptapi_get_auth(m_lua, name, NULL, &privs);
4414 bool Server::checkPriv(const std::string &name, const std::string &priv)
4416 std::set<std::string> privs = getPlayerEffectivePrivs(name);
4417 return (privs.count(priv) != 0);
4420 void Server::reportPrivsModified(const std::string &name)
4423 for(core::map<u16, RemoteClient*>::Iterator
4424 i = m_clients.getIterator();
4425 i.atEnd() == false; i++){
4426 RemoteClient *client = i.getNode()->getValue();
4427 Player *player = m_env->getPlayer(client->peer_id);
4428 reportPrivsModified(player->getName());
4431 Player *player = m_env->getPlayer(name.c_str());
4434 SendPlayerPrivileges(player->peer_id);
4435 PlayerSAO *sao = player->getPlayerSAO();
4438 sao->updatePrivileges(
4439 getPlayerEffectivePrivs(name),
4444 void Server::reportInventoryFormspecModified(const std::string &name)
4446 Player *player = m_env->getPlayer(name.c_str());
4449 SendPlayerInventoryFormspec(player->peer_id);
4452 // Saves g_settings to configpath given at initialization
4453 void Server::saveConfig()
4455 if(m_path_config != "")
4456 g_settings->updateConfigFile(m_path_config.c_str());
4459 void Server::notifyPlayer(const char *name, const std::wstring msg)
4461 Player *player = m_env->getPlayer(name);
4464 SendChatMessage(player->peer_id, std::wstring(L"Server: -!- ")+msg);
4467 void Server::notifyPlayers(const std::wstring msg)
4469 BroadcastChatMessage(msg);
4472 void Server::queueBlockEmerge(v3s16 blockpos, bool allow_generate)
4476 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
4477 m_emerge_queue.addBlock(PEER_ID_INEXISTENT, blockpos, flags);
4480 // IGameDef interface
4482 IItemDefManager* Server::getItemDefManager()
4486 INodeDefManager* Server::getNodeDefManager()
4490 ICraftDefManager* Server::getCraftDefManager()
4494 ITextureSource* Server::getTextureSource()
4498 u16 Server::allocateUnknownNodeId(const std::string &name)
4500 return m_nodedef->allocateDummy(name);
4502 ISoundManager* Server::getSoundManager()
4504 return &dummySoundManager;
4506 MtEventManager* Server::getEventManager()
4511 IWritableItemDefManager* Server::getWritableItemDefManager()
4515 IWritableNodeDefManager* Server::getWritableNodeDefManager()
4519 IWritableCraftDefManager* Server::getWritableCraftDefManager()
4524 const ModSpec* Server::getModSpec(const std::string &modname)
4526 for(core::list<ModSpec>::Iterator i = m_mods.begin();
4527 i != m_mods.end(); i++){
4528 const ModSpec &mod = *i;
4529 if(mod.name == modname)
4534 void Server::getModNames(core::list<std::string> &modlist)
4536 for(core::list<ModSpec>::Iterator i = m_mods.begin(); i != m_mods.end(); i++)
4538 modlist.push_back((*i).name);
4541 std::string Server::getBuiltinLuaPath()
4543 return porting::path_share + DIR_DELIM + "builtin";
4546 v3f findSpawnPos(ServerMap &map)
4548 //return v3f(50,50,50)*BS;
4553 nodepos = v2s16(0,0);
4558 // Try to find a good place a few times
4559 for(s32 i=0; i<1000; i++)
4562 // We're going to try to throw the player to this position
4563 v2s16 nodepos2d = v2s16(-range + (myrand()%(range*2)),
4564 -range + (myrand()%(range*2)));
4565 //v2s16 sectorpos = getNodeSectorPos(nodepos2d);
4566 // Get ground height at point (fallbacks to heightmap function)
4567 s16 groundheight = map.findGroundLevel(nodepos2d);
4568 // Don't go underwater
4569 if(groundheight < WATER_LEVEL)
4571 //infostream<<"-> Underwater"<<std::endl;
4574 // Don't go to high places
4575 if(groundheight > WATER_LEVEL + 4)
4577 //infostream<<"-> Underwater"<<std::endl;
4581 nodepos = v3s16(nodepos2d.X, groundheight-2, nodepos2d.Y);
4582 bool is_good = false;
4584 for(s32 i=0; i<10; i++){
4585 v3s16 blockpos = getNodeBlockPos(nodepos);
4586 map.emergeBlock(blockpos, true);
4587 MapNode n = map.getNodeNoEx(nodepos);
4588 if(n.getContent() == CONTENT_AIR){
4599 // Found a good place
4600 //infostream<<"Searched through "<<i<<" places."<<std::endl;
4606 return intToFloat(nodepos, BS);
4609 PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id)
4611 RemotePlayer *player = NULL;
4612 bool newplayer = false;
4615 Try to get an existing player
4617 player = static_cast<RemotePlayer*>(m_env->getPlayer(name));
4619 // If player is already connected, cancel
4620 if(player != NULL && player->peer_id != 0)
4622 infostream<<"emergePlayer(): Player already connected"<<std::endl;
4627 If player with the wanted peer_id already exists, cancel.
4629 if(m_env->getPlayer(peer_id) != NULL)
4631 infostream<<"emergePlayer(): Player with wrong name but same"
4632 " peer_id already exists"<<std::endl;
4637 Create a new player if it doesn't exist yet
4642 player = new RemotePlayer(this);
4643 player->updateName(name);
4645 /* Set player position */
4646 infostream<<"Server: Finding spawn place for player \""
4647 <<name<<"\""<<std::endl;
4648 v3f pos = findSpawnPos(m_env->getServerMap());
4649 player->setPosition(pos);
4651 /* Add player to environment */
4652 m_env->addPlayer(player);
4656 Create a new player active object
4658 PlayerSAO *playersao = new PlayerSAO(m_env, player, peer_id,
4659 getPlayerEffectivePrivs(player->getName()),
4662 /* Add object to environment */
4663 m_env->addActiveObject(playersao);
4667 scriptapi_on_newplayer(m_lua, playersao);
4669 scriptapi_on_joinplayer(m_lua, playersao);
4672 if(g_settings->getBool("creative_mode"))
4673 playersao->createCreativeInventory();
4678 void Server::handlePeerChange(PeerChange &c)
4680 JMutexAutoLock envlock(m_env_mutex);
4681 JMutexAutoLock conlock(m_con_mutex);
4683 if(c.type == PEER_ADDED)
4690 core::map<u16, RemoteClient*>::Node *n;
4691 n = m_clients.find(c.peer_id);
4692 // The client shouldn't already exist
4696 RemoteClient *client = new RemoteClient();
4697 client->peer_id = c.peer_id;
4698 m_clients.insert(client->peer_id, client);
4701 else if(c.type == PEER_REMOVED)
4708 core::map<u16, RemoteClient*>::Node *n;
4709 n = m_clients.find(c.peer_id);
4710 // The client should exist
4714 Mark objects to be not known by the client
4716 RemoteClient *client = n->getValue();
4718 for(core::map<u16, bool>::Iterator
4719 i = client->m_known_objects.getIterator();
4720 i.atEnd()==false; i++)
4723 u16 id = i.getNode()->getKey();
4724 ServerActiveObject* obj = m_env->getActiveObject(id);
4726 if(obj && obj->m_known_by_count > 0)
4727 obj->m_known_by_count--;
4731 Clear references to playing sounds
4733 for(std::map<s32, ServerPlayingSound>::iterator
4734 i = m_playing_sounds.begin();
4735 i != m_playing_sounds.end();)
4737 ServerPlayingSound &psound = i->second;
4738 psound.clients.erase(c.peer_id);
4739 if(psound.clients.size() == 0)
4740 m_playing_sounds.erase(i++);
4745 Player *player = m_env->getPlayer(c.peer_id);
4747 // Collect information about leaving in chat
4748 std::wstring message;
4752 std::wstring name = narrow_to_wide(player->getName());
4755 message += L" left the game.";
4757 message += L" (timed out)";
4761 /* Run scripts and remove from environment */
4765 PlayerSAO *playersao = player->getPlayerSAO();
4768 scriptapi_on_leaveplayer(m_lua, playersao);
4770 playersao->disconnected();
4780 std::ostringstream os(std::ios_base::binary);
4781 for(core::map<u16, RemoteClient*>::Iterator
4782 i = m_clients.getIterator();
4783 i.atEnd() == false; i++)
4785 RemoteClient *client = i.getNode()->getValue();
4786 assert(client->peer_id == i.getNode()->getKey());
4787 if(client->serialization_version == SER_FMT_VER_INVALID)
4790 Player *player = m_env->getPlayer(client->peer_id);
4793 // Get name of player
4794 os<<player->getName()<<" ";
4797 actionstream<<player->getName()<<" "
4798 <<(c.timeout?"times out.":"leaves game.")
4799 <<" List of players: "
4800 <<os.str()<<std::endl;
4805 delete m_clients[c.peer_id];
4806 m_clients.remove(c.peer_id);
4808 // Send player info to all remaining clients
4809 //SendPlayerInfos();
4811 // Send leave chat message to all remaining clients
4812 if(message.length() != 0)
4813 BroadcastChatMessage(message);
4822 void Server::handlePeerChanges()
4824 while(m_peer_change_queue.size() > 0)
4826 PeerChange c = m_peer_change_queue.pop_front();
4828 verbosestream<<"Server: Handling peer change: "
4829 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4832 handlePeerChange(c);
4836 void dedicated_server_loop(Server &server, bool &kill)
4838 DSTACK(__FUNCTION_NAME);
4840 verbosestream<<"dedicated_server_loop()"<<std::endl;
4842 IntervalLimiter m_profiler_interval;
4846 float steplen = g_settings->getFloat("dedicated_server_step");
4847 // This is kind of a hack but can be done like this
4848 // because server.step() is very light
4850 ScopeProfiler sp(g_profiler, "dedicated server sleep");
4851 sleep_ms((int)(steplen*1000.0));
4853 server.step(steplen);
4855 if(server.getShutdownRequested() || kill)
4857 infostream<<"Dedicated server quitting"<<std::endl;
4864 float profiler_print_interval =
4865 g_settings->getFloat("profiler_print_interval");
4866 if(profiler_print_interval != 0)
4868 if(m_profiler_interval.step(steplen, profiler_print_interval))
4870 infostream<<"Profiler:"<<std::endl;
4871 g_profiler->print(infostream);
4872 g_profiler->clear();