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),
941 m_itemdef(createItemDefManager()),
942 m_nodedef(createNodeDefManager()),
943 m_craftdef(createCraftDefManager()),
944 m_event(new EventManager()),
946 m_emergethread(this),
947 m_time_of_day_send_timer(0),
949 m_shutdown_requested(false),
950 m_ignore_map_edit_events(false),
951 m_ignore_map_edit_events_peer_id(0)
953 m_liquid_transform_timer = 0.0;
954 m_print_info_timer = 0.0;
955 m_objectdata_timer = 0.0;
956 m_emergethread_trigger_timer = 0.0;
957 m_savemap_timer = 0.0;
961 m_step_dtime_mutex.Init();
965 throw ServerError("Supplied empty world path");
967 if(!gamespec.isValid())
968 throw ServerError("Supplied invalid gamespec");
970 infostream<<"Server created for gameid \""<<m_gamespec.id<<"\"";
971 if(m_simple_singleplayer_mode)
972 infostream<<" in simple singleplayer mode"<<std::endl;
974 infostream<<std::endl;
975 infostream<<"- world: "<<m_path_world<<std::endl;
976 infostream<<"- config: "<<m_path_config<<std::endl;
977 infostream<<"- game: "<<m_gamespec.path<<std::endl;
979 // Create rollback manager
980 std::string rollback_path = m_path_world+DIR_DELIM+"rollback.txt";
981 m_rollback = createRollbackManager(rollback_path, this);
983 // Add world mod search path
984 m_modspaths.push_front(m_path_world + DIR_DELIM + "worldmods");
985 // Add addon mod search path
986 for(std::set<std::string>::const_iterator i = m_gamespec.mods_paths.begin();
987 i != m_gamespec.mods_paths.end(); i++)
988 m_modspaths.push_front((*i));
990 // Print out mod search paths
991 for(core::list<std::string>::Iterator i = m_modspaths.begin();
992 i != m_modspaths.end(); i++){
993 std::string modspath = *i;
994 infostream<<"- mods: "<<modspath<<std::endl;
997 // Path to builtin.lua
998 std::string builtinpath = getBuiltinLuaPath() + DIR_DELIM + "builtin.lua";
1000 // Create world if it doesn't exist
1001 if(!initializeWorld(m_path_world, m_gamespec.id))
1002 throw ServerError("Failed to initialize world");
1005 JMutexAutoLock envlock(m_env_mutex);
1006 JMutexAutoLock conlock(m_con_mutex);
1008 // Initialize scripting
1010 infostream<<"Server: Initializing Lua"<<std::endl;
1011 m_lua = script_init();
1014 scriptapi_export(m_lua, this);
1015 // Load and run builtin.lua
1016 infostream<<"Server: Loading builtin.lua [\""
1017 <<builtinpath<<"\"]"<<std::endl;
1018 bool success = scriptapi_loadmod(m_lua, builtinpath, "__builtin");
1020 errorstream<<"Server: Failed to load and run "
1021 <<builtinpath<<std::endl;
1022 throw ModError("Failed to load and run "+builtinpath);
1024 // Find mods in mod search paths
1025 m_mods = getMods(m_modspaths);
1027 infostream<<"Server: Loading mods: ";
1028 for(core::list<ModSpec>::Iterator i = m_mods.begin();
1029 i != m_mods.end(); i++){
1030 const ModSpec &mod = *i;
1031 infostream<<mod.name<<" ";
1033 infostream<<std::endl;
1034 // Load and run "mod" scripts
1035 for(core::list<ModSpec>::Iterator i = m_mods.begin();
1036 i != m_mods.end(); i++){
1037 const ModSpec &mod = *i;
1038 std::string scriptpath = mod.path + DIR_DELIM + "init.lua";
1039 infostream<<" ["<<padStringRight(mod.name, 12)<<"] [\""
1040 <<scriptpath<<"\"]"<<std::endl;
1041 bool success = scriptapi_loadmod(m_lua, scriptpath, mod.name);
1043 errorstream<<"Server: Failed to load and run "
1044 <<scriptpath<<std::endl;
1045 throw ModError("Failed to load and run "+scriptpath);
1049 // Read Textures and calculate sha1 sums
1052 // Apply item aliases in the node definition manager
1053 m_nodedef->updateAliases(m_itemdef);
1055 // Initialize Environment
1057 m_env = new ServerEnvironment(new ServerMap(path_world, this), m_lua,
1060 // Give environment reference to scripting api
1061 scriptapi_add_environment(m_lua, m_env);
1063 // Register us to receive map edit events
1064 m_env->getMap().addEventReceiver(this);
1066 // If file exists, load environment metadata
1067 if(fs::PathExists(m_path_world+DIR_DELIM+"env_meta.txt"))
1069 infostream<<"Server: Loading environment metadata"<<std::endl;
1070 m_env->loadMeta(m_path_world);
1074 infostream<<"Server: Loading players"<<std::endl;
1075 m_env->deSerializePlayers(m_path_world);
1078 Add some test ActiveBlockModifiers to environment
1080 add_legacy_abms(m_env, m_nodedef);
1085 infostream<<"Server destructing"<<std::endl;
1088 Send shutdown message
1091 JMutexAutoLock conlock(m_con_mutex);
1093 std::wstring line = L"*** Server shutting down";
1096 Send the message to clients
1098 for(core::map<u16, RemoteClient*>::Iterator
1099 i = m_clients.getIterator();
1100 i.atEnd() == false; i++)
1102 // Get client and check that it is valid
1103 RemoteClient *client = i.getNode()->getValue();
1104 assert(client->peer_id == i.getNode()->getKey());
1105 if(client->serialization_version == SER_FMT_VER_INVALID)
1109 SendChatMessage(client->peer_id, line);
1111 catch(con::PeerNotFoundException &e)
1117 JMutexAutoLock envlock(m_env_mutex);
1122 infostream<<"Server: Saving players"<<std::endl;
1123 m_env->serializePlayers(m_path_world);
1126 Save environment metadata
1128 infostream<<"Server: Saving environment metadata"<<std::endl;
1129 m_env->saveMeta(m_path_world);
1141 JMutexAutoLock clientslock(m_con_mutex);
1143 for(core::map<u16, RemoteClient*>::Iterator
1144 i = m_clients.getIterator();
1145 i.atEnd() == false; i++)
1148 // NOTE: These are removed by env destructor
1150 u16 peer_id = i.getNode()->getKey();
1151 JMutexAutoLock envlock(m_env_mutex);
1152 m_env->removePlayer(peer_id);
1156 delete i.getNode()->getValue();
1160 // Delete things in the reverse order of creation
1168 // Deinitialize scripting
1169 infostream<<"Server: Deinitializing scripting"<<std::endl;
1170 script_deinit(m_lua);
1172 // Delete detached inventories
1174 for(std::map<std::string, Inventory*>::iterator
1175 i = m_detached_inventories.begin();
1176 i != m_detached_inventories.end(); i++){
1182 void Server::start(unsigned short port)
1184 DSTACK(__FUNCTION_NAME);
1185 infostream<<"Starting server on port "<<port<<"..."<<std::endl;
1187 // Stop thread if already running
1190 // Initialize connection
1191 m_con.SetTimeoutMs(30);
1195 m_thread.setRun(true);
1198 // ASCII art for the win!
1200 <<" .__ __ __ "<<std::endl
1201 <<" _____ |__| ____ _____/ |_ ____ _______/ |_ "<<std::endl
1202 <<" / \\| |/ \\_/ __ \\ __\\/ __ \\ / ___/\\ __\\"<<std::endl
1203 <<"| Y Y \\ | | \\ ___/| | \\ ___/ \\___ \\ | | "<<std::endl
1204 <<"|__|_| /__|___| /\\___ >__| \\___ >____ > |__| "<<std::endl
1205 <<" \\/ \\/ \\/ \\/ \\/ "<<std::endl;
1206 actionstream<<"World at ["<<m_path_world<<"]"<<std::endl;
1207 actionstream<<"Server for gameid=\""<<m_gamespec.id
1208 <<"\" listening on port "<<port<<"."<<std::endl;
1213 DSTACK(__FUNCTION_NAME);
1215 infostream<<"Server: Stopping and waiting threads"<<std::endl;
1217 // Stop threads (set run=false first so both start stopping)
1218 m_thread.setRun(false);
1219 m_emergethread.setRun(false);
1221 m_emergethread.stop();
1223 infostream<<"Server: Threads stopped"<<std::endl;
1226 void Server::step(float dtime)
1228 DSTACK(__FUNCTION_NAME);
1233 JMutexAutoLock lock(m_step_dtime_mutex);
1234 m_step_dtime += dtime;
1236 // Throw if fatal error occurred in thread
1237 std::string async_err = m_async_fatal_error.get();
1238 if(async_err != ""){
1239 throw ServerError(async_err);
1243 void Server::AsyncRunStep()
1245 DSTACK(__FUNCTION_NAME);
1247 g_profiler->add("Server::AsyncRunStep (num)", 1);
1251 JMutexAutoLock lock1(m_step_dtime_mutex);
1252 dtime = m_step_dtime;
1256 // Send blocks to clients
1263 g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
1265 //infostream<<"Server steps "<<dtime<<std::endl;
1266 //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1269 JMutexAutoLock lock1(m_step_dtime_mutex);
1270 m_step_dtime -= dtime;
1277 m_uptime.set(m_uptime.get() + dtime);
1281 // Process connection's timeouts
1282 JMutexAutoLock lock2(m_con_mutex);
1283 ScopeProfiler sp(g_profiler, "Server: connection timeout processing");
1284 m_con.RunTimeouts(dtime);
1288 // This has to be called so that the client list gets synced
1289 // with the peer list of the connection
1290 handlePeerChanges();
1294 Update time of day and overall game time
1297 JMutexAutoLock envlock(m_env_mutex);
1299 m_env->setTimeOfDaySpeed(g_settings->getFloat("time_speed"));
1302 Send to clients at constant intervals
1305 m_time_of_day_send_timer -= dtime;
1306 if(m_time_of_day_send_timer < 0.0)
1308 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
1310 //JMutexAutoLock envlock(m_env_mutex);
1311 JMutexAutoLock conlock(m_con_mutex);
1313 for(core::map<u16, RemoteClient*>::Iterator
1314 i = m_clients.getIterator();
1315 i.atEnd() == false; i++)
1317 RemoteClient *client = i.getNode()->getValue();
1318 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1319 m_env->getTimeOfDay(), g_settings->getFloat("time_speed"));
1321 m_con.Send(client->peer_id, 0, data, true);
1327 JMutexAutoLock lock(m_env_mutex);
1329 ScopeProfiler sp(g_profiler, "SEnv step");
1330 ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
1334 const float map_timer_and_unload_dtime = 2.92;
1335 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
1337 JMutexAutoLock lock(m_env_mutex);
1338 // Run Map's timers and unload unused data
1339 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
1340 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
1341 g_settings->getFloat("server_unload_unused_data_timeout"));
1352 JMutexAutoLock lock(m_env_mutex);
1353 JMutexAutoLock lock2(m_con_mutex);
1355 ScopeProfiler sp(g_profiler, "Server: handle players");
1357 for(core::map<u16, RemoteClient*>::Iterator
1358 i = m_clients.getIterator();
1359 i.atEnd() == false; i++)
1361 RemoteClient *client = i.getNode()->getValue();
1362 PlayerSAO *playersao = getPlayerSAO(client->peer_id);
1363 if(playersao == NULL)
1367 Handle player HPs (die if hp=0)
1369 if(playersao->getHP() == 0 && playersao->m_hp_not_sent)
1370 DiePlayer(client->peer_id);
1373 Send player inventories and HPs if necessary
1375 if(playersao->m_teleported){
1376 SendMovePlayer(client->peer_id);
1377 playersao->m_teleported = false;
1379 if(playersao->m_inventory_not_sent){
1380 UpdateCrafting(client->peer_id);
1381 SendInventory(client->peer_id);
1383 if(playersao->m_hp_not_sent){
1384 SendPlayerHP(client->peer_id);
1389 /* Transform liquids */
1390 m_liquid_transform_timer += dtime;
1391 if(m_liquid_transform_timer >= 1.00)
1393 m_liquid_transform_timer -= 1.00;
1395 JMutexAutoLock lock(m_env_mutex);
1397 ScopeProfiler sp(g_profiler, "Server: liquid transform");
1399 core::map<v3s16, MapBlock*> modified_blocks;
1400 m_env->getMap().transformLiquids(modified_blocks);
1405 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1406 ServerMap &map = ((ServerMap&)m_env->getMap());
1407 map.updateLighting(modified_blocks, lighting_modified_blocks);
1409 // Add blocks modified by lighting to modified_blocks
1410 for(core::map<v3s16, MapBlock*>::Iterator
1411 i = lighting_modified_blocks.getIterator();
1412 i.atEnd() == false; i++)
1414 MapBlock *block = i.getNode()->getValue();
1415 modified_blocks.insert(block->getPos(), block);
1419 Set the modified blocks unsent for all the clients
1422 JMutexAutoLock lock2(m_con_mutex);
1424 for(core::map<u16, RemoteClient*>::Iterator
1425 i = m_clients.getIterator();
1426 i.atEnd() == false; i++)
1428 RemoteClient *client = i.getNode()->getValue();
1430 if(modified_blocks.size() > 0)
1432 // Remove block from sent history
1433 client->SetBlocksNotSent(modified_blocks);
1438 // Periodically print some info
1440 float &counter = m_print_info_timer;
1446 JMutexAutoLock lock2(m_con_mutex);
1448 if(m_clients.size() != 0)
1449 infostream<<"Players:"<<std::endl;
1450 for(core::map<u16, RemoteClient*>::Iterator
1451 i = m_clients.getIterator();
1452 i.atEnd() == false; i++)
1454 //u16 peer_id = i.getNode()->getKey();
1455 RemoteClient *client = i.getNode()->getValue();
1456 Player *player = m_env->getPlayer(client->peer_id);
1459 infostream<<"* "<<player->getName()<<"\t";
1460 client->PrintInfo(infostream);
1465 //if(g_settings->getBool("enable_experimental"))
1469 Check added and deleted active objects
1472 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
1473 JMutexAutoLock envlock(m_env_mutex);
1474 JMutexAutoLock conlock(m_con_mutex);
1476 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
1478 // Radius inside which objects are active
1479 s16 radius = g_settings->getS16("active_object_send_range_blocks");
1480 radius *= MAP_BLOCKSIZE;
1482 for(core::map<u16, RemoteClient*>::Iterator
1483 i = m_clients.getIterator();
1484 i.atEnd() == false; i++)
1486 RemoteClient *client = i.getNode()->getValue();
1488 // If definitions and textures have not been sent, don't
1489 // send objects either
1490 if(!client->definitions_sent)
1493 Player *player = m_env->getPlayer(client->peer_id);
1496 // This can happen if the client timeouts somehow
1497 /*infostream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
1499 <<" has no associated player"<<std::endl;*/
1502 v3s16 pos = floatToInt(player->getPosition(), BS);
1504 core::map<u16, bool> removed_objects;
1505 core::map<u16, bool> added_objects;
1506 m_env->getRemovedActiveObjects(pos, radius,
1507 client->m_known_objects, removed_objects);
1508 m_env->getAddedActiveObjects(pos, radius,
1509 client->m_known_objects, added_objects);
1511 // Ignore if nothing happened
1512 if(removed_objects.size() == 0 && added_objects.size() == 0)
1514 //infostream<<"active objects: none changed"<<std::endl;
1518 std::string data_buffer;
1522 // Handle removed objects
1523 writeU16((u8*)buf, removed_objects.size());
1524 data_buffer.append(buf, 2);
1525 for(core::map<u16, bool>::Iterator
1526 i = removed_objects.getIterator();
1527 i.atEnd()==false; i++)
1530 u16 id = i.getNode()->getKey();
1531 ServerActiveObject* obj = m_env->getActiveObject(id);
1533 // Add to data buffer for sending
1534 writeU16((u8*)buf, i.getNode()->getKey());
1535 data_buffer.append(buf, 2);
1537 // Remove from known objects
1538 client->m_known_objects.remove(i.getNode()->getKey());
1540 if(obj && obj->m_known_by_count > 0)
1541 obj->m_known_by_count--;
1544 // Handle added objects
1545 writeU16((u8*)buf, added_objects.size());
1546 data_buffer.append(buf, 2);
1547 for(core::map<u16, bool>::Iterator
1548 i = added_objects.getIterator();
1549 i.atEnd()==false; i++)
1552 u16 id = i.getNode()->getKey();
1553 ServerActiveObject* obj = m_env->getActiveObject(id);
1556 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1558 infostream<<"WARNING: "<<__FUNCTION_NAME
1559 <<": NULL object"<<std::endl;
1561 type = obj->getSendType();
1563 // Add to data buffer for sending
1564 writeU16((u8*)buf, id);
1565 data_buffer.append(buf, 2);
1566 writeU8((u8*)buf, type);
1567 data_buffer.append(buf, 1);
1570 data_buffer.append(serializeLongString(
1571 obj->getClientInitializationData()));
1573 data_buffer.append(serializeLongString(""));
1575 // Add to known objects
1576 client->m_known_objects.insert(i.getNode()->getKey(), false);
1579 obj->m_known_by_count++;
1583 SharedBuffer<u8> reply(2 + data_buffer.size());
1584 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1585 memcpy((char*)&reply[2], data_buffer.c_str(),
1586 data_buffer.size());
1588 m_con.Send(client->peer_id, 0, reply, true);
1590 verbosestream<<"Server: Sent object remove/add: "
1591 <<removed_objects.size()<<" removed, "
1592 <<added_objects.size()<<" added, "
1593 <<"packet size is "<<reply.getSize()<<std::endl;
1598 Collect a list of all the objects known by the clients
1599 and report it back to the environment.
1602 core::map<u16, bool> all_known_objects;
1604 for(core::map<u16, RemoteClient*>::Iterator
1605 i = m_clients.getIterator();
1606 i.atEnd() == false; i++)
1608 RemoteClient *client = i.getNode()->getValue();
1609 // Go through all known objects of client
1610 for(core::map<u16, bool>::Iterator
1611 i = client->m_known_objects.getIterator();
1612 i.atEnd()==false; i++)
1614 u16 id = i.getNode()->getKey();
1615 all_known_objects[id] = true;
1619 m_env->setKnownActiveObjects(whatever);
1625 Send object messages
1628 JMutexAutoLock envlock(m_env_mutex);
1629 JMutexAutoLock conlock(m_con_mutex);
1631 ScopeProfiler sp(g_profiler, "Server: sending object messages");
1634 // Value = data sent by object
1635 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1637 // Get active object messages from environment
1640 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
1644 core::list<ActiveObjectMessage>* message_list = NULL;
1645 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1646 n = buffered_messages.find(aom.id);
1649 message_list = new core::list<ActiveObjectMessage>;
1650 buffered_messages.insert(aom.id, message_list);
1654 message_list = n->getValue();
1656 message_list->push_back(aom);
1659 // Route data to every client
1660 for(core::map<u16, RemoteClient*>::Iterator
1661 i = m_clients.getIterator();
1662 i.atEnd()==false; i++)
1664 RemoteClient *client = i.getNode()->getValue();
1665 std::string reliable_data;
1666 std::string unreliable_data;
1667 // Go through all objects in message buffer
1668 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1669 j = buffered_messages.getIterator();
1670 j.atEnd()==false; j++)
1672 // If object is not known by client, skip it
1673 u16 id = j.getNode()->getKey();
1674 if(client->m_known_objects.find(id) == NULL)
1676 // Get message list of object
1677 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1678 // Go through every message
1679 for(core::list<ActiveObjectMessage>::Iterator
1680 k = list->begin(); k != list->end(); k++)
1682 // Compose the full new data with header
1683 ActiveObjectMessage aom = *k;
1684 std::string new_data;
1687 writeU16((u8*)&buf[0], aom.id);
1688 new_data.append(buf, 2);
1690 new_data += serializeString(aom.datastring);
1691 // Add data to buffer
1693 reliable_data += new_data;
1695 unreliable_data += new_data;
1699 reliable_data and unreliable_data are now ready.
1702 if(reliable_data.size() > 0)
1704 SharedBuffer<u8> reply(2 + reliable_data.size());
1705 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1706 memcpy((char*)&reply[2], reliable_data.c_str(),
1707 reliable_data.size());
1709 m_con.Send(client->peer_id, 0, reply, true);
1711 if(unreliable_data.size() > 0)
1713 SharedBuffer<u8> reply(2 + unreliable_data.size());
1714 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1715 memcpy((char*)&reply[2], unreliable_data.c_str(),
1716 unreliable_data.size());
1717 // Send as unreliable
1718 m_con.Send(client->peer_id, 0, reply, false);
1721 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1723 infostream<<"Server: Size of object message data: "
1724 <<"reliable: "<<reliable_data.size()
1725 <<", unreliable: "<<unreliable_data.size()
1730 // Clear buffered_messages
1731 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1732 i = buffered_messages.getIterator();
1733 i.atEnd()==false; i++)
1735 delete i.getNode()->getValue();
1739 } // enable_experimental
1742 Send queued-for-sending map edit events.
1745 // We will be accessing the environment and the connection
1746 JMutexAutoLock lock(m_env_mutex);
1747 JMutexAutoLock conlock(m_con_mutex);
1749 // Don't send too many at a time
1752 // Single change sending is disabled if queue size is not small
1753 bool disable_single_change_sending = false;
1754 if(m_unsent_map_edit_queue.size() >= 4)
1755 disable_single_change_sending = true;
1757 int event_count = m_unsent_map_edit_queue.size();
1759 // We'll log the amount of each
1762 while(m_unsent_map_edit_queue.size() != 0)
1764 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1766 // Players far away from the change are stored here.
1767 // Instead of sending the changes, MapBlocks are set not sent
1769 core::list<u16> far_players;
1771 if(event->type == MEET_ADDNODE)
1773 //infostream<<"Server: MEET_ADDNODE"<<std::endl;
1774 prof.add("MEET_ADDNODE", 1);
1775 if(disable_single_change_sending)
1776 sendAddNode(event->p, event->n, event->already_known_by_peer,
1779 sendAddNode(event->p, event->n, event->already_known_by_peer,
1782 else if(event->type == MEET_REMOVENODE)
1784 //infostream<<"Server: MEET_REMOVENODE"<<std::endl;
1785 prof.add("MEET_REMOVENODE", 1);
1786 if(disable_single_change_sending)
1787 sendRemoveNode(event->p, event->already_known_by_peer,
1790 sendRemoveNode(event->p, event->already_known_by_peer,
1793 else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
1795 infostream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
1796 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
1797 setBlockNotSent(event->p);
1799 else if(event->type == MEET_OTHER)
1801 infostream<<"Server: MEET_OTHER"<<std::endl;
1802 prof.add("MEET_OTHER", 1);
1803 for(core::map<v3s16, bool>::Iterator
1804 i = event->modified_blocks.getIterator();
1805 i.atEnd()==false; i++)
1807 v3s16 p = i.getNode()->getKey();
1813 prof.add("unknown", 1);
1814 infostream<<"WARNING: Server: Unknown MapEditEvent "
1815 <<((u32)event->type)<<std::endl;
1819 Set blocks not sent to far players
1821 if(far_players.size() > 0)
1823 // Convert list format to that wanted by SetBlocksNotSent
1824 core::map<v3s16, MapBlock*> modified_blocks2;
1825 for(core::map<v3s16, bool>::Iterator
1826 i = event->modified_blocks.getIterator();
1827 i.atEnd()==false; i++)
1829 v3s16 p = i.getNode()->getKey();
1830 modified_blocks2.insert(p,
1831 m_env->getMap().getBlockNoCreateNoEx(p));
1833 // Set blocks not sent
1834 for(core::list<u16>::Iterator
1835 i = far_players.begin();
1836 i != far_players.end(); i++)
1839 RemoteClient *client = getClient(peer_id);
1842 client->SetBlocksNotSent(modified_blocks2);
1848 /*// Don't send too many at a time
1850 if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
1854 if(event_count >= 5){
1855 infostream<<"Server: MapEditEvents:"<<std::endl;
1856 prof.print(infostream);
1857 } else if(event_count != 0){
1858 verbosestream<<"Server: MapEditEvents:"<<std::endl;
1859 prof.print(verbosestream);
1865 Trigger emergethread (it somehow gets to a non-triggered but
1866 bysy state sometimes)
1869 float &counter = m_emergethread_trigger_timer;
1875 m_emergethread.trigger();
1879 // Save map, players and auth stuff
1881 float &counter = m_savemap_timer;
1883 if(counter >= g_settings->getFloat("server_map_save_interval"))
1886 JMutexAutoLock lock(m_env_mutex);
1888 ScopeProfiler sp(g_profiler, "Server: saving stuff");
1891 if(m_banmanager.isModified())
1892 m_banmanager.save();
1894 // Save changed parts of map
1895 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
1898 m_env->serializePlayers(m_path_world);
1900 // Save environment metadata
1901 m_env->saveMeta(m_path_world);
1906 void Server::Receive()
1908 DSTACK(__FUNCTION_NAME);
1909 SharedBuffer<u8> data;
1914 JMutexAutoLock conlock(m_con_mutex);
1915 datasize = m_con.Receive(peer_id, data);
1918 // This has to be called so that the client list gets synced
1919 // with the peer list of the connection
1920 handlePeerChanges();
1922 ProcessData(*data, datasize, peer_id);
1924 catch(con::InvalidIncomingDataException &e)
1926 infostream<<"Server::Receive(): "
1927 "InvalidIncomingDataException: what()="
1928 <<e.what()<<std::endl;
1930 catch(con::PeerNotFoundException &e)
1932 //NOTE: This is not needed anymore
1934 // The peer has been disconnected.
1935 // Find the associated player and remove it.
1937 /*JMutexAutoLock envlock(m_env_mutex);
1939 infostream<<"ServerThread: peer_id="<<peer_id
1940 <<" has apparently closed connection. "
1941 <<"Removing player."<<std::endl;
1943 m_env->removePlayer(peer_id);*/
1947 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1949 DSTACK(__FUNCTION_NAME);
1950 // Environment is locked first.
1951 JMutexAutoLock envlock(m_env_mutex);
1952 JMutexAutoLock conlock(m_con_mutex);
1954 ScopeProfiler sp(g_profiler, "Server::ProcessData");
1957 Address address = m_con.GetPeerAddress(peer_id);
1958 std::string addr_s = address.serializeString();
1960 // drop player if is ip is banned
1961 if(m_banmanager.isIpBanned(addr_s)){
1962 infostream<<"Server: A banned client tried to connect from "
1963 <<addr_s<<"; banned name was "
1964 <<m_banmanager.getBanName(addr_s)<<std::endl;
1965 // This actually doesn't seem to transfer to the client
1966 SendAccessDenied(m_con, peer_id,
1967 L"Your ip is banned. Banned name was "
1968 +narrow_to_wide(m_banmanager.getBanName(addr_s)));
1969 m_con.DeletePeer(peer_id);
1973 catch(con::PeerNotFoundException &e)
1975 infostream<<"Server::ProcessData(): Cancelling: peer "
1976 <<peer_id<<" not found"<<std::endl;
1980 std::string addr_s = m_con.GetPeerAddress(peer_id).serializeString();
1982 u8 peer_ser_ver = getClient(peer_id)->serialization_version;
1990 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1992 if(command == TOSERVER_INIT)
1994 // [0] u16 TOSERVER_INIT
1995 // [2] u8 SER_FMT_VER_HIGHEST
1996 // [3] u8[20] player_name
1997 // [23] u8[28] password <--- can be sent without this, from old versions
1999 if(datasize < 2+1+PLAYERNAME_SIZE)
2002 verbosestream<<"Server: Got TOSERVER_INIT from "
2003 <<peer_id<<std::endl;
2005 // First byte after command is maximum supported
2006 // serialization version
2007 u8 client_max = data[2];
2008 u8 our_max = SER_FMT_VER_HIGHEST;
2009 // Use the highest version supported by both
2010 u8 deployed = core::min_(client_max, our_max);
2011 // If it's lower than the lowest supported, give up.
2012 if(deployed < SER_FMT_VER_LOWEST)
2013 deployed = SER_FMT_VER_INVALID;
2015 //peer->serialization_version = deployed;
2016 getClient(peer_id)->pending_serialization_version = deployed;
2018 if(deployed == SER_FMT_VER_INVALID)
2020 actionstream<<"Server: A mismatched client tried to connect from "
2021 <<addr_s<<std::endl;
2022 infostream<<"Server: Cannot negotiate "
2023 "serialization version with peer "
2024 <<peer_id<<std::endl;
2025 SendAccessDenied(m_con, peer_id, std::wstring(
2026 L"Your client's version is not supported.\n"
2027 L"Server version is ")
2028 + narrow_to_wide(VERSION_STRING) + L"."
2034 Read and check network protocol version
2037 u16 net_proto_version = 0;
2038 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
2040 net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
2043 getClient(peer_id)->net_proto_version = net_proto_version;
2045 if(net_proto_version == 0)
2047 actionstream<<"Server: An old tried to connect from "<<addr_s
2049 SendAccessDenied(m_con, peer_id, std::wstring(
2050 L"Your client's version is not supported.\n"
2051 L"Server version is ")
2052 + narrow_to_wide(VERSION_STRING) + L"."
2057 if(g_settings->getBool("strict_protocol_version_checking"))
2059 if(net_proto_version != PROTOCOL_VERSION)
2061 actionstream<<"Server: A mismatched client tried to connect"
2062 <<" from "<<addr_s<<std::endl;
2063 SendAccessDenied(m_con, peer_id, std::wstring(
2064 L"Your client's version is not supported.\n"
2065 L"Server version is ")
2066 + narrow_to_wide(VERSION_STRING) + L",\n"
2067 + L"server's PROTOCOL_VERSION is "
2068 + narrow_to_wide(itos(PROTOCOL_VERSION))
2069 + L", client's PROTOCOL_VERSION is "
2070 + narrow_to_wide(itos(net_proto_version))
2081 char playername[PLAYERNAME_SIZE];
2082 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
2084 playername[i] = data[3+i];
2086 playername[PLAYERNAME_SIZE-1] = 0;
2088 if(playername[0]=='\0')
2090 actionstream<<"Server: Player with an empty name "
2091 <<"tried to connect from "<<addr_s<<std::endl;
2092 SendAccessDenied(m_con, peer_id,
2097 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
2099 actionstream<<"Server: Player with an invalid name "
2100 <<"tried to connect from "<<addr_s<<std::endl;
2101 SendAccessDenied(m_con, peer_id,
2102 L"Name contains unallowed characters");
2106 infostream<<"Server: New connection: \""<<playername<<"\" from "
2107 <<m_con.GetPeerAddress(peer_id).serializeString()<<std::endl;
2110 char given_password[PASSWORD_SIZE];
2111 if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
2113 // old version - assume blank password
2114 given_password[0] = 0;
2118 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2120 given_password[i] = data[23+i];
2122 given_password[PASSWORD_SIZE-1] = 0;
2125 if(!base64_is_valid(given_password)){
2126 infostream<<"Server: "<<playername
2127 <<" supplied invalid password hash"<<std::endl;
2128 SendAccessDenied(m_con, peer_id, L"Invalid password hash");
2132 std::string checkpwd; // Password hash to check against
2133 bool has_auth = scriptapi_get_auth(m_lua, playername, &checkpwd, NULL);
2135 // If no authentication info exists for user, create it
2137 if(!isSingleplayer() &&
2138 g_settings->getBool("disallow_empty_password") &&
2139 std::string(given_password) == ""){
2140 SendAccessDenied(m_con, peer_id, L"Empty passwords are "
2141 L"disallowed. Set a password and try again.");
2144 std::wstring raw_default_password =
2145 narrow_to_wide(g_settings->get("default_password"));
2146 std::string initial_password =
2147 translatePassword(playername, raw_default_password);
2149 // If default_password is empty, allow any initial password
2150 if (raw_default_password.length() == 0)
2151 initial_password = given_password;
2153 scriptapi_create_auth(m_lua, playername, initial_password);
2156 has_auth = scriptapi_get_auth(m_lua, playername, &checkpwd, NULL);
2159 SendAccessDenied(m_con, peer_id, L"Not allowed to login");
2163 if(given_password != checkpwd){
2164 infostream<<"Server: peer_id="<<peer_id
2165 <<": supplied invalid password for "
2166 <<playername<<std::endl;
2167 SendAccessDenied(m_con, peer_id, L"Invalid password");
2171 // Do not allow multiple players in simple singleplayer mode.
2172 // This isn't a perfect way to do it, but will suffice for now.
2173 if(m_simple_singleplayer_mode && m_clients.size() > 1){
2174 infostream<<"Server: Not allowing another client to connect in"
2175 <<" simple singleplayer mode"<<std::endl;
2176 SendAccessDenied(m_con, peer_id,
2177 L"Running in simple singleplayer mode.");
2181 // Enforce user limit.
2182 // Don't enforce for users that have some admin right
2183 if(m_clients.size() >= g_settings->getU16("max_users") &&
2184 !checkPriv(playername, "server") &&
2185 !checkPriv(playername, "ban") &&
2186 !checkPriv(playername, "privs") &&
2187 !checkPriv(playername, "password") &&
2188 playername != g_settings->get("name"))
2190 actionstream<<"Server: "<<playername<<" tried to join, but there"
2191 <<" are already max_users="
2192 <<g_settings->getU16("max_users")<<" players."<<std::endl;
2193 SendAccessDenied(m_con, peer_id, L"Too many users.");
2198 PlayerSAO *playersao = emergePlayer(playername, peer_id);
2200 // If failed, cancel
2201 if(playersao == NULL)
2203 errorstream<<"Server: peer_id="<<peer_id
2204 <<": failed to emerge player"<<std::endl;
2209 Answer with a TOCLIENT_INIT
2212 SharedBuffer<u8> reply(2+1+6+8);
2213 writeU16(&reply[0], TOCLIENT_INIT);
2214 writeU8(&reply[2], deployed);
2215 writeV3S16(&reply[2+1], floatToInt(playersao->getPlayer()->getPosition()+v3f(0,BS/2,0), BS));
2216 writeU64(&reply[2+1+6], m_env->getServerMap().getSeed());
2219 m_con.Send(peer_id, 0, reply, true);
2223 Send complete position information
2225 SendMovePlayer(peer_id);
2230 if(command == TOSERVER_INIT2)
2232 verbosestream<<"Server: Got TOSERVER_INIT2 from "
2233 <<peer_id<<std::endl;
2235 Player *player = m_env->getPlayer(peer_id);
2237 verbosestream<<"Server: TOSERVER_INIT2: "
2238 <<"Player not found; ignoring."<<std::endl;
2242 getClient(peer_id)->serialization_version
2243 = getClient(peer_id)->pending_serialization_version;
2246 Send some initialization data
2249 infostream<<"Server: Sending content to "
2250 <<getPlayerName(peer_id)<<std::endl;
2252 // Send item definitions
2253 SendItemDef(m_con, peer_id, m_itemdef);
2255 // Send node definitions
2256 SendNodeDef(m_con, peer_id, m_nodedef);
2258 // Send media announcement
2259 sendMediaAnnouncement(peer_id);
2262 SendPlayerPrivileges(peer_id);
2264 // Send inventory formspec
2265 SendPlayerInventoryFormspec(peer_id);
2268 UpdateCrafting(peer_id);
2269 SendInventory(peer_id);
2272 SendPlayerHP(peer_id);
2274 // Send detached inventories
2275 sendDetachedInventories(peer_id);
2277 // Show death screen if necessary
2279 SendDeathscreen(m_con, peer_id, false, v3f(0,0,0));
2283 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2284 m_env->getTimeOfDay(), g_settings->getFloat("time_speed"));
2285 m_con.Send(peer_id, 0, data, true);
2288 // Note things in chat if not in simple singleplayer mode
2289 if(!m_simple_singleplayer_mode)
2291 // Send information about server to player in chat
2292 SendChatMessage(peer_id, getStatusString());
2294 // Send information about joining in chat
2296 std::wstring name = L"unknown";
2297 Player *player = m_env->getPlayer(peer_id);
2299 name = narrow_to_wide(player->getName());
2301 std::wstring message;
2304 message += L" joined the game.";
2305 BroadcastChatMessage(message);
2309 // Warnings about protocol version can be issued here
2310 if(getClient(peer_id)->net_proto_version < PROTOCOL_VERSION)
2312 SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT IS OLD AND MAY WORK PROPERLY WITH THIS SERVER!");
2319 std::ostringstream os(std::ios_base::binary);
2320 for(core::map<u16, RemoteClient*>::Iterator
2321 i = m_clients.getIterator();
2322 i.atEnd() == false; i++)
2324 RemoteClient *client = i.getNode()->getValue();
2325 assert(client->peer_id == i.getNode()->getKey());
2326 if(client->serialization_version == SER_FMT_VER_INVALID)
2329 Player *player = m_env->getPlayer(client->peer_id);
2332 // Get name of player
2333 os<<player->getName()<<" ";
2336 actionstream<<player->getName()<<" joins game. List of players: "
2337 <<os.str()<<std::endl;
2343 if(peer_ser_ver == SER_FMT_VER_INVALID)
2345 infostream<<"Server::ProcessData(): Cancelling: Peer"
2346 " serialization format invalid or not initialized."
2347 " Skipping incoming command="<<command<<std::endl;
2351 Player *player = m_env->getPlayer(peer_id);
2353 infostream<<"Server::ProcessData(): Cancelling: "
2354 "No player for peer_id="<<peer_id
2359 PlayerSAO *playersao = player->getPlayerSAO();
2360 if(playersao == NULL){
2361 infostream<<"Server::ProcessData(): Cancelling: "
2362 "No player object for peer_id="<<peer_id
2367 if(command == TOSERVER_PLAYERPOS)
2369 if(datasize < 2+12+12+4+4)
2373 v3s32 ps = readV3S32(&data[start+2]);
2374 v3s32 ss = readV3S32(&data[start+2+12]);
2375 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2376 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2377 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2378 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2379 pitch = wrapDegrees(pitch);
2380 yaw = wrapDegrees(yaw);
2382 player->setPosition(position);
2383 player->setSpeed(speed);
2384 player->setPitch(pitch);
2385 player->setYaw(yaw);
2387 /*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2388 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2389 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2391 else if(command == TOSERVER_GOTBLOCKS)
2404 u16 count = data[2];
2405 for(u16 i=0; i<count; i++)
2407 if((s16)datasize < 2+1+(i+1)*6)
2408 throw con::InvalidIncomingDataException
2409 ("GOTBLOCKS length is too short");
2410 v3s16 p = readV3S16(&data[2+1+i*6]);
2411 /*infostream<<"Server: GOTBLOCKS ("
2412 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2413 RemoteClient *client = getClient(peer_id);
2414 client->GotBlock(p);
2417 else if(command == TOSERVER_DELETEDBLOCKS)
2430 u16 count = data[2];
2431 for(u16 i=0; i<count; i++)
2433 if((s16)datasize < 2+1+(i+1)*6)
2434 throw con::InvalidIncomingDataException
2435 ("DELETEDBLOCKS length is too short");
2436 v3s16 p = readV3S16(&data[2+1+i*6]);
2437 /*infostream<<"Server: DELETEDBLOCKS ("
2438 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2439 RemoteClient *client = getClient(peer_id);
2440 client->SetBlockNotSent(p);
2443 else if(command == TOSERVER_CLICK_OBJECT)
2445 infostream<<"Server: CLICK_OBJECT not supported anymore"<<std::endl;
2448 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2450 infostream<<"Server: CLICK_ACTIVEOBJECT not supported anymore"<<std::endl;
2453 else if(command == TOSERVER_GROUND_ACTION)
2455 infostream<<"Server: GROUND_ACTION not supported anymore"<<std::endl;
2459 else if(command == TOSERVER_RELEASE)
2461 infostream<<"Server: RELEASE not supported anymore"<<std::endl;
2464 else if(command == TOSERVER_SIGNTEXT)
2466 infostream<<"Server: SIGNTEXT not supported anymore"
2470 else if(command == TOSERVER_SIGNNODETEXT)
2472 infostream<<"Server: SIGNNODETEXT not supported anymore"
2476 else if(command == TOSERVER_INVENTORY_ACTION)
2478 // Strip command and create a stream
2479 std::string datastring((char*)&data[2], datasize-2);
2480 verbosestream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2481 std::istringstream is(datastring, std::ios_base::binary);
2483 InventoryAction *a = InventoryAction::deSerialize(is);
2486 infostream<<"TOSERVER_INVENTORY_ACTION: "
2487 <<"InventoryAction::deSerialize() returned NULL"
2492 // If something goes wrong, this player is to blame
2493 RollbackScopeActor rollback_scope(m_rollback,
2494 std::string("player:")+player->getName());
2497 Note: Always set inventory not sent, to repair cases
2498 where the client made a bad prediction.
2502 Handle restrictions and special cases of the move action
2504 if(a->getType() == IACTION_MOVE)
2506 IMoveAction *ma = (IMoveAction*)a;
2508 ma->from_inv.applyCurrentPlayer(player->getName());
2509 ma->to_inv.applyCurrentPlayer(player->getName());
2511 setInventoryModified(ma->from_inv);
2512 setInventoryModified(ma->to_inv);
2514 bool from_inv_is_current_player =
2515 (ma->from_inv.type == InventoryLocation::PLAYER) &&
2516 (ma->from_inv.name == player->getName());
2518 bool to_inv_is_current_player =
2519 (ma->to_inv.type == InventoryLocation::PLAYER) &&
2520 (ma->to_inv.name == player->getName());
2523 Disable moving items out of craftpreview
2525 if(ma->from_list == "craftpreview")
2527 infostream<<"Ignoring IMoveAction from "
2528 <<(ma->from_inv.dump())<<":"<<ma->from_list
2529 <<" to "<<(ma->to_inv.dump())<<":"<<ma->to_list
2530 <<" because src is "<<ma->from_list<<std::endl;
2536 Disable moving items into craftresult and craftpreview
2538 if(ma->to_list == "craftpreview" || ma->to_list == "craftresult")
2540 infostream<<"Ignoring IMoveAction from "
2541 <<(ma->from_inv.dump())<<":"<<ma->from_list
2542 <<" to "<<(ma->to_inv.dump())<<":"<<ma->to_list
2543 <<" because dst is "<<ma->to_list<<std::endl;
2548 // Disallow moving items in elsewhere than player's inventory
2549 // if not allowed to interact
2550 if(!checkPriv(player->getName(), "interact") &&
2551 (!from_inv_is_current_player ||
2552 !to_inv_is_current_player))
2554 infostream<<"Cannot move outside of player's inventory: "
2555 <<"No interact privilege"<<std::endl;
2561 Handle restrictions and special cases of the drop action
2563 else if(a->getType() == IACTION_DROP)
2565 IDropAction *da = (IDropAction*)a;
2567 da->from_inv.applyCurrentPlayer(player->getName());
2569 setInventoryModified(da->from_inv);
2571 // Disallow dropping items if not allowed to interact
2572 if(!checkPriv(player->getName(), "interact"))
2579 Handle restrictions and special cases of the craft action
2581 else if(a->getType() == IACTION_CRAFT)
2583 ICraftAction *ca = (ICraftAction*)a;
2585 ca->craft_inv.applyCurrentPlayer(player->getName());
2587 setInventoryModified(ca->craft_inv);
2589 //bool craft_inv_is_current_player =
2590 // (ca->craft_inv.type == InventoryLocation::PLAYER) &&
2591 // (ca->craft_inv.name == player->getName());
2593 // Disallow crafting if not allowed to interact
2594 if(!checkPriv(player->getName(), "interact"))
2596 infostream<<"Cannot craft: "
2597 <<"No interact privilege"<<std::endl;
2604 a->apply(this, playersao, this);
2608 else if(command == TOSERVER_CHAT_MESSAGE)
2616 std::string datastring((char*)&data[2], datasize-2);
2617 std::istringstream is(datastring, std::ios_base::binary);
2620 is.read((char*)buf, 2);
2621 u16 len = readU16(buf);
2623 std::wstring message;
2624 for(u16 i=0; i<len; i++)
2626 is.read((char*)buf, 2);
2627 message += (wchar_t)readU16(buf);
2630 // If something goes wrong, this player is to blame
2631 RollbackScopeActor rollback_scope(m_rollback,
2632 std::string("player:")+player->getName());
2634 // Get player name of this client
2635 std::wstring name = narrow_to_wide(player->getName());
2638 bool ate = scriptapi_on_chat_message(m_lua, player->getName(),
2639 wide_to_narrow(message));
2640 // If script ate the message, don't proceed
2644 // Line to send to players
2646 // Whether to send to the player that sent the line
2647 bool send_to_sender = false;
2648 // Whether to send to other players
2649 bool send_to_others = false;
2652 if(message[0] == L'/')
2654 size_t strip_size = 1;
2655 if (message[1] == L'#') // support old-style commans
2657 message = message.substr(strip_size);
2659 WStrfnd f1(message);
2660 f1.next(L" "); // Skip over /#whatever
2661 std::wstring paramstring = f1.next(L"");
2663 ServerCommandContext *ctx = new ServerCommandContext(
2664 str_split(message, L' '),
2670 std::wstring reply(processServerCommand(ctx));
2671 send_to_sender = ctx->flags & SEND_TO_SENDER;
2672 send_to_others = ctx->flags & SEND_TO_OTHERS;
2674 if (ctx->flags & SEND_NO_PREFIX)
2677 line += L"Server: " + reply;
2684 if(checkPriv(player->getName(), "shout")){
2689 send_to_others = true;
2691 line += L"-!- You don't have permission to shout.";
2692 send_to_sender = true;
2699 actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2702 Send the message to clients
2704 for(core::map<u16, RemoteClient*>::Iterator
2705 i = m_clients.getIterator();
2706 i.atEnd() == false; i++)
2708 // Get client and check that it is valid
2709 RemoteClient *client = i.getNode()->getValue();
2710 assert(client->peer_id == i.getNode()->getKey());
2711 if(client->serialization_version == SER_FMT_VER_INVALID)
2715 bool sender_selected = (peer_id == client->peer_id);
2716 if(sender_selected == true && send_to_sender == false)
2718 if(sender_selected == false && send_to_others == false)
2721 SendChatMessage(client->peer_id, line);
2725 else if(command == TOSERVER_DAMAGE)
2727 std::string datastring((char*)&data[2], datasize-2);
2728 std::istringstream is(datastring, std::ios_base::binary);
2729 u8 damage = readU8(is);
2731 actionstream<<player->getName()<<" damaged by "
2732 <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
2735 playersao->setHP(playersao->getHP() - damage);
2737 if(playersao->getHP() == 0 && playersao->m_hp_not_sent)
2740 if(playersao->m_hp_not_sent)
2741 SendPlayerHP(peer_id);
2743 else if(command == TOSERVER_PASSWORD)
2746 [0] u16 TOSERVER_PASSWORD
2747 [2] u8[28] old password
2748 [30] u8[28] new password
2751 if(datasize != 2+PASSWORD_SIZE*2)
2753 /*char password[PASSWORD_SIZE];
2754 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2755 password[i] = data[2+i];
2756 password[PASSWORD_SIZE-1] = 0;*/
2758 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2766 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2768 char c = data[2+PASSWORD_SIZE+i];
2774 if(!base64_is_valid(newpwd)){
2775 infostream<<"Server: "<<player->getName()<<" supplied invalid password hash"<<std::endl;
2776 // Wrong old password supplied!!
2777 SendChatMessage(peer_id, L"Invalid new password hash supplied. Password NOT changed.");
2781 infostream<<"Server: Client requests a password change from "
2782 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
2784 std::string playername = player->getName();
2786 std::string checkpwd;
2787 scriptapi_get_auth(m_lua, playername, &checkpwd, NULL);
2789 if(oldpwd != checkpwd)
2791 infostream<<"Server: invalid old password"<<std::endl;
2792 // Wrong old password supplied!!
2793 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
2797 bool success = scriptapi_set_password(m_lua, playername, newpwd);
2799 actionstream<<player->getName()<<" changes password"<<std::endl;
2800 SendChatMessage(peer_id, L"Password change successful.");
2802 actionstream<<player->getName()<<" tries to change password but "
2803 <<"it fails"<<std::endl;
2804 SendChatMessage(peer_id, L"Password change failed or inavailable.");
2807 else if(command == TOSERVER_PLAYERITEM)
2812 u16 item = readU16(&data[2]);
2813 playersao->setWieldIndex(item);
2815 else if(command == TOSERVER_RESPAWN)
2820 RespawnPlayer(peer_id);
2822 actionstream<<player->getName()<<" respawns at "
2823 <<PP(player->getPosition()/BS)<<std::endl;
2825 // ActiveObject is added to environment in AsyncRunStep after
2826 // the previous addition has been succesfully removed
2828 else if(command == TOSERVER_REQUEST_MEDIA) {
2829 std::string datastring((char*)&data[2], datasize-2);
2830 std::istringstream is(datastring, std::ios_base::binary);
2832 core::list<MediaRequest> tosend;
2833 u16 numfiles = readU16(is);
2835 infostream<<"Sending "<<numfiles<<" files to "
2836 <<getPlayerName(peer_id)<<std::endl;
2837 verbosestream<<"TOSERVER_REQUEST_MEDIA: "<<std::endl;
2839 for(int i = 0; i < numfiles; i++) {
2840 std::string name = deSerializeString(is);
2841 tosend.push_back(MediaRequest(name));
2842 verbosestream<<"TOSERVER_REQUEST_MEDIA: requested file "
2846 sendRequestedMedia(peer_id, tosend);
2848 // Now the client should know about everything
2849 // (definitions and files)
2850 getClient(peer_id)->definitions_sent = true;
2852 else if(command == TOSERVER_INTERACT)
2854 std::string datastring((char*)&data[2], datasize-2);
2855 std::istringstream is(datastring, std::ios_base::binary);
2861 [5] u32 length of the next item
2862 [9] serialized PointedThing
2864 0: start digging (from undersurface) or use
2865 1: stop digging (all parameters ignored)
2866 2: digging completed
2867 3: place block or item (to abovesurface)
2870 u8 action = readU8(is);
2871 u16 item_i = readU16(is);
2872 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
2873 PointedThing pointed;
2874 pointed.deSerialize(tmp_is);
2876 verbosestream<<"TOSERVER_INTERACT: action="<<(int)action<<", item="
2877 <<item_i<<", pointed="<<pointed.dump()<<std::endl;
2881 verbosestream<<"TOSERVER_INTERACT: "<<player->getName()
2882 <<" tried to interact, but is dead!"<<std::endl;
2886 v3f player_pos = playersao->getLastGoodPosition();
2888 // Update wielded item
2889 playersao->setWieldIndex(item_i);
2891 // Get pointed to node (undefined if not POINTEDTYPE_NODE)
2892 v3s16 p_under = pointed.node_undersurface;
2893 v3s16 p_above = pointed.node_abovesurface;
2895 // Get pointed to object (NULL if not POINTEDTYPE_OBJECT)
2896 ServerActiveObject *pointed_object = NULL;
2897 if(pointed.type == POINTEDTHING_OBJECT)
2899 pointed_object = m_env->getActiveObject(pointed.object_id);
2900 if(pointed_object == NULL)
2902 verbosestream<<"TOSERVER_INTERACT: "
2903 "pointed object is NULL"<<std::endl;
2909 v3f pointed_pos_under = player_pos;
2910 v3f pointed_pos_above = player_pos;
2911 if(pointed.type == POINTEDTHING_NODE)
2913 pointed_pos_under = intToFloat(p_under, BS);
2914 pointed_pos_above = intToFloat(p_above, BS);
2916 else if(pointed.type == POINTEDTHING_OBJECT)
2918 pointed_pos_under = pointed_object->getBasePosition();
2919 pointed_pos_above = pointed_pos_under;
2923 Check that target is reasonably close
2924 (only when digging or placing things)
2926 if(action == 0 || action == 2 || action == 3)
2928 float d = player_pos.getDistanceFrom(pointed_pos_under);
2929 float max_d = BS * 14; // Just some large enough value
2931 actionstream<<"Player "<<player->getName()
2932 <<" tried to access "<<pointed.dump()
2934 <<"d="<<d<<", max_d="<<max_d
2935 <<". ignoring."<<std::endl;
2936 // Re-send block to revert change on client-side
2937 RemoteClient *client = getClient(peer_id);
2938 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2939 client->SetBlockNotSent(blockpos);
2946 Make sure the player is allowed to do it
2948 if(!checkPriv(player->getName(), "interact"))
2950 actionstream<<player->getName()<<" attempted to interact with "
2951 <<pointed.dump()<<" without 'interact' privilege"
2953 // Re-send block to revert change on client-side
2954 RemoteClient *client = getClient(peer_id);
2955 // Digging completed -> under
2957 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2958 client->SetBlockNotSent(blockpos);
2960 // Placement -> above
2962 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
2963 client->SetBlockNotSent(blockpos);
2969 If something goes wrong, this player is to blame
2971 RollbackScopeActor rollback_scope(m_rollback,
2972 std::string("player:")+player->getName());
2975 0: start digging or punch object
2979 if(pointed.type == POINTEDTHING_NODE)
2982 NOTE: This can be used in the future to check if
2983 somebody is cheating, by checking the timing.
2985 MapNode n(CONTENT_IGNORE);
2988 n = m_env->getMap().getNode(p_under);
2990 catch(InvalidPositionException &e)
2992 infostream<<"Server: Not punching: Node not found."
2993 <<" Adding block to emerge queue."
2995 m_emerge_queue.addBlock(peer_id,
2996 getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
2998 if(n.getContent() != CONTENT_IGNORE)
2999 scriptapi_node_on_punch(m_lua, p_under, n, playersao);
3001 playersao->noCheatDigStart(p_under);
3003 else if(pointed.type == POINTEDTHING_OBJECT)
3005 // Skip if object has been removed
3006 if(pointed_object->m_removed)
3009 actionstream<<player->getName()<<" punches object "
3010 <<pointed.object_id<<": "
3011 <<pointed_object->getDescription()<<std::endl;
3013 ItemStack punchitem = playersao->getWieldedItem();
3014 ToolCapabilities toolcap =
3015 punchitem.getToolCapabilities(m_itemdef);
3016 v3f dir = (pointed_object->getBasePosition() -
3017 (player->getPosition() + player->getEyeOffset())
3019 float time_from_last_punch =
3020 playersao->resetTimeFromLastPunch();
3021 pointed_object->punch(dir, &toolcap, playersao,
3022 time_from_last_punch);
3030 else if(action == 1)
3035 2: Digging completed
3037 else if(action == 2)
3039 // Only digging of nodes
3040 if(pointed.type == POINTEDTHING_NODE)
3042 MapNode n(CONTENT_IGNORE);
3045 n = m_env->getMap().getNode(p_under);
3047 catch(InvalidPositionException &e)
3049 infostream<<"Server: Not finishing digging: Node not found."
3050 <<" Adding block to emerge queue."
3052 m_emerge_queue.addBlock(peer_id,
3053 getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
3056 /* Cheat prevention */
3057 bool is_valid_dig = true;
3058 if(!isSingleplayer() && !g_settings->getBool("disable_anticheat"))
3060 v3s16 nocheat_p = playersao->getNoCheatDigPos();
3061 float nocheat_t = playersao->getNoCheatDigTime();
3062 playersao->noCheatDigEnd();
3063 // If player didn't start digging this, ignore dig
3064 if(nocheat_p != p_under){
3065 infostream<<"Server: NoCheat: "<<player->getName()
3066 <<" started digging "
3067 <<PP(nocheat_p)<<" and completed digging "
3068 <<PP(p_under)<<"; not digging."<<std::endl;
3069 is_valid_dig = false;
3071 // Get player's wielded item
3072 ItemStack playeritem;
3073 InventoryList *mlist = playersao->getInventory()->getList("main");
3075 playeritem = mlist->getItem(playersao->getWieldIndex());
3076 ToolCapabilities playeritem_toolcap =
3077 playeritem.getToolCapabilities(m_itemdef);
3078 // Get diggability and expected digging time
3079 DigParams params = getDigParams(m_nodedef->get(n).groups,
3080 &playeritem_toolcap);
3081 // If can't dig, try hand
3082 if(!params.diggable){
3083 const ItemDefinition &hand = m_itemdef->get("");
3084 const ToolCapabilities *tp = hand.tool_capabilities;
3086 params = getDigParams(m_nodedef->get(n).groups, tp);
3088 // If can't dig, ignore dig
3089 if(!params.diggable){
3090 infostream<<"Server: NoCheat: "<<player->getName()
3091 <<" completed digging "<<PP(p_under)
3092 <<", which is not diggable with tool. not digging."
3094 is_valid_dig = false;
3096 // If time is considerably too short, ignore dig
3097 // Check time only for medium and slow timed digs
3098 if(params.diggable && params.time > 0.3 && nocheat_t < 0.5 * params.time){
3099 infostream<<"Server: NoCheat: "<<player->getName()
3100 <<" completed digging "
3101 <<PP(p_under)<<" in "<<nocheat_t<<"s; expected "
3102 <<params.time<<"s; not digging."<<std::endl;
3103 is_valid_dig = false;
3107 /* Actually dig node */
3109 if(is_valid_dig && n.getContent() != CONTENT_IGNORE)
3110 scriptapi_node_on_dig(m_lua, p_under, n, playersao);
3112 // Send unusual result (that is, node not being removed)
3113 if(m_env->getMap().getNodeNoEx(p_under).getContent() != CONTENT_AIR)
3115 // Re-send block to revert change on client-side
3116 RemoteClient *client = getClient(peer_id);
3117 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
3118 client->SetBlockNotSent(blockpos);
3124 3: place block or right-click object
3126 else if(action == 3)
3128 ItemStack item = playersao->getWieldedItem();
3130 // Reset build time counter
3131 if(pointed.type == POINTEDTHING_NODE &&
3132 item.getDefinition(m_itemdef).type == ITEM_NODE)
3133 getClient(peer_id)->m_time_from_building = 0.0;
3135 if(pointed.type == POINTEDTHING_OBJECT)
3137 // Right click object
3139 // Skip if object has been removed
3140 if(pointed_object->m_removed)
3143 actionstream<<player->getName()<<" right-clicks object "
3144 <<pointed.object_id<<": "
3145 <<pointed_object->getDescription()<<std::endl;
3148 pointed_object->rightClick(playersao);
3150 else if(scriptapi_item_on_place(m_lua,
3151 item, playersao, pointed))
3153 // Placement was handled in lua
3155 // Apply returned ItemStack
3156 playersao->setWieldedItem(item);
3159 // If item has node placement prediction, always send the above
3160 // node to make sure the client knows what exactly happened
3161 if(item.getDefinition(m_itemdef).node_placement_prediction != ""){
3162 RemoteClient *client = getClient(peer_id);
3163 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
3164 client->SetBlockNotSent(blockpos);
3171 else if(action == 4)
3173 ItemStack item = playersao->getWieldedItem();
3175 actionstream<<player->getName()<<" uses "<<item.name
3176 <<", pointing at "<<pointed.dump()<<std::endl;
3178 if(scriptapi_item_on_use(m_lua,
3179 item, playersao, pointed))
3181 // Apply returned ItemStack
3182 playersao->setWieldedItem(item);
3188 Catch invalid actions
3192 infostream<<"WARNING: Server: Invalid action "
3193 <<action<<std::endl;
3196 else if(command == TOSERVER_REMOVED_SOUNDS)
3198 std::string datastring((char*)&data[2], datasize-2);
3199 std::istringstream is(datastring, std::ios_base::binary);
3201 int num = readU16(is);
3202 for(int k=0; k<num; k++){
3203 s32 id = readS32(is);
3204 std::map<s32, ServerPlayingSound>::iterator i =
3205 m_playing_sounds.find(id);
3206 if(i == m_playing_sounds.end())
3208 ServerPlayingSound &psound = i->second;
3209 psound.clients.erase(peer_id);
3210 if(psound.clients.size() == 0)
3211 m_playing_sounds.erase(i++);
3214 else if(command == TOSERVER_NODEMETA_FIELDS)
3216 std::string datastring((char*)&data[2], datasize-2);
3217 std::istringstream is(datastring, std::ios_base::binary);
3219 v3s16 p = readV3S16(is);
3220 std::string formname = deSerializeString(is);
3221 int num = readU16(is);
3222 std::map<std::string, std::string> fields;
3223 for(int k=0; k<num; k++){
3224 std::string fieldname = deSerializeString(is);
3225 std::string fieldvalue = deSerializeLongString(is);
3226 fields[fieldname] = fieldvalue;
3229 // If something goes wrong, this player is to blame
3230 RollbackScopeActor rollback_scope(m_rollback,
3231 std::string("player:")+player->getName());
3233 // Check the target node for rollback data; leave others unnoticed
3234 RollbackNode rn_old(&m_env->getMap(), p, this);
3236 scriptapi_node_on_receive_fields(m_lua, p, formname, fields,
3239 // Report rollback data
3240 RollbackNode rn_new(&m_env->getMap(), p, this);
3241 if(rollback() && rn_new != rn_old){
3242 RollbackAction action;
3243 action.setSetNode(p, rn_old, rn_new);
3244 rollback()->reportAction(action);
3247 else if(command == TOSERVER_INVENTORY_FIELDS)
3249 std::string datastring((char*)&data[2], datasize-2);
3250 std::istringstream is(datastring, std::ios_base::binary);
3252 std::string formname = deSerializeString(is);
3253 int num = readU16(is);
3254 std::map<std::string, std::string> fields;
3255 for(int k=0; k<num; k++){
3256 std::string fieldname = deSerializeString(is);
3257 std::string fieldvalue = deSerializeLongString(is);
3258 fields[fieldname] = fieldvalue;
3261 scriptapi_on_player_receive_fields(m_lua, playersao, formname, fields);
3265 infostream<<"Server::ProcessData(): Ignoring "
3266 "unknown command "<<command<<std::endl;
3270 catch(SendFailedException &e)
3272 errorstream<<"Server::ProcessData(): SendFailedException: "
3278 void Server::onMapEditEvent(MapEditEvent *event)
3280 //infostream<<"Server::onMapEditEvent()"<<std::endl;
3281 if(m_ignore_map_edit_events)
3283 if(m_ignore_map_edit_events_area.contains(event->getArea()))
3285 MapEditEvent *e = event->clone();
3286 m_unsent_map_edit_queue.push_back(e);
3289 Inventory* Server::getInventory(const InventoryLocation &loc)
3292 case InventoryLocation::UNDEFINED:
3295 case InventoryLocation::CURRENT_PLAYER:
3298 case InventoryLocation::PLAYER:
3300 Player *player = m_env->getPlayer(loc.name.c_str());
3303 PlayerSAO *playersao = player->getPlayerSAO();
3306 return playersao->getInventory();
3309 case InventoryLocation::NODEMETA:
3311 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
3314 return meta->getInventory();
3317 case InventoryLocation::DETACHED:
3319 if(m_detached_inventories.count(loc.name) == 0)
3321 return m_detached_inventories[loc.name];
3329 void Server::setInventoryModified(const InventoryLocation &loc)
3332 case InventoryLocation::UNDEFINED:
3335 case InventoryLocation::PLAYER:
3337 Player *player = m_env->getPlayer(loc.name.c_str());
3340 PlayerSAO *playersao = player->getPlayerSAO();
3343 playersao->m_inventory_not_sent = true;
3344 playersao->m_wielded_item_not_sent = true;
3347 case InventoryLocation::NODEMETA:
3349 v3s16 blockpos = getNodeBlockPos(loc.p);
3351 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3353 block->raiseModified(MOD_STATE_WRITE_NEEDED);
3355 setBlockNotSent(blockpos);
3358 case InventoryLocation::DETACHED:
3360 sendDetachedInventoryToAll(loc.name);
3368 core::list<PlayerInfo> Server::getPlayerInfo()
3370 DSTACK(__FUNCTION_NAME);
3371 JMutexAutoLock envlock(m_env_mutex);
3372 JMutexAutoLock conlock(m_con_mutex);
3374 core::list<PlayerInfo> list;
3376 core::list<Player*> players = m_env->getPlayers();
3378 core::list<Player*>::Iterator i;
3379 for(i = players.begin();
3380 i != players.end(); i++)
3384 Player *player = *i;
3387 // Copy info from connection to info struct
3388 info.id = player->peer_id;
3389 info.address = m_con.GetPeerAddress(player->peer_id);
3390 info.avg_rtt = m_con.GetPeerAvgRTT(player->peer_id);
3392 catch(con::PeerNotFoundException &e)
3394 // Set dummy peer info
3396 info.address = Address(0,0,0,0,0);
3400 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3401 info.position = player->getPosition();
3403 list.push_back(info);
3410 void Server::peerAdded(con::Peer *peer)
3412 DSTACK(__FUNCTION_NAME);
3413 verbosestream<<"Server::peerAdded(): peer->id="
3414 <<peer->id<<std::endl;
3417 c.type = PEER_ADDED;
3418 c.peer_id = peer->id;
3420 m_peer_change_queue.push_back(c);
3423 void Server::deletingPeer(con::Peer *peer, bool timeout)
3425 DSTACK(__FUNCTION_NAME);
3426 verbosestream<<"Server::deletingPeer(): peer->id="
3427 <<peer->id<<", timeout="<<timeout<<std::endl;
3430 c.type = PEER_REMOVED;
3431 c.peer_id = peer->id;
3432 c.timeout = timeout;
3433 m_peer_change_queue.push_back(c);
3440 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3442 DSTACK(__FUNCTION_NAME);
3443 std::ostringstream os(std::ios_base::binary);
3445 writeU16(os, TOCLIENT_HP);
3449 std::string s = os.str();
3450 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3452 con.Send(peer_id, 0, data, true);
3455 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3456 const std::wstring &reason)
3458 DSTACK(__FUNCTION_NAME);
3459 std::ostringstream os(std::ios_base::binary);
3461 writeU16(os, TOCLIENT_ACCESS_DENIED);
3462 os<<serializeWideString(reason);
3465 std::string s = os.str();
3466 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3468 con.Send(peer_id, 0, data, true);
3471 void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
3472 bool set_camera_point_target, v3f camera_point_target)
3474 DSTACK(__FUNCTION_NAME);
3475 std::ostringstream os(std::ios_base::binary);
3477 writeU16(os, TOCLIENT_DEATHSCREEN);
3478 writeU8(os, set_camera_point_target);
3479 writeV3F1000(os, camera_point_target);
3482 std::string s = os.str();
3483 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3485 con.Send(peer_id, 0, data, true);
3488 void Server::SendItemDef(con::Connection &con, u16 peer_id,
3489 IItemDefManager *itemdef)
3491 DSTACK(__FUNCTION_NAME);
3492 std::ostringstream os(std::ios_base::binary);
3496 u32 length of the next item
3497 zlib-compressed serialized ItemDefManager
3499 writeU16(os, TOCLIENT_ITEMDEF);
3500 std::ostringstream tmp_os(std::ios::binary);
3501 itemdef->serialize(tmp_os);
3502 std::ostringstream tmp_os2(std::ios::binary);
3503 compressZlib(tmp_os.str(), tmp_os2);
3504 os<<serializeLongString(tmp_os2.str());
3507 std::string s = os.str();
3508 verbosestream<<"Server: Sending item definitions to id("<<peer_id
3509 <<"): size="<<s.size()<<std::endl;
3510 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3512 con.Send(peer_id, 0, data, true);
3515 void Server::SendNodeDef(con::Connection &con, u16 peer_id,
3516 INodeDefManager *nodedef)
3518 DSTACK(__FUNCTION_NAME);
3519 std::ostringstream os(std::ios_base::binary);
3523 u32 length of the next item
3524 zlib-compressed serialized NodeDefManager
3526 writeU16(os, TOCLIENT_NODEDEF);
3527 std::ostringstream tmp_os(std::ios::binary);
3528 nodedef->serialize(tmp_os);
3529 std::ostringstream tmp_os2(std::ios::binary);
3530 compressZlib(tmp_os.str(), tmp_os2);
3531 os<<serializeLongString(tmp_os2.str());
3534 std::string s = os.str();
3535 verbosestream<<"Server: Sending node definitions to id("<<peer_id
3536 <<"): size="<<s.size()<<std::endl;
3537 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3539 con.Send(peer_id, 0, data, true);
3543 Non-static send methods
3546 void Server::SendInventory(u16 peer_id)
3548 DSTACK(__FUNCTION_NAME);
3550 PlayerSAO *playersao = getPlayerSAO(peer_id);
3553 playersao->m_inventory_not_sent = false;
3559 std::ostringstream os;
3560 playersao->getInventory()->serialize(os);
3562 std::string s = os.str();
3564 SharedBuffer<u8> data(s.size()+2);
3565 writeU16(&data[0], TOCLIENT_INVENTORY);
3566 memcpy(&data[2], s.c_str(), s.size());
3569 m_con.Send(peer_id, 0, data, true);
3572 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3574 DSTACK(__FUNCTION_NAME);
3576 std::ostringstream os(std::ios_base::binary);
3580 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3581 os.write((char*)buf, 2);
3584 writeU16(buf, message.size());
3585 os.write((char*)buf, 2);
3588 for(u32 i=0; i<message.size(); i++)
3592 os.write((char*)buf, 2);
3596 std::string s = os.str();
3597 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3599 m_con.Send(peer_id, 0, data, true);
3602 void Server::BroadcastChatMessage(const std::wstring &message)
3604 for(core::map<u16, RemoteClient*>::Iterator
3605 i = m_clients.getIterator();
3606 i.atEnd() == false; i++)
3608 // Get client and check that it is valid
3609 RemoteClient *client = i.getNode()->getValue();
3610 assert(client->peer_id == i.getNode()->getKey());
3611 if(client->serialization_version == SER_FMT_VER_INVALID)
3614 SendChatMessage(client->peer_id, message);
3618 void Server::SendPlayerHP(u16 peer_id)
3620 DSTACK(__FUNCTION_NAME);
3621 PlayerSAO *playersao = getPlayerSAO(peer_id);
3623 playersao->m_hp_not_sent = false;
3624 SendHP(m_con, peer_id, playersao->getHP());
3627 void Server::SendMovePlayer(u16 peer_id)
3629 DSTACK(__FUNCTION_NAME);
3630 Player *player = m_env->getPlayer(peer_id);
3633 std::ostringstream os(std::ios_base::binary);
3634 writeU16(os, TOCLIENT_MOVE_PLAYER);
3635 writeV3F1000(os, player->getPosition());
3636 writeF1000(os, player->getPitch());
3637 writeF1000(os, player->getYaw());
3640 v3f pos = player->getPosition();
3641 f32 pitch = player->getPitch();
3642 f32 yaw = player->getYaw();
3643 verbosestream<<"Server: Sending TOCLIENT_MOVE_PLAYER"
3644 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3651 std::string s = os.str();
3652 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3654 m_con.Send(peer_id, 0, data, true);
3657 void Server::SendPlayerPrivileges(u16 peer_id)
3659 Player *player = m_env->getPlayer(peer_id);
3661 if(player->peer_id == PEER_ID_INEXISTENT)
3664 std::set<std::string> privs;
3665 scriptapi_get_auth(m_lua, player->getName(), NULL, &privs);
3667 std::ostringstream os(std::ios_base::binary);
3668 writeU16(os, TOCLIENT_PRIVILEGES);
3669 writeU16(os, privs.size());
3670 for(std::set<std::string>::const_iterator i = privs.begin();
3671 i != privs.end(); i++){
3672 os<<serializeString(*i);
3676 std::string s = os.str();
3677 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3679 m_con.Send(peer_id, 0, data, true);
3682 void Server::SendPlayerInventoryFormspec(u16 peer_id)
3684 Player *player = m_env->getPlayer(peer_id);
3686 if(player->peer_id == PEER_ID_INEXISTENT)
3689 std::ostringstream os(std::ios_base::binary);
3690 writeU16(os, TOCLIENT_INVENTORY_FORMSPEC);
3691 os<<serializeLongString(player->inventory_formspec);
3694 std::string s = os.str();
3695 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3697 m_con.Send(peer_id, 0, data, true);
3700 s32 Server::playSound(const SimpleSoundSpec &spec,
3701 const ServerSoundParams ¶ms)
3703 // Find out initial position of sound
3704 bool pos_exists = false;
3705 v3f pos = params.getPos(m_env, &pos_exists);
3706 // If position is not found while it should be, cancel sound
3707 if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
3709 // Filter destination clients
3710 std::set<RemoteClient*> dst_clients;
3711 if(params.to_player != "")
3713 Player *player = m_env->getPlayer(params.to_player.c_str());
3715 infostream<<"Server::playSound: Player \""<<params.to_player
3716 <<"\" not found"<<std::endl;
3719 if(player->peer_id == PEER_ID_INEXISTENT){
3720 infostream<<"Server::playSound: Player \""<<params.to_player
3721 <<"\" not connected"<<std::endl;
3724 RemoteClient *client = getClient(player->peer_id);
3725 dst_clients.insert(client);
3729 for(core::map<u16, RemoteClient*>::Iterator
3730 i = m_clients.getIterator(); i.atEnd() == false; i++)
3732 RemoteClient *client = i.getNode()->getValue();
3733 Player *player = m_env->getPlayer(client->peer_id);
3737 if(player->getPosition().getDistanceFrom(pos) >
3738 params.max_hear_distance)
3741 dst_clients.insert(client);
3744 if(dst_clients.size() == 0)
3747 s32 id = m_next_sound_id++;
3748 // The sound will exist as a reference in m_playing_sounds
3749 m_playing_sounds[id] = ServerPlayingSound();
3750 ServerPlayingSound &psound = m_playing_sounds[id];
3751 psound.params = params;
3752 for(std::set<RemoteClient*>::iterator i = dst_clients.begin();
3753 i != dst_clients.end(); i++)
3754 psound.clients.insert((*i)->peer_id);
3756 std::ostringstream os(std::ios_base::binary);
3757 writeU16(os, TOCLIENT_PLAY_SOUND);
3759 os<<serializeString(spec.name);
3760 writeF1000(os, spec.gain * params.gain);
3761 writeU8(os, params.type);
3762 writeV3F1000(os, pos);
3763 writeU16(os, params.object);
3764 writeU8(os, params.loop);
3766 std::string s = os.str();
3767 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3769 for(std::set<RemoteClient*>::iterator i = dst_clients.begin();
3770 i != dst_clients.end(); i++){
3772 m_con.Send((*i)->peer_id, 0, data, true);
3776 void Server::stopSound(s32 handle)
3778 // Get sound reference
3779 std::map<s32, ServerPlayingSound>::iterator i =
3780 m_playing_sounds.find(handle);
3781 if(i == m_playing_sounds.end())
3783 ServerPlayingSound &psound = i->second;
3785 std::ostringstream os(std::ios_base::binary);
3786 writeU16(os, TOCLIENT_STOP_SOUND);
3787 writeS32(os, handle);
3789 std::string s = os.str();
3790 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3792 for(std::set<u16>::iterator i = psound.clients.begin();
3793 i != psound.clients.end(); i++){
3795 m_con.Send(*i, 0, data, true);
3797 // Remove sound reference
3798 m_playing_sounds.erase(i);
3801 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3802 core::list<u16> *far_players, float far_d_nodes)
3804 float maxd = far_d_nodes*BS;
3805 v3f p_f = intToFloat(p, BS);
3809 SharedBuffer<u8> reply(replysize);
3810 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3811 writeS16(&reply[2], p.X);
3812 writeS16(&reply[4], p.Y);
3813 writeS16(&reply[6], p.Z);
3815 for(core::map<u16, RemoteClient*>::Iterator
3816 i = m_clients.getIterator();
3817 i.atEnd() == false; i++)
3819 // Get client and check that it is valid
3820 RemoteClient *client = i.getNode()->getValue();
3821 assert(client->peer_id == i.getNode()->getKey());
3822 if(client->serialization_version == SER_FMT_VER_INVALID)
3825 // Don't send if it's the same one
3826 if(client->peer_id == ignore_id)
3832 Player *player = m_env->getPlayer(client->peer_id);
3835 // If player is far away, only set modified blocks not sent
3836 v3f player_pos = player->getPosition();
3837 if(player_pos.getDistanceFrom(p_f) > maxd)
3839 far_players->push_back(client->peer_id);
3846 m_con.Send(client->peer_id, 0, reply, true);
3850 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3851 core::list<u16> *far_players, float far_d_nodes)
3853 float maxd = far_d_nodes*BS;
3854 v3f p_f = intToFloat(p, BS);
3856 for(core::map<u16, RemoteClient*>::Iterator
3857 i = m_clients.getIterator();
3858 i.atEnd() == false; i++)
3860 // Get client and check that it is valid
3861 RemoteClient *client = i.getNode()->getValue();
3862 assert(client->peer_id == i.getNode()->getKey());
3863 if(client->serialization_version == SER_FMT_VER_INVALID)
3866 // Don't send if it's the same one
3867 if(client->peer_id == ignore_id)
3873 Player *player = m_env->getPlayer(client->peer_id);
3876 // If player is far away, only set modified blocks not sent
3877 v3f player_pos = player->getPosition();
3878 if(player_pos.getDistanceFrom(p_f) > maxd)
3880 far_players->push_back(client->peer_id);
3887 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3888 SharedBuffer<u8> reply(replysize);
3889 writeU16(&reply[0], TOCLIENT_ADDNODE);
3890 writeS16(&reply[2], p.X);
3891 writeS16(&reply[4], p.Y);
3892 writeS16(&reply[6], p.Z);
3893 n.serialize(&reply[8], client->serialization_version);
3896 m_con.Send(client->peer_id, 0, reply, true);
3900 void Server::setBlockNotSent(v3s16 p)
3902 for(core::map<u16, RemoteClient*>::Iterator
3903 i = m_clients.getIterator();
3904 i.atEnd()==false; i++)
3906 RemoteClient *client = i.getNode()->getValue();
3907 client->SetBlockNotSent(p);
3911 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3913 DSTACK(__FUNCTION_NAME);
3915 v3s16 p = block->getPos();
3919 bool completely_air = true;
3920 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3921 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3922 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3924 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
3926 completely_air = false;
3927 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
3932 infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
3934 infostream<<"[completely air] ";
3935 infostream<<std::endl;
3939 Create a packet with the block in the right format
3942 std::ostringstream os(std::ios_base::binary);
3943 block->serialize(os, ver, false);
3944 std::string s = os.str();
3945 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3947 u32 replysize = 8 + blockdata.getSize();
3948 SharedBuffer<u8> reply(replysize);
3949 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3950 writeS16(&reply[2], p.X);
3951 writeS16(&reply[4], p.Y);
3952 writeS16(&reply[6], p.Z);
3953 memcpy(&reply[8], *blockdata, blockdata.getSize());
3955 /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3956 <<": \tpacket size: "<<replysize<<std::endl;*/
3961 m_con.Send(peer_id, 1, reply, true);
3964 void Server::SendBlocks(float dtime)
3966 DSTACK(__FUNCTION_NAME);
3968 JMutexAutoLock envlock(m_env_mutex);
3969 JMutexAutoLock conlock(m_con_mutex);
3971 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
3973 core::array<PrioritySortedBlockTransfer> queue;
3975 s32 total_sending = 0;
3978 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
3980 for(core::map<u16, RemoteClient*>::Iterator
3981 i = m_clients.getIterator();
3982 i.atEnd() == false; i++)
3984 RemoteClient *client = i.getNode()->getValue();
3985 assert(client->peer_id == i.getNode()->getKey());
3987 // If definitions and textures have not been sent, don't
3988 // send MapBlocks either
3989 if(!client->definitions_sent)
3992 total_sending += client->SendingCount();
3994 if(client->serialization_version == SER_FMT_VER_INVALID)
3997 client->GetNextBlocks(this, dtime, queue);
4002 // Lowest priority number comes first.
4003 // Lowest is most important.
4006 for(u32 i=0; i<queue.size(); i++)
4008 //TODO: Calculate limit dynamically
4009 if(total_sending >= g_settings->getS32
4010 ("max_simultaneous_block_sends_server_total"))
4013 PrioritySortedBlockTransfer q = queue[i];
4015 MapBlock *block = NULL;
4018 block = m_env->getMap().getBlockNoCreate(q.pos);
4020 catch(InvalidPositionException &e)
4025 RemoteClient *client = getClient(q.peer_id);
4027 SendBlockNoLock(q.peer_id, block, client->serialization_version);
4029 client->SentBlock(q.pos);
4035 void Server::fillMediaCache()
4037 DSTACK(__FUNCTION_NAME);
4039 infostream<<"Server: Calculating media file checksums"<<std::endl;
4041 // Collect all media file paths
4042 std::list<std::string> paths;
4043 for(core::list<ModSpec>::Iterator i = m_mods.begin();
4044 i != m_mods.end(); i++){
4045 const ModSpec &mod = *i;
4046 paths.push_back(mod.path + DIR_DELIM + "textures");
4047 paths.push_back(mod.path + DIR_DELIM + "sounds");
4048 paths.push_back(mod.path + DIR_DELIM + "media");
4050 std::string path_all = "textures";
4051 paths.push_back(path_all + DIR_DELIM + "all");
4053 // Collect media file information from paths into cache
4054 for(std::list<std::string>::iterator i = paths.begin();
4055 i != paths.end(); i++)
4057 std::string mediapath = *i;
4058 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
4059 for(u32 j=0; j<dirlist.size(); j++){
4060 if(dirlist[j].dir) // Ignode dirs
4062 std::string filename = dirlist[j].name;
4063 // If name contains illegal characters, ignore the file
4064 if(!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)){
4065 infostream<<"Server: ignoring illegal file name: \""
4066 <<filename<<"\""<<std::endl;
4069 // If name is not in a supported format, ignore it
4070 const char *supported_ext[] = {
4071 ".png", ".jpg", ".bmp", ".tga",
4072 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
4076 if(removeStringEnd(filename, supported_ext) == ""){
4077 infostream<<"Server: ignoring unsupported file extension: \""
4078 <<filename<<"\""<<std::endl;
4081 // Ok, attempt to load the file and add to cache
4082 std::string filepath = mediapath + DIR_DELIM + filename;
4084 std::ifstream fis(filepath.c_str(), std::ios_base::binary);
4085 if(fis.good() == false){
4086 errorstream<<"Server::fillMediaCache(): Could not open \""
4087 <<filename<<"\" for reading"<<std::endl;
4090 std::ostringstream tmp_os(std::ios_base::binary);
4094 fis.read(buf, 1024);
4095 std::streamsize len = fis.gcount();
4096 tmp_os.write(buf, len);
4105 errorstream<<"Server::fillMediaCache(): Failed to read \""
4106 <<filename<<"\""<<std::endl;
4109 if(tmp_os.str().length() == 0){
4110 errorstream<<"Server::fillMediaCache(): Empty file \""
4111 <<filepath<<"\""<<std::endl;
4116 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
4118 unsigned char *digest = sha1.getDigest();
4119 std::string sha1_base64 = base64_encode(digest, 20);
4120 std::string sha1_hex = hex_encode((char*)digest, 20);
4124 this->m_media[filename] = MediaInfo(filepath, sha1_base64);
4125 verbosestream<<"Server: "<<sha1_hex<<" is "<<filename<<std::endl;
4130 struct SendableMediaAnnouncement
4133 std::string sha1_digest;
4135 SendableMediaAnnouncement(const std::string name_="",
4136 const std::string sha1_digest_=""):
4138 sha1_digest(sha1_digest_)
4142 void Server::sendMediaAnnouncement(u16 peer_id)
4144 DSTACK(__FUNCTION_NAME);
4146 verbosestream<<"Server: Announcing files to id("<<peer_id<<")"
4149 core::list<SendableMediaAnnouncement> file_announcements;
4151 for(std::map<std::string, MediaInfo>::iterator i = m_media.begin();
4152 i != m_media.end(); i++){
4154 file_announcements.push_back(
4155 SendableMediaAnnouncement(i->first, i->second.sha1_digest));
4159 std::ostringstream os(std::ios_base::binary);
4167 u16 length of sha1_digest
4172 writeU16(os, TOCLIENT_ANNOUNCE_MEDIA);
4173 writeU16(os, file_announcements.size());
4175 for(core::list<SendableMediaAnnouncement>::Iterator
4176 j = file_announcements.begin();
4177 j != file_announcements.end(); j++){
4178 os<<serializeString(j->name);
4179 os<<serializeString(j->sha1_digest);
4183 std::string s = os.str();
4184 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4187 m_con.Send(peer_id, 0, data, true);
4191 struct SendableMedia
4197 SendableMedia(const std::string &name_="", const std::string path_="",
4198 const std::string &data_=""):
4205 void Server::sendRequestedMedia(u16 peer_id,
4206 const core::list<MediaRequest> &tosend)
4208 DSTACK(__FUNCTION_NAME);
4210 verbosestream<<"Server::sendRequestedMedia(): "
4211 <<"Sending files to client"<<std::endl;
4215 // Put 5kB in one bunch (this is not accurate)
4216 u32 bytes_per_bunch = 5000;
4218 core::array< core::list<SendableMedia> > file_bunches;
4219 file_bunches.push_back(core::list<SendableMedia>());
4221 u32 file_size_bunch_total = 0;
4223 for(core::list<MediaRequest>::ConstIterator i = tosend.begin();
4224 i != tosend.end(); i++)
4226 if(m_media.find(i->name) == m_media.end()){
4227 errorstream<<"Server::sendRequestedMedia(): Client asked for "
4228 <<"unknown file \""<<(i->name)<<"\""<<std::endl;
4232 //TODO get path + name
4233 std::string tpath = m_media[(*i).name].path;
4236 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
4237 if(fis.good() == false){
4238 errorstream<<"Server::sendRequestedMedia(): Could not open \""
4239 <<tpath<<"\" for reading"<<std::endl;
4242 std::ostringstream tmp_os(std::ios_base::binary);
4246 fis.read(buf, 1024);
4247 std::streamsize len = fis.gcount();
4248 tmp_os.write(buf, len);
4249 file_size_bunch_total += len;
4258 errorstream<<"Server::sendRequestedMedia(): Failed to read \""
4259 <<(*i).name<<"\""<<std::endl;
4262 /*infostream<<"Server::sendRequestedMedia(): Loaded \""
4263 <<tname<<"\""<<std::endl;*/
4265 file_bunches[file_bunches.size()-1].push_back(
4266 SendableMedia((*i).name, tpath, tmp_os.str()));
4268 // Start next bunch if got enough data
4269 if(file_size_bunch_total >= bytes_per_bunch){
4270 file_bunches.push_back(core::list<SendableMedia>());
4271 file_size_bunch_total = 0;
4276 /* Create and send packets */
4278 u32 num_bunches = file_bunches.size();
4279 for(u32 i=0; i<num_bunches; i++)
4281 std::ostringstream os(std::ios_base::binary);
4285 u16 total number of texture bunches
4286 u16 index of this bunch
4287 u32 number of files in this bunch
4296 writeU16(os, TOCLIENT_MEDIA);
4297 writeU16(os, num_bunches);
4299 writeU32(os, file_bunches[i].size());
4301 for(core::list<SendableMedia>::Iterator
4302 j = file_bunches[i].begin();
4303 j != file_bunches[i].end(); j++){
4304 os<<serializeString(j->name);
4305 os<<serializeLongString(j->data);
4309 std::string s = os.str();
4310 verbosestream<<"Server::sendRequestedMedia(): bunch "
4311 <<i<<"/"<<num_bunches
4312 <<" files="<<file_bunches[i].size()
4313 <<" size=" <<s.size()<<std::endl;
4314 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4316 m_con.Send(peer_id, 0, data, true);
4320 void Server::sendDetachedInventory(const std::string &name, u16 peer_id)
4322 if(m_detached_inventories.count(name) == 0){
4323 errorstream<<__FUNCTION_NAME<<": \""<<name<<"\" not found"<<std::endl;
4326 Inventory *inv = m_detached_inventories[name];
4328 std::ostringstream os(std::ios_base::binary);
4329 writeU16(os, TOCLIENT_DETACHED_INVENTORY);
4330 os<<serializeString(name);
4334 std::string s = os.str();
4335 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4337 m_con.Send(peer_id, 0, data, true);
4340 void Server::sendDetachedInventoryToAll(const std::string &name)
4342 DSTACK(__FUNCTION_NAME);
4344 for(core::map<u16, RemoteClient*>::Iterator
4345 i = m_clients.getIterator();
4346 i.atEnd() == false; i++){
4347 RemoteClient *client = i.getNode()->getValue();
4348 sendDetachedInventory(name, client->peer_id);
4352 void Server::sendDetachedInventories(u16 peer_id)
4354 DSTACK(__FUNCTION_NAME);
4356 for(std::map<std::string, Inventory*>::iterator
4357 i = m_detached_inventories.begin();
4358 i != m_detached_inventories.end(); i++){
4359 const std::string &name = i->first;
4360 //Inventory *inv = i->second;
4361 sendDetachedInventory(name, peer_id);
4369 void Server::DiePlayer(u16 peer_id)
4371 DSTACK(__FUNCTION_NAME);
4373 PlayerSAO *playersao = getPlayerSAO(peer_id);
4376 infostream<<"Server::DiePlayer(): Player "
4377 <<playersao->getPlayer()->getName()
4378 <<" dies"<<std::endl;
4380 playersao->setHP(0);
4382 // Trigger scripted stuff
4383 scriptapi_on_dieplayer(m_lua, playersao);
4385 SendPlayerHP(peer_id);
4386 SendDeathscreen(m_con, peer_id, false, v3f(0,0,0));
4389 void Server::RespawnPlayer(u16 peer_id)
4391 DSTACK(__FUNCTION_NAME);
4393 PlayerSAO *playersao = getPlayerSAO(peer_id);
4396 infostream<<"Server::RespawnPlayer(): Player "
4397 <<playersao->getPlayer()->getName()
4398 <<" respawns"<<std::endl;
4400 playersao->setHP(PLAYER_MAX_HP);
4402 bool repositioned = scriptapi_on_respawnplayer(m_lua, playersao);
4404 v3f pos = findSpawnPos(m_env->getServerMap());
4405 playersao->setPos(pos);
4409 void Server::UpdateCrafting(u16 peer_id)
4411 DSTACK(__FUNCTION_NAME);
4413 Player* player = m_env->getPlayer(peer_id);
4416 // Get a preview for crafting
4418 getCraftingResult(&player->inventory, preview, false, this);
4420 // Put the new preview in
4421 InventoryList *plist = player->inventory.getList("craftpreview");
4423 assert(plist->getSize() >= 1);
4424 plist->changeItem(0, preview);
4427 RemoteClient* Server::getClient(u16 peer_id)
4429 DSTACK(__FUNCTION_NAME);
4430 //JMutexAutoLock lock(m_con_mutex);
4431 core::map<u16, RemoteClient*>::Node *n;
4432 n = m_clients.find(peer_id);
4433 // A client should exist for all peers
4435 return n->getValue();
4438 std::wstring Server::getStatusString()
4440 std::wostringstream os(std::ios_base::binary);
4443 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4445 os<<L", uptime="<<m_uptime.get();
4446 // Information about clients
4448 for(core::map<u16, RemoteClient*>::Iterator
4449 i = m_clients.getIterator();
4450 i.atEnd() == false; i++)
4452 // Get client and check that it is valid
4453 RemoteClient *client = i.getNode()->getValue();
4454 assert(client->peer_id == i.getNode()->getKey());
4455 if(client->serialization_version == SER_FMT_VER_INVALID)
4458 Player *player = m_env->getPlayer(client->peer_id);
4459 // Get name of player
4460 std::wstring name = L"unknown";
4462 name = narrow_to_wide(player->getName());
4463 // Add name to information string
4467 if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
4468 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4469 if(g_settings->get("motd") != "")
4470 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4474 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
4476 std::set<std::string> privs;
4477 scriptapi_get_auth(m_lua, name, NULL, &privs);
4481 bool Server::checkPriv(const std::string &name, const std::string &priv)
4483 std::set<std::string> privs = getPlayerEffectivePrivs(name);
4484 return (privs.count(priv) != 0);
4487 void Server::reportPrivsModified(const std::string &name)
4490 for(core::map<u16, RemoteClient*>::Iterator
4491 i = m_clients.getIterator();
4492 i.atEnd() == false; i++){
4493 RemoteClient *client = i.getNode()->getValue();
4494 Player *player = m_env->getPlayer(client->peer_id);
4495 reportPrivsModified(player->getName());
4498 Player *player = m_env->getPlayer(name.c_str());
4501 SendPlayerPrivileges(player->peer_id);
4502 PlayerSAO *sao = player->getPlayerSAO();
4505 sao->updatePrivileges(
4506 getPlayerEffectivePrivs(name),
4511 void Server::reportInventoryFormspecModified(const std::string &name)
4513 Player *player = m_env->getPlayer(name.c_str());
4516 SendPlayerInventoryFormspec(player->peer_id);
4519 // Saves g_settings to configpath given at initialization
4520 void Server::saveConfig()
4522 if(m_path_config != "")
4523 g_settings->updateConfigFile(m_path_config.c_str());
4526 void Server::notifyPlayer(const char *name, const std::wstring msg)
4528 Player *player = m_env->getPlayer(name);
4531 SendChatMessage(player->peer_id, std::wstring(L"Server: -!- ")+msg);
4534 void Server::notifyPlayers(const std::wstring msg)
4536 BroadcastChatMessage(msg);
4539 void Server::queueBlockEmerge(v3s16 blockpos, bool allow_generate)
4543 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
4544 m_emerge_queue.addBlock(PEER_ID_INEXISTENT, blockpos, flags);
4547 Inventory* Server::createDetachedInventory(const std::string &name)
4549 if(m_detached_inventories.count(name) > 0){
4550 infostream<<"Server clearing detached inventory \""<<name<<"\""<<std::endl;
4551 delete m_detached_inventories[name];
4553 infostream<<"Server creating detached inventory \""<<name<<"\""<<std::endl;
4555 Inventory *inv = new Inventory(m_itemdef);
4557 m_detached_inventories[name] = inv;
4558 sendDetachedInventoryToAll(name);
4565 BoolScopeSet(bool *dst, bool val):
4568 m_orig_state = *m_dst;
4573 *m_dst = m_orig_state;
4580 // actions: time-reversed list
4581 // Return value: success/failure
4582 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
4583 std::list<std::string> *log)
4585 infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
4586 ServerMap *map = (ServerMap*)(&m_env->getMap());
4587 // Disable rollback report sink while reverting
4588 BoolScopeSet rollback_scope_disable(&m_rollback_sink_enabled, false);
4590 // Fail if no actions to handle
4591 if(actions.empty()){
4592 log->push_back("Nothing to do.");
4599 for(std::list<RollbackAction>::const_iterator
4600 i = actions.begin();
4601 i != actions.end(); i++)
4603 const RollbackAction &action = *i;
4605 bool success = action.applyRevert(map, this, this);
4608 std::ostringstream os;
4609 os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
4610 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
4612 log->push_back(os.str());
4614 std::ostringstream os;
4615 os<<"Succesfully reverted step ("<<num_tried<<") "<<action.toString();
4616 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
4618 log->push_back(os.str());
4622 infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
4623 <<" failed"<<std::endl;
4625 // Call it done if less than half failed
4626 return num_failed <= num_tried/2;
4629 // IGameDef interface
4631 IItemDefManager* Server::getItemDefManager()
4635 INodeDefManager* Server::getNodeDefManager()
4639 ICraftDefManager* Server::getCraftDefManager()
4643 ITextureSource* Server::getTextureSource()
4647 u16 Server::allocateUnknownNodeId(const std::string &name)
4649 return m_nodedef->allocateDummy(name);
4651 ISoundManager* Server::getSoundManager()
4653 return &dummySoundManager;
4655 MtEventManager* Server::getEventManager()
4659 IRollbackReportSink* Server::getRollbackReportSink()
4661 if(!m_rollback_sink_enabled)
4666 IWritableItemDefManager* Server::getWritableItemDefManager()
4670 IWritableNodeDefManager* Server::getWritableNodeDefManager()
4674 IWritableCraftDefManager* Server::getWritableCraftDefManager()
4679 const ModSpec* Server::getModSpec(const std::string &modname)
4681 for(core::list<ModSpec>::Iterator i = m_mods.begin();
4682 i != m_mods.end(); i++){
4683 const ModSpec &mod = *i;
4684 if(mod.name == modname)
4689 void Server::getModNames(core::list<std::string> &modlist)
4691 for(core::list<ModSpec>::Iterator i = m_mods.begin(); i != m_mods.end(); i++)
4693 modlist.push_back((*i).name);
4696 std::string Server::getBuiltinLuaPath()
4698 return porting::path_share + DIR_DELIM + "builtin";
4701 v3f findSpawnPos(ServerMap &map)
4703 //return v3f(50,50,50)*BS;
4708 nodepos = v2s16(0,0);
4713 // Try to find a good place a few times
4714 for(s32 i=0; i<1000; i++)
4717 // We're going to try to throw the player to this position
4718 v2s16 nodepos2d = v2s16(-range + (myrand()%(range*2)),
4719 -range + (myrand()%(range*2)));
4720 //v2s16 sectorpos = getNodeSectorPos(nodepos2d);
4721 // Get ground height at point (fallbacks to heightmap function)
4722 s16 groundheight = map.findGroundLevel(nodepos2d);
4723 // Don't go underwater
4724 if(groundheight < WATER_LEVEL)
4726 //infostream<<"-> Underwater"<<std::endl;
4729 // Don't go to high places
4730 if(groundheight > WATER_LEVEL + 4)
4732 //infostream<<"-> Underwater"<<std::endl;
4736 nodepos = v3s16(nodepos2d.X, groundheight-2, nodepos2d.Y);
4737 bool is_good = false;
4739 for(s32 i=0; i<10; i++){
4740 v3s16 blockpos = getNodeBlockPos(nodepos);
4741 map.emergeBlock(blockpos, true);
4742 MapNode n = map.getNodeNoEx(nodepos);
4743 if(n.getContent() == CONTENT_AIR){
4754 // Found a good place
4755 //infostream<<"Searched through "<<i<<" places."<<std::endl;
4761 return intToFloat(nodepos, BS);
4764 PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id)
4766 RemotePlayer *player = NULL;
4767 bool newplayer = false;
4770 Try to get an existing player
4772 player = static_cast<RemotePlayer*>(m_env->getPlayer(name));
4774 // If player is already connected, cancel
4775 if(player != NULL && player->peer_id != 0)
4777 infostream<<"emergePlayer(): Player already connected"<<std::endl;
4782 If player with the wanted peer_id already exists, cancel.
4784 if(m_env->getPlayer(peer_id) != NULL)
4786 infostream<<"emergePlayer(): Player with wrong name but same"
4787 " peer_id already exists"<<std::endl;
4792 Create a new player if it doesn't exist yet
4797 player = new RemotePlayer(this);
4798 player->updateName(name);
4800 /* Set player position */
4801 infostream<<"Server: Finding spawn place for player \""
4802 <<name<<"\""<<std::endl;
4803 v3f pos = findSpawnPos(m_env->getServerMap());
4804 player->setPosition(pos);
4806 /* Add player to environment */
4807 m_env->addPlayer(player);
4811 Create a new player active object
4813 PlayerSAO *playersao = new PlayerSAO(m_env, player, peer_id,
4814 getPlayerEffectivePrivs(player->getName()),
4817 /* Add object to environment */
4818 m_env->addActiveObject(playersao);
4822 scriptapi_on_newplayer(m_lua, playersao);
4824 scriptapi_on_joinplayer(m_lua, playersao);
4829 void Server::handlePeerChange(PeerChange &c)
4831 JMutexAutoLock envlock(m_env_mutex);
4832 JMutexAutoLock conlock(m_con_mutex);
4834 if(c.type == PEER_ADDED)
4841 core::map<u16, RemoteClient*>::Node *n;
4842 n = m_clients.find(c.peer_id);
4843 // The client shouldn't already exist
4847 RemoteClient *client = new RemoteClient();
4848 client->peer_id = c.peer_id;
4849 m_clients.insert(client->peer_id, client);
4852 else if(c.type == PEER_REMOVED)
4859 core::map<u16, RemoteClient*>::Node *n;
4860 n = m_clients.find(c.peer_id);
4861 // The client should exist
4865 Mark objects to be not known by the client
4867 RemoteClient *client = n->getValue();
4869 for(core::map<u16, bool>::Iterator
4870 i = client->m_known_objects.getIterator();
4871 i.atEnd()==false; i++)
4874 u16 id = i.getNode()->getKey();
4875 ServerActiveObject* obj = m_env->getActiveObject(id);
4877 if(obj && obj->m_known_by_count > 0)
4878 obj->m_known_by_count--;
4882 Clear references to playing sounds
4884 for(std::map<s32, ServerPlayingSound>::iterator
4885 i = m_playing_sounds.begin();
4886 i != m_playing_sounds.end();)
4888 ServerPlayingSound &psound = i->second;
4889 psound.clients.erase(c.peer_id);
4890 if(psound.clients.size() == 0)
4891 m_playing_sounds.erase(i++);
4896 Player *player = m_env->getPlayer(c.peer_id);
4898 // Collect information about leaving in chat
4899 std::wstring message;
4903 std::wstring name = narrow_to_wide(player->getName());
4906 message += L" left the game.";
4908 message += L" (timed out)";
4912 /* Run scripts and remove from environment */
4916 PlayerSAO *playersao = player->getPlayerSAO();
4919 scriptapi_on_leaveplayer(m_lua, playersao);
4921 playersao->disconnected();
4931 std::ostringstream os(std::ios_base::binary);
4932 for(core::map<u16, RemoteClient*>::Iterator
4933 i = m_clients.getIterator();
4934 i.atEnd() == false; i++)
4936 RemoteClient *client = i.getNode()->getValue();
4937 assert(client->peer_id == i.getNode()->getKey());
4938 if(client->serialization_version == SER_FMT_VER_INVALID)
4941 Player *player = m_env->getPlayer(client->peer_id);
4944 // Get name of player
4945 os<<player->getName()<<" ";
4948 actionstream<<player->getName()<<" "
4949 <<(c.timeout?"times out.":"leaves game.")
4950 <<" List of players: "
4951 <<os.str()<<std::endl;
4956 delete m_clients[c.peer_id];
4957 m_clients.remove(c.peer_id);
4959 // Send player info to all remaining clients
4960 //SendPlayerInfos();
4962 // Send leave chat message to all remaining clients
4963 if(message.length() != 0)
4964 BroadcastChatMessage(message);
4973 void Server::handlePeerChanges()
4975 while(m_peer_change_queue.size() > 0)
4977 PeerChange c = m_peer_change_queue.pop_front();
4979 verbosestream<<"Server: Handling peer change: "
4980 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4983 handlePeerChange(c);
4987 void dedicated_server_loop(Server &server, bool &kill)
4989 DSTACK(__FUNCTION_NAME);
4991 verbosestream<<"dedicated_server_loop()"<<std::endl;
4993 IntervalLimiter m_profiler_interval;
4997 float steplen = g_settings->getFloat("dedicated_server_step");
4998 // This is kind of a hack but can be done like this
4999 // because server.step() is very light
5001 ScopeProfiler sp(g_profiler, "dedicated server sleep");
5002 sleep_ms((int)(steplen*1000.0));
5004 server.step(steplen);
5006 if(server.getShutdownRequested() || kill)
5008 infostream<<"Dedicated server quitting"<<std::endl;
5015 float profiler_print_interval =
5016 g_settings->getFloat("profiler_print_interval");
5017 if(profiler_print_interval != 0)
5019 if(m_profiler_interval.step(steplen, profiler_print_interval))
5021 infostream<<"Profiler:"<<std::endl;
5022 g_profiler->print(infostream);
5023 g_profiler->clear();