3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include "clientserver.h"
25 #include "jmutexautolock.h"
27 #include "constants.h"
30 #include "servercommand.h"
33 #include "serverobject.h"
38 #include "scriptapi.h"
43 #include "content_mapnode.h"
44 #include "content_nodemeta.h"
45 #include "content_abm.h"
46 #include "content_sao.h"
51 #include "sound.h" // dummySoundManager
52 #include "event_manager.h"
54 #include "util/string.h"
55 #include "util/pointedthing.h"
56 #include "util/mathconstants.h"
58 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
60 #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
62 class MapEditEventIgnorer
65 MapEditEventIgnorer(bool *flag):
74 ~MapEditEventIgnorer()
87 class MapEditEventAreaIgnorer
90 MapEditEventAreaIgnorer(VoxelArea *ignorevariable, const VoxelArea &a):
91 m_ignorevariable(ignorevariable)
93 if(m_ignorevariable->getVolume() == 0)
94 *m_ignorevariable = a;
96 m_ignorevariable = NULL;
99 ~MapEditEventAreaIgnorer()
103 assert(m_ignorevariable->getVolume() != 0);
104 *m_ignorevariable = VoxelArea();
109 VoxelArea *m_ignorevariable;
112 void * ServerThread::Thread()
116 log_register_thread("ServerThread");
118 DSTACK(__FUNCTION_NAME);
120 BEGIN_DEBUG_EXCEPTION_HANDLER
125 //TimeTaker timer("AsyncRunStep() + Receive()");
128 //TimeTaker timer("AsyncRunStep()");
129 m_server->AsyncRunStep();
132 //infostream<<"Running m_server->Receive()"<<std::endl;
135 catch(con::NoIncomingDataException &e)
138 catch(con::PeerNotFoundException &e)
140 infostream<<"Server: PeerNotFoundException"<<std::endl;
142 catch(con::ConnectionBindFailed &e)
144 m_server->setAsyncFatalError(e.what());
148 m_server->setAsyncFatalError(e.what());
152 END_DEBUG_EXCEPTION_HANDLER(errorstream)
157 void * EmergeThread::Thread()
161 log_register_thread("EmergeThread");
163 DSTACK(__FUNCTION_NAME);
165 BEGIN_DEBUG_EXCEPTION_HANDLER
167 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
169 v3s16 last_tried_pos(-32768,-32768,-32768); // For error output
172 Get block info from queue, emerge them and send them
175 After queue is empty, exit.
179 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
183 SharedPtr<QueuedBlockEmerge> q(qptr);
191 Do not generate over-limit
193 if(blockpos_over_limit(p))
196 //infostream<<"EmergeThread::Thread(): running"<<std::endl;
198 //TimeTaker timer("block emerge");
201 Try to emerge it from somewhere.
203 If it is only wanted as optional, only loading from disk
208 Check if any peer wants it as non-optional. In that case it
211 Also decrement the emerge queue count in clients.
214 bool only_from_disk = true;
217 core::map<u16, u8>::Iterator i;
218 for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
220 //u16 peer_id = i.getNode()->getKey();
223 u8 flags = i.getNode()->getValue();
224 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
225 only_from_disk = false;
230 if(enable_mapgen_debug_info)
231 infostream<<"EmergeThread: p="
232 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
233 <<"only_from_disk="<<only_from_disk<<std::endl;
235 ServerMap &map = ((ServerMap&)m_server->m_env->getMap());
237 MapBlock *block = NULL;
238 bool got_block = true;
239 core::map<v3s16, MapBlock*> modified_blocks;
242 Try to fetch block from memory or disk.
243 If not found and asked to generate, initialize generator.
246 bool started_generate = false;
247 mapgen::BlockMakeData data;
250 JMutexAutoLock envlock(m_server->m_env_mutex);
252 // Load sector if it isn't loaded
253 if(map.getSectorNoGenerateNoEx(p2d) == NULL)
254 map.loadSectorMeta(p2d);
256 // Attempt to load block
257 block = map.getBlockNoCreateNoEx(p);
258 if(!block || block->isDummy() || !block->isGenerated())
260 if(enable_mapgen_debug_info)
261 infostream<<"EmergeThread: not in memory, "
262 <<"attempting to load from disk"<<std::endl;
264 block = map.loadBlock(p);
267 // If could not load and allowed to generate, start generation
268 // inside this same envlock
269 if(only_from_disk == false &&
270 (block == NULL || block->isGenerated() == false)){
271 if(enable_mapgen_debug_info)
272 infostream<<"EmergeThread: generating"<<std::endl;
273 started_generate = true;
275 map.initBlockMake(&data, p);
280 If generator was initialized, generate now when envlock is free.
285 ScopeProfiler sp(g_profiler, "EmergeThread: mapgen::make_block",
287 TimeTaker t("mapgen::make_block()");
289 mapgen::make_block(&data);
291 if(enable_mapgen_debug_info == false)
292 t.stop(true); // Hide output
296 // Lock environment again to access the map
297 JMutexAutoLock envlock(m_server->m_env_mutex);
299 ScopeProfiler sp(g_profiler, "EmergeThread: after "
300 "mapgen::make_block (envlock)", SPT_AVG);
302 // Blit data back on map, update lighting, add mobs and
303 // whatever this does
304 map.finishBlockMake(&data, modified_blocks);
307 block = map.getBlockNoCreateNoEx(p);
309 // If block doesn't exist, don't try doing anything with it
310 // This happens if the block is not in generation boundaries
315 Do some post-generate stuff
318 v3s16 minp = data.blockpos_min*MAP_BLOCKSIZE;
319 v3s16 maxp = data.blockpos_max*MAP_BLOCKSIZE +
320 v3s16(1,1,1)*(MAP_BLOCKSIZE-1);
323 Ignore map edit events, they will not need to be
324 sent to anybody because the block hasn't been sent
327 //MapEditEventIgnorer ign(&m_server->m_ignore_map_edit_events);
328 MapEditEventAreaIgnorer ign(
329 &m_server->m_ignore_map_edit_events_area,
330 VoxelArea(minp, maxp));
332 TimeTaker timer("on_generated");
333 scriptapi_environment_on_generated(m_server->m_lua,
334 minp, maxp, mapgen::get_blockseed(data.seed, minp));
335 /*int t = timer.stop(true);
336 dstream<<"on_generated took "<<t<<"ms"<<std::endl;*/
339 if(enable_mapgen_debug_info)
340 infostream<<"EmergeThread: ended up with: "
341 <<analyze_block(block)<<std::endl;
343 // Activate objects and stuff
344 m_server->m_env->activateBlock(block, 0);
352 Set sent status of modified blocks on clients
355 // NOTE: Server's clients are also behind the connection mutex
356 JMutexAutoLock lock(m_server->m_con_mutex);
359 Add the originally fetched block to the modified list
363 modified_blocks.insert(p, block);
367 Set the modified blocks unsent for all the clients
370 for(core::map<u16, RemoteClient*>::Iterator
371 i = m_server->m_clients.getIterator();
372 i.atEnd() == false; i++)
374 RemoteClient *client = i.getNode()->getValue();
376 if(modified_blocks.size() > 0)
378 // Remove block from sent history
379 client->SetBlocksNotSent(modified_blocks);
383 catch(VersionMismatchException &e)
385 std::ostringstream err;
386 err<<"World data version mismatch in MapBlock "<<PP(last_tried_pos)<<std::endl;
387 err<<"----"<<std::endl;
388 err<<"\""<<e.what()<<"\""<<std::endl;
389 err<<"See debug.txt."<<std::endl;
390 err<<"World probably saved by a newer version of Minetest."<<std::endl;
391 m_server->setAsyncFatalError(err.str());
393 catch(SerializationError &e)
395 std::ostringstream err;
396 err<<"Invalid data in MapBlock "<<PP(last_tried_pos)<<std::endl;
397 err<<"----"<<std::endl;
398 err<<"\""<<e.what()<<"\""<<std::endl;
399 err<<"See debug.txt."<<std::endl;
400 err<<"You can ignore this using [ignore_world_load_errors = true]."<<std::endl;
401 m_server->setAsyncFatalError(err.str());
404 END_DEBUG_EXCEPTION_HANDLER(errorstream)
406 log_deregister_thread();
411 v3f ServerSoundParams::getPos(ServerEnvironment *env, bool *pos_exists) const
413 if(pos_exists) *pos_exists = false;
418 if(pos_exists) *pos_exists = true;
423 ServerActiveObject *sao = env->getActiveObject(object);
426 if(pos_exists) *pos_exists = true;
427 return sao->getBasePosition(); }
432 void RemoteClient::GetNextBlocks(Server *server, float dtime,
433 core::array<PrioritySortedBlockTransfer> &dest)
435 DSTACK(__FUNCTION_NAME);
438 TimeTaker timer("RemoteClient::GetNextBlocks", &timer_result);*/
441 m_nothing_to_send_pause_timer -= dtime;
442 m_nearest_unsent_reset_timer += dtime;
444 if(m_nothing_to_send_pause_timer >= 0)
449 // Won't send anything if already sending
450 if(m_blocks_sending.size() >= g_settings->getU16
451 ("max_simultaneous_block_sends_per_client"))
453 //infostream<<"Not sending any blocks, Queue full."<<std::endl;
457 //TimeTaker timer("RemoteClient::GetNextBlocks");
459 Player *player = server->m_env->getPlayer(peer_id);
461 assert(player != NULL);
463 v3f playerpos = player->getPosition();
464 v3f playerspeed = player->getSpeed();
465 v3f playerspeeddir(0,0,0);
466 if(playerspeed.getLength() > 1.0*BS)
467 playerspeeddir = playerspeed / playerspeed.getLength();
468 // Predict to next block
469 v3f playerpos_predicted = playerpos + playerspeeddir*MAP_BLOCKSIZE*BS;
471 v3s16 center_nodepos = floatToInt(playerpos_predicted, BS);
473 v3s16 center = getNodeBlockPos(center_nodepos);
475 // Camera position and direction
476 v3f camera_pos = player->getEyePosition();
477 v3f camera_dir = v3f(0,0,1);
478 camera_dir.rotateYZBy(player->getPitch());
479 camera_dir.rotateXZBy(player->getYaw());
481 /*infostream<<"camera_dir=("<<camera_dir.X<<","<<camera_dir.Y<<","
482 <<camera_dir.Z<<")"<<std::endl;*/
485 Get the starting value of the block finder radius.
488 if(m_last_center != center)
490 m_nearest_unsent_d = 0;
491 m_last_center = center;
494 /*infostream<<"m_nearest_unsent_reset_timer="
495 <<m_nearest_unsent_reset_timer<<std::endl;*/
497 // Reset periodically to workaround for some bugs or stuff
498 if(m_nearest_unsent_reset_timer > 20.0)
500 m_nearest_unsent_reset_timer = 0;
501 m_nearest_unsent_d = 0;
502 //infostream<<"Resetting m_nearest_unsent_d for "
503 // <<server->getPlayerName(peer_id)<<std::endl;
506 //s16 last_nearest_unsent_d = m_nearest_unsent_d;
507 s16 d_start = m_nearest_unsent_d;
509 //infostream<<"d_start="<<d_start<<std::endl;
511 u16 max_simul_sends_setting = g_settings->getU16
512 ("max_simultaneous_block_sends_per_client");
513 u16 max_simul_sends_usually = max_simul_sends_setting;
516 Check the time from last addNode/removeNode.
518 Decrease send rate if player is building stuff.
520 m_time_from_building += dtime;
521 if(m_time_from_building < g_settings->getFloat(
522 "full_block_send_enable_min_time_from_building"))
524 max_simul_sends_usually
525 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
529 Number of blocks sending + number of blocks selected for sending
531 u32 num_blocks_selected = m_blocks_sending.size();
534 next time d will be continued from the d from which the nearest
535 unsent block was found this time.
537 This is because not necessarily any of the blocks found this
538 time are actually sent.
540 s32 new_nearest_unsent_d = -1;
542 s16 d_max = g_settings->getS16("max_block_send_distance");
543 s16 d_max_gen = g_settings->getS16("max_block_generate_distance");
545 // Don't loop very much at a time
546 s16 max_d_increment_at_time = 2;
547 if(d_max > d_start + max_d_increment_at_time)
548 d_max = d_start + max_d_increment_at_time;
549 /*if(d_max_gen > d_start+2)
550 d_max_gen = d_start+2;*/
552 //infostream<<"Starting from "<<d_start<<std::endl;
554 s32 nearest_emerged_d = -1;
555 s32 nearest_emergefull_d = -1;
556 s32 nearest_sent_d = -1;
557 bool queue_is_full = false;
560 for(d = d_start; d <= d_max; d++)
562 /*errorstream<<"checking d="<<d<<" for "
563 <<server->getPlayerName(peer_id)<<std::endl;*/
564 //infostream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
567 If m_nearest_unsent_d was changed by the EmergeThread
568 (it can change it to 0 through SetBlockNotSent),
570 Else update m_nearest_unsent_d
572 /*if(m_nearest_unsent_d != last_nearest_unsent_d)
574 d = m_nearest_unsent_d;
575 last_nearest_unsent_d = m_nearest_unsent_d;
579 Get the border/face dot coordinates of a "d-radiused"
582 core::list<v3s16> list;
583 getFacePositions(list, d);
585 core::list<v3s16>::Iterator li;
586 for(li=list.begin(); li!=list.end(); li++)
588 v3s16 p = *li + center;
592 - Don't allow too many simultaneous transfers
593 - EXCEPT when the blocks are very close
595 Also, don't send blocks that are already flying.
598 // Start with the usual maximum
599 u16 max_simul_dynamic = max_simul_sends_usually;
601 // If block is very close, allow full maximum
602 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
603 max_simul_dynamic = max_simul_sends_setting;
605 // Don't select too many blocks for sending
606 if(num_blocks_selected >= max_simul_dynamic)
608 queue_is_full = true;
609 goto queue_full_break;
612 // Don't send blocks that are currently being transferred
613 if(m_blocks_sending.find(p) != NULL)
619 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
620 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
621 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
622 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
623 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
624 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
627 // If this is true, inexistent block will be made from scratch
628 bool generate = d <= d_max_gen;
631 /*// Limit the generating area vertically to 2/3
632 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
635 // Limit the send area vertically to 1/2
636 if(abs(p.Y - center.Y) > d_max / 2)
642 If block is far away, don't generate it unless it is
648 // Block center y in nodes
649 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
650 // Don't generate if it's very high or very low
651 if(y < -64 || y > 64)
655 v2s16 p2d_nodes_center(
659 // Get ground height in nodes
660 s16 gh = server->m_env->getServerMap().findGroundLevel(
663 // If differs a lot, don't generate
664 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
666 // Actually, don't even send it
672 //infostream<<"d="<<d<<std::endl;
675 Don't generate or send if not in sight
676 FIXME This only works if the client uses a small enough
677 FOV setting. The default of 72 degrees is fine.
680 float camera_fov = (72.0*M_PI/180) * 4./3.;
681 if(isBlockInSight(p, camera_pos, camera_dir, camera_fov, 10000*BS) == false)
687 Don't send already sent blocks
690 if(m_blocks_sent.find(p) != NULL)
697 Check if map has this block
699 MapBlock *block = server->m_env->getMap().getBlockNoCreateNoEx(p);
701 bool surely_not_found_on_disk = false;
702 bool block_is_invalid = false;
705 // Reset usage timer, this block will be of use in the future.
706 block->resetUsageTimer();
708 // Block is dummy if data doesn't exist.
709 // It means it has been not found from disk and not generated
712 surely_not_found_on_disk = true;
715 // Block is valid if lighting is up-to-date and data exists
716 if(block->isValid() == false)
718 block_is_invalid = true;
721 /*if(block->isFullyGenerated() == false)
723 block_is_invalid = true;
728 ServerMap *map = (ServerMap*)(&server->m_env->getMap());
729 v2s16 chunkpos = map->sector_to_chunk(p2d);
730 if(map->chunkNonVolatile(chunkpos) == false)
731 block_is_invalid = true;
733 if(block->isGenerated() == false)
734 block_is_invalid = true;
737 If block is not close, don't send it unless it is near
740 Block is near ground level if night-time mesh
741 differs from day-time mesh.
745 if(block->getDayNightDiff() == false)
752 If block has been marked to not exist on disk (dummy)
753 and generating new ones is not wanted, skip block.
755 if(generate == false && surely_not_found_on_disk == true)
762 Add inexistent block to emerge queue.
764 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
766 //TODO: Get value from somewhere
767 // Allow only one block in emerge queue
768 //if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
769 // Allow two blocks in queue per client
770 //if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
772 // Make it more responsive when needing to generate stuff
773 if(surely_not_found_on_disk)
775 if(server->m_emerge_queue.peerItemCount(peer_id) < max_emerge)
777 //infostream<<"Adding block to emerge queue"<<std::endl;
779 // Add it to the emerge queue and trigger the thread
782 if(generate == false)
783 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
785 server->m_emerge_queue.addBlock(peer_id, p, flags);
786 server->m_emergethread.trigger();
788 if(nearest_emerged_d == -1)
789 nearest_emerged_d = d;
791 if(nearest_emergefull_d == -1)
792 nearest_emergefull_d = d;
799 if(nearest_sent_d == -1)
803 Add block to send queue
806 /*errorstream<<"sending from d="<<d<<" to "
807 <<server->getPlayerName(peer_id)<<std::endl;*/
809 PrioritySortedBlockTransfer q((float)d, p, peer_id);
813 num_blocks_selected += 1;
818 //infostream<<"Stopped at "<<d<<std::endl;
820 // If nothing was found for sending and nothing was queued for
821 // emerging, continue next time browsing from here
822 if(nearest_emerged_d != -1){
823 new_nearest_unsent_d = nearest_emerged_d;
824 } else if(nearest_emergefull_d != -1){
825 new_nearest_unsent_d = nearest_emergefull_d;
827 if(d > g_settings->getS16("max_block_send_distance")){
828 new_nearest_unsent_d = 0;
829 m_nothing_to_send_pause_timer = 2.0;
830 /*infostream<<"GetNextBlocks(): d wrapped around for "
831 <<server->getPlayerName(peer_id)
832 <<"; setting to 0 and pausing"<<std::endl;*/
834 if(nearest_sent_d != -1)
835 new_nearest_unsent_d = nearest_sent_d;
837 new_nearest_unsent_d = d;
841 if(new_nearest_unsent_d != -1)
842 m_nearest_unsent_d = new_nearest_unsent_d;
844 /*timer_result = timer.stop(true);
845 if(timer_result != 0)
846 infostream<<"GetNextBlocks timeout: "<<timer_result<<" (!=0)"<<std::endl;*/
849 void RemoteClient::GotBlock(v3s16 p)
851 if(m_blocks_sending.find(p) != NULL)
852 m_blocks_sending.remove(p);
855 /*infostream<<"RemoteClient::GotBlock(): Didn't find in"
856 " m_blocks_sending"<<std::endl;*/
857 m_excess_gotblocks++;
859 m_blocks_sent.insert(p, true);
862 void RemoteClient::SentBlock(v3s16 p)
864 if(m_blocks_sending.find(p) == NULL)
865 m_blocks_sending.insert(p, 0.0);
867 infostream<<"RemoteClient::SentBlock(): Sent block"
868 " already in m_blocks_sending"<<std::endl;
871 void RemoteClient::SetBlockNotSent(v3s16 p)
873 m_nearest_unsent_d = 0;
875 if(m_blocks_sending.find(p) != NULL)
876 m_blocks_sending.remove(p);
877 if(m_blocks_sent.find(p) != NULL)
878 m_blocks_sent.remove(p);
881 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
883 m_nearest_unsent_d = 0;
885 for(core::map<v3s16, MapBlock*>::Iterator
886 i = blocks.getIterator();
887 i.atEnd()==false; i++)
889 v3s16 p = i.getNode()->getKey();
891 if(m_blocks_sending.find(p) != NULL)
892 m_blocks_sending.remove(p);
893 if(m_blocks_sent.find(p) != NULL)
894 m_blocks_sent.remove(p);
902 PlayerInfo::PlayerInfo()
908 void PlayerInfo::PrintLine(std::ostream *s)
911 (*s)<<"\""<<name<<"\" ("
912 <<(position.X/10)<<","<<(position.Y/10)
913 <<","<<(position.Z/10)<<") ";
915 (*s)<<" avg_rtt="<<avg_rtt;
924 const std::string &path_world,
925 const std::string &path_config,
926 const SubgameSpec &gamespec,
927 bool simple_singleplayer_mode
929 m_path_world(path_world),
930 m_path_config(path_config),
931 m_gamespec(gamespec),
932 m_simple_singleplayer_mode(simple_singleplayer_mode),
933 m_async_fatal_error(""),
935 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
936 m_banmanager(path_world+DIR_DELIM+"ipban.txt"),
938 m_itemdef(createItemDefManager()),
939 m_nodedef(createNodeDefManager()),
940 m_craftdef(createCraftDefManager()),
941 m_event(new EventManager()),
943 m_emergethread(this),
944 m_time_of_day_send_timer(0),
946 m_shutdown_requested(false),
947 m_ignore_map_edit_events(false),
948 m_ignore_map_edit_events_peer_id(0)
950 m_liquid_transform_timer = 0.0;
951 m_print_info_timer = 0.0;
952 m_objectdata_timer = 0.0;
953 m_emergethread_trigger_timer = 0.0;
954 m_savemap_timer = 0.0;
958 m_step_dtime_mutex.Init();
962 throw ServerError("Supplied empty world path");
964 if(!gamespec.isValid())
965 throw ServerError("Supplied invalid gamespec");
967 infostream<<"Server created for gameid \""<<m_gamespec.id<<"\"";
968 if(m_simple_singleplayer_mode)
969 infostream<<" in simple singleplayer mode"<<std::endl;
971 infostream<<std::endl;
972 infostream<<"- world: "<<m_path_world<<std::endl;
973 infostream<<"- config: "<<m_path_config<<std::endl;
974 infostream<<"- game: "<<m_gamespec.path<<std::endl;
976 // Add world mod search path
977 m_modspaths.push_front(m_path_world + DIR_DELIM + "worldmods");
978 // Add addon mod search path
979 for(std::set<std::string>::const_iterator i = m_gamespec.mods_paths.begin();
980 i != m_gamespec.mods_paths.end(); i++)
981 m_modspaths.push_front((*i));
983 // Print out mod search paths
984 for(core::list<std::string>::Iterator i = m_modspaths.begin();
985 i != m_modspaths.end(); i++){
986 std::string modspath = *i;
987 infostream<<"- mods: "<<modspath<<std::endl;
990 // Path to builtin.lua
991 std::string builtinpath = getBuiltinLuaPath() + DIR_DELIM + "builtin.lua";
993 // Create world if it doesn't exist
994 if(!initializeWorld(m_path_world, m_gamespec.id))
995 throw ServerError("Failed to initialize world");
998 JMutexAutoLock envlock(m_env_mutex);
999 JMutexAutoLock conlock(m_con_mutex);
1001 // Initialize scripting
1003 infostream<<"Server: Initializing Lua"<<std::endl;
1004 m_lua = script_init();
1007 scriptapi_export(m_lua, this);
1008 // Load and run builtin.lua
1009 infostream<<"Server: Loading builtin.lua [\""
1010 <<builtinpath<<"\"]"<<std::endl;
1011 bool success = scriptapi_loadmod(m_lua, builtinpath, "__builtin");
1013 errorstream<<"Server: Failed to load and run "
1014 <<builtinpath<<std::endl;
1015 throw ModError("Failed to load and run "+builtinpath);
1017 // Find mods in mod search paths
1018 m_mods = getMods(m_modspaths);
1020 infostream<<"Server: Loading mods: ";
1021 for(core::list<ModSpec>::Iterator i = m_mods.begin();
1022 i != m_mods.end(); i++){
1023 const ModSpec &mod = *i;
1024 infostream<<mod.name<<" ";
1026 infostream<<std::endl;
1027 // Load and run "mod" scripts
1028 for(core::list<ModSpec>::Iterator i = m_mods.begin();
1029 i != m_mods.end(); i++){
1030 const ModSpec &mod = *i;
1031 std::string scriptpath = mod.path + DIR_DELIM + "init.lua";
1032 infostream<<" ["<<padStringRight(mod.name, 12)<<"] [\""
1033 <<scriptpath<<"\"]"<<std::endl;
1034 bool success = scriptapi_loadmod(m_lua, scriptpath, mod.name);
1036 errorstream<<"Server: Failed to load and run "
1037 <<scriptpath<<std::endl;
1038 throw ModError("Failed to load and run "+scriptpath);
1042 // Read Textures and calculate sha1 sums
1045 // Apply item aliases in the node definition manager
1046 m_nodedef->updateAliases(m_itemdef);
1048 // Initialize Environment
1050 m_env = new ServerEnvironment(new ServerMap(path_world, this), m_lua,
1053 // Give environment reference to scripting api
1054 scriptapi_add_environment(m_lua, m_env);
1056 // Register us to receive map edit events
1057 m_env->getMap().addEventReceiver(this);
1059 // If file exists, load environment metadata
1060 if(fs::PathExists(m_path_world+DIR_DELIM+"env_meta.txt"))
1062 infostream<<"Server: Loading environment metadata"<<std::endl;
1063 m_env->loadMeta(m_path_world);
1067 infostream<<"Server: Loading players"<<std::endl;
1068 m_env->deSerializePlayers(m_path_world);
1071 Add some test ActiveBlockModifiers to environment
1073 add_legacy_abms(m_env, m_nodedef);
1078 infostream<<"Server destructing"<<std::endl;
1081 Send shutdown message
1084 JMutexAutoLock conlock(m_con_mutex);
1086 std::wstring line = L"*** Server shutting down";
1089 Send the message to clients
1091 for(core::map<u16, RemoteClient*>::Iterator
1092 i = m_clients.getIterator();
1093 i.atEnd() == false; i++)
1095 // Get client and check that it is valid
1096 RemoteClient *client = i.getNode()->getValue();
1097 assert(client->peer_id == i.getNode()->getKey());
1098 if(client->serialization_version == SER_FMT_VER_INVALID)
1102 SendChatMessage(client->peer_id, line);
1104 catch(con::PeerNotFoundException &e)
1110 JMutexAutoLock envlock(m_env_mutex);
1115 infostream<<"Server: Saving players"<<std::endl;
1116 m_env->serializePlayers(m_path_world);
1119 Save environment metadata
1121 infostream<<"Server: Saving environment metadata"<<std::endl;
1122 m_env->saveMeta(m_path_world);
1134 JMutexAutoLock clientslock(m_con_mutex);
1136 for(core::map<u16, RemoteClient*>::Iterator
1137 i = m_clients.getIterator();
1138 i.atEnd() == false; i++)
1141 // NOTE: These are removed by env destructor
1143 u16 peer_id = i.getNode()->getKey();
1144 JMutexAutoLock envlock(m_env_mutex);
1145 m_env->removePlayer(peer_id);
1149 delete i.getNode()->getValue();
1153 // Delete things in the reverse order of creation
1160 // Deinitialize scripting
1161 infostream<<"Server: Deinitializing scripting"<<std::endl;
1162 script_deinit(m_lua);
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,
3251 else if(command == TOSERVER_INVENTORY_FIELDS)
3253 std::string datastring((char*)&data[2], datasize-2);
3254 std::istringstream is(datastring, std::ios_base::binary);
3256 std::string formname = deSerializeString(is);
3257 int num = readU16(is);
3258 std::map<std::string, std::string> fields;
3259 for(int k=0; k<num; k++){
3260 std::string fieldname = deSerializeString(is);
3261 std::string fieldvalue = deSerializeLongString(is);
3262 fields[fieldname] = fieldvalue;
3265 scriptapi_on_player_receive_fields(m_lua, playersao, formname, fields);
3269 infostream<<"Server::ProcessData(): Ignoring "
3270 "unknown command "<<command<<std::endl;
3274 catch(SendFailedException &e)
3276 errorstream<<"Server::ProcessData(): SendFailedException: "
3282 void Server::onMapEditEvent(MapEditEvent *event)
3284 //infostream<<"Server::onMapEditEvent()"<<std::endl;
3285 if(m_ignore_map_edit_events)
3287 if(m_ignore_map_edit_events_area.contains(event->getArea()))
3289 MapEditEvent *e = event->clone();
3290 m_unsent_map_edit_queue.push_back(e);
3293 Inventory* Server::getInventory(const InventoryLocation &loc)
3296 case InventoryLocation::UNDEFINED:
3299 case InventoryLocation::CURRENT_PLAYER:
3302 case InventoryLocation::PLAYER:
3304 Player *player = m_env->getPlayer(loc.name.c_str());
3307 PlayerSAO *playersao = player->getPlayerSAO();
3310 return playersao->getInventory();
3313 case InventoryLocation::NODEMETA:
3315 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
3318 return meta->getInventory();
3326 void Server::setInventoryModified(const InventoryLocation &loc)
3329 case InventoryLocation::UNDEFINED:
3332 case InventoryLocation::PLAYER:
3334 Player *player = m_env->getPlayer(loc.name.c_str());
3337 PlayerSAO *playersao = player->getPlayerSAO();
3340 playersao->m_inventory_not_sent = true;
3341 playersao->m_wielded_item_not_sent = true;
3344 case InventoryLocation::NODEMETA:
3346 v3s16 blockpos = getNodeBlockPos(loc.p);
3348 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3350 block->raiseModified(MOD_STATE_WRITE_NEEDED);
3352 setBlockNotSent(blockpos);
3360 core::list<PlayerInfo> Server::getPlayerInfo()
3362 DSTACK(__FUNCTION_NAME);
3363 JMutexAutoLock envlock(m_env_mutex);
3364 JMutexAutoLock conlock(m_con_mutex);
3366 core::list<PlayerInfo> list;
3368 core::list<Player*> players = m_env->getPlayers();
3370 core::list<Player*>::Iterator i;
3371 for(i = players.begin();
3372 i != players.end(); i++)
3376 Player *player = *i;
3379 // Copy info from connection to info struct
3380 info.id = player->peer_id;
3381 info.address = m_con.GetPeerAddress(player->peer_id);
3382 info.avg_rtt = m_con.GetPeerAvgRTT(player->peer_id);
3384 catch(con::PeerNotFoundException &e)
3386 // Set dummy peer info
3388 info.address = Address(0,0,0,0,0);
3392 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3393 info.position = player->getPosition();
3395 list.push_back(info);
3402 void Server::peerAdded(con::Peer *peer)
3404 DSTACK(__FUNCTION_NAME);
3405 verbosestream<<"Server::peerAdded(): peer->id="
3406 <<peer->id<<std::endl;
3409 c.type = PEER_ADDED;
3410 c.peer_id = peer->id;
3412 m_peer_change_queue.push_back(c);
3415 void Server::deletingPeer(con::Peer *peer, bool timeout)
3417 DSTACK(__FUNCTION_NAME);
3418 verbosestream<<"Server::deletingPeer(): peer->id="
3419 <<peer->id<<", timeout="<<timeout<<std::endl;
3422 c.type = PEER_REMOVED;
3423 c.peer_id = peer->id;
3424 c.timeout = timeout;
3425 m_peer_change_queue.push_back(c);
3432 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3434 DSTACK(__FUNCTION_NAME);
3435 std::ostringstream os(std::ios_base::binary);
3437 writeU16(os, TOCLIENT_HP);
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::SendAccessDenied(con::Connection &con, u16 peer_id,
3448 const std::wstring &reason)
3450 DSTACK(__FUNCTION_NAME);
3451 std::ostringstream os(std::ios_base::binary);
3453 writeU16(os, TOCLIENT_ACCESS_DENIED);
3454 os<<serializeWideString(reason);
3457 std::string s = os.str();
3458 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3460 con.Send(peer_id, 0, data, true);
3463 void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
3464 bool set_camera_point_target, v3f camera_point_target)
3466 DSTACK(__FUNCTION_NAME);
3467 std::ostringstream os(std::ios_base::binary);
3469 writeU16(os, TOCLIENT_DEATHSCREEN);
3470 writeU8(os, set_camera_point_target);
3471 writeV3F1000(os, camera_point_target);
3474 std::string s = os.str();
3475 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3477 con.Send(peer_id, 0, data, true);
3480 void Server::SendItemDef(con::Connection &con, u16 peer_id,
3481 IItemDefManager *itemdef)
3483 DSTACK(__FUNCTION_NAME);
3484 std::ostringstream os(std::ios_base::binary);
3488 u32 length of the next item
3489 zlib-compressed serialized ItemDefManager
3491 writeU16(os, TOCLIENT_ITEMDEF);
3492 std::ostringstream tmp_os(std::ios::binary);
3493 itemdef->serialize(tmp_os);
3494 std::ostringstream tmp_os2(std::ios::binary);
3495 compressZlib(tmp_os.str(), tmp_os2);
3496 os<<serializeLongString(tmp_os2.str());
3499 std::string s = os.str();
3500 verbosestream<<"Server: Sending item definitions to id("<<peer_id
3501 <<"): size="<<s.size()<<std::endl;
3502 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3504 con.Send(peer_id, 0, data, true);
3507 void Server::SendNodeDef(con::Connection &con, u16 peer_id,
3508 INodeDefManager *nodedef)
3510 DSTACK(__FUNCTION_NAME);
3511 std::ostringstream os(std::ios_base::binary);
3515 u32 length of the next item
3516 zlib-compressed serialized NodeDefManager
3518 writeU16(os, TOCLIENT_NODEDEF);
3519 std::ostringstream tmp_os(std::ios::binary);
3520 nodedef->serialize(tmp_os);
3521 std::ostringstream tmp_os2(std::ios::binary);
3522 compressZlib(tmp_os.str(), tmp_os2);
3523 os<<serializeLongString(tmp_os2.str());
3526 std::string s = os.str();
3527 verbosestream<<"Server: Sending node definitions to id("<<peer_id
3528 <<"): size="<<s.size()<<std::endl;
3529 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3531 con.Send(peer_id, 0, data, true);
3535 Non-static send methods
3538 void Server::SendInventory(u16 peer_id)
3540 DSTACK(__FUNCTION_NAME);
3542 PlayerSAO *playersao = getPlayerSAO(peer_id);
3545 playersao->m_inventory_not_sent = false;
3551 std::ostringstream os;
3552 playersao->getInventory()->serialize(os);
3554 std::string s = os.str();
3556 SharedBuffer<u8> data(s.size()+2);
3557 writeU16(&data[0], TOCLIENT_INVENTORY);
3558 memcpy(&data[2], s.c_str(), s.size());
3561 m_con.Send(peer_id, 0, data, true);
3564 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3566 DSTACK(__FUNCTION_NAME);
3568 std::ostringstream os(std::ios_base::binary);
3572 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3573 os.write((char*)buf, 2);
3576 writeU16(buf, message.size());
3577 os.write((char*)buf, 2);
3580 for(u32 i=0; i<message.size(); i++)
3584 os.write((char*)buf, 2);
3588 std::string s = os.str();
3589 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3591 m_con.Send(peer_id, 0, data, true);
3594 void Server::BroadcastChatMessage(const std::wstring &message)
3596 for(core::map<u16, RemoteClient*>::Iterator
3597 i = m_clients.getIterator();
3598 i.atEnd() == false; i++)
3600 // Get client and check that it is valid
3601 RemoteClient *client = i.getNode()->getValue();
3602 assert(client->peer_id == i.getNode()->getKey());
3603 if(client->serialization_version == SER_FMT_VER_INVALID)
3606 SendChatMessage(client->peer_id, message);
3610 void Server::SendPlayerHP(u16 peer_id)
3612 DSTACK(__FUNCTION_NAME);
3613 PlayerSAO *playersao = getPlayerSAO(peer_id);
3615 playersao->m_hp_not_sent = false;
3616 SendHP(m_con, peer_id, playersao->getHP());
3619 void Server::SendMovePlayer(u16 peer_id)
3621 DSTACK(__FUNCTION_NAME);
3622 Player *player = m_env->getPlayer(peer_id);
3625 std::ostringstream os(std::ios_base::binary);
3626 writeU16(os, TOCLIENT_MOVE_PLAYER);
3627 writeV3F1000(os, player->getPosition());
3628 writeF1000(os, player->getPitch());
3629 writeF1000(os, player->getYaw());
3632 v3f pos = player->getPosition();
3633 f32 pitch = player->getPitch();
3634 f32 yaw = player->getYaw();
3635 verbosestream<<"Server: Sending TOCLIENT_MOVE_PLAYER"
3636 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3643 std::string s = os.str();
3644 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3646 m_con.Send(peer_id, 0, data, true);
3649 void Server::SendPlayerPrivileges(u16 peer_id)
3651 Player *player = m_env->getPlayer(peer_id);
3653 if(player->peer_id == PEER_ID_INEXISTENT)
3656 std::set<std::string> privs;
3657 scriptapi_get_auth(m_lua, player->getName(), NULL, &privs);
3659 std::ostringstream os(std::ios_base::binary);
3660 writeU16(os, TOCLIENT_PRIVILEGES);
3661 writeU16(os, privs.size());
3662 for(std::set<std::string>::const_iterator i = privs.begin();
3663 i != privs.end(); i++){
3664 os<<serializeString(*i);
3668 std::string s = os.str();
3669 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3671 m_con.Send(peer_id, 0, data, true);
3674 void Server::SendPlayerInventoryFormspec(u16 peer_id)
3676 Player *player = m_env->getPlayer(peer_id);
3678 if(player->peer_id == PEER_ID_INEXISTENT)
3681 std::ostringstream os(std::ios_base::binary);
3682 writeU16(os, TOCLIENT_INVENTORY_FORMSPEC);
3683 os<<serializeLongString(player->inventory_formspec);
3686 std::string s = os.str();
3687 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3689 m_con.Send(peer_id, 0, data, true);
3692 s32 Server::playSound(const SimpleSoundSpec &spec,
3693 const ServerSoundParams ¶ms)
3695 // Find out initial position of sound
3696 bool pos_exists = false;
3697 v3f pos = params.getPos(m_env, &pos_exists);
3698 // If position is not found while it should be, cancel sound
3699 if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
3701 // Filter destination clients
3702 std::set<RemoteClient*> dst_clients;
3703 if(params.to_player != "")
3705 Player *player = m_env->getPlayer(params.to_player.c_str());
3707 infostream<<"Server::playSound: Player \""<<params.to_player
3708 <<"\" not found"<<std::endl;
3711 if(player->peer_id == PEER_ID_INEXISTENT){
3712 infostream<<"Server::playSound: Player \""<<params.to_player
3713 <<"\" not connected"<<std::endl;
3716 RemoteClient *client = getClient(player->peer_id);
3717 dst_clients.insert(client);
3721 for(core::map<u16, RemoteClient*>::Iterator
3722 i = m_clients.getIterator(); i.atEnd() == false; i++)
3724 RemoteClient *client = i.getNode()->getValue();
3725 Player *player = m_env->getPlayer(client->peer_id);
3729 if(player->getPosition().getDistanceFrom(pos) >
3730 params.max_hear_distance)
3733 dst_clients.insert(client);
3736 if(dst_clients.size() == 0)
3739 s32 id = m_next_sound_id++;
3740 // The sound will exist as a reference in m_playing_sounds
3741 m_playing_sounds[id] = ServerPlayingSound();
3742 ServerPlayingSound &psound = m_playing_sounds[id];
3743 psound.params = params;
3744 for(std::set<RemoteClient*>::iterator i = dst_clients.begin();
3745 i != dst_clients.end(); i++)
3746 psound.clients.insert((*i)->peer_id);
3748 std::ostringstream os(std::ios_base::binary);
3749 writeU16(os, TOCLIENT_PLAY_SOUND);
3751 os<<serializeString(spec.name);
3752 writeF1000(os, spec.gain * params.gain);
3753 writeU8(os, params.type);
3754 writeV3F1000(os, pos);
3755 writeU16(os, params.object);
3756 writeU8(os, params.loop);
3758 std::string s = os.str();
3759 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3761 for(std::set<RemoteClient*>::iterator i = dst_clients.begin();
3762 i != dst_clients.end(); i++){
3764 m_con.Send((*i)->peer_id, 0, data, true);
3768 void Server::stopSound(s32 handle)
3770 // Get sound reference
3771 std::map<s32, ServerPlayingSound>::iterator i =
3772 m_playing_sounds.find(handle);
3773 if(i == m_playing_sounds.end())
3775 ServerPlayingSound &psound = i->second;
3777 std::ostringstream os(std::ios_base::binary);
3778 writeU16(os, TOCLIENT_STOP_SOUND);
3779 writeS32(os, handle);
3781 std::string s = os.str();
3782 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3784 for(std::set<u16>::iterator i = psound.clients.begin();
3785 i != psound.clients.end(); i++){
3787 m_con.Send(*i, 0, data, true);
3789 // Remove sound reference
3790 m_playing_sounds.erase(i);
3793 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3794 core::list<u16> *far_players, float far_d_nodes)
3796 float maxd = far_d_nodes*BS;
3797 v3f p_f = intToFloat(p, BS);
3801 SharedBuffer<u8> reply(replysize);
3802 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3803 writeS16(&reply[2], p.X);
3804 writeS16(&reply[4], p.Y);
3805 writeS16(&reply[6], p.Z);
3807 for(core::map<u16, RemoteClient*>::Iterator
3808 i = m_clients.getIterator();
3809 i.atEnd() == false; i++)
3811 // Get client and check that it is valid
3812 RemoteClient *client = i.getNode()->getValue();
3813 assert(client->peer_id == i.getNode()->getKey());
3814 if(client->serialization_version == SER_FMT_VER_INVALID)
3817 // Don't send if it's the same one
3818 if(client->peer_id == ignore_id)
3824 Player *player = m_env->getPlayer(client->peer_id);
3827 // If player is far away, only set modified blocks not sent
3828 v3f player_pos = player->getPosition();
3829 if(player_pos.getDistanceFrom(p_f) > maxd)
3831 far_players->push_back(client->peer_id);
3838 m_con.Send(client->peer_id, 0, reply, true);
3842 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3843 core::list<u16> *far_players, float far_d_nodes)
3845 float maxd = far_d_nodes*BS;
3846 v3f p_f = intToFloat(p, BS);
3848 for(core::map<u16, RemoteClient*>::Iterator
3849 i = m_clients.getIterator();
3850 i.atEnd() == false; i++)
3852 // Get client and check that it is valid
3853 RemoteClient *client = i.getNode()->getValue();
3854 assert(client->peer_id == i.getNode()->getKey());
3855 if(client->serialization_version == SER_FMT_VER_INVALID)
3858 // Don't send if it's the same one
3859 if(client->peer_id == ignore_id)
3865 Player *player = m_env->getPlayer(client->peer_id);
3868 // If player is far away, only set modified blocks not sent
3869 v3f player_pos = player->getPosition();
3870 if(player_pos.getDistanceFrom(p_f) > maxd)
3872 far_players->push_back(client->peer_id);
3879 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3880 SharedBuffer<u8> reply(replysize);
3881 writeU16(&reply[0], TOCLIENT_ADDNODE);
3882 writeS16(&reply[2], p.X);
3883 writeS16(&reply[4], p.Y);
3884 writeS16(&reply[6], p.Z);
3885 n.serialize(&reply[8], client->serialization_version);
3888 m_con.Send(client->peer_id, 0, reply, true);
3892 void Server::setBlockNotSent(v3s16 p)
3894 for(core::map<u16, RemoteClient*>::Iterator
3895 i = m_clients.getIterator();
3896 i.atEnd()==false; i++)
3898 RemoteClient *client = i.getNode()->getValue();
3899 client->SetBlockNotSent(p);
3903 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3905 DSTACK(__FUNCTION_NAME);
3907 v3s16 p = block->getPos();
3911 bool completely_air = true;
3912 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3913 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3914 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3916 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
3918 completely_air = false;
3919 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
3924 infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
3926 infostream<<"[completely air] ";
3927 infostream<<std::endl;
3931 Create a packet with the block in the right format
3934 std::ostringstream os(std::ios_base::binary);
3935 block->serialize(os, ver, false);
3936 std::string s = os.str();
3937 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3939 u32 replysize = 8 + blockdata.getSize();
3940 SharedBuffer<u8> reply(replysize);
3941 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3942 writeS16(&reply[2], p.X);
3943 writeS16(&reply[4], p.Y);
3944 writeS16(&reply[6], p.Z);
3945 memcpy(&reply[8], *blockdata, blockdata.getSize());
3947 /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3948 <<": \tpacket size: "<<replysize<<std::endl;*/
3953 m_con.Send(peer_id, 1, reply, true);
3956 void Server::SendBlocks(float dtime)
3958 DSTACK(__FUNCTION_NAME);
3960 JMutexAutoLock envlock(m_env_mutex);
3961 JMutexAutoLock conlock(m_con_mutex);
3963 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
3965 core::array<PrioritySortedBlockTransfer> queue;
3967 s32 total_sending = 0;
3970 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
3972 for(core::map<u16, RemoteClient*>::Iterator
3973 i = m_clients.getIterator();
3974 i.atEnd() == false; i++)
3976 RemoteClient *client = i.getNode()->getValue();
3977 assert(client->peer_id == i.getNode()->getKey());
3979 // If definitions and textures have not been sent, don't
3980 // send MapBlocks either
3981 if(!client->definitions_sent)
3984 total_sending += client->SendingCount();
3986 if(client->serialization_version == SER_FMT_VER_INVALID)
3989 client->GetNextBlocks(this, dtime, queue);
3994 // Lowest priority number comes first.
3995 // Lowest is most important.
3998 for(u32 i=0; i<queue.size(); i++)
4000 //TODO: Calculate limit dynamically
4001 if(total_sending >= g_settings->getS32
4002 ("max_simultaneous_block_sends_server_total"))
4005 PrioritySortedBlockTransfer q = queue[i];
4007 MapBlock *block = NULL;
4010 block = m_env->getMap().getBlockNoCreate(q.pos);
4012 catch(InvalidPositionException &e)
4017 RemoteClient *client = getClient(q.peer_id);
4019 SendBlockNoLock(q.peer_id, block, 24);//client->serialization_version);
4021 client->SentBlock(q.pos);
4027 void Server::fillMediaCache()
4029 DSTACK(__FUNCTION_NAME);
4031 infostream<<"Server: Calculating media file checksums"<<std::endl;
4033 // Collect all media file paths
4034 std::list<std::string> paths;
4035 for(core::list<ModSpec>::Iterator i = m_mods.begin();
4036 i != m_mods.end(); i++){
4037 const ModSpec &mod = *i;
4038 paths.push_back(mod.path + DIR_DELIM + "textures");
4039 paths.push_back(mod.path + DIR_DELIM + "sounds");
4040 paths.push_back(mod.path + DIR_DELIM + "media");
4042 std::string path_all = "textures";
4043 paths.push_back(path_all + DIR_DELIM + "all");
4045 // Collect media file information from paths into cache
4046 for(std::list<std::string>::iterator i = paths.begin();
4047 i != paths.end(); i++)
4049 std::string mediapath = *i;
4050 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
4051 for(u32 j=0; j<dirlist.size(); j++){
4052 if(dirlist[j].dir) // Ignode dirs
4054 std::string filename = dirlist[j].name;
4055 // If name contains illegal characters, ignore the file
4056 if(!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)){
4057 infostream<<"Server: ignoring illegal file name: \""
4058 <<filename<<"\""<<std::endl;
4061 // If name is not in a supported format, ignore it
4062 const char *supported_ext[] = {
4063 ".png", ".jpg", ".bmp", ".tga",
4064 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
4068 if(removeStringEnd(filename, supported_ext) == ""){
4069 infostream<<"Server: ignoring unsupported file extension: \""
4070 <<filename<<"\""<<std::endl;
4073 // Ok, attempt to load the file and add to cache
4074 std::string filepath = mediapath + DIR_DELIM + filename;
4076 std::ifstream fis(filepath.c_str(), std::ios_base::binary);
4077 if(fis.good() == false){
4078 errorstream<<"Server::fillMediaCache(): Could not open \""
4079 <<filename<<"\" for reading"<<std::endl;
4082 std::ostringstream tmp_os(std::ios_base::binary);
4086 fis.read(buf, 1024);
4087 std::streamsize len = fis.gcount();
4088 tmp_os.write(buf, len);
4097 errorstream<<"Server::fillMediaCache(): Failed to read \""
4098 <<filename<<"\""<<std::endl;
4101 if(tmp_os.str().length() == 0){
4102 errorstream<<"Server::fillMediaCache(): Empty file \""
4103 <<filepath<<"\""<<std::endl;
4108 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
4110 unsigned char *digest = sha1.getDigest();
4111 std::string sha1_base64 = base64_encode(digest, 20);
4112 std::string sha1_hex = hex_encode((char*)digest, 20);
4116 this->m_media[filename] = MediaInfo(filepath, sha1_base64);
4117 verbosestream<<"Server: "<<sha1_hex<<" is "<<filename<<std::endl;
4122 struct SendableMediaAnnouncement
4125 std::string sha1_digest;
4127 SendableMediaAnnouncement(const std::string name_="",
4128 const std::string sha1_digest_=""):
4130 sha1_digest(sha1_digest_)
4134 void Server::sendMediaAnnouncement(u16 peer_id)
4136 DSTACK(__FUNCTION_NAME);
4138 verbosestream<<"Server: Announcing files to id("<<peer_id<<")"
4141 core::list<SendableMediaAnnouncement> file_announcements;
4143 for(std::map<std::string, MediaInfo>::iterator i = m_media.begin();
4144 i != m_media.end(); i++){
4146 file_announcements.push_back(
4147 SendableMediaAnnouncement(i->first, i->second.sha1_digest));
4151 std::ostringstream os(std::ios_base::binary);
4159 u16 length of sha1_digest
4164 writeU16(os, TOCLIENT_ANNOUNCE_MEDIA);
4165 writeU16(os, file_announcements.size());
4167 for(core::list<SendableMediaAnnouncement>::Iterator
4168 j = file_announcements.begin();
4169 j != file_announcements.end(); j++){
4170 os<<serializeString(j->name);
4171 os<<serializeString(j->sha1_digest);
4175 std::string s = os.str();
4176 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4179 m_con.Send(peer_id, 0, data, true);
4183 struct SendableMedia
4189 SendableMedia(const std::string &name_="", const std::string path_="",
4190 const std::string &data_=""):
4197 void Server::sendRequestedMedia(u16 peer_id,
4198 const core::list<MediaRequest> &tosend)
4200 DSTACK(__FUNCTION_NAME);
4202 verbosestream<<"Server::sendRequestedMedia(): "
4203 <<"Sending files to client"<<std::endl;
4207 // Put 5kB in one bunch (this is not accurate)
4208 u32 bytes_per_bunch = 5000;
4210 core::array< core::list<SendableMedia> > file_bunches;
4211 file_bunches.push_back(core::list<SendableMedia>());
4213 u32 file_size_bunch_total = 0;
4215 for(core::list<MediaRequest>::ConstIterator i = tosend.begin();
4216 i != tosend.end(); i++)
4218 if(m_media.find(i->name) == m_media.end()){
4219 errorstream<<"Server::sendRequestedMedia(): Client asked for "
4220 <<"unknown file \""<<(i->name)<<"\""<<std::endl;
4224 //TODO get path + name
4225 std::string tpath = m_media[(*i).name].path;
4228 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
4229 if(fis.good() == false){
4230 errorstream<<"Server::sendRequestedMedia(): Could not open \""
4231 <<tpath<<"\" for reading"<<std::endl;
4234 std::ostringstream tmp_os(std::ios_base::binary);
4238 fis.read(buf, 1024);
4239 std::streamsize len = fis.gcount();
4240 tmp_os.write(buf, len);
4241 file_size_bunch_total += len;
4250 errorstream<<"Server::sendRequestedMedia(): Failed to read \""
4251 <<(*i).name<<"\""<<std::endl;
4254 /*infostream<<"Server::sendRequestedMedia(): Loaded \""
4255 <<tname<<"\""<<std::endl;*/
4257 file_bunches[file_bunches.size()-1].push_back(
4258 SendableMedia((*i).name, tpath, tmp_os.str()));
4260 // Start next bunch if got enough data
4261 if(file_size_bunch_total >= bytes_per_bunch){
4262 file_bunches.push_back(core::list<SendableMedia>());
4263 file_size_bunch_total = 0;
4268 /* Create and send packets */
4270 u32 num_bunches = file_bunches.size();
4271 for(u32 i=0; i<num_bunches; i++)
4273 std::ostringstream os(std::ios_base::binary);
4277 u16 total number of texture bunches
4278 u16 index of this bunch
4279 u32 number of files in this bunch
4288 writeU16(os, TOCLIENT_MEDIA);
4289 writeU16(os, num_bunches);
4291 writeU32(os, file_bunches[i].size());
4293 for(core::list<SendableMedia>::Iterator
4294 j = file_bunches[i].begin();
4295 j != file_bunches[i].end(); j++){
4296 os<<serializeString(j->name);
4297 os<<serializeLongString(j->data);
4301 std::string s = os.str();
4302 verbosestream<<"Server::sendRequestedMedia(): bunch "
4303 <<i<<"/"<<num_bunches
4304 <<" files="<<file_bunches[i].size()
4305 <<" size=" <<s.size()<<std::endl;
4306 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4308 m_con.Send(peer_id, 0, data, true);
4316 void Server::DiePlayer(u16 peer_id)
4318 DSTACK(__FUNCTION_NAME);
4320 PlayerSAO *playersao = getPlayerSAO(peer_id);
4323 infostream<<"Server::DiePlayer(): Player "
4324 <<playersao->getPlayer()->getName()
4325 <<" dies"<<std::endl;
4327 playersao->setHP(0);
4329 // Trigger scripted stuff
4330 scriptapi_on_dieplayer(m_lua, playersao);
4332 SendPlayerHP(peer_id);
4333 SendDeathscreen(m_con, peer_id, false, v3f(0,0,0));
4336 void Server::RespawnPlayer(u16 peer_id)
4338 DSTACK(__FUNCTION_NAME);
4340 PlayerSAO *playersao = getPlayerSAO(peer_id);
4343 infostream<<"Server::RespawnPlayer(): Player "
4344 <<playersao->getPlayer()->getName()
4345 <<" respawns"<<std::endl;
4347 playersao->setHP(PLAYER_MAX_HP);
4349 bool repositioned = scriptapi_on_respawnplayer(m_lua, playersao);
4351 v3f pos = findSpawnPos(m_env->getServerMap());
4352 playersao->setPos(pos);
4356 void Server::UpdateCrafting(u16 peer_id)
4358 DSTACK(__FUNCTION_NAME);
4360 Player* player = m_env->getPlayer(peer_id);
4363 // Get a preview for crafting
4365 // No crafting in creative mode
4366 if(g_settings->getBool("creative_mode") == false)
4367 getCraftingResult(&player->inventory, preview, false, this);
4369 // Put the new preview in
4370 InventoryList *plist = player->inventory.getList("craftpreview");
4372 assert(plist->getSize() >= 1);
4373 plist->changeItem(0, preview);
4376 RemoteClient* Server::getClient(u16 peer_id)
4378 DSTACK(__FUNCTION_NAME);
4379 //JMutexAutoLock lock(m_con_mutex);
4380 core::map<u16, RemoteClient*>::Node *n;
4381 n = m_clients.find(peer_id);
4382 // A client should exist for all peers
4384 return n->getValue();
4387 std::wstring Server::getStatusString()
4389 std::wostringstream os(std::ios_base::binary);
4392 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4394 os<<L", uptime="<<m_uptime.get();
4395 // Information about clients
4397 for(core::map<u16, RemoteClient*>::Iterator
4398 i = m_clients.getIterator();
4399 i.atEnd() == false; i++)
4401 // Get client and check that it is valid
4402 RemoteClient *client = i.getNode()->getValue();
4403 assert(client->peer_id == i.getNode()->getKey());
4404 if(client->serialization_version == SER_FMT_VER_INVALID)
4407 Player *player = m_env->getPlayer(client->peer_id);
4408 // Get name of player
4409 std::wstring name = L"unknown";
4411 name = narrow_to_wide(player->getName());
4412 // Add name to information string
4416 if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
4417 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4418 if(g_settings->get("motd") != "")
4419 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4423 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
4425 std::set<std::string> privs;
4426 scriptapi_get_auth(m_lua, name, NULL, &privs);
4430 bool Server::checkPriv(const std::string &name, const std::string &priv)
4432 std::set<std::string> privs = getPlayerEffectivePrivs(name);
4433 return (privs.count(priv) != 0);
4436 void Server::reportPrivsModified(const std::string &name)
4439 for(core::map<u16, RemoteClient*>::Iterator
4440 i = m_clients.getIterator();
4441 i.atEnd() == false; i++){
4442 RemoteClient *client = i.getNode()->getValue();
4443 Player *player = m_env->getPlayer(client->peer_id);
4444 reportPrivsModified(player->getName());
4447 Player *player = m_env->getPlayer(name.c_str());
4450 SendPlayerPrivileges(player->peer_id);
4451 PlayerSAO *sao = player->getPlayerSAO();
4454 sao->updatePrivileges(
4455 getPlayerEffectivePrivs(name),
4460 void Server::reportInventoryFormspecModified(const std::string &name)
4462 Player *player = m_env->getPlayer(name.c_str());
4465 SendPlayerInventoryFormspec(player->peer_id);
4468 // Saves g_settings to configpath given at initialization
4469 void Server::saveConfig()
4471 if(m_path_config != "")
4472 g_settings->updateConfigFile(m_path_config.c_str());
4475 void Server::notifyPlayer(const char *name, const std::wstring msg)
4477 Player *player = m_env->getPlayer(name);
4480 SendChatMessage(player->peer_id, std::wstring(L"Server: -!- ")+msg);
4483 void Server::notifyPlayers(const std::wstring msg)
4485 BroadcastChatMessage(msg);
4488 void Server::queueBlockEmerge(v3s16 blockpos, bool allow_generate)
4492 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
4493 m_emerge_queue.addBlock(PEER_ID_INEXISTENT, blockpos, flags);
4496 // IGameDef interface
4498 IItemDefManager* Server::getItemDefManager()
4502 INodeDefManager* Server::getNodeDefManager()
4506 ICraftDefManager* Server::getCraftDefManager()
4510 ITextureSource* Server::getTextureSource()
4514 u16 Server::allocateUnknownNodeId(const std::string &name)
4516 return m_nodedef->allocateDummy(name);
4518 ISoundManager* Server::getSoundManager()
4520 return &dummySoundManager;
4522 MtEventManager* Server::getEventManager()
4527 IWritableItemDefManager* Server::getWritableItemDefManager()
4531 IWritableNodeDefManager* Server::getWritableNodeDefManager()
4535 IWritableCraftDefManager* Server::getWritableCraftDefManager()
4540 const ModSpec* Server::getModSpec(const std::string &modname)
4542 for(core::list<ModSpec>::Iterator i = m_mods.begin();
4543 i != m_mods.end(); i++){
4544 const ModSpec &mod = *i;
4545 if(mod.name == modname)
4550 void Server::getModNames(core::list<std::string> &modlist)
4552 for(core::list<ModSpec>::Iterator i = m_mods.begin(); i != m_mods.end(); i++)
4554 modlist.push_back((*i).name);
4557 std::string Server::getBuiltinLuaPath()
4559 return porting::path_share + DIR_DELIM + "builtin";
4562 v3f findSpawnPos(ServerMap &map)
4564 //return v3f(50,50,50)*BS;
4569 nodepos = v2s16(0,0);
4574 // Try to find a good place a few times
4575 for(s32 i=0; i<1000; i++)
4578 // We're going to try to throw the player to this position
4579 v2s16 nodepos2d = v2s16(-range + (myrand()%(range*2)),
4580 -range + (myrand()%(range*2)));
4581 //v2s16 sectorpos = getNodeSectorPos(nodepos2d);
4582 // Get ground height at point (fallbacks to heightmap function)
4583 s16 groundheight = map.findGroundLevel(nodepos2d);
4584 // Don't go underwater
4585 if(groundheight < WATER_LEVEL)
4587 //infostream<<"-> Underwater"<<std::endl;
4590 // Don't go to high places
4591 if(groundheight > WATER_LEVEL + 4)
4593 //infostream<<"-> Underwater"<<std::endl;
4597 nodepos = v3s16(nodepos2d.X, groundheight-2, nodepos2d.Y);
4598 bool is_good = false;
4600 for(s32 i=0; i<10; i++){
4601 v3s16 blockpos = getNodeBlockPos(nodepos);
4602 map.emergeBlock(blockpos, true);
4603 MapNode n = map.getNodeNoEx(nodepos);
4604 if(n.getContent() == CONTENT_AIR){
4615 // Found a good place
4616 //infostream<<"Searched through "<<i<<" places."<<std::endl;
4622 return intToFloat(nodepos, BS);
4625 PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id)
4627 RemotePlayer *player = NULL;
4628 bool newplayer = false;
4631 Try to get an existing player
4633 player = static_cast<RemotePlayer*>(m_env->getPlayer(name));
4635 // If player is already connected, cancel
4636 if(player != NULL && player->peer_id != 0)
4638 infostream<<"emergePlayer(): Player already connected"<<std::endl;
4643 If player with the wanted peer_id already exists, cancel.
4645 if(m_env->getPlayer(peer_id) != NULL)
4647 infostream<<"emergePlayer(): Player with wrong name but same"
4648 " peer_id already exists"<<std::endl;
4653 Create a new player if it doesn't exist yet
4658 player = new RemotePlayer(this);
4659 player->updateName(name);
4661 /* Set player position */
4662 infostream<<"Server: Finding spawn place for player \""
4663 <<name<<"\""<<std::endl;
4664 v3f pos = findSpawnPos(m_env->getServerMap());
4665 player->setPosition(pos);
4667 /* Add player to environment */
4668 m_env->addPlayer(player);
4672 Create a new player active object
4674 PlayerSAO *playersao = new PlayerSAO(m_env, player, peer_id,
4675 getPlayerEffectivePrivs(player->getName()),
4678 /* Add object to environment */
4679 m_env->addActiveObject(playersao);
4683 scriptapi_on_newplayer(m_lua, playersao);
4685 scriptapi_on_joinplayer(m_lua, playersao);
4688 if(g_settings->getBool("creative_mode"))
4689 playersao->createCreativeInventory();
4694 void Server::handlePeerChange(PeerChange &c)
4696 JMutexAutoLock envlock(m_env_mutex);
4697 JMutexAutoLock conlock(m_con_mutex);
4699 if(c.type == PEER_ADDED)
4706 core::map<u16, RemoteClient*>::Node *n;
4707 n = m_clients.find(c.peer_id);
4708 // The client shouldn't already exist
4712 RemoteClient *client = new RemoteClient();
4713 client->peer_id = c.peer_id;
4714 m_clients.insert(client->peer_id, client);
4717 else if(c.type == PEER_REMOVED)
4724 core::map<u16, RemoteClient*>::Node *n;
4725 n = m_clients.find(c.peer_id);
4726 // The client should exist
4730 Mark objects to be not known by the client
4732 RemoteClient *client = n->getValue();
4734 for(core::map<u16, bool>::Iterator
4735 i = client->m_known_objects.getIterator();
4736 i.atEnd()==false; i++)
4739 u16 id = i.getNode()->getKey();
4740 ServerActiveObject* obj = m_env->getActiveObject(id);
4742 if(obj && obj->m_known_by_count > 0)
4743 obj->m_known_by_count--;
4747 Clear references to playing sounds
4749 for(std::map<s32, ServerPlayingSound>::iterator
4750 i = m_playing_sounds.begin();
4751 i != m_playing_sounds.end();)
4753 ServerPlayingSound &psound = i->second;
4754 psound.clients.erase(c.peer_id);
4755 if(psound.clients.size() == 0)
4756 m_playing_sounds.erase(i++);
4761 Player *player = m_env->getPlayer(c.peer_id);
4763 // Collect information about leaving in chat
4764 std::wstring message;
4768 std::wstring name = narrow_to_wide(player->getName());
4771 message += L" left the game.";
4773 message += L" (timed out)";
4777 /* Run scripts and remove from environment */
4781 PlayerSAO *playersao = player->getPlayerSAO();
4784 scriptapi_on_leaveplayer(m_lua, playersao);
4786 playersao->disconnected();
4796 std::ostringstream os(std::ios_base::binary);
4797 for(core::map<u16, RemoteClient*>::Iterator
4798 i = m_clients.getIterator();
4799 i.atEnd() == false; i++)
4801 RemoteClient *client = i.getNode()->getValue();
4802 assert(client->peer_id == i.getNode()->getKey());
4803 if(client->serialization_version == SER_FMT_VER_INVALID)
4806 Player *player = m_env->getPlayer(client->peer_id);
4809 // Get name of player
4810 os<<player->getName()<<" ";
4813 actionstream<<player->getName()<<" "
4814 <<(c.timeout?"times out.":"leaves game.")
4815 <<" List of players: "
4816 <<os.str()<<std::endl;
4821 delete m_clients[c.peer_id];
4822 m_clients.remove(c.peer_id);
4824 // Send player info to all remaining clients
4825 //SendPlayerInfos();
4827 // Send leave chat message to all remaining clients
4828 if(message.length() != 0)
4829 BroadcastChatMessage(message);
4838 void Server::handlePeerChanges()
4840 while(m_peer_change_queue.size() > 0)
4842 PeerChange c = m_peer_change_queue.pop_front();
4844 verbosestream<<"Server: Handling peer change: "
4845 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4848 handlePeerChange(c);
4852 void dedicated_server_loop(Server &server, bool &kill)
4854 DSTACK(__FUNCTION_NAME);
4856 verbosestream<<"dedicated_server_loop()"<<std::endl;
4858 IntervalLimiter m_profiler_interval;
4862 float steplen = g_settings->getFloat("dedicated_server_step");
4863 // This is kind of a hack but can be done like this
4864 // because server.step() is very light
4866 ScopeProfiler sp(g_profiler, "dedicated server sleep");
4867 sleep_ms((int)(steplen*1000.0));
4869 server.step(steplen);
4871 if(server.getShutdownRequested() || kill)
4873 infostream<<"Dedicated server quitting"<<std::endl;
4880 float profiler_print_interval =
4881 g_settings->getFloat("profiler_print_interval");
4882 if(profiler_print_interval != 0)
4884 if(m_profiler_interval.step(steplen, profiler_print_interval))
4886 infostream<<"Profiler:"<<std::endl;
4887 g_profiler->print(infostream);
4888 g_profiler->clear();