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"
59 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
61 #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
63 class MapEditEventIgnorer
66 MapEditEventIgnorer(bool *flag):
75 ~MapEditEventIgnorer()
88 class MapEditEventAreaIgnorer
91 MapEditEventAreaIgnorer(VoxelArea *ignorevariable, const VoxelArea &a):
92 m_ignorevariable(ignorevariable)
94 if(m_ignorevariable->getVolume() == 0)
95 *m_ignorevariable = a;
97 m_ignorevariable = NULL;
100 ~MapEditEventAreaIgnorer()
104 assert(m_ignorevariable->getVolume() != 0);
105 *m_ignorevariable = VoxelArea();
110 VoxelArea *m_ignorevariable;
113 void * ServerThread::Thread()
117 log_register_thread("ServerThread");
119 DSTACK(__FUNCTION_NAME);
121 BEGIN_DEBUG_EXCEPTION_HANDLER
126 //TimeTaker timer("AsyncRunStep() + Receive()");
129 //TimeTaker timer("AsyncRunStep()");
130 m_server->AsyncRunStep();
133 //infostream<<"Running m_server->Receive()"<<std::endl;
136 catch(con::NoIncomingDataException &e)
139 catch(con::PeerNotFoundException &e)
141 infostream<<"Server: PeerNotFoundException"<<std::endl;
143 catch(con::ConnectionBindFailed &e)
145 m_server->setAsyncFatalError(e.what());
149 m_server->setAsyncFatalError(e.what());
153 END_DEBUG_EXCEPTION_HANDLER(errorstream)
158 void * EmergeThread::Thread()
162 log_register_thread("EmergeThread");
164 DSTACK(__FUNCTION_NAME);
166 BEGIN_DEBUG_EXCEPTION_HANDLER
168 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
170 v3s16 last_tried_pos(-32768,-32768,-32768); // For error output
173 Get block info from queue, emerge them and send them
176 After queue is empty, exit.
180 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
184 SharedPtr<QueuedBlockEmerge> q(qptr);
192 Do not generate over-limit
194 if(blockpos_over_limit(p))
197 //infostream<<"EmergeThread::Thread(): running"<<std::endl;
199 //TimeTaker timer("block emerge");
202 Try to emerge it from somewhere.
204 If it is only wanted as optional, only loading from disk
209 Check if any peer wants it as non-optional. In that case it
212 Also decrement the emerge queue count in clients.
215 bool only_from_disk = true;
218 core::map<u16, u8>::Iterator i;
219 for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
221 //u16 peer_id = i.getNode()->getKey();
224 u8 flags = i.getNode()->getValue();
225 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
226 only_from_disk = false;
231 if(enable_mapgen_debug_info)
232 infostream<<"EmergeThread: p="
233 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
234 <<"only_from_disk="<<only_from_disk<<std::endl;
236 ServerMap &map = ((ServerMap&)m_server->m_env->getMap());
238 MapBlock *block = NULL;
239 bool got_block = true;
240 core::map<v3s16, MapBlock*> modified_blocks;
243 Try to fetch block from memory or disk.
244 If not found and asked to generate, initialize generator.
247 bool started_generate = false;
248 mapgen::BlockMakeData data;
251 JMutexAutoLock envlock(m_server->m_env_mutex);
253 // Load sector if it isn't loaded
254 if(map.getSectorNoGenerateNoEx(p2d) == NULL)
255 map.loadSectorMeta(p2d);
257 // Attempt to load block
258 block = map.getBlockNoCreateNoEx(p);
259 if(!block || block->isDummy() || !block->isGenerated())
261 if(enable_mapgen_debug_info)
262 infostream<<"EmergeThread: not in memory, "
263 <<"attempting to load from disk"<<std::endl;
265 block = map.loadBlock(p);
268 // If could not load and allowed to generate, start generation
269 // inside this same envlock
270 if(only_from_disk == false &&
271 (block == NULL || block->isGenerated() == false)){
272 if(enable_mapgen_debug_info)
273 infostream<<"EmergeThread: generating"<<std::endl;
274 started_generate = true;
276 map.initBlockMake(&data, p);
281 If generator was initialized, generate now when envlock is free.
286 ScopeProfiler sp(g_profiler, "EmergeThread: mapgen::make_block",
288 TimeTaker t("mapgen::make_block()");
290 mapgen::make_block(&data);
292 if(enable_mapgen_debug_info == false)
293 t.stop(true); // Hide output
297 // Lock environment again to access the map
298 JMutexAutoLock envlock(m_server->m_env_mutex);
300 ScopeProfiler sp(g_profiler, "EmergeThread: after "
301 "mapgen::make_block (envlock)", SPT_AVG);
303 // Blit data back on map, update lighting, add mobs and
304 // whatever this does
305 map.finishBlockMake(&data, modified_blocks);
308 block = map.getBlockNoCreateNoEx(p);
310 // If block doesn't exist, don't try doing anything with it
311 // This happens if the block is not in generation boundaries
316 Do some post-generate stuff
319 v3s16 minp = data.blockpos_min*MAP_BLOCKSIZE;
320 v3s16 maxp = data.blockpos_max*MAP_BLOCKSIZE +
321 v3s16(1,1,1)*(MAP_BLOCKSIZE-1);
324 Ignore map edit events, they will not need to be
325 sent to anybody because the block hasn't been sent
328 //MapEditEventIgnorer ign(&m_server->m_ignore_map_edit_events);
329 MapEditEventAreaIgnorer ign(
330 &m_server->m_ignore_map_edit_events_area,
331 VoxelArea(minp, maxp));
333 TimeTaker timer("on_generated");
334 scriptapi_environment_on_generated(m_server->m_lua,
335 minp, maxp, mapgen::get_blockseed(data.seed, minp));
336 /*int t = timer.stop(true);
337 dstream<<"on_generated took "<<t<<"ms"<<std::endl;*/
340 if(enable_mapgen_debug_info)
341 infostream<<"EmergeThread: ended up with: "
342 <<analyze_block(block)<<std::endl;
344 // Activate objects and stuff
345 m_server->m_env->activateBlock(block, 0);
353 Set sent status of modified blocks on clients
356 // NOTE: Server's clients are also behind the connection mutex
357 JMutexAutoLock lock(m_server->m_con_mutex);
360 Add the originally fetched block to the modified list
364 modified_blocks.insert(p, block);
368 Set the modified blocks unsent for all the clients
371 for(core::map<u16, RemoteClient*>::Iterator
372 i = m_server->m_clients.getIterator();
373 i.atEnd() == false; i++)
375 RemoteClient *client = i.getNode()->getValue();
377 if(modified_blocks.size() > 0)
379 // Remove block from sent history
380 client->SetBlocksNotSent(modified_blocks);
384 catch(VersionMismatchException &e)
386 std::ostringstream err;
387 err<<"World data version mismatch in MapBlock "<<PP(last_tried_pos)<<std::endl;
388 err<<"----"<<std::endl;
389 err<<"\""<<e.what()<<"\""<<std::endl;
390 err<<"See debug.txt."<<std::endl;
391 err<<"World probably saved by a newer version of Minetest."<<std::endl;
392 m_server->setAsyncFatalError(err.str());
394 catch(SerializationError &e)
396 std::ostringstream err;
397 err<<"Invalid data in MapBlock "<<PP(last_tried_pos)<<std::endl;
398 err<<"----"<<std::endl;
399 err<<"\""<<e.what()<<"\""<<std::endl;
400 err<<"See debug.txt."<<std::endl;
401 err<<"You can ignore this using [ignore_world_load_errors = true]."<<std::endl;
402 m_server->setAsyncFatalError(err.str());
405 END_DEBUG_EXCEPTION_HANDLER(errorstream)
407 log_deregister_thread();
412 v3f ServerSoundParams::getPos(ServerEnvironment *env, bool *pos_exists) const
414 if(pos_exists) *pos_exists = false;
419 if(pos_exists) *pos_exists = true;
424 ServerActiveObject *sao = env->getActiveObject(object);
427 if(pos_exists) *pos_exists = true;
428 return sao->getBasePosition(); }
433 void RemoteClient::GetNextBlocks(Server *server, float dtime,
434 core::array<PrioritySortedBlockTransfer> &dest)
436 DSTACK(__FUNCTION_NAME);
439 TimeTaker timer("RemoteClient::GetNextBlocks", &timer_result);*/
442 m_nothing_to_send_pause_timer -= dtime;
443 m_nearest_unsent_reset_timer += dtime;
445 if(m_nothing_to_send_pause_timer >= 0)
450 // Won't send anything if already sending
451 if(m_blocks_sending.size() >= g_settings->getU16
452 ("max_simultaneous_block_sends_per_client"))
454 //infostream<<"Not sending any blocks, Queue full."<<std::endl;
458 //TimeTaker timer("RemoteClient::GetNextBlocks");
460 Player *player = server->m_env->getPlayer(peer_id);
462 assert(player != NULL);
464 v3f playerpos = player->getPosition();
465 v3f playerspeed = player->getSpeed();
466 v3f playerspeeddir(0,0,0);
467 if(playerspeed.getLength() > 1.0*BS)
468 playerspeeddir = playerspeed / playerspeed.getLength();
469 // Predict to next block
470 v3f playerpos_predicted = playerpos + playerspeeddir*MAP_BLOCKSIZE*BS;
472 v3s16 center_nodepos = floatToInt(playerpos_predicted, BS);
474 v3s16 center = getNodeBlockPos(center_nodepos);
476 // Camera position and direction
477 v3f camera_pos = player->getEyePosition();
478 v3f camera_dir = v3f(0,0,1);
479 camera_dir.rotateYZBy(player->getPitch());
480 camera_dir.rotateXZBy(player->getYaw());
482 /*infostream<<"camera_dir=("<<camera_dir.X<<","<<camera_dir.Y<<","
483 <<camera_dir.Z<<")"<<std::endl;*/
486 Get the starting value of the block finder radius.
489 if(m_last_center != center)
491 m_nearest_unsent_d = 0;
492 m_last_center = center;
495 /*infostream<<"m_nearest_unsent_reset_timer="
496 <<m_nearest_unsent_reset_timer<<std::endl;*/
498 // Reset periodically to workaround for some bugs or stuff
499 if(m_nearest_unsent_reset_timer > 20.0)
501 m_nearest_unsent_reset_timer = 0;
502 m_nearest_unsent_d = 0;
503 //infostream<<"Resetting m_nearest_unsent_d for "
504 // <<server->getPlayerName(peer_id)<<std::endl;
507 //s16 last_nearest_unsent_d = m_nearest_unsent_d;
508 s16 d_start = m_nearest_unsent_d;
510 //infostream<<"d_start="<<d_start<<std::endl;
512 u16 max_simul_sends_setting = g_settings->getU16
513 ("max_simultaneous_block_sends_per_client");
514 u16 max_simul_sends_usually = max_simul_sends_setting;
517 Check the time from last addNode/removeNode.
519 Decrease send rate if player is building stuff.
521 m_time_from_building += dtime;
522 if(m_time_from_building < g_settings->getFloat(
523 "full_block_send_enable_min_time_from_building"))
525 max_simul_sends_usually
526 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
530 Number of blocks sending + number of blocks selected for sending
532 u32 num_blocks_selected = m_blocks_sending.size();
535 next time d will be continued from the d from which the nearest
536 unsent block was found this time.
538 This is because not necessarily any of the blocks found this
539 time are actually sent.
541 s32 new_nearest_unsent_d = -1;
543 s16 d_max = g_settings->getS16("max_block_send_distance");
544 s16 d_max_gen = g_settings->getS16("max_block_generate_distance");
546 // Don't loop very much at a time
547 s16 max_d_increment_at_time = 2;
548 if(d_max > d_start + max_d_increment_at_time)
549 d_max = d_start + max_d_increment_at_time;
550 /*if(d_max_gen > d_start+2)
551 d_max_gen = d_start+2;*/
553 //infostream<<"Starting from "<<d_start<<std::endl;
555 s32 nearest_emerged_d = -1;
556 s32 nearest_emergefull_d = -1;
557 s32 nearest_sent_d = -1;
558 bool queue_is_full = false;
561 for(d = d_start; d <= d_max; d++)
563 /*errorstream<<"checking d="<<d<<" for "
564 <<server->getPlayerName(peer_id)<<std::endl;*/
565 //infostream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
568 If m_nearest_unsent_d was changed by the EmergeThread
569 (it can change it to 0 through SetBlockNotSent),
571 Else update m_nearest_unsent_d
573 /*if(m_nearest_unsent_d != last_nearest_unsent_d)
575 d = m_nearest_unsent_d;
576 last_nearest_unsent_d = m_nearest_unsent_d;
580 Get the border/face dot coordinates of a "d-radiused"
583 core::list<v3s16> list;
584 getFacePositions(list, d);
586 core::list<v3s16>::Iterator li;
587 for(li=list.begin(); li!=list.end(); li++)
589 v3s16 p = *li + center;
593 - Don't allow too many simultaneous transfers
594 - EXCEPT when the blocks are very close
596 Also, don't send blocks that are already flying.
599 // Start with the usual maximum
600 u16 max_simul_dynamic = max_simul_sends_usually;
602 // If block is very close, allow full maximum
603 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
604 max_simul_dynamic = max_simul_sends_setting;
606 // Don't select too many blocks for sending
607 if(num_blocks_selected >= max_simul_dynamic)
609 queue_is_full = true;
610 goto queue_full_break;
613 // Don't send blocks that are currently being transferred
614 if(m_blocks_sending.find(p) != NULL)
620 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
621 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
622 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
623 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
624 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
625 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
628 // If this is true, inexistent block will be made from scratch
629 bool generate = d <= d_max_gen;
632 /*// Limit the generating area vertically to 2/3
633 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
636 // Limit the send area vertically to 1/2
637 if(abs(p.Y - center.Y) > d_max / 2)
643 If block is far away, don't generate it unless it is
649 // Block center y in nodes
650 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
651 // Don't generate if it's very high or very low
652 if(y < -64 || y > 64)
656 v2s16 p2d_nodes_center(
660 // Get ground height in nodes
661 s16 gh = server->m_env->getServerMap().findGroundLevel(
664 // If differs a lot, don't generate
665 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
667 // Actually, don't even send it
673 //infostream<<"d="<<d<<std::endl;
676 Don't generate or send if not in sight
677 FIXME This only works if the client uses a small enough
678 FOV setting. The default of 72 degrees is fine.
681 float camera_fov = (72.0*M_PI/180) * 4./3.;
682 if(isBlockInSight(p, camera_pos, camera_dir, camera_fov, 10000*BS) == false)
688 Don't send already sent blocks
691 if(m_blocks_sent.find(p) != NULL)
698 Check if map has this block
700 MapBlock *block = server->m_env->getMap().getBlockNoCreateNoEx(p);
702 bool surely_not_found_on_disk = false;
703 bool block_is_invalid = false;
706 // Reset usage timer, this block will be of use in the future.
707 block->resetUsageTimer();
709 // Block is dummy if data doesn't exist.
710 // It means it has been not found from disk and not generated
713 surely_not_found_on_disk = true;
716 // Block is valid if lighting is up-to-date and data exists
717 if(block->isValid() == false)
719 block_is_invalid = true;
722 /*if(block->isFullyGenerated() == false)
724 block_is_invalid = true;
729 ServerMap *map = (ServerMap*)(&server->m_env->getMap());
730 v2s16 chunkpos = map->sector_to_chunk(p2d);
731 if(map->chunkNonVolatile(chunkpos) == false)
732 block_is_invalid = true;
734 if(block->isGenerated() == false)
735 block_is_invalid = true;
738 If block is not close, don't send it unless it is near
741 Block is near ground level if night-time mesh
742 differs from day-time mesh.
746 if(block->getDayNightDiff() == false)
753 If block has been marked to not exist on disk (dummy)
754 and generating new ones is not wanted, skip block.
756 if(generate == false && surely_not_found_on_disk == true)
763 Add inexistent block to emerge queue.
765 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
767 //TODO: Get value from somewhere
768 // Allow only one block in emerge queue
769 //if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
770 // Allow two blocks in queue per client
771 //if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
773 // Make it more responsive when needing to generate stuff
774 if(surely_not_found_on_disk)
776 if(server->m_emerge_queue.peerItemCount(peer_id) < max_emerge)
778 //infostream<<"Adding block to emerge queue"<<std::endl;
780 // Add it to the emerge queue and trigger the thread
783 if(generate == false)
784 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
786 server->m_emerge_queue.addBlock(peer_id, p, flags);
787 server->m_emergethread.trigger();
789 if(nearest_emerged_d == -1)
790 nearest_emerged_d = d;
792 if(nearest_emergefull_d == -1)
793 nearest_emergefull_d = d;
800 if(nearest_sent_d == -1)
804 Add block to send queue
807 /*errorstream<<"sending from d="<<d<<" to "
808 <<server->getPlayerName(peer_id)<<std::endl;*/
810 PrioritySortedBlockTransfer q((float)d, p, peer_id);
814 num_blocks_selected += 1;
819 //infostream<<"Stopped at "<<d<<std::endl;
821 // If nothing was found for sending and nothing was queued for
822 // emerging, continue next time browsing from here
823 if(nearest_emerged_d != -1){
824 new_nearest_unsent_d = nearest_emerged_d;
825 } else if(nearest_emergefull_d != -1){
826 new_nearest_unsent_d = nearest_emergefull_d;
828 if(d > g_settings->getS16("max_block_send_distance")){
829 new_nearest_unsent_d = 0;
830 m_nothing_to_send_pause_timer = 2.0;
831 /*infostream<<"GetNextBlocks(): d wrapped around for "
832 <<server->getPlayerName(peer_id)
833 <<"; setting to 0 and pausing"<<std::endl;*/
835 if(nearest_sent_d != -1)
836 new_nearest_unsent_d = nearest_sent_d;
838 new_nearest_unsent_d = d;
842 if(new_nearest_unsent_d != -1)
843 m_nearest_unsent_d = new_nearest_unsent_d;
845 /*timer_result = timer.stop(true);
846 if(timer_result != 0)
847 infostream<<"GetNextBlocks timeout: "<<timer_result<<" (!=0)"<<std::endl;*/
850 void RemoteClient::GotBlock(v3s16 p)
852 if(m_blocks_sending.find(p) != NULL)
853 m_blocks_sending.remove(p);
856 /*infostream<<"RemoteClient::GotBlock(): Didn't find in"
857 " m_blocks_sending"<<std::endl;*/
858 m_excess_gotblocks++;
860 m_blocks_sent.insert(p, true);
863 void RemoteClient::SentBlock(v3s16 p)
865 if(m_blocks_sending.find(p) == NULL)
866 m_blocks_sending.insert(p, 0.0);
868 infostream<<"RemoteClient::SentBlock(): Sent block"
869 " already in m_blocks_sending"<<std::endl;
872 void RemoteClient::SetBlockNotSent(v3s16 p)
874 m_nearest_unsent_d = 0;
876 if(m_blocks_sending.find(p) != NULL)
877 m_blocks_sending.remove(p);
878 if(m_blocks_sent.find(p) != NULL)
879 m_blocks_sent.remove(p);
882 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
884 m_nearest_unsent_d = 0;
886 for(core::map<v3s16, MapBlock*>::Iterator
887 i = blocks.getIterator();
888 i.atEnd()==false; i++)
890 v3s16 p = i.getNode()->getKey();
892 if(m_blocks_sending.find(p) != NULL)
893 m_blocks_sending.remove(p);
894 if(m_blocks_sent.find(p) != NULL)
895 m_blocks_sent.remove(p);
903 PlayerInfo::PlayerInfo()
909 void PlayerInfo::PrintLine(std::ostream *s)
912 (*s)<<"\""<<name<<"\" ("
913 <<(position.X/10)<<","<<(position.Y/10)
914 <<","<<(position.Z/10)<<") ";
916 (*s)<<" avg_rtt="<<avg_rtt;
925 const std::string &path_world,
926 const std::string &path_config,
927 const SubgameSpec &gamespec,
928 bool simple_singleplayer_mode
930 m_path_world(path_world),
931 m_path_config(path_config),
932 m_gamespec(gamespec),
933 m_simple_singleplayer_mode(simple_singleplayer_mode),
934 m_async_fatal_error(""),
936 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
937 m_banmanager(path_world+DIR_DELIM+"ipban.txt"),
939 m_rollback_sink_enabled(true),
940 m_enable_rollback_recording(false),
942 m_itemdef(createItemDefManager()),
943 m_nodedef(createNodeDefManager()),
944 m_craftdef(createCraftDefManager()),
945 m_event(new EventManager()),
947 m_emergethread(this),
948 m_time_of_day_send_timer(0),
950 m_shutdown_requested(false),
951 m_ignore_map_edit_events(false),
952 m_ignore_map_edit_events_peer_id(0)
954 m_liquid_transform_timer = 0.0;
955 m_print_info_timer = 0.0;
956 m_objectdata_timer = 0.0;
957 m_emergethread_trigger_timer = 0.0;
958 m_savemap_timer = 0.0;
962 m_step_dtime_mutex.Init();
966 throw ServerError("Supplied empty world path");
968 if(!gamespec.isValid())
969 throw ServerError("Supplied invalid gamespec");
971 infostream<<"Server created for gameid \""<<m_gamespec.id<<"\"";
972 if(m_simple_singleplayer_mode)
973 infostream<<" in simple singleplayer mode"<<std::endl;
975 infostream<<std::endl;
976 infostream<<"- world: "<<m_path_world<<std::endl;
977 infostream<<"- config: "<<m_path_config<<std::endl;
978 infostream<<"- game: "<<m_gamespec.path<<std::endl;
980 // Create rollback manager
981 std::string rollback_path = m_path_world+DIR_DELIM+"rollback.txt";
982 m_rollback = createRollbackManager(rollback_path, this);
984 // Add world mod search path
985 m_modspaths.push_front(m_path_world + DIR_DELIM + "worldmods");
986 // Add addon mod search path
987 for(std::set<std::string>::const_iterator i = m_gamespec.mods_paths.begin();
988 i != m_gamespec.mods_paths.end(); i++)
989 m_modspaths.push_front((*i));
991 // Print out mod search paths
992 for(core::list<std::string>::Iterator i = m_modspaths.begin();
993 i != m_modspaths.end(); i++){
994 std::string modspath = *i;
995 infostream<<"- mods: "<<modspath<<std::endl;
998 // Path to builtin.lua
999 std::string builtinpath = getBuiltinLuaPath() + DIR_DELIM + "builtin.lua";
1001 // Create world if it doesn't exist
1002 if(!initializeWorld(m_path_world, m_gamespec.id))
1003 throw ServerError("Failed to initialize world");
1006 JMutexAutoLock envlock(m_env_mutex);
1007 JMutexAutoLock conlock(m_con_mutex);
1009 // Initialize scripting
1011 infostream<<"Server: Initializing Lua"<<std::endl;
1012 m_lua = script_init();
1015 scriptapi_export(m_lua, this);
1016 // Load and run builtin.lua
1017 infostream<<"Server: Loading builtin.lua [\""
1018 <<builtinpath<<"\"]"<<std::endl;
1019 bool success = scriptapi_loadmod(m_lua, builtinpath, "__builtin");
1021 errorstream<<"Server: Failed to load and run "
1022 <<builtinpath<<std::endl;
1023 throw ModError("Failed to load and run "+builtinpath);
1025 // Find mods in mod search paths
1026 m_mods = getMods(m_modspaths);
1028 infostream<<"Server: Loading mods: ";
1029 for(core::list<ModSpec>::Iterator i = m_mods.begin();
1030 i != m_mods.end(); i++){
1031 const ModSpec &mod = *i;
1032 infostream<<mod.name<<" ";
1034 infostream<<std::endl;
1035 // Load and run "mod" scripts
1036 for(core::list<ModSpec>::Iterator i = m_mods.begin();
1037 i != m_mods.end(); i++){
1038 const ModSpec &mod = *i;
1039 std::string scriptpath = mod.path + DIR_DELIM + "init.lua";
1040 infostream<<" ["<<padStringRight(mod.name, 12)<<"] [\""
1041 <<scriptpath<<"\"]"<<std::endl;
1042 bool success = scriptapi_loadmod(m_lua, scriptpath, mod.name);
1044 errorstream<<"Server: Failed to load and run "
1045 <<scriptpath<<std::endl;
1046 throw ModError("Failed to load and run "+scriptpath);
1050 // Read Textures and calculate sha1 sums
1053 // Apply item aliases in the node definition manager
1054 m_nodedef->updateAliases(m_itemdef);
1056 // Initialize Environment
1058 m_env = new ServerEnvironment(new ServerMap(path_world, this), m_lua,
1061 // Give environment reference to scripting api
1062 scriptapi_add_environment(m_lua, m_env);
1064 // Register us to receive map edit events
1065 m_env->getMap().addEventReceiver(this);
1067 // If file exists, load environment metadata
1068 if(fs::PathExists(m_path_world+DIR_DELIM+"env_meta.txt"))
1070 infostream<<"Server: Loading environment metadata"<<std::endl;
1071 m_env->loadMeta(m_path_world);
1075 infostream<<"Server: Loading players"<<std::endl;
1076 m_env->deSerializePlayers(m_path_world);
1079 Add some test ActiveBlockModifiers to environment
1081 add_legacy_abms(m_env, m_nodedef);
1086 infostream<<"Server destructing"<<std::endl;
1089 Send shutdown message
1092 JMutexAutoLock conlock(m_con_mutex);
1094 std::wstring line = L"*** Server shutting down";
1097 Send the message to clients
1099 for(core::map<u16, RemoteClient*>::Iterator
1100 i = m_clients.getIterator();
1101 i.atEnd() == false; i++)
1103 // Get client and check that it is valid
1104 RemoteClient *client = i.getNode()->getValue();
1105 assert(client->peer_id == i.getNode()->getKey());
1106 if(client->serialization_version == SER_FMT_VER_INVALID)
1110 SendChatMessage(client->peer_id, line);
1112 catch(con::PeerNotFoundException &e)
1118 JMutexAutoLock envlock(m_env_mutex);
1123 infostream<<"Server: Saving players"<<std::endl;
1124 m_env->serializePlayers(m_path_world);
1127 Save environment metadata
1129 infostream<<"Server: Saving environment metadata"<<std::endl;
1130 m_env->saveMeta(m_path_world);
1142 JMutexAutoLock clientslock(m_con_mutex);
1144 for(core::map<u16, RemoteClient*>::Iterator
1145 i = m_clients.getIterator();
1146 i.atEnd() == false; i++)
1149 // NOTE: These are removed by env destructor
1151 u16 peer_id = i.getNode()->getKey();
1152 JMutexAutoLock envlock(m_env_mutex);
1153 m_env->removePlayer(peer_id);
1157 delete i.getNode()->getValue();
1161 // Delete things in the reverse order of creation
1169 // Deinitialize scripting
1170 infostream<<"Server: Deinitializing scripting"<<std::endl;
1171 script_deinit(m_lua);
1173 // Delete detached inventories
1175 for(std::map<std::string, Inventory*>::iterator
1176 i = m_detached_inventories.begin();
1177 i != m_detached_inventories.end(); i++){
1183 void Server::start(unsigned short port)
1185 DSTACK(__FUNCTION_NAME);
1186 infostream<<"Starting server on port "<<port<<"..."<<std::endl;
1188 // Stop thread if already running
1191 // Initialize connection
1192 m_con.SetTimeoutMs(30);
1196 m_thread.setRun(true);
1199 // ASCII art for the win!
1201 <<" .__ __ __ "<<std::endl
1202 <<" _____ |__| ____ _____/ |_ ____ _______/ |_ "<<std::endl
1203 <<" / \\| |/ \\_/ __ \\ __\\/ __ \\ / ___/\\ __\\"<<std::endl
1204 <<"| Y Y \\ | | \\ ___/| | \\ ___/ \\___ \\ | | "<<std::endl
1205 <<"|__|_| /__|___| /\\___ >__| \\___ >____ > |__| "<<std::endl
1206 <<" \\/ \\/ \\/ \\/ \\/ "<<std::endl;
1207 actionstream<<"World at ["<<m_path_world<<"]"<<std::endl;
1208 actionstream<<"Server for gameid=\""<<m_gamespec.id
1209 <<"\" listening on port "<<port<<"."<<std::endl;
1214 DSTACK(__FUNCTION_NAME);
1216 infostream<<"Server: Stopping and waiting threads"<<std::endl;
1218 // Stop threads (set run=false first so both start stopping)
1219 m_thread.setRun(false);
1220 m_emergethread.setRun(false);
1222 m_emergethread.stop();
1224 infostream<<"Server: Threads stopped"<<std::endl;
1227 void Server::step(float dtime)
1229 DSTACK(__FUNCTION_NAME);
1234 JMutexAutoLock lock(m_step_dtime_mutex);
1235 m_step_dtime += dtime;
1237 // Throw if fatal error occurred in thread
1238 std::string async_err = m_async_fatal_error.get();
1239 if(async_err != ""){
1240 throw ServerError(async_err);
1244 void Server::AsyncRunStep()
1246 DSTACK(__FUNCTION_NAME);
1248 g_profiler->add("Server::AsyncRunStep (num)", 1);
1252 JMutexAutoLock lock1(m_step_dtime_mutex);
1253 dtime = m_step_dtime;
1257 // Send blocks to clients
1264 g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
1266 //infostream<<"Server steps "<<dtime<<std::endl;
1267 //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1270 JMutexAutoLock lock1(m_step_dtime_mutex);
1271 m_step_dtime -= dtime;
1278 m_uptime.set(m_uptime.get() + dtime);
1282 // Process connection's timeouts
1283 JMutexAutoLock lock2(m_con_mutex);
1284 ScopeProfiler sp(g_profiler, "Server: connection timeout processing");
1285 m_con.RunTimeouts(dtime);
1289 // This has to be called so that the client list gets synced
1290 // with the peer list of the connection
1291 handlePeerChanges();
1295 Update time of day and overall game time
1298 JMutexAutoLock envlock(m_env_mutex);
1300 m_env->setTimeOfDaySpeed(g_settings->getFloat("time_speed"));
1303 Send to clients at constant intervals
1306 m_time_of_day_send_timer -= dtime;
1307 if(m_time_of_day_send_timer < 0.0)
1309 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
1311 //JMutexAutoLock envlock(m_env_mutex);
1312 JMutexAutoLock conlock(m_con_mutex);
1314 for(core::map<u16, RemoteClient*>::Iterator
1315 i = m_clients.getIterator();
1316 i.atEnd() == false; i++)
1318 RemoteClient *client = i.getNode()->getValue();
1319 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1320 m_env->getTimeOfDay(), g_settings->getFloat("time_speed"));
1322 m_con.Send(client->peer_id, 0, data, true);
1328 JMutexAutoLock lock(m_env_mutex);
1330 ScopeProfiler sp(g_profiler, "SEnv step");
1331 ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
1335 const float map_timer_and_unload_dtime = 2.92;
1336 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
1338 JMutexAutoLock lock(m_env_mutex);
1339 // Run Map's timers and unload unused data
1340 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
1341 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
1342 g_settings->getFloat("server_unload_unused_data_timeout"));
1353 JMutexAutoLock lock(m_env_mutex);
1354 JMutexAutoLock lock2(m_con_mutex);
1356 ScopeProfiler sp(g_profiler, "Server: handle players");
1358 for(core::map<u16, RemoteClient*>::Iterator
1359 i = m_clients.getIterator();
1360 i.atEnd() == false; i++)
1362 RemoteClient *client = i.getNode()->getValue();
1363 PlayerSAO *playersao = getPlayerSAO(client->peer_id);
1364 if(playersao == NULL)
1368 Handle player HPs (die if hp=0)
1370 if(playersao->getHP() == 0 && playersao->m_hp_not_sent)
1371 DiePlayer(client->peer_id);
1374 Send player inventories and HPs if necessary
1376 if(playersao->m_teleported){
1377 SendMovePlayer(client->peer_id);
1378 playersao->m_teleported = false;
1380 if(playersao->m_inventory_not_sent){
1381 UpdateCrafting(client->peer_id);
1382 SendInventory(client->peer_id);
1384 if(playersao->m_hp_not_sent){
1385 SendPlayerHP(client->peer_id);
1390 /* Transform liquids */
1391 m_liquid_transform_timer += dtime;
1392 if(m_liquid_transform_timer >= 1.00)
1394 m_liquid_transform_timer -= 1.00;
1396 JMutexAutoLock lock(m_env_mutex);
1398 ScopeProfiler sp(g_profiler, "Server: liquid transform");
1400 core::map<v3s16, MapBlock*> modified_blocks;
1401 m_env->getMap().transformLiquids(modified_blocks);
1406 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1407 ServerMap &map = ((ServerMap&)m_env->getMap());
1408 map.updateLighting(modified_blocks, lighting_modified_blocks);
1410 // Add blocks modified by lighting to modified_blocks
1411 for(core::map<v3s16, MapBlock*>::Iterator
1412 i = lighting_modified_blocks.getIterator();
1413 i.atEnd() == false; i++)
1415 MapBlock *block = i.getNode()->getValue();
1416 modified_blocks.insert(block->getPos(), block);
1420 Set the modified blocks unsent for all the clients
1423 JMutexAutoLock lock2(m_con_mutex);
1425 for(core::map<u16, RemoteClient*>::Iterator
1426 i = m_clients.getIterator();
1427 i.atEnd() == false; i++)
1429 RemoteClient *client = i.getNode()->getValue();
1431 if(modified_blocks.size() > 0)
1433 // Remove block from sent history
1434 client->SetBlocksNotSent(modified_blocks);
1439 // Periodically print some info
1441 float &counter = m_print_info_timer;
1447 JMutexAutoLock lock2(m_con_mutex);
1449 if(m_clients.size() != 0)
1450 infostream<<"Players:"<<std::endl;
1451 for(core::map<u16, RemoteClient*>::Iterator
1452 i = m_clients.getIterator();
1453 i.atEnd() == false; i++)
1455 //u16 peer_id = i.getNode()->getKey();
1456 RemoteClient *client = i.getNode()->getValue();
1457 Player *player = m_env->getPlayer(client->peer_id);
1460 infostream<<"* "<<player->getName()<<"\t";
1461 client->PrintInfo(infostream);
1466 //if(g_settings->getBool("enable_experimental"))
1470 Check added and deleted active objects
1473 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
1474 JMutexAutoLock envlock(m_env_mutex);
1475 JMutexAutoLock conlock(m_con_mutex);
1477 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
1479 // Radius inside which objects are active
1480 s16 radius = g_settings->getS16("active_object_send_range_blocks");
1481 radius *= MAP_BLOCKSIZE;
1483 for(core::map<u16, RemoteClient*>::Iterator
1484 i = m_clients.getIterator();
1485 i.atEnd() == false; i++)
1487 RemoteClient *client = i.getNode()->getValue();
1489 // If definitions and textures have not been sent, don't
1490 // send objects either
1491 if(!client->definitions_sent)
1494 Player *player = m_env->getPlayer(client->peer_id);
1497 // This can happen if the client timeouts somehow
1498 /*infostream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
1500 <<" has no associated player"<<std::endl;*/
1503 v3s16 pos = floatToInt(player->getPosition(), BS);
1505 core::map<u16, bool> removed_objects;
1506 core::map<u16, bool> added_objects;
1507 m_env->getRemovedActiveObjects(pos, radius,
1508 client->m_known_objects, removed_objects);
1509 m_env->getAddedActiveObjects(pos, radius,
1510 client->m_known_objects, added_objects);
1512 // Ignore if nothing happened
1513 if(removed_objects.size() == 0 && added_objects.size() == 0)
1515 //infostream<<"active objects: none changed"<<std::endl;
1519 std::string data_buffer;
1523 // Handle removed objects
1524 writeU16((u8*)buf, removed_objects.size());
1525 data_buffer.append(buf, 2);
1526 for(core::map<u16, bool>::Iterator
1527 i = removed_objects.getIterator();
1528 i.atEnd()==false; i++)
1531 u16 id = i.getNode()->getKey();
1532 ServerActiveObject* obj = m_env->getActiveObject(id);
1534 // Add to data buffer for sending
1535 writeU16((u8*)buf, i.getNode()->getKey());
1536 data_buffer.append(buf, 2);
1538 // Remove from known objects
1539 client->m_known_objects.remove(i.getNode()->getKey());
1541 if(obj && obj->m_known_by_count > 0)
1542 obj->m_known_by_count--;
1545 // Handle added objects
1546 writeU16((u8*)buf, added_objects.size());
1547 data_buffer.append(buf, 2);
1548 for(core::map<u16, bool>::Iterator
1549 i = added_objects.getIterator();
1550 i.atEnd()==false; i++)
1553 u16 id = i.getNode()->getKey();
1554 ServerActiveObject* obj = m_env->getActiveObject(id);
1557 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1559 infostream<<"WARNING: "<<__FUNCTION_NAME
1560 <<": NULL object"<<std::endl;
1562 type = obj->getSendType();
1564 // Add to data buffer for sending
1565 writeU16((u8*)buf, id);
1566 data_buffer.append(buf, 2);
1567 writeU8((u8*)buf, type);
1568 data_buffer.append(buf, 1);
1571 data_buffer.append(serializeLongString(
1572 obj->getClientInitializationData()));
1574 data_buffer.append(serializeLongString(""));
1576 // Add to known objects
1577 client->m_known_objects.insert(i.getNode()->getKey(), false);
1580 obj->m_known_by_count++;
1584 SharedBuffer<u8> reply(2 + data_buffer.size());
1585 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1586 memcpy((char*)&reply[2], data_buffer.c_str(),
1587 data_buffer.size());
1589 m_con.Send(client->peer_id, 0, reply, true);
1591 verbosestream<<"Server: Sent object remove/add: "
1592 <<removed_objects.size()<<" removed, "
1593 <<added_objects.size()<<" added, "
1594 <<"packet size is "<<reply.getSize()<<std::endl;
1599 Collect a list of all the objects known by the clients
1600 and report it back to the environment.
1603 core::map<u16, bool> all_known_objects;
1605 for(core::map<u16, RemoteClient*>::Iterator
1606 i = m_clients.getIterator();
1607 i.atEnd() == false; i++)
1609 RemoteClient *client = i.getNode()->getValue();
1610 // Go through all known objects of client
1611 for(core::map<u16, bool>::Iterator
1612 i = client->m_known_objects.getIterator();
1613 i.atEnd()==false; i++)
1615 u16 id = i.getNode()->getKey();
1616 all_known_objects[id] = true;
1620 m_env->setKnownActiveObjects(whatever);
1626 Send object messages
1629 JMutexAutoLock envlock(m_env_mutex);
1630 JMutexAutoLock conlock(m_con_mutex);
1632 ScopeProfiler sp(g_profiler, "Server: sending object messages");
1635 // Value = data sent by object
1636 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1638 // Get active object messages from environment
1641 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
1645 core::list<ActiveObjectMessage>* message_list = NULL;
1646 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1647 n = buffered_messages.find(aom.id);
1650 message_list = new core::list<ActiveObjectMessage>;
1651 buffered_messages.insert(aom.id, message_list);
1655 message_list = n->getValue();
1657 message_list->push_back(aom);
1660 // Route data to every client
1661 for(core::map<u16, RemoteClient*>::Iterator
1662 i = m_clients.getIterator();
1663 i.atEnd()==false; i++)
1665 RemoteClient *client = i.getNode()->getValue();
1666 std::string reliable_data;
1667 std::string unreliable_data;
1668 // Go through all objects in message buffer
1669 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1670 j = buffered_messages.getIterator();
1671 j.atEnd()==false; j++)
1673 // If object is not known by client, skip it
1674 u16 id = j.getNode()->getKey();
1675 if(client->m_known_objects.find(id) == NULL)
1677 // Get message list of object
1678 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1679 // Go through every message
1680 for(core::list<ActiveObjectMessage>::Iterator
1681 k = list->begin(); k != list->end(); k++)
1683 // Compose the full new data with header
1684 ActiveObjectMessage aom = *k;
1685 std::string new_data;
1688 writeU16((u8*)&buf[0], aom.id);
1689 new_data.append(buf, 2);
1691 new_data += serializeString(aom.datastring);
1692 // Add data to buffer
1694 reliable_data += new_data;
1696 unreliable_data += new_data;
1700 reliable_data and unreliable_data are now ready.
1703 if(reliable_data.size() > 0)
1705 SharedBuffer<u8> reply(2 + reliable_data.size());
1706 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1707 memcpy((char*)&reply[2], reliable_data.c_str(),
1708 reliable_data.size());
1710 m_con.Send(client->peer_id, 0, reply, true);
1712 if(unreliable_data.size() > 0)
1714 SharedBuffer<u8> reply(2 + unreliable_data.size());
1715 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1716 memcpy((char*)&reply[2], unreliable_data.c_str(),
1717 unreliable_data.size());
1718 // Send as unreliable
1719 m_con.Send(client->peer_id, 0, reply, false);
1722 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1724 infostream<<"Server: Size of object message data: "
1725 <<"reliable: "<<reliable_data.size()
1726 <<", unreliable: "<<unreliable_data.size()
1731 // Clear buffered_messages
1732 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1733 i = buffered_messages.getIterator();
1734 i.atEnd()==false; i++)
1736 delete i.getNode()->getValue();
1740 } // enable_experimental
1743 Send queued-for-sending map edit events.
1746 // We will be accessing the environment and the connection
1747 JMutexAutoLock lock(m_env_mutex);
1748 JMutexAutoLock conlock(m_con_mutex);
1750 // Don't send too many at a time
1753 // Single change sending is disabled if queue size is not small
1754 bool disable_single_change_sending = false;
1755 if(m_unsent_map_edit_queue.size() >= 4)
1756 disable_single_change_sending = true;
1758 int event_count = m_unsent_map_edit_queue.size();
1760 // We'll log the amount of each
1763 while(m_unsent_map_edit_queue.size() != 0)
1765 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1767 // Players far away from the change are stored here.
1768 // Instead of sending the changes, MapBlocks are set not sent
1770 core::list<u16> far_players;
1772 if(event->type == MEET_ADDNODE)
1774 //infostream<<"Server: MEET_ADDNODE"<<std::endl;
1775 prof.add("MEET_ADDNODE", 1);
1776 if(disable_single_change_sending)
1777 sendAddNode(event->p, event->n, event->already_known_by_peer,
1780 sendAddNode(event->p, event->n, event->already_known_by_peer,
1783 else if(event->type == MEET_REMOVENODE)
1785 //infostream<<"Server: MEET_REMOVENODE"<<std::endl;
1786 prof.add("MEET_REMOVENODE", 1);
1787 if(disable_single_change_sending)
1788 sendRemoveNode(event->p, event->already_known_by_peer,
1791 sendRemoveNode(event->p, event->already_known_by_peer,
1794 else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
1796 infostream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
1797 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
1798 setBlockNotSent(event->p);
1800 else if(event->type == MEET_OTHER)
1802 infostream<<"Server: MEET_OTHER"<<std::endl;
1803 prof.add("MEET_OTHER", 1);
1804 for(core::map<v3s16, bool>::Iterator
1805 i = event->modified_blocks.getIterator();
1806 i.atEnd()==false; i++)
1808 v3s16 p = i.getNode()->getKey();
1814 prof.add("unknown", 1);
1815 infostream<<"WARNING: Server: Unknown MapEditEvent "
1816 <<((u32)event->type)<<std::endl;
1820 Set blocks not sent to far players
1822 if(far_players.size() > 0)
1824 // Convert list format to that wanted by SetBlocksNotSent
1825 core::map<v3s16, MapBlock*> modified_blocks2;
1826 for(core::map<v3s16, bool>::Iterator
1827 i = event->modified_blocks.getIterator();
1828 i.atEnd()==false; i++)
1830 v3s16 p = i.getNode()->getKey();
1831 modified_blocks2.insert(p,
1832 m_env->getMap().getBlockNoCreateNoEx(p));
1834 // Set blocks not sent
1835 for(core::list<u16>::Iterator
1836 i = far_players.begin();
1837 i != far_players.end(); i++)
1840 RemoteClient *client = getClient(peer_id);
1843 client->SetBlocksNotSent(modified_blocks2);
1849 /*// Don't send too many at a time
1851 if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
1855 if(event_count >= 5){
1856 infostream<<"Server: MapEditEvents:"<<std::endl;
1857 prof.print(infostream);
1858 } else if(event_count != 0){
1859 verbosestream<<"Server: MapEditEvents:"<<std::endl;
1860 prof.print(verbosestream);
1866 Trigger emergethread (it somehow gets to a non-triggered but
1867 bysy state sometimes)
1870 float &counter = m_emergethread_trigger_timer;
1876 m_emergethread.trigger();
1878 // Update m_enable_rollback_recording here too
1879 m_enable_rollback_recording =
1880 g_settings->getBool("enable_rollback_recording");
1884 // Save map, players and auth stuff
1886 float &counter = m_savemap_timer;
1888 if(counter >= g_settings->getFloat("server_map_save_interval"))
1891 JMutexAutoLock lock(m_env_mutex);
1893 ScopeProfiler sp(g_profiler, "Server: saving stuff");
1896 if(m_banmanager.isModified())
1897 m_banmanager.save();
1899 // Save changed parts of map
1900 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
1903 m_env->serializePlayers(m_path_world);
1905 // Save environment metadata
1906 m_env->saveMeta(m_path_world);
1911 void Server::Receive()
1913 DSTACK(__FUNCTION_NAME);
1914 SharedBuffer<u8> data;
1919 JMutexAutoLock conlock(m_con_mutex);
1920 datasize = m_con.Receive(peer_id, data);
1923 // This has to be called so that the client list gets synced
1924 // with the peer list of the connection
1925 handlePeerChanges();
1927 ProcessData(*data, datasize, peer_id);
1929 catch(con::InvalidIncomingDataException &e)
1931 infostream<<"Server::Receive(): "
1932 "InvalidIncomingDataException: what()="
1933 <<e.what()<<std::endl;
1935 catch(con::PeerNotFoundException &e)
1937 //NOTE: This is not needed anymore
1939 // The peer has been disconnected.
1940 // Find the associated player and remove it.
1942 /*JMutexAutoLock envlock(m_env_mutex);
1944 infostream<<"ServerThread: peer_id="<<peer_id
1945 <<" has apparently closed connection. "
1946 <<"Removing player."<<std::endl;
1948 m_env->removePlayer(peer_id);*/
1952 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1954 DSTACK(__FUNCTION_NAME);
1955 // Environment is locked first.
1956 JMutexAutoLock envlock(m_env_mutex);
1957 JMutexAutoLock conlock(m_con_mutex);
1959 ScopeProfiler sp(g_profiler, "Server::ProcessData");
1962 Address address = m_con.GetPeerAddress(peer_id);
1963 std::string addr_s = address.serializeString();
1965 // drop player if is ip is banned
1966 if(m_banmanager.isIpBanned(addr_s)){
1967 infostream<<"Server: A banned client tried to connect from "
1968 <<addr_s<<"; banned name was "
1969 <<m_banmanager.getBanName(addr_s)<<std::endl;
1970 // This actually doesn't seem to transfer to the client
1971 SendAccessDenied(m_con, peer_id,
1972 L"Your ip is banned. Banned name was "
1973 +narrow_to_wide(m_banmanager.getBanName(addr_s)));
1974 m_con.DeletePeer(peer_id);
1978 catch(con::PeerNotFoundException &e)
1980 infostream<<"Server::ProcessData(): Cancelling: peer "
1981 <<peer_id<<" not found"<<std::endl;
1985 std::string addr_s = m_con.GetPeerAddress(peer_id).serializeString();
1987 u8 peer_ser_ver = getClient(peer_id)->serialization_version;
1995 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1997 if(command == TOSERVER_INIT)
1999 // [0] u16 TOSERVER_INIT
2000 // [2] u8 SER_FMT_VER_HIGHEST
2001 // [3] u8[20] player_name
2002 // [23] u8[28] password <--- can be sent without this, from old versions
2004 if(datasize < 2+1+PLAYERNAME_SIZE)
2007 verbosestream<<"Server: Got TOSERVER_INIT from "
2008 <<peer_id<<std::endl;
2010 // First byte after command is maximum supported
2011 // serialization version
2012 u8 client_max = data[2];
2013 u8 our_max = SER_FMT_VER_HIGHEST;
2014 // Use the highest version supported by both
2015 u8 deployed = core::min_(client_max, our_max);
2016 // If it's lower than the lowest supported, give up.
2017 if(deployed < SER_FMT_VER_LOWEST)
2018 deployed = SER_FMT_VER_INVALID;
2020 //peer->serialization_version = deployed;
2021 getClient(peer_id)->pending_serialization_version = deployed;
2023 if(deployed == SER_FMT_VER_INVALID)
2025 actionstream<<"Server: A mismatched client tried to connect from "
2026 <<addr_s<<std::endl;
2027 infostream<<"Server: Cannot negotiate "
2028 "serialization version with peer "
2029 <<peer_id<<std::endl;
2030 SendAccessDenied(m_con, peer_id, std::wstring(
2031 L"Your client's version is not supported.\n"
2032 L"Server version is ")
2033 + narrow_to_wide(VERSION_STRING) + L"."
2039 Read and check network protocol version
2042 u16 net_proto_version = 0;
2043 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
2045 net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
2048 getClient(peer_id)->net_proto_version = net_proto_version;
2050 if(net_proto_version == 0)
2052 actionstream<<"Server: An old tried to connect from "<<addr_s
2054 SendAccessDenied(m_con, peer_id, std::wstring(
2055 L"Your client's version is not supported.\n"
2056 L"Server version is ")
2057 + narrow_to_wide(VERSION_STRING) + L"."
2062 if(g_settings->getBool("strict_protocol_version_checking"))
2064 if(net_proto_version != PROTOCOL_VERSION)
2066 actionstream<<"Server: A mismatched client tried to connect"
2067 <<" from "<<addr_s<<std::endl;
2068 SendAccessDenied(m_con, peer_id, std::wstring(
2069 L"Your client's version is not supported.\n"
2070 L"Server version is ")
2071 + narrow_to_wide(VERSION_STRING) + L",\n"
2072 + L"server's PROTOCOL_VERSION is "
2073 + narrow_to_wide(itos(PROTOCOL_VERSION))
2074 + L", client's PROTOCOL_VERSION is "
2075 + narrow_to_wide(itos(net_proto_version))
2086 char playername[PLAYERNAME_SIZE];
2087 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
2089 playername[i] = data[3+i];
2091 playername[PLAYERNAME_SIZE-1] = 0;
2093 if(playername[0]=='\0')
2095 actionstream<<"Server: Player with an empty name "
2096 <<"tried to connect from "<<addr_s<<std::endl;
2097 SendAccessDenied(m_con, peer_id,
2102 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
2104 actionstream<<"Server: Player with an invalid name "
2105 <<"tried to connect from "<<addr_s<<std::endl;
2106 SendAccessDenied(m_con, peer_id,
2107 L"Name contains unallowed characters");
2111 infostream<<"Server: New connection: \""<<playername<<"\" from "
2112 <<m_con.GetPeerAddress(peer_id).serializeString()<<std::endl;
2115 char given_password[PASSWORD_SIZE];
2116 if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
2118 // old version - assume blank password
2119 given_password[0] = 0;
2123 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2125 given_password[i] = data[23+i];
2127 given_password[PASSWORD_SIZE-1] = 0;
2130 if(!base64_is_valid(given_password)){
2131 infostream<<"Server: "<<playername
2132 <<" supplied invalid password hash"<<std::endl;
2133 SendAccessDenied(m_con, peer_id, L"Invalid password hash");
2137 std::string checkpwd; // Password hash to check against
2138 bool has_auth = scriptapi_get_auth(m_lua, playername, &checkpwd, NULL);
2140 // If no authentication info exists for user, create it
2142 if(!isSingleplayer() &&
2143 g_settings->getBool("disallow_empty_password") &&
2144 std::string(given_password) == ""){
2145 SendAccessDenied(m_con, peer_id, L"Empty passwords are "
2146 L"disallowed. Set a password and try again.");
2149 std::wstring raw_default_password =
2150 narrow_to_wide(g_settings->get("default_password"));
2151 std::string initial_password =
2152 translatePassword(playername, raw_default_password);
2154 // If default_password is empty, allow any initial password
2155 if (raw_default_password.length() == 0)
2156 initial_password = given_password;
2158 scriptapi_create_auth(m_lua, playername, initial_password);
2161 has_auth = scriptapi_get_auth(m_lua, playername, &checkpwd, NULL);
2164 SendAccessDenied(m_con, peer_id, L"Not allowed to login");
2168 if(given_password != checkpwd){
2169 infostream<<"Server: peer_id="<<peer_id
2170 <<": supplied invalid password for "
2171 <<playername<<std::endl;
2172 SendAccessDenied(m_con, peer_id, L"Invalid password");
2176 // Do not allow multiple players in simple singleplayer mode.
2177 // This isn't a perfect way to do it, but will suffice for now.
2178 if(m_simple_singleplayer_mode && m_clients.size() > 1){
2179 infostream<<"Server: Not allowing another client to connect in"
2180 <<" simple singleplayer mode"<<std::endl;
2181 SendAccessDenied(m_con, peer_id,
2182 L"Running in simple singleplayer mode.");
2186 // Enforce user limit.
2187 // Don't enforce for users that have some admin right
2188 if(m_clients.size() >= g_settings->getU16("max_users") &&
2189 !checkPriv(playername, "server") &&
2190 !checkPriv(playername, "ban") &&
2191 !checkPriv(playername, "privs") &&
2192 !checkPriv(playername, "password") &&
2193 playername != g_settings->get("name"))
2195 actionstream<<"Server: "<<playername<<" tried to join, but there"
2196 <<" are already max_users="
2197 <<g_settings->getU16("max_users")<<" players."<<std::endl;
2198 SendAccessDenied(m_con, peer_id, L"Too many users.");
2203 PlayerSAO *playersao = emergePlayer(playername, peer_id);
2205 // If failed, cancel
2206 if(playersao == NULL)
2208 errorstream<<"Server: peer_id="<<peer_id
2209 <<": failed to emerge player"<<std::endl;
2214 Answer with a TOCLIENT_INIT
2217 SharedBuffer<u8> reply(2+1+6+8);
2218 writeU16(&reply[0], TOCLIENT_INIT);
2219 writeU8(&reply[2], deployed);
2220 writeV3S16(&reply[2+1], floatToInt(playersao->getPlayer()->getPosition()+v3f(0,BS/2,0), BS));
2221 writeU64(&reply[2+1+6], m_env->getServerMap().getSeed());
2224 m_con.Send(peer_id, 0, reply, true);
2228 Send complete position information
2230 SendMovePlayer(peer_id);
2235 if(command == TOSERVER_INIT2)
2237 verbosestream<<"Server: Got TOSERVER_INIT2 from "
2238 <<peer_id<<std::endl;
2240 Player *player = m_env->getPlayer(peer_id);
2242 verbosestream<<"Server: TOSERVER_INIT2: "
2243 <<"Player not found; ignoring."<<std::endl;
2247 getClient(peer_id)->serialization_version
2248 = getClient(peer_id)->pending_serialization_version;
2251 Send some initialization data
2254 infostream<<"Server: Sending content to "
2255 <<getPlayerName(peer_id)<<std::endl;
2257 // Send item definitions
2258 SendItemDef(m_con, peer_id, m_itemdef);
2260 // Send node definitions
2261 SendNodeDef(m_con, peer_id, m_nodedef);
2263 // Send media announcement
2264 sendMediaAnnouncement(peer_id);
2267 SendPlayerPrivileges(peer_id);
2269 // Send inventory formspec
2270 SendPlayerInventoryFormspec(peer_id);
2273 UpdateCrafting(peer_id);
2274 SendInventory(peer_id);
2277 SendPlayerHP(peer_id);
2279 // Send detached inventories
2280 sendDetachedInventories(peer_id);
2282 // Show death screen if necessary
2284 SendDeathscreen(m_con, peer_id, false, v3f(0,0,0));
2288 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2289 m_env->getTimeOfDay(), g_settings->getFloat("time_speed"));
2290 m_con.Send(peer_id, 0, data, true);
2293 // Note things in chat if not in simple singleplayer mode
2294 if(!m_simple_singleplayer_mode)
2296 // Send information about server to player in chat
2297 SendChatMessage(peer_id, getStatusString());
2299 // Send information about joining in chat
2301 std::wstring name = L"unknown";
2302 Player *player = m_env->getPlayer(peer_id);
2304 name = narrow_to_wide(player->getName());
2306 std::wstring message;
2309 message += L" joined the game.";
2310 BroadcastChatMessage(message);
2314 // Warnings about protocol version can be issued here
2315 if(getClient(peer_id)->net_proto_version < PROTOCOL_VERSION)
2317 SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT IS OLD AND MAY WORK PROPERLY WITH THIS SERVER!");
2324 std::ostringstream os(std::ios_base::binary);
2325 for(core::map<u16, RemoteClient*>::Iterator
2326 i = m_clients.getIterator();
2327 i.atEnd() == false; i++)
2329 RemoteClient *client = i.getNode()->getValue();
2330 assert(client->peer_id == i.getNode()->getKey());
2331 if(client->serialization_version == SER_FMT_VER_INVALID)
2334 Player *player = m_env->getPlayer(client->peer_id);
2337 // Get name of player
2338 os<<player->getName()<<" ";
2341 actionstream<<player->getName()<<" joins game. List of players: "
2342 <<os.str()<<std::endl;
2348 if(peer_ser_ver == SER_FMT_VER_INVALID)
2350 infostream<<"Server::ProcessData(): Cancelling: Peer"
2351 " serialization format invalid or not initialized."
2352 " Skipping incoming command="<<command<<std::endl;
2356 Player *player = m_env->getPlayer(peer_id);
2358 infostream<<"Server::ProcessData(): Cancelling: "
2359 "No player for peer_id="<<peer_id
2364 PlayerSAO *playersao = player->getPlayerSAO();
2365 if(playersao == NULL){
2366 infostream<<"Server::ProcessData(): Cancelling: "
2367 "No player object for peer_id="<<peer_id
2372 if(command == TOSERVER_PLAYERPOS)
2374 if(datasize < 2+12+12+4+4)
2378 v3s32 ps = readV3S32(&data[start+2]);
2379 v3s32 ss = readV3S32(&data[start+2+12]);
2380 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2381 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2382 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2383 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2384 pitch = wrapDegrees(pitch);
2385 yaw = wrapDegrees(yaw);
2387 player->setPosition(position);
2388 player->setSpeed(speed);
2389 player->setPitch(pitch);
2390 player->setYaw(yaw);
2392 /*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2393 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2394 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2396 else if(command == TOSERVER_GOTBLOCKS)
2409 u16 count = data[2];
2410 for(u16 i=0; i<count; i++)
2412 if((s16)datasize < 2+1+(i+1)*6)
2413 throw con::InvalidIncomingDataException
2414 ("GOTBLOCKS length is too short");
2415 v3s16 p = readV3S16(&data[2+1+i*6]);
2416 /*infostream<<"Server: GOTBLOCKS ("
2417 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2418 RemoteClient *client = getClient(peer_id);
2419 client->GotBlock(p);
2422 else if(command == TOSERVER_DELETEDBLOCKS)
2435 u16 count = data[2];
2436 for(u16 i=0; i<count; i++)
2438 if((s16)datasize < 2+1+(i+1)*6)
2439 throw con::InvalidIncomingDataException
2440 ("DELETEDBLOCKS length is too short");
2441 v3s16 p = readV3S16(&data[2+1+i*6]);
2442 /*infostream<<"Server: DELETEDBLOCKS ("
2443 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2444 RemoteClient *client = getClient(peer_id);
2445 client->SetBlockNotSent(p);
2448 else if(command == TOSERVER_CLICK_OBJECT)
2450 infostream<<"Server: CLICK_OBJECT not supported anymore"<<std::endl;
2453 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2455 infostream<<"Server: CLICK_ACTIVEOBJECT not supported anymore"<<std::endl;
2458 else if(command == TOSERVER_GROUND_ACTION)
2460 infostream<<"Server: GROUND_ACTION not supported anymore"<<std::endl;
2464 else if(command == TOSERVER_RELEASE)
2466 infostream<<"Server: RELEASE not supported anymore"<<std::endl;
2469 else if(command == TOSERVER_SIGNTEXT)
2471 infostream<<"Server: SIGNTEXT not supported anymore"
2475 else if(command == TOSERVER_SIGNNODETEXT)
2477 infostream<<"Server: SIGNNODETEXT not supported anymore"
2481 else if(command == TOSERVER_INVENTORY_ACTION)
2483 // Strip command and create a stream
2484 std::string datastring((char*)&data[2], datasize-2);
2485 verbosestream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2486 std::istringstream is(datastring, std::ios_base::binary);
2488 InventoryAction *a = InventoryAction::deSerialize(is);
2491 infostream<<"TOSERVER_INVENTORY_ACTION: "
2492 <<"InventoryAction::deSerialize() returned NULL"
2497 // If something goes wrong, this player is to blame
2498 RollbackScopeActor rollback_scope(m_rollback,
2499 std::string("player:")+player->getName());
2502 Note: Always set inventory not sent, to repair cases
2503 where the client made a bad prediction.
2507 Handle restrictions and special cases of the move action
2509 if(a->getType() == IACTION_MOVE)
2511 IMoveAction *ma = (IMoveAction*)a;
2513 ma->from_inv.applyCurrentPlayer(player->getName());
2514 ma->to_inv.applyCurrentPlayer(player->getName());
2516 setInventoryModified(ma->from_inv);
2517 setInventoryModified(ma->to_inv);
2519 bool from_inv_is_current_player =
2520 (ma->from_inv.type == InventoryLocation::PLAYER) &&
2521 (ma->from_inv.name == player->getName());
2523 bool to_inv_is_current_player =
2524 (ma->to_inv.type == InventoryLocation::PLAYER) &&
2525 (ma->to_inv.name == player->getName());
2528 Disable moving items out of craftpreview
2530 if(ma->from_list == "craftpreview")
2532 infostream<<"Ignoring IMoveAction from "
2533 <<(ma->from_inv.dump())<<":"<<ma->from_list
2534 <<" to "<<(ma->to_inv.dump())<<":"<<ma->to_list
2535 <<" because src is "<<ma->from_list<<std::endl;
2541 Disable moving items into craftresult and craftpreview
2543 if(ma->to_list == "craftpreview" || ma->to_list == "craftresult")
2545 infostream<<"Ignoring IMoveAction from "
2546 <<(ma->from_inv.dump())<<":"<<ma->from_list
2547 <<" to "<<(ma->to_inv.dump())<<":"<<ma->to_list
2548 <<" because dst is "<<ma->to_list<<std::endl;
2553 // Disallow moving items in elsewhere than player's inventory
2554 // if not allowed to interact
2555 if(!checkPriv(player->getName(), "interact") &&
2556 (!from_inv_is_current_player ||
2557 !to_inv_is_current_player))
2559 infostream<<"Cannot move outside of player's inventory: "
2560 <<"No interact privilege"<<std::endl;
2566 Handle restrictions and special cases of the drop action
2568 else if(a->getType() == IACTION_DROP)
2570 IDropAction *da = (IDropAction*)a;
2572 da->from_inv.applyCurrentPlayer(player->getName());
2574 setInventoryModified(da->from_inv);
2576 // Disallow dropping items if not allowed to interact
2577 if(!checkPriv(player->getName(), "interact"))
2584 Handle restrictions and special cases of the craft action
2586 else if(a->getType() == IACTION_CRAFT)
2588 ICraftAction *ca = (ICraftAction*)a;
2590 ca->craft_inv.applyCurrentPlayer(player->getName());
2592 setInventoryModified(ca->craft_inv);
2594 //bool craft_inv_is_current_player =
2595 // (ca->craft_inv.type == InventoryLocation::PLAYER) &&
2596 // (ca->craft_inv.name == player->getName());
2598 // Disallow crafting if not allowed to interact
2599 if(!checkPriv(player->getName(), "interact"))
2601 infostream<<"Cannot craft: "
2602 <<"No interact privilege"<<std::endl;
2609 a->apply(this, playersao, this);
2613 else if(command == TOSERVER_CHAT_MESSAGE)
2621 std::string datastring((char*)&data[2], datasize-2);
2622 std::istringstream is(datastring, std::ios_base::binary);
2625 is.read((char*)buf, 2);
2626 u16 len = readU16(buf);
2628 std::wstring message;
2629 for(u16 i=0; i<len; i++)
2631 is.read((char*)buf, 2);
2632 message += (wchar_t)readU16(buf);
2635 // If something goes wrong, this player is to blame
2636 RollbackScopeActor rollback_scope(m_rollback,
2637 std::string("player:")+player->getName());
2639 // Get player name of this client
2640 std::wstring name = narrow_to_wide(player->getName());
2643 bool ate = scriptapi_on_chat_message(m_lua, player->getName(),
2644 wide_to_narrow(message));
2645 // If script ate the message, don't proceed
2649 // Line to send to players
2651 // Whether to send to the player that sent the line
2652 bool send_to_sender = false;
2653 // Whether to send to other players
2654 bool send_to_others = false;
2657 if(message[0] == L'/')
2659 size_t strip_size = 1;
2660 if (message[1] == L'#') // support old-style commans
2662 message = message.substr(strip_size);
2664 WStrfnd f1(message);
2665 f1.next(L" "); // Skip over /#whatever
2666 std::wstring paramstring = f1.next(L"");
2668 ServerCommandContext *ctx = new ServerCommandContext(
2669 str_split(message, L' '),
2675 std::wstring reply(processServerCommand(ctx));
2676 send_to_sender = ctx->flags & SEND_TO_SENDER;
2677 send_to_others = ctx->flags & SEND_TO_OTHERS;
2679 if (ctx->flags & SEND_NO_PREFIX)
2682 line += L"Server: " + reply;
2689 if(checkPriv(player->getName(), "shout")){
2694 send_to_others = true;
2696 line += L"-!- You don't have permission to shout.";
2697 send_to_sender = true;
2704 actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2707 Send the message to clients
2709 for(core::map<u16, RemoteClient*>::Iterator
2710 i = m_clients.getIterator();
2711 i.atEnd() == false; i++)
2713 // Get client and check that it is valid
2714 RemoteClient *client = i.getNode()->getValue();
2715 assert(client->peer_id == i.getNode()->getKey());
2716 if(client->serialization_version == SER_FMT_VER_INVALID)
2720 bool sender_selected = (peer_id == client->peer_id);
2721 if(sender_selected == true && send_to_sender == false)
2723 if(sender_selected == false && send_to_others == false)
2726 SendChatMessage(client->peer_id, line);
2730 else if(command == TOSERVER_DAMAGE)
2732 std::string datastring((char*)&data[2], datasize-2);
2733 std::istringstream is(datastring, std::ios_base::binary);
2734 u8 damage = readU8(is);
2736 actionstream<<player->getName()<<" damaged by "
2737 <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
2740 playersao->setHP(playersao->getHP() - damage);
2742 if(playersao->getHP() == 0 && playersao->m_hp_not_sent)
2745 if(playersao->m_hp_not_sent)
2746 SendPlayerHP(peer_id);
2748 else if(command == TOSERVER_PASSWORD)
2751 [0] u16 TOSERVER_PASSWORD
2752 [2] u8[28] old password
2753 [30] u8[28] new password
2756 if(datasize != 2+PASSWORD_SIZE*2)
2758 /*char password[PASSWORD_SIZE];
2759 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2760 password[i] = data[2+i];
2761 password[PASSWORD_SIZE-1] = 0;*/
2763 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2771 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2773 char c = data[2+PASSWORD_SIZE+i];
2779 if(!base64_is_valid(newpwd)){
2780 infostream<<"Server: "<<player->getName()<<" supplied invalid password hash"<<std::endl;
2781 // Wrong old password supplied!!
2782 SendChatMessage(peer_id, L"Invalid new password hash supplied. Password NOT changed.");
2786 infostream<<"Server: Client requests a password change from "
2787 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
2789 std::string playername = player->getName();
2791 std::string checkpwd;
2792 scriptapi_get_auth(m_lua, playername, &checkpwd, NULL);
2794 if(oldpwd != checkpwd)
2796 infostream<<"Server: invalid old password"<<std::endl;
2797 // Wrong old password supplied!!
2798 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
2802 bool success = scriptapi_set_password(m_lua, playername, newpwd);
2804 actionstream<<player->getName()<<" changes password"<<std::endl;
2805 SendChatMessage(peer_id, L"Password change successful.");
2807 actionstream<<player->getName()<<" tries to change password but "
2808 <<"it fails"<<std::endl;
2809 SendChatMessage(peer_id, L"Password change failed or inavailable.");
2812 else if(command == TOSERVER_PLAYERITEM)
2817 u16 item = readU16(&data[2]);
2818 playersao->setWieldIndex(item);
2820 else if(command == TOSERVER_RESPAWN)
2825 RespawnPlayer(peer_id);
2827 actionstream<<player->getName()<<" respawns at "
2828 <<PP(player->getPosition()/BS)<<std::endl;
2830 // ActiveObject is added to environment in AsyncRunStep after
2831 // the previous addition has been succesfully removed
2833 else if(command == TOSERVER_REQUEST_MEDIA) {
2834 std::string datastring((char*)&data[2], datasize-2);
2835 std::istringstream is(datastring, std::ios_base::binary);
2837 core::list<MediaRequest> tosend;
2838 u16 numfiles = readU16(is);
2840 infostream<<"Sending "<<numfiles<<" files to "
2841 <<getPlayerName(peer_id)<<std::endl;
2842 verbosestream<<"TOSERVER_REQUEST_MEDIA: "<<std::endl;
2844 for(int i = 0; i < numfiles; i++) {
2845 std::string name = deSerializeString(is);
2846 tosend.push_back(MediaRequest(name));
2847 verbosestream<<"TOSERVER_REQUEST_MEDIA: requested file "
2851 sendRequestedMedia(peer_id, tosend);
2853 // Now the client should know about everything
2854 // (definitions and files)
2855 getClient(peer_id)->definitions_sent = true;
2857 else if(command == TOSERVER_INTERACT)
2859 std::string datastring((char*)&data[2], datasize-2);
2860 std::istringstream is(datastring, std::ios_base::binary);
2866 [5] u32 length of the next item
2867 [9] serialized PointedThing
2869 0: start digging (from undersurface) or use
2870 1: stop digging (all parameters ignored)
2871 2: digging completed
2872 3: place block or item (to abovesurface)
2875 u8 action = readU8(is);
2876 u16 item_i = readU16(is);
2877 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
2878 PointedThing pointed;
2879 pointed.deSerialize(tmp_is);
2881 verbosestream<<"TOSERVER_INTERACT: action="<<(int)action<<", item="
2882 <<item_i<<", pointed="<<pointed.dump()<<std::endl;
2886 verbosestream<<"TOSERVER_INTERACT: "<<player->getName()
2887 <<" tried to interact, but is dead!"<<std::endl;
2891 v3f player_pos = playersao->getLastGoodPosition();
2893 // Update wielded item
2894 playersao->setWieldIndex(item_i);
2896 // Get pointed to node (undefined if not POINTEDTYPE_NODE)
2897 v3s16 p_under = pointed.node_undersurface;
2898 v3s16 p_above = pointed.node_abovesurface;
2900 // Get pointed to object (NULL if not POINTEDTYPE_OBJECT)
2901 ServerActiveObject *pointed_object = NULL;
2902 if(pointed.type == POINTEDTHING_OBJECT)
2904 pointed_object = m_env->getActiveObject(pointed.object_id);
2905 if(pointed_object == NULL)
2907 verbosestream<<"TOSERVER_INTERACT: "
2908 "pointed object is NULL"<<std::endl;
2914 v3f pointed_pos_under = player_pos;
2915 v3f pointed_pos_above = player_pos;
2916 if(pointed.type == POINTEDTHING_NODE)
2918 pointed_pos_under = intToFloat(p_under, BS);
2919 pointed_pos_above = intToFloat(p_above, BS);
2921 else if(pointed.type == POINTEDTHING_OBJECT)
2923 pointed_pos_under = pointed_object->getBasePosition();
2924 pointed_pos_above = pointed_pos_under;
2928 Check that target is reasonably close
2929 (only when digging or placing things)
2931 if(action == 0 || action == 2 || action == 3)
2933 float d = player_pos.getDistanceFrom(pointed_pos_under);
2934 float max_d = BS * 14; // Just some large enough value
2936 actionstream<<"Player "<<player->getName()
2937 <<" tried to access "<<pointed.dump()
2939 <<"d="<<d<<", max_d="<<max_d
2940 <<". ignoring."<<std::endl;
2941 // Re-send block to revert change on client-side
2942 RemoteClient *client = getClient(peer_id);
2943 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2944 client->SetBlockNotSent(blockpos);
2951 Make sure the player is allowed to do it
2953 if(!checkPriv(player->getName(), "interact"))
2955 actionstream<<player->getName()<<" attempted to interact with "
2956 <<pointed.dump()<<" without 'interact' privilege"
2958 // Re-send block to revert change on client-side
2959 RemoteClient *client = getClient(peer_id);
2960 // Digging completed -> under
2962 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2963 client->SetBlockNotSent(blockpos);
2965 // Placement -> above
2967 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
2968 client->SetBlockNotSent(blockpos);
2974 If something goes wrong, this player is to blame
2976 RollbackScopeActor rollback_scope(m_rollback,
2977 std::string("player:")+player->getName());
2980 0: start digging or punch object
2984 if(pointed.type == POINTEDTHING_NODE)
2987 NOTE: This can be used in the future to check if
2988 somebody is cheating, by checking the timing.
2990 MapNode n(CONTENT_IGNORE);
2993 n = m_env->getMap().getNode(p_under);
2995 catch(InvalidPositionException &e)
2997 infostream<<"Server: Not punching: Node not found."
2998 <<" Adding block to emerge queue."
3000 m_emerge_queue.addBlock(peer_id,
3001 getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
3003 if(n.getContent() != CONTENT_IGNORE)
3004 scriptapi_node_on_punch(m_lua, p_under, n, playersao);
3006 playersao->noCheatDigStart(p_under);
3008 else if(pointed.type == POINTEDTHING_OBJECT)
3010 // Skip if object has been removed
3011 if(pointed_object->m_removed)
3014 actionstream<<player->getName()<<" punches object "
3015 <<pointed.object_id<<": "
3016 <<pointed_object->getDescription()<<std::endl;
3018 ItemStack punchitem = playersao->getWieldedItem();
3019 ToolCapabilities toolcap =
3020 punchitem.getToolCapabilities(m_itemdef);
3021 v3f dir = (pointed_object->getBasePosition() -
3022 (player->getPosition() + player->getEyeOffset())
3024 float time_from_last_punch =
3025 playersao->resetTimeFromLastPunch();
3026 pointed_object->punch(dir, &toolcap, playersao,
3027 time_from_last_punch);
3035 else if(action == 1)
3040 2: Digging completed
3042 else if(action == 2)
3044 // Only digging of nodes
3045 if(pointed.type == POINTEDTHING_NODE)
3047 MapNode n(CONTENT_IGNORE);
3050 n = m_env->getMap().getNode(p_under);
3052 catch(InvalidPositionException &e)
3054 infostream<<"Server: Not finishing digging: Node not found."
3055 <<" Adding block to emerge queue."
3057 m_emerge_queue.addBlock(peer_id,
3058 getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
3061 /* Cheat prevention */
3062 bool is_valid_dig = true;
3063 if(!isSingleplayer() && !g_settings->getBool("disable_anticheat"))
3065 v3s16 nocheat_p = playersao->getNoCheatDigPos();
3066 float nocheat_t = playersao->getNoCheatDigTime();
3067 playersao->noCheatDigEnd();
3068 // If player didn't start digging this, ignore dig
3069 if(nocheat_p != p_under){
3070 infostream<<"Server: NoCheat: "<<player->getName()
3071 <<" started digging "
3072 <<PP(nocheat_p)<<" and completed digging "
3073 <<PP(p_under)<<"; not digging."<<std::endl;
3074 is_valid_dig = false;
3076 // Get player's wielded item
3077 ItemStack playeritem;
3078 InventoryList *mlist = playersao->getInventory()->getList("main");
3080 playeritem = mlist->getItem(playersao->getWieldIndex());
3081 ToolCapabilities playeritem_toolcap =
3082 playeritem.getToolCapabilities(m_itemdef);
3083 // Get diggability and expected digging time
3084 DigParams params = getDigParams(m_nodedef->get(n).groups,
3085 &playeritem_toolcap);
3086 // If can't dig, try hand
3087 if(!params.diggable){
3088 const ItemDefinition &hand = m_itemdef->get("");
3089 const ToolCapabilities *tp = hand.tool_capabilities;
3091 params = getDigParams(m_nodedef->get(n).groups, tp);
3093 // If can't dig, ignore dig
3094 if(!params.diggable){
3095 infostream<<"Server: NoCheat: "<<player->getName()
3096 <<" completed digging "<<PP(p_under)
3097 <<", which is not diggable with tool. not digging."
3099 is_valid_dig = false;
3101 // If time is considerably too short, ignore dig
3102 // Check time only for medium and slow timed digs
3103 if(params.diggable && params.time > 0.3 && nocheat_t < 0.5 * params.time){
3104 infostream<<"Server: NoCheat: "<<player->getName()
3105 <<" completed digging "
3106 <<PP(p_under)<<" in "<<nocheat_t<<"s; expected "
3107 <<params.time<<"s; not digging."<<std::endl;
3108 is_valid_dig = false;
3112 /* Actually dig node */
3114 if(is_valid_dig && n.getContent() != CONTENT_IGNORE)
3115 scriptapi_node_on_dig(m_lua, p_under, n, playersao);
3117 // Send unusual result (that is, node not being removed)
3118 if(m_env->getMap().getNodeNoEx(p_under).getContent() != CONTENT_AIR)
3120 // Re-send block to revert change on client-side
3121 RemoteClient *client = getClient(peer_id);
3122 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
3123 client->SetBlockNotSent(blockpos);
3129 3: place block or right-click object
3131 else if(action == 3)
3133 ItemStack item = playersao->getWieldedItem();
3135 // Reset build time counter
3136 if(pointed.type == POINTEDTHING_NODE &&
3137 item.getDefinition(m_itemdef).type == ITEM_NODE)
3138 getClient(peer_id)->m_time_from_building = 0.0;
3140 if(pointed.type == POINTEDTHING_OBJECT)
3142 // Right click object
3144 // Skip if object has been removed
3145 if(pointed_object->m_removed)
3148 actionstream<<player->getName()<<" right-clicks object "
3149 <<pointed.object_id<<": "
3150 <<pointed_object->getDescription()<<std::endl;
3153 pointed_object->rightClick(playersao);
3155 else if(scriptapi_item_on_place(m_lua,
3156 item, playersao, pointed))
3158 // Placement was handled in lua
3160 // Apply returned ItemStack
3161 playersao->setWieldedItem(item);
3164 // If item has node placement prediction, always send the above
3165 // node to make sure the client knows what exactly happened
3166 if(item.getDefinition(m_itemdef).node_placement_prediction != ""){
3167 RemoteClient *client = getClient(peer_id);
3168 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
3169 client->SetBlockNotSent(blockpos);
3176 else if(action == 4)
3178 ItemStack item = playersao->getWieldedItem();
3180 actionstream<<player->getName()<<" uses "<<item.name
3181 <<", pointing at "<<pointed.dump()<<std::endl;
3183 if(scriptapi_item_on_use(m_lua,
3184 item, playersao, pointed))
3186 // Apply returned ItemStack
3187 playersao->setWieldedItem(item);
3193 Catch invalid actions
3197 infostream<<"WARNING: Server: Invalid action "
3198 <<action<<std::endl;
3201 else if(command == TOSERVER_REMOVED_SOUNDS)
3203 std::string datastring((char*)&data[2], datasize-2);
3204 std::istringstream is(datastring, std::ios_base::binary);
3206 int num = readU16(is);
3207 for(int k=0; k<num; k++){
3208 s32 id = readS32(is);
3209 std::map<s32, ServerPlayingSound>::iterator i =
3210 m_playing_sounds.find(id);
3211 if(i == m_playing_sounds.end())
3213 ServerPlayingSound &psound = i->second;
3214 psound.clients.erase(peer_id);
3215 if(psound.clients.size() == 0)
3216 m_playing_sounds.erase(i++);
3219 else if(command == TOSERVER_NODEMETA_FIELDS)
3221 std::string datastring((char*)&data[2], datasize-2);
3222 std::istringstream is(datastring, std::ios_base::binary);
3224 v3s16 p = readV3S16(is);
3225 std::string formname = deSerializeString(is);
3226 int num = readU16(is);
3227 std::map<std::string, std::string> fields;
3228 for(int k=0; k<num; k++){
3229 std::string fieldname = deSerializeString(is);
3230 std::string fieldvalue = deSerializeLongString(is);
3231 fields[fieldname] = fieldvalue;
3234 // If something goes wrong, this player is to blame
3235 RollbackScopeActor rollback_scope(m_rollback,
3236 std::string("player:")+player->getName());
3238 // Check the target node for rollback data; leave others unnoticed
3239 RollbackNode rn_old(&m_env->getMap(), p, this);
3241 scriptapi_node_on_receive_fields(m_lua, p, formname, fields,
3244 // Report rollback data
3245 RollbackNode rn_new(&m_env->getMap(), p, this);
3246 if(rollback() && rn_new != rn_old){
3247 RollbackAction action;
3248 action.setSetNode(p, rn_old, rn_new);
3249 rollback()->reportAction(action);
3252 else if(command == TOSERVER_INVENTORY_FIELDS)
3254 std::string datastring((char*)&data[2], datasize-2);
3255 std::istringstream is(datastring, std::ios_base::binary);
3257 std::string formname = deSerializeString(is);
3258 int num = readU16(is);
3259 std::map<std::string, std::string> fields;
3260 for(int k=0; k<num; k++){
3261 std::string fieldname = deSerializeString(is);
3262 std::string fieldvalue = deSerializeLongString(is);
3263 fields[fieldname] = fieldvalue;
3266 scriptapi_on_player_receive_fields(m_lua, playersao, formname, fields);
3270 infostream<<"Server::ProcessData(): Ignoring "
3271 "unknown command "<<command<<std::endl;
3275 catch(SendFailedException &e)
3277 errorstream<<"Server::ProcessData(): SendFailedException: "
3283 void Server::onMapEditEvent(MapEditEvent *event)
3285 //infostream<<"Server::onMapEditEvent()"<<std::endl;
3286 if(m_ignore_map_edit_events)
3288 if(m_ignore_map_edit_events_area.contains(event->getArea()))
3290 MapEditEvent *e = event->clone();
3291 m_unsent_map_edit_queue.push_back(e);
3294 Inventory* Server::getInventory(const InventoryLocation &loc)
3297 case InventoryLocation::UNDEFINED:
3300 case InventoryLocation::CURRENT_PLAYER:
3303 case InventoryLocation::PLAYER:
3305 Player *player = m_env->getPlayer(loc.name.c_str());
3308 PlayerSAO *playersao = player->getPlayerSAO();
3311 return playersao->getInventory();
3314 case InventoryLocation::NODEMETA:
3316 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
3319 return meta->getInventory();
3322 case InventoryLocation::DETACHED:
3324 if(m_detached_inventories.count(loc.name) == 0)
3326 return m_detached_inventories[loc.name];
3334 void Server::setInventoryModified(const InventoryLocation &loc)
3337 case InventoryLocation::UNDEFINED:
3340 case InventoryLocation::PLAYER:
3342 Player *player = m_env->getPlayer(loc.name.c_str());
3345 PlayerSAO *playersao = player->getPlayerSAO();
3348 playersao->m_inventory_not_sent = true;
3349 playersao->m_wielded_item_not_sent = true;
3352 case InventoryLocation::NODEMETA:
3354 v3s16 blockpos = getNodeBlockPos(loc.p);
3356 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3358 block->raiseModified(MOD_STATE_WRITE_NEEDED);
3360 setBlockNotSent(blockpos);
3363 case InventoryLocation::DETACHED:
3365 sendDetachedInventoryToAll(loc.name);
3373 core::list<PlayerInfo> Server::getPlayerInfo()
3375 DSTACK(__FUNCTION_NAME);
3376 JMutexAutoLock envlock(m_env_mutex);
3377 JMutexAutoLock conlock(m_con_mutex);
3379 core::list<PlayerInfo> list;
3381 core::list<Player*> players = m_env->getPlayers();
3383 core::list<Player*>::Iterator i;
3384 for(i = players.begin();
3385 i != players.end(); i++)
3389 Player *player = *i;
3392 // Copy info from connection to info struct
3393 info.id = player->peer_id;
3394 info.address = m_con.GetPeerAddress(player->peer_id);
3395 info.avg_rtt = m_con.GetPeerAvgRTT(player->peer_id);
3397 catch(con::PeerNotFoundException &e)
3399 // Set dummy peer info
3401 info.address = Address(0,0,0,0,0);
3405 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3406 info.position = player->getPosition();
3408 list.push_back(info);
3415 void Server::peerAdded(con::Peer *peer)
3417 DSTACK(__FUNCTION_NAME);
3418 verbosestream<<"Server::peerAdded(): peer->id="
3419 <<peer->id<<std::endl;
3422 c.type = PEER_ADDED;
3423 c.peer_id = peer->id;
3425 m_peer_change_queue.push_back(c);
3428 void Server::deletingPeer(con::Peer *peer, bool timeout)
3430 DSTACK(__FUNCTION_NAME);
3431 verbosestream<<"Server::deletingPeer(): peer->id="
3432 <<peer->id<<", timeout="<<timeout<<std::endl;
3435 c.type = PEER_REMOVED;
3436 c.peer_id = peer->id;
3437 c.timeout = timeout;
3438 m_peer_change_queue.push_back(c);
3445 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3447 DSTACK(__FUNCTION_NAME);
3448 std::ostringstream os(std::ios_base::binary);
3450 writeU16(os, TOCLIENT_HP);
3454 std::string s = os.str();
3455 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3457 con.Send(peer_id, 0, data, true);
3460 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3461 const std::wstring &reason)
3463 DSTACK(__FUNCTION_NAME);
3464 std::ostringstream os(std::ios_base::binary);
3466 writeU16(os, TOCLIENT_ACCESS_DENIED);
3467 os<<serializeWideString(reason);
3470 std::string s = os.str();
3471 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3473 con.Send(peer_id, 0, data, true);
3476 void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
3477 bool set_camera_point_target, v3f camera_point_target)
3479 DSTACK(__FUNCTION_NAME);
3480 std::ostringstream os(std::ios_base::binary);
3482 writeU16(os, TOCLIENT_DEATHSCREEN);
3483 writeU8(os, set_camera_point_target);
3484 writeV3F1000(os, camera_point_target);
3487 std::string s = os.str();
3488 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3490 con.Send(peer_id, 0, data, true);
3493 void Server::SendItemDef(con::Connection &con, u16 peer_id,
3494 IItemDefManager *itemdef)
3496 DSTACK(__FUNCTION_NAME);
3497 std::ostringstream os(std::ios_base::binary);
3501 u32 length of the next item
3502 zlib-compressed serialized ItemDefManager
3504 writeU16(os, TOCLIENT_ITEMDEF);
3505 std::ostringstream tmp_os(std::ios::binary);
3506 itemdef->serialize(tmp_os);
3507 std::ostringstream tmp_os2(std::ios::binary);
3508 compressZlib(tmp_os.str(), tmp_os2);
3509 os<<serializeLongString(tmp_os2.str());
3512 std::string s = os.str();
3513 verbosestream<<"Server: Sending item definitions to id("<<peer_id
3514 <<"): size="<<s.size()<<std::endl;
3515 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3517 con.Send(peer_id, 0, data, true);
3520 void Server::SendNodeDef(con::Connection &con, u16 peer_id,
3521 INodeDefManager *nodedef)
3523 DSTACK(__FUNCTION_NAME);
3524 std::ostringstream os(std::ios_base::binary);
3528 u32 length of the next item
3529 zlib-compressed serialized NodeDefManager
3531 writeU16(os, TOCLIENT_NODEDEF);
3532 std::ostringstream tmp_os(std::ios::binary);
3533 nodedef->serialize(tmp_os);
3534 std::ostringstream tmp_os2(std::ios::binary);
3535 compressZlib(tmp_os.str(), tmp_os2);
3536 os<<serializeLongString(tmp_os2.str());
3539 std::string s = os.str();
3540 verbosestream<<"Server: Sending node definitions to id("<<peer_id
3541 <<"): size="<<s.size()<<std::endl;
3542 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3544 con.Send(peer_id, 0, data, true);
3548 Non-static send methods
3551 void Server::SendInventory(u16 peer_id)
3553 DSTACK(__FUNCTION_NAME);
3555 PlayerSAO *playersao = getPlayerSAO(peer_id);
3558 playersao->m_inventory_not_sent = false;
3564 std::ostringstream os;
3565 playersao->getInventory()->serialize(os);
3567 std::string s = os.str();
3569 SharedBuffer<u8> data(s.size()+2);
3570 writeU16(&data[0], TOCLIENT_INVENTORY);
3571 memcpy(&data[2], s.c_str(), s.size());
3574 m_con.Send(peer_id, 0, data, true);
3577 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3579 DSTACK(__FUNCTION_NAME);
3581 std::ostringstream os(std::ios_base::binary);
3585 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3586 os.write((char*)buf, 2);
3589 writeU16(buf, message.size());
3590 os.write((char*)buf, 2);
3593 for(u32 i=0; i<message.size(); i++)
3597 os.write((char*)buf, 2);
3601 std::string s = os.str();
3602 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3604 m_con.Send(peer_id, 0, data, true);
3607 void Server::BroadcastChatMessage(const std::wstring &message)
3609 for(core::map<u16, RemoteClient*>::Iterator
3610 i = m_clients.getIterator();
3611 i.atEnd() == false; i++)
3613 // Get client and check that it is valid
3614 RemoteClient *client = i.getNode()->getValue();
3615 assert(client->peer_id == i.getNode()->getKey());
3616 if(client->serialization_version == SER_FMT_VER_INVALID)
3619 SendChatMessage(client->peer_id, message);
3623 void Server::SendPlayerHP(u16 peer_id)
3625 DSTACK(__FUNCTION_NAME);
3626 PlayerSAO *playersao = getPlayerSAO(peer_id);
3628 playersao->m_hp_not_sent = false;
3629 SendHP(m_con, peer_id, playersao->getHP());
3632 void Server::SendMovePlayer(u16 peer_id)
3634 DSTACK(__FUNCTION_NAME);
3635 Player *player = m_env->getPlayer(peer_id);
3638 std::ostringstream os(std::ios_base::binary);
3639 writeU16(os, TOCLIENT_MOVE_PLAYER);
3640 writeV3F1000(os, player->getPosition());
3641 writeF1000(os, player->getPitch());
3642 writeF1000(os, player->getYaw());
3645 v3f pos = player->getPosition();
3646 f32 pitch = player->getPitch();
3647 f32 yaw = player->getYaw();
3648 verbosestream<<"Server: Sending TOCLIENT_MOVE_PLAYER"
3649 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3656 std::string s = os.str();
3657 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3659 m_con.Send(peer_id, 0, data, true);
3662 void Server::SendPlayerPrivileges(u16 peer_id)
3664 Player *player = m_env->getPlayer(peer_id);
3666 if(player->peer_id == PEER_ID_INEXISTENT)
3669 std::set<std::string> privs;
3670 scriptapi_get_auth(m_lua, player->getName(), NULL, &privs);
3672 std::ostringstream os(std::ios_base::binary);
3673 writeU16(os, TOCLIENT_PRIVILEGES);
3674 writeU16(os, privs.size());
3675 for(std::set<std::string>::const_iterator i = privs.begin();
3676 i != privs.end(); i++){
3677 os<<serializeString(*i);
3681 std::string s = os.str();
3682 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3684 m_con.Send(peer_id, 0, data, true);
3687 void Server::SendPlayerInventoryFormspec(u16 peer_id)
3689 Player *player = m_env->getPlayer(peer_id);
3691 if(player->peer_id == PEER_ID_INEXISTENT)
3694 std::ostringstream os(std::ios_base::binary);
3695 writeU16(os, TOCLIENT_INVENTORY_FORMSPEC);
3696 os<<serializeLongString(player->inventory_formspec);
3699 std::string s = os.str();
3700 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3702 m_con.Send(peer_id, 0, data, true);
3705 s32 Server::playSound(const SimpleSoundSpec &spec,
3706 const ServerSoundParams ¶ms)
3708 // Find out initial position of sound
3709 bool pos_exists = false;
3710 v3f pos = params.getPos(m_env, &pos_exists);
3711 // If position is not found while it should be, cancel sound
3712 if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
3714 // Filter destination clients
3715 std::set<RemoteClient*> dst_clients;
3716 if(params.to_player != "")
3718 Player *player = m_env->getPlayer(params.to_player.c_str());
3720 infostream<<"Server::playSound: Player \""<<params.to_player
3721 <<"\" not found"<<std::endl;
3724 if(player->peer_id == PEER_ID_INEXISTENT){
3725 infostream<<"Server::playSound: Player \""<<params.to_player
3726 <<"\" not connected"<<std::endl;
3729 RemoteClient *client = getClient(player->peer_id);
3730 dst_clients.insert(client);
3734 for(core::map<u16, RemoteClient*>::Iterator
3735 i = m_clients.getIterator(); i.atEnd() == false; i++)
3737 RemoteClient *client = i.getNode()->getValue();
3738 Player *player = m_env->getPlayer(client->peer_id);
3742 if(player->getPosition().getDistanceFrom(pos) >
3743 params.max_hear_distance)
3746 dst_clients.insert(client);
3749 if(dst_clients.size() == 0)
3752 s32 id = m_next_sound_id++;
3753 // The sound will exist as a reference in m_playing_sounds
3754 m_playing_sounds[id] = ServerPlayingSound();
3755 ServerPlayingSound &psound = m_playing_sounds[id];
3756 psound.params = params;
3757 for(std::set<RemoteClient*>::iterator i = dst_clients.begin();
3758 i != dst_clients.end(); i++)
3759 psound.clients.insert((*i)->peer_id);
3761 std::ostringstream os(std::ios_base::binary);
3762 writeU16(os, TOCLIENT_PLAY_SOUND);
3764 os<<serializeString(spec.name);
3765 writeF1000(os, spec.gain * params.gain);
3766 writeU8(os, params.type);
3767 writeV3F1000(os, pos);
3768 writeU16(os, params.object);
3769 writeU8(os, params.loop);
3771 std::string s = os.str();
3772 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3774 for(std::set<RemoteClient*>::iterator i = dst_clients.begin();
3775 i != dst_clients.end(); i++){
3777 m_con.Send((*i)->peer_id, 0, data, true);
3781 void Server::stopSound(s32 handle)
3783 // Get sound reference
3784 std::map<s32, ServerPlayingSound>::iterator i =
3785 m_playing_sounds.find(handle);
3786 if(i == m_playing_sounds.end())
3788 ServerPlayingSound &psound = i->second;
3790 std::ostringstream os(std::ios_base::binary);
3791 writeU16(os, TOCLIENT_STOP_SOUND);
3792 writeS32(os, handle);
3794 std::string s = os.str();
3795 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3797 for(std::set<u16>::iterator i = psound.clients.begin();
3798 i != psound.clients.end(); i++){
3800 m_con.Send(*i, 0, data, true);
3802 // Remove sound reference
3803 m_playing_sounds.erase(i);
3806 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3807 core::list<u16> *far_players, float far_d_nodes)
3809 float maxd = far_d_nodes*BS;
3810 v3f p_f = intToFloat(p, BS);
3814 SharedBuffer<u8> reply(replysize);
3815 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3816 writeS16(&reply[2], p.X);
3817 writeS16(&reply[4], p.Y);
3818 writeS16(&reply[6], p.Z);
3820 for(core::map<u16, RemoteClient*>::Iterator
3821 i = m_clients.getIterator();
3822 i.atEnd() == false; i++)
3824 // Get client and check that it is valid
3825 RemoteClient *client = i.getNode()->getValue();
3826 assert(client->peer_id == i.getNode()->getKey());
3827 if(client->serialization_version == SER_FMT_VER_INVALID)
3830 // Don't send if it's the same one
3831 if(client->peer_id == ignore_id)
3837 Player *player = m_env->getPlayer(client->peer_id);
3840 // If player is far away, only set modified blocks not sent
3841 v3f player_pos = player->getPosition();
3842 if(player_pos.getDistanceFrom(p_f) > maxd)
3844 far_players->push_back(client->peer_id);
3851 m_con.Send(client->peer_id, 0, reply, true);
3855 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3856 core::list<u16> *far_players, float far_d_nodes)
3858 float maxd = far_d_nodes*BS;
3859 v3f p_f = intToFloat(p, BS);
3861 for(core::map<u16, RemoteClient*>::Iterator
3862 i = m_clients.getIterator();
3863 i.atEnd() == false; i++)
3865 // Get client and check that it is valid
3866 RemoteClient *client = i.getNode()->getValue();
3867 assert(client->peer_id == i.getNode()->getKey());
3868 if(client->serialization_version == SER_FMT_VER_INVALID)
3871 // Don't send if it's the same one
3872 if(client->peer_id == ignore_id)
3878 Player *player = m_env->getPlayer(client->peer_id);
3881 // If player is far away, only set modified blocks not sent
3882 v3f player_pos = player->getPosition();
3883 if(player_pos.getDistanceFrom(p_f) > maxd)
3885 far_players->push_back(client->peer_id);
3892 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3893 SharedBuffer<u8> reply(replysize);
3894 writeU16(&reply[0], TOCLIENT_ADDNODE);
3895 writeS16(&reply[2], p.X);
3896 writeS16(&reply[4], p.Y);
3897 writeS16(&reply[6], p.Z);
3898 n.serialize(&reply[8], client->serialization_version);
3901 m_con.Send(client->peer_id, 0, reply, true);
3905 void Server::setBlockNotSent(v3s16 p)
3907 for(core::map<u16, RemoteClient*>::Iterator
3908 i = m_clients.getIterator();
3909 i.atEnd()==false; i++)
3911 RemoteClient *client = i.getNode()->getValue();
3912 client->SetBlockNotSent(p);
3916 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3918 DSTACK(__FUNCTION_NAME);
3920 v3s16 p = block->getPos();
3924 bool completely_air = true;
3925 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3926 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3927 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3929 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
3931 completely_air = false;
3932 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
3937 infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
3939 infostream<<"[completely air] ";
3940 infostream<<std::endl;
3944 Create a packet with the block in the right format
3947 std::ostringstream os(std::ios_base::binary);
3948 block->serialize(os, ver, false);
3949 std::string s = os.str();
3950 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3952 u32 replysize = 8 + blockdata.getSize();
3953 SharedBuffer<u8> reply(replysize);
3954 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3955 writeS16(&reply[2], p.X);
3956 writeS16(&reply[4], p.Y);
3957 writeS16(&reply[6], p.Z);
3958 memcpy(&reply[8], *blockdata, blockdata.getSize());
3960 /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3961 <<": \tpacket size: "<<replysize<<std::endl;*/
3966 m_con.Send(peer_id, 1, reply, true);
3969 void Server::SendBlocks(float dtime)
3971 DSTACK(__FUNCTION_NAME);
3973 JMutexAutoLock envlock(m_env_mutex);
3974 JMutexAutoLock conlock(m_con_mutex);
3976 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
3978 core::array<PrioritySortedBlockTransfer> queue;
3980 s32 total_sending = 0;
3983 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
3985 for(core::map<u16, RemoteClient*>::Iterator
3986 i = m_clients.getIterator();
3987 i.atEnd() == false; i++)
3989 RemoteClient *client = i.getNode()->getValue();
3990 assert(client->peer_id == i.getNode()->getKey());
3992 // If definitions and textures have not been sent, don't
3993 // send MapBlocks either
3994 if(!client->definitions_sent)
3997 total_sending += client->SendingCount();
3999 if(client->serialization_version == SER_FMT_VER_INVALID)
4002 client->GetNextBlocks(this, dtime, queue);
4007 // Lowest priority number comes first.
4008 // Lowest is most important.
4011 for(u32 i=0; i<queue.size(); i++)
4013 //TODO: Calculate limit dynamically
4014 if(total_sending >= g_settings->getS32
4015 ("max_simultaneous_block_sends_server_total"))
4018 PrioritySortedBlockTransfer q = queue[i];
4020 MapBlock *block = NULL;
4023 block = m_env->getMap().getBlockNoCreate(q.pos);
4025 catch(InvalidPositionException &e)
4030 RemoteClient *client = getClient(q.peer_id);
4032 SendBlockNoLock(q.peer_id, block, client->serialization_version);
4034 client->SentBlock(q.pos);
4040 void Server::fillMediaCache()
4042 DSTACK(__FUNCTION_NAME);
4044 infostream<<"Server: Calculating media file checksums"<<std::endl;
4046 // Collect all media file paths
4047 std::list<std::string> paths;
4048 for(core::list<ModSpec>::Iterator i = m_mods.begin();
4049 i != m_mods.end(); i++){
4050 const ModSpec &mod = *i;
4051 paths.push_back(mod.path + DIR_DELIM + "textures");
4052 paths.push_back(mod.path + DIR_DELIM + "sounds");
4053 paths.push_back(mod.path + DIR_DELIM + "media");
4055 std::string path_all = "textures";
4056 paths.push_back(path_all + DIR_DELIM + "all");
4058 // Collect media file information from paths into cache
4059 for(std::list<std::string>::iterator i = paths.begin();
4060 i != paths.end(); i++)
4062 std::string mediapath = *i;
4063 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
4064 for(u32 j=0; j<dirlist.size(); j++){
4065 if(dirlist[j].dir) // Ignode dirs
4067 std::string filename = dirlist[j].name;
4068 // If name contains illegal characters, ignore the file
4069 if(!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)){
4070 infostream<<"Server: ignoring illegal file name: \""
4071 <<filename<<"\""<<std::endl;
4074 // If name is not in a supported format, ignore it
4075 const char *supported_ext[] = {
4076 ".png", ".jpg", ".bmp", ".tga",
4077 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
4081 if(removeStringEnd(filename, supported_ext) == ""){
4082 infostream<<"Server: ignoring unsupported file extension: \""
4083 <<filename<<"\""<<std::endl;
4086 // Ok, attempt to load the file and add to cache
4087 std::string filepath = mediapath + DIR_DELIM + filename;
4089 std::ifstream fis(filepath.c_str(), std::ios_base::binary);
4090 if(fis.good() == false){
4091 errorstream<<"Server::fillMediaCache(): Could not open \""
4092 <<filename<<"\" for reading"<<std::endl;
4095 std::ostringstream tmp_os(std::ios_base::binary);
4099 fis.read(buf, 1024);
4100 std::streamsize len = fis.gcount();
4101 tmp_os.write(buf, len);
4110 errorstream<<"Server::fillMediaCache(): Failed to read \""
4111 <<filename<<"\""<<std::endl;
4114 if(tmp_os.str().length() == 0){
4115 errorstream<<"Server::fillMediaCache(): Empty file \""
4116 <<filepath<<"\""<<std::endl;
4121 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
4123 unsigned char *digest = sha1.getDigest();
4124 std::string sha1_base64 = base64_encode(digest, 20);
4125 std::string sha1_hex = hex_encode((char*)digest, 20);
4129 this->m_media[filename] = MediaInfo(filepath, sha1_base64);
4130 verbosestream<<"Server: "<<sha1_hex<<" is "<<filename<<std::endl;
4135 struct SendableMediaAnnouncement
4138 std::string sha1_digest;
4140 SendableMediaAnnouncement(const std::string name_="",
4141 const std::string sha1_digest_=""):
4143 sha1_digest(sha1_digest_)
4147 void Server::sendMediaAnnouncement(u16 peer_id)
4149 DSTACK(__FUNCTION_NAME);
4151 verbosestream<<"Server: Announcing files to id("<<peer_id<<")"
4154 core::list<SendableMediaAnnouncement> file_announcements;
4156 for(std::map<std::string, MediaInfo>::iterator i = m_media.begin();
4157 i != m_media.end(); i++){
4159 file_announcements.push_back(
4160 SendableMediaAnnouncement(i->first, i->second.sha1_digest));
4164 std::ostringstream os(std::ios_base::binary);
4172 u16 length of sha1_digest
4177 writeU16(os, TOCLIENT_ANNOUNCE_MEDIA);
4178 writeU16(os, file_announcements.size());
4180 for(core::list<SendableMediaAnnouncement>::Iterator
4181 j = file_announcements.begin();
4182 j != file_announcements.end(); j++){
4183 os<<serializeString(j->name);
4184 os<<serializeString(j->sha1_digest);
4188 std::string s = os.str();
4189 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4192 m_con.Send(peer_id, 0, data, true);
4196 struct SendableMedia
4202 SendableMedia(const std::string &name_="", const std::string path_="",
4203 const std::string &data_=""):
4210 void Server::sendRequestedMedia(u16 peer_id,
4211 const core::list<MediaRequest> &tosend)
4213 DSTACK(__FUNCTION_NAME);
4215 verbosestream<<"Server::sendRequestedMedia(): "
4216 <<"Sending files to client"<<std::endl;
4220 // Put 5kB in one bunch (this is not accurate)
4221 u32 bytes_per_bunch = 5000;
4223 core::array< core::list<SendableMedia> > file_bunches;
4224 file_bunches.push_back(core::list<SendableMedia>());
4226 u32 file_size_bunch_total = 0;
4228 for(core::list<MediaRequest>::ConstIterator i = tosend.begin();
4229 i != tosend.end(); i++)
4231 if(m_media.find(i->name) == m_media.end()){
4232 errorstream<<"Server::sendRequestedMedia(): Client asked for "
4233 <<"unknown file \""<<(i->name)<<"\""<<std::endl;
4237 //TODO get path + name
4238 std::string tpath = m_media[(*i).name].path;
4241 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
4242 if(fis.good() == false){
4243 errorstream<<"Server::sendRequestedMedia(): Could not open \""
4244 <<tpath<<"\" for reading"<<std::endl;
4247 std::ostringstream tmp_os(std::ios_base::binary);
4251 fis.read(buf, 1024);
4252 std::streamsize len = fis.gcount();
4253 tmp_os.write(buf, len);
4254 file_size_bunch_total += len;
4263 errorstream<<"Server::sendRequestedMedia(): Failed to read \""
4264 <<(*i).name<<"\""<<std::endl;
4267 /*infostream<<"Server::sendRequestedMedia(): Loaded \""
4268 <<tname<<"\""<<std::endl;*/
4270 file_bunches[file_bunches.size()-1].push_back(
4271 SendableMedia((*i).name, tpath, tmp_os.str()));
4273 // Start next bunch if got enough data
4274 if(file_size_bunch_total >= bytes_per_bunch){
4275 file_bunches.push_back(core::list<SendableMedia>());
4276 file_size_bunch_total = 0;
4281 /* Create and send packets */
4283 u32 num_bunches = file_bunches.size();
4284 for(u32 i=0; i<num_bunches; i++)
4286 std::ostringstream os(std::ios_base::binary);
4290 u16 total number of texture bunches
4291 u16 index of this bunch
4292 u32 number of files in this bunch
4301 writeU16(os, TOCLIENT_MEDIA);
4302 writeU16(os, num_bunches);
4304 writeU32(os, file_bunches[i].size());
4306 for(core::list<SendableMedia>::Iterator
4307 j = file_bunches[i].begin();
4308 j != file_bunches[i].end(); j++){
4309 os<<serializeString(j->name);
4310 os<<serializeLongString(j->data);
4314 std::string s = os.str();
4315 verbosestream<<"Server::sendRequestedMedia(): bunch "
4316 <<i<<"/"<<num_bunches
4317 <<" files="<<file_bunches[i].size()
4318 <<" size=" <<s.size()<<std::endl;
4319 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4321 m_con.Send(peer_id, 0, data, true);
4325 void Server::sendDetachedInventory(const std::string &name, u16 peer_id)
4327 if(m_detached_inventories.count(name) == 0){
4328 errorstream<<__FUNCTION_NAME<<": \""<<name<<"\" not found"<<std::endl;
4331 Inventory *inv = m_detached_inventories[name];
4333 std::ostringstream os(std::ios_base::binary);
4334 writeU16(os, TOCLIENT_DETACHED_INVENTORY);
4335 os<<serializeString(name);
4339 std::string s = os.str();
4340 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4342 m_con.Send(peer_id, 0, data, true);
4345 void Server::sendDetachedInventoryToAll(const std::string &name)
4347 DSTACK(__FUNCTION_NAME);
4349 for(core::map<u16, RemoteClient*>::Iterator
4350 i = m_clients.getIterator();
4351 i.atEnd() == false; i++){
4352 RemoteClient *client = i.getNode()->getValue();
4353 sendDetachedInventory(name, client->peer_id);
4357 void Server::sendDetachedInventories(u16 peer_id)
4359 DSTACK(__FUNCTION_NAME);
4361 for(std::map<std::string, Inventory*>::iterator
4362 i = m_detached_inventories.begin();
4363 i != m_detached_inventories.end(); i++){
4364 const std::string &name = i->first;
4365 //Inventory *inv = i->second;
4366 sendDetachedInventory(name, peer_id);
4374 void Server::DiePlayer(u16 peer_id)
4376 DSTACK(__FUNCTION_NAME);
4378 PlayerSAO *playersao = getPlayerSAO(peer_id);
4381 infostream<<"Server::DiePlayer(): Player "
4382 <<playersao->getPlayer()->getName()
4383 <<" dies"<<std::endl;
4385 playersao->setHP(0);
4387 // Trigger scripted stuff
4388 scriptapi_on_dieplayer(m_lua, playersao);
4390 SendPlayerHP(peer_id);
4391 SendDeathscreen(m_con, peer_id, false, v3f(0,0,0));
4394 void Server::RespawnPlayer(u16 peer_id)
4396 DSTACK(__FUNCTION_NAME);
4398 PlayerSAO *playersao = getPlayerSAO(peer_id);
4401 infostream<<"Server::RespawnPlayer(): Player "
4402 <<playersao->getPlayer()->getName()
4403 <<" respawns"<<std::endl;
4405 playersao->setHP(PLAYER_MAX_HP);
4407 bool repositioned = scriptapi_on_respawnplayer(m_lua, playersao);
4409 v3f pos = findSpawnPos(m_env->getServerMap());
4410 playersao->setPos(pos);
4414 void Server::UpdateCrafting(u16 peer_id)
4416 DSTACK(__FUNCTION_NAME);
4418 Player* player = m_env->getPlayer(peer_id);
4421 // Get a preview for crafting
4423 getCraftingResult(&player->inventory, preview, false, this);
4425 // Put the new preview in
4426 InventoryList *plist = player->inventory.getList("craftpreview");
4428 assert(plist->getSize() >= 1);
4429 plist->changeItem(0, preview);
4432 RemoteClient* Server::getClient(u16 peer_id)
4434 DSTACK(__FUNCTION_NAME);
4435 //JMutexAutoLock lock(m_con_mutex);
4436 core::map<u16, RemoteClient*>::Node *n;
4437 n = m_clients.find(peer_id);
4438 // A client should exist for all peers
4440 return n->getValue();
4443 std::wstring Server::getStatusString()
4445 std::wostringstream os(std::ios_base::binary);
4448 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4450 os<<L", uptime="<<m_uptime.get();
4451 // Information about clients
4453 for(core::map<u16, RemoteClient*>::Iterator
4454 i = m_clients.getIterator();
4455 i.atEnd() == false; i++)
4457 // Get client and check that it is valid
4458 RemoteClient *client = i.getNode()->getValue();
4459 assert(client->peer_id == i.getNode()->getKey());
4460 if(client->serialization_version == SER_FMT_VER_INVALID)
4463 Player *player = m_env->getPlayer(client->peer_id);
4464 // Get name of player
4465 std::wstring name = L"unknown";
4467 name = narrow_to_wide(player->getName());
4468 // Add name to information string
4472 if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
4473 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4474 if(g_settings->get("motd") != "")
4475 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4479 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
4481 std::set<std::string> privs;
4482 scriptapi_get_auth(m_lua, name, NULL, &privs);
4486 bool Server::checkPriv(const std::string &name, const std::string &priv)
4488 std::set<std::string> privs = getPlayerEffectivePrivs(name);
4489 return (privs.count(priv) != 0);
4492 void Server::reportPrivsModified(const std::string &name)
4495 for(core::map<u16, RemoteClient*>::Iterator
4496 i = m_clients.getIterator();
4497 i.atEnd() == false; i++){
4498 RemoteClient *client = i.getNode()->getValue();
4499 Player *player = m_env->getPlayer(client->peer_id);
4500 reportPrivsModified(player->getName());
4503 Player *player = m_env->getPlayer(name.c_str());
4506 SendPlayerPrivileges(player->peer_id);
4507 PlayerSAO *sao = player->getPlayerSAO();
4510 sao->updatePrivileges(
4511 getPlayerEffectivePrivs(name),
4516 void Server::reportInventoryFormspecModified(const std::string &name)
4518 Player *player = m_env->getPlayer(name.c_str());
4521 SendPlayerInventoryFormspec(player->peer_id);
4524 // Saves g_settings to configpath given at initialization
4525 void Server::saveConfig()
4527 if(m_path_config != "")
4528 g_settings->updateConfigFile(m_path_config.c_str());
4531 void Server::notifyPlayer(const char *name, const std::wstring msg)
4533 Player *player = m_env->getPlayer(name);
4536 SendChatMessage(player->peer_id, std::wstring(L"Server: -!- ")+msg);
4539 void Server::notifyPlayers(const std::wstring msg)
4541 BroadcastChatMessage(msg);
4544 void Server::queueBlockEmerge(v3s16 blockpos, bool allow_generate)
4548 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
4549 m_emerge_queue.addBlock(PEER_ID_INEXISTENT, blockpos, flags);
4552 Inventory* Server::createDetachedInventory(const std::string &name)
4554 if(m_detached_inventories.count(name) > 0){
4555 infostream<<"Server clearing detached inventory \""<<name<<"\""<<std::endl;
4556 delete m_detached_inventories[name];
4558 infostream<<"Server creating detached inventory \""<<name<<"\""<<std::endl;
4560 Inventory *inv = new Inventory(m_itemdef);
4562 m_detached_inventories[name] = inv;
4563 sendDetachedInventoryToAll(name);
4570 BoolScopeSet(bool *dst, bool val):
4573 m_orig_state = *m_dst;
4578 *m_dst = m_orig_state;
4585 // actions: time-reversed list
4586 // Return value: success/failure
4587 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
4588 std::list<std::string> *log)
4590 infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
4591 ServerMap *map = (ServerMap*)(&m_env->getMap());
4592 // Disable rollback report sink while reverting
4593 BoolScopeSet rollback_scope_disable(&m_rollback_sink_enabled, false);
4595 // Fail if no actions to handle
4596 if(actions.empty()){
4597 log->push_back("Nothing to do.");
4604 for(std::list<RollbackAction>::const_iterator
4605 i = actions.begin();
4606 i != actions.end(); i++)
4608 const RollbackAction &action = *i;
4610 bool success = action.applyRevert(map, this, this);
4613 std::ostringstream os;
4614 os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
4615 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
4617 log->push_back(os.str());
4619 std::ostringstream os;
4620 os<<"Succesfully reverted step ("<<num_tried<<") "<<action.toString();
4621 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
4623 log->push_back(os.str());
4627 infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
4628 <<" failed"<<std::endl;
4630 // Call it done if less than half failed
4631 return num_failed <= num_tried/2;
4634 // IGameDef interface
4636 IItemDefManager* Server::getItemDefManager()
4640 INodeDefManager* Server::getNodeDefManager()
4644 ICraftDefManager* Server::getCraftDefManager()
4648 ITextureSource* Server::getTextureSource()
4652 u16 Server::allocateUnknownNodeId(const std::string &name)
4654 return m_nodedef->allocateDummy(name);
4656 ISoundManager* Server::getSoundManager()
4658 return &dummySoundManager;
4660 MtEventManager* Server::getEventManager()
4664 IRollbackReportSink* Server::getRollbackReportSink()
4666 if(!m_enable_rollback_recording)
4668 if(!m_rollback_sink_enabled)
4673 IWritableItemDefManager* Server::getWritableItemDefManager()
4677 IWritableNodeDefManager* Server::getWritableNodeDefManager()
4681 IWritableCraftDefManager* Server::getWritableCraftDefManager()
4686 const ModSpec* Server::getModSpec(const std::string &modname)
4688 for(core::list<ModSpec>::Iterator i = m_mods.begin();
4689 i != m_mods.end(); i++){
4690 const ModSpec &mod = *i;
4691 if(mod.name == modname)
4696 void Server::getModNames(core::list<std::string> &modlist)
4698 for(core::list<ModSpec>::Iterator i = m_mods.begin(); i != m_mods.end(); i++)
4700 modlist.push_back((*i).name);
4703 std::string Server::getBuiltinLuaPath()
4705 return porting::path_share + DIR_DELIM + "builtin";
4708 v3f findSpawnPos(ServerMap &map)
4710 //return v3f(50,50,50)*BS;
4715 nodepos = v2s16(0,0);
4720 // Try to find a good place a few times
4721 for(s32 i=0; i<1000; i++)
4724 // We're going to try to throw the player to this position
4725 v2s16 nodepos2d = v2s16(-range + (myrand()%(range*2)),
4726 -range + (myrand()%(range*2)));
4727 //v2s16 sectorpos = getNodeSectorPos(nodepos2d);
4728 // Get ground height at point (fallbacks to heightmap function)
4729 s16 groundheight = map.findGroundLevel(nodepos2d);
4730 // Don't go underwater
4731 if(groundheight < WATER_LEVEL)
4733 //infostream<<"-> Underwater"<<std::endl;
4736 // Don't go to high places
4737 if(groundheight > WATER_LEVEL + 4)
4739 //infostream<<"-> Underwater"<<std::endl;
4743 nodepos = v3s16(nodepos2d.X, groundheight-2, nodepos2d.Y);
4744 bool is_good = false;
4746 for(s32 i=0; i<10; i++){
4747 v3s16 blockpos = getNodeBlockPos(nodepos);
4748 map.emergeBlock(blockpos, true);
4749 MapNode n = map.getNodeNoEx(nodepos);
4750 if(n.getContent() == CONTENT_AIR){
4761 // Found a good place
4762 //infostream<<"Searched through "<<i<<" places."<<std::endl;
4768 return intToFloat(nodepos, BS);
4771 PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id)
4773 RemotePlayer *player = NULL;
4774 bool newplayer = false;
4777 Try to get an existing player
4779 player = static_cast<RemotePlayer*>(m_env->getPlayer(name));
4781 // If player is already connected, cancel
4782 if(player != NULL && player->peer_id != 0)
4784 infostream<<"emergePlayer(): Player already connected"<<std::endl;
4789 If player with the wanted peer_id already exists, cancel.
4791 if(m_env->getPlayer(peer_id) != NULL)
4793 infostream<<"emergePlayer(): Player with wrong name but same"
4794 " peer_id already exists"<<std::endl;
4799 Create a new player if it doesn't exist yet
4804 player = new RemotePlayer(this);
4805 player->updateName(name);
4807 /* Set player position */
4808 infostream<<"Server: Finding spawn place for player \""
4809 <<name<<"\""<<std::endl;
4810 v3f pos = findSpawnPos(m_env->getServerMap());
4811 player->setPosition(pos);
4813 /* Add player to environment */
4814 m_env->addPlayer(player);
4818 Create a new player active object
4820 PlayerSAO *playersao = new PlayerSAO(m_env, player, peer_id,
4821 getPlayerEffectivePrivs(player->getName()),
4824 /* Add object to environment */
4825 m_env->addActiveObject(playersao);
4829 scriptapi_on_newplayer(m_lua, playersao);
4831 scriptapi_on_joinplayer(m_lua, playersao);
4836 void Server::handlePeerChange(PeerChange &c)
4838 JMutexAutoLock envlock(m_env_mutex);
4839 JMutexAutoLock conlock(m_con_mutex);
4841 if(c.type == PEER_ADDED)
4848 core::map<u16, RemoteClient*>::Node *n;
4849 n = m_clients.find(c.peer_id);
4850 // The client shouldn't already exist
4854 RemoteClient *client = new RemoteClient();
4855 client->peer_id = c.peer_id;
4856 m_clients.insert(client->peer_id, client);
4859 else if(c.type == PEER_REMOVED)
4866 core::map<u16, RemoteClient*>::Node *n;
4867 n = m_clients.find(c.peer_id);
4868 // The client should exist
4872 Mark objects to be not known by the client
4874 RemoteClient *client = n->getValue();
4876 for(core::map<u16, bool>::Iterator
4877 i = client->m_known_objects.getIterator();
4878 i.atEnd()==false; i++)
4881 u16 id = i.getNode()->getKey();
4882 ServerActiveObject* obj = m_env->getActiveObject(id);
4884 if(obj && obj->m_known_by_count > 0)
4885 obj->m_known_by_count--;
4889 Clear references to playing sounds
4891 for(std::map<s32, ServerPlayingSound>::iterator
4892 i = m_playing_sounds.begin();
4893 i != m_playing_sounds.end();)
4895 ServerPlayingSound &psound = i->second;
4896 psound.clients.erase(c.peer_id);
4897 if(psound.clients.size() == 0)
4898 m_playing_sounds.erase(i++);
4903 Player *player = m_env->getPlayer(c.peer_id);
4905 // Collect information about leaving in chat
4906 std::wstring message;
4910 std::wstring name = narrow_to_wide(player->getName());
4913 message += L" left the game.";
4915 message += L" (timed out)";
4919 /* Run scripts and remove from environment */
4923 PlayerSAO *playersao = player->getPlayerSAO();
4926 scriptapi_on_leaveplayer(m_lua, playersao);
4928 playersao->disconnected();
4938 std::ostringstream os(std::ios_base::binary);
4939 for(core::map<u16, RemoteClient*>::Iterator
4940 i = m_clients.getIterator();
4941 i.atEnd() == false; i++)
4943 RemoteClient *client = i.getNode()->getValue();
4944 assert(client->peer_id == i.getNode()->getKey());
4945 if(client->serialization_version == SER_FMT_VER_INVALID)
4948 Player *player = m_env->getPlayer(client->peer_id);
4951 // Get name of player
4952 os<<player->getName()<<" ";
4955 actionstream<<player->getName()<<" "
4956 <<(c.timeout?"times out.":"leaves game.")
4957 <<" List of players: "
4958 <<os.str()<<std::endl;
4963 delete m_clients[c.peer_id];
4964 m_clients.remove(c.peer_id);
4966 // Send player info to all remaining clients
4967 //SendPlayerInfos();
4969 // Send leave chat message to all remaining clients
4970 if(message.length() != 0)
4971 BroadcastChatMessage(message);
4980 void Server::handlePeerChanges()
4982 while(m_peer_change_queue.size() > 0)
4984 PeerChange c = m_peer_change_queue.pop_front();
4986 verbosestream<<"Server: Handling peer change: "
4987 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4990 handlePeerChange(c);
4994 void dedicated_server_loop(Server &server, bool &kill)
4996 DSTACK(__FUNCTION_NAME);
4998 verbosestream<<"dedicated_server_loop()"<<std::endl;
5000 IntervalLimiter m_profiler_interval;
5004 float steplen = g_settings->getFloat("dedicated_server_step");
5005 // This is kind of a hack but can be done like this
5006 // because server.step() is very light
5008 ScopeProfiler sp(g_profiler, "dedicated server sleep");
5009 sleep_ms((int)(steplen*1000.0));
5011 server.step(steplen);
5013 if(server.getShutdownRequested() || kill)
5015 infostream<<"Dedicated server quitting"<<std::endl;
5022 float profiler_print_interval =
5023 g_settings->getFloat("profiler_print_interval");
5024 if(profiler_print_interval != 0)
5026 if(m_profiler_interval.step(steplen, profiler_print_interval))
5028 infostream<<"Profiler:"<<std::endl;
5029 g_profiler->print(infostream);
5030 g_profiler->clear();