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"
32 #include "serverobject.h"
37 #include "scriptapi.h"
42 #include "content_mapnode.h"
43 #include "content_nodemeta.h"
44 #include "content_abm.h"
45 #include "content_sao.h"
50 #include "sound.h" // dummySoundManager
51 #include "event_manager.h"
53 #include "util/string.h"
54 #include "util/pointedthing.h"
55 #include "util/mathconstants.h"
58 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
60 #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
62 class MapEditEventIgnorer
65 MapEditEventIgnorer(bool *flag):
74 ~MapEditEventIgnorer()
87 class MapEditEventAreaIgnorer
90 MapEditEventAreaIgnorer(VoxelArea *ignorevariable, const VoxelArea &a):
91 m_ignorevariable(ignorevariable)
93 if(m_ignorevariable->getVolume() == 0)
94 *m_ignorevariable = a;
96 m_ignorevariable = NULL;
99 ~MapEditEventAreaIgnorer()
103 assert(m_ignorevariable->getVolume() != 0);
104 *m_ignorevariable = VoxelArea();
109 VoxelArea *m_ignorevariable;
112 void * ServerThread::Thread()
116 log_register_thread("ServerThread");
118 DSTACK(__FUNCTION_NAME);
120 BEGIN_DEBUG_EXCEPTION_HANDLER
125 //TimeTaker timer("AsyncRunStep() + Receive()");
128 //TimeTaker timer("AsyncRunStep()");
129 m_server->AsyncRunStep();
132 //infostream<<"Running m_server->Receive()"<<std::endl;
135 catch(con::NoIncomingDataException &e)
138 catch(con::PeerNotFoundException &e)
140 infostream<<"Server: PeerNotFoundException"<<std::endl;
142 catch(con::ConnectionBindFailed &e)
144 m_server->setAsyncFatalError(e.what());
148 m_server->setAsyncFatalError(e.what());
152 END_DEBUG_EXCEPTION_HANDLER(errorstream)
157 void * EmergeThread::Thread()
161 log_register_thread("EmergeThread");
163 DSTACK(__FUNCTION_NAME);
165 BEGIN_DEBUG_EXCEPTION_HANDLER
167 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
169 v3s16 last_tried_pos(-32768,-32768,-32768); // For error output
172 Get block info from queue, emerge them and send them
175 After queue is empty, exit.
179 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
183 SharedPtr<QueuedBlockEmerge> q(qptr);
191 Do not generate over-limit
193 if(blockpos_over_limit(p))
196 //infostream<<"EmergeThread::Thread(): running"<<std::endl;
198 //TimeTaker timer("block emerge");
201 Try to emerge it from somewhere.
203 If it is only wanted as optional, only loading from disk
208 Check if any peer wants it as non-optional. In that case it
211 Also decrement the emerge queue count in clients.
214 bool only_from_disk = true;
217 core::map<u16, u8>::Iterator i;
218 for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
220 //u16 peer_id = i.getNode()->getKey();
223 u8 flags = i.getNode()->getValue();
224 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
225 only_from_disk = false;
230 if(enable_mapgen_debug_info)
231 infostream<<"EmergeThread: p="
232 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
233 <<"only_from_disk="<<only_from_disk<<std::endl;
235 ServerMap &map = ((ServerMap&)m_server->m_env->getMap());
237 MapBlock *block = NULL;
238 bool got_block = true;
239 core::map<v3s16, MapBlock*> modified_blocks;
242 Try to fetch block from memory or disk.
243 If not found and asked to generate, initialize generator.
246 bool started_generate = false;
247 mapgen::BlockMakeData data;
250 JMutexAutoLock envlock(m_server->m_env_mutex);
252 // Load sector if it isn't loaded
253 if(map.getSectorNoGenerateNoEx(p2d) == NULL)
254 map.loadSectorMeta(p2d);
256 // Attempt to load block
257 block = map.getBlockNoCreateNoEx(p);
258 if(!block || block->isDummy() || !block->isGenerated())
260 if(enable_mapgen_debug_info)
261 infostream<<"EmergeThread: not in memory, "
262 <<"attempting to load from disk"<<std::endl;
264 block = map.loadBlock(p);
267 // If could not load and allowed to generate, start generation
268 // inside this same envlock
269 if(only_from_disk == false &&
270 (block == NULL || block->isGenerated() == false)){
271 if(enable_mapgen_debug_info)
272 infostream<<"EmergeThread: generating"<<std::endl;
273 started_generate = true;
275 map.initBlockMake(&data, p);
280 If generator was initialized, generate now when envlock is free.
285 ScopeProfiler sp(g_profiler, "EmergeThread: mapgen::make_block",
287 TimeTaker t("mapgen::make_block()");
289 mapgen::make_block(&data);
291 if(enable_mapgen_debug_info == false)
292 t.stop(true); // Hide output
296 // Lock environment again to access the map
297 JMutexAutoLock envlock(m_server->m_env_mutex);
299 ScopeProfiler sp(g_profiler, "EmergeThread: after "
300 "mapgen::make_block (envlock)", SPT_AVG);
302 // Blit data back on map, update lighting, add mobs and
303 // whatever this does
304 map.finishBlockMake(&data, modified_blocks);
307 block = map.getBlockNoCreateNoEx(p);
309 // If block doesn't exist, don't try doing anything with it
310 // This happens if the block is not in generation boundaries
315 Do some post-generate stuff
318 v3s16 minp = data.blockpos_min*MAP_BLOCKSIZE;
319 v3s16 maxp = data.blockpos_max*MAP_BLOCKSIZE +
320 v3s16(1,1,1)*(MAP_BLOCKSIZE-1);
323 Ignore map edit events, they will not need to be
324 sent to anybody because the block hasn't been sent
327 //MapEditEventIgnorer ign(&m_server->m_ignore_map_edit_events);
328 MapEditEventAreaIgnorer ign(
329 &m_server->m_ignore_map_edit_events_area,
330 VoxelArea(minp, maxp));
332 TimeTaker timer("on_generated");
333 scriptapi_environment_on_generated(m_server->m_lua,
334 minp, maxp, mapgen::get_blockseed(data.seed, minp));
335 /*int t = timer.stop(true);
336 dstream<<"on_generated took "<<t<<"ms"<<std::endl;*/
339 if(enable_mapgen_debug_info)
340 infostream<<"EmergeThread: ended up with: "
341 <<analyze_block(block)<<std::endl;
343 // Activate objects and stuff
344 m_server->m_env->activateBlock(block, 0);
352 Set sent status of modified blocks on clients
355 // NOTE: Server's clients are also behind the connection mutex
356 JMutexAutoLock lock(m_server->m_con_mutex);
359 Add the originally fetched block to the modified list
363 modified_blocks.insert(p, block);
367 Set the modified blocks unsent for all the clients
370 for(core::map<u16, RemoteClient*>::Iterator
371 i = m_server->m_clients.getIterator();
372 i.atEnd() == false; i++)
374 RemoteClient *client = i.getNode()->getValue();
376 if(modified_blocks.size() > 0)
378 // Remove block from sent history
379 client->SetBlocksNotSent(modified_blocks);
383 catch(VersionMismatchException &e)
385 std::ostringstream err;
386 err<<"World data version mismatch in MapBlock "<<PP(last_tried_pos)<<std::endl;
387 err<<"----"<<std::endl;
388 err<<"\""<<e.what()<<"\""<<std::endl;
389 err<<"See debug.txt."<<std::endl;
390 err<<"World probably saved by a newer version of Minetest."<<std::endl;
391 m_server->setAsyncFatalError(err.str());
393 catch(SerializationError &e)
395 std::ostringstream err;
396 err<<"Invalid data in MapBlock "<<PP(last_tried_pos)<<std::endl;
397 err<<"----"<<std::endl;
398 err<<"\""<<e.what()<<"\""<<std::endl;
399 err<<"See debug.txt."<<std::endl;
400 err<<"You can ignore this using [ignore_world_load_errors = true]."<<std::endl;
401 m_server->setAsyncFatalError(err.str());
404 END_DEBUG_EXCEPTION_HANDLER(errorstream)
406 log_deregister_thread();
411 v3f ServerSoundParams::getPos(ServerEnvironment *env, bool *pos_exists) const
413 if(pos_exists) *pos_exists = false;
418 if(pos_exists) *pos_exists = true;
423 ServerActiveObject *sao = env->getActiveObject(object);
426 if(pos_exists) *pos_exists = true;
427 return sao->getBasePosition(); }
432 void RemoteClient::GetNextBlocks(Server *server, float dtime,
433 core::array<PrioritySortedBlockTransfer> &dest)
435 DSTACK(__FUNCTION_NAME);
438 TimeTaker timer("RemoteClient::GetNextBlocks", &timer_result);*/
441 m_nothing_to_send_pause_timer -= dtime;
442 m_nearest_unsent_reset_timer += dtime;
444 if(m_nothing_to_send_pause_timer >= 0)
447 Player *player = server->m_env->getPlayer(peer_id);
448 // This can happen sometimes; clients and players are not in perfect sync.
452 // Won't send anything if already sending
453 if(m_blocks_sending.size() >= g_settings->getU16
454 ("max_simultaneous_block_sends_per_client"))
456 //infostream<<"Not sending any blocks, Queue full."<<std::endl;
460 //TimeTaker timer("RemoteClient::GetNextBlocks");
462 v3f playerpos = player->getPosition();
463 v3f playerspeed = player->getSpeed();
464 v3f playerspeeddir(0,0,0);
465 if(playerspeed.getLength() > 1.0*BS)
466 playerspeeddir = playerspeed / playerspeed.getLength();
467 // Predict to next block
468 v3f playerpos_predicted = playerpos + playerspeeddir*MAP_BLOCKSIZE*BS;
470 v3s16 center_nodepos = floatToInt(playerpos_predicted, BS);
472 v3s16 center = getNodeBlockPos(center_nodepos);
474 // Camera position and direction
475 v3f camera_pos = player->getEyePosition();
476 v3f camera_dir = v3f(0,0,1);
477 camera_dir.rotateYZBy(player->getPitch());
478 camera_dir.rotateXZBy(player->getYaw());
480 /*infostream<<"camera_dir=("<<camera_dir.X<<","<<camera_dir.Y<<","
481 <<camera_dir.Z<<")"<<std::endl;*/
484 Get the starting value of the block finder radius.
487 if(m_last_center != center)
489 m_nearest_unsent_d = 0;
490 m_last_center = center;
493 /*infostream<<"m_nearest_unsent_reset_timer="
494 <<m_nearest_unsent_reset_timer<<std::endl;*/
496 // Reset periodically to workaround for some bugs or stuff
497 if(m_nearest_unsent_reset_timer > 20.0)
499 m_nearest_unsent_reset_timer = 0;
500 m_nearest_unsent_d = 0;
501 //infostream<<"Resetting m_nearest_unsent_d for "
502 // <<server->getPlayerName(peer_id)<<std::endl;
505 //s16 last_nearest_unsent_d = m_nearest_unsent_d;
506 s16 d_start = m_nearest_unsent_d;
508 //infostream<<"d_start="<<d_start<<std::endl;
510 u16 max_simul_sends_setting = g_settings->getU16
511 ("max_simultaneous_block_sends_per_client");
512 u16 max_simul_sends_usually = max_simul_sends_setting;
515 Check the time from last addNode/removeNode.
517 Decrease send rate if player is building stuff.
519 m_time_from_building += dtime;
520 if(m_time_from_building < g_settings->getFloat(
521 "full_block_send_enable_min_time_from_building"))
523 max_simul_sends_usually
524 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
528 Number of blocks sending + number of blocks selected for sending
530 u32 num_blocks_selected = m_blocks_sending.size();
533 next time d will be continued from the d from which the nearest
534 unsent block was found this time.
536 This is because not necessarily any of the blocks found this
537 time are actually sent.
539 s32 new_nearest_unsent_d = -1;
541 s16 d_max = g_settings->getS16("max_block_send_distance");
542 s16 d_max_gen = g_settings->getS16("max_block_generate_distance");
544 // Don't loop very much at a time
545 s16 max_d_increment_at_time = 2;
546 if(d_max > d_start + max_d_increment_at_time)
547 d_max = d_start + max_d_increment_at_time;
548 /*if(d_max_gen > d_start+2)
549 d_max_gen = d_start+2;*/
551 //infostream<<"Starting from "<<d_start<<std::endl;
553 s32 nearest_emerged_d = -1;
554 s32 nearest_emergefull_d = -1;
555 s32 nearest_sent_d = -1;
556 bool queue_is_full = false;
559 for(d = d_start; d <= d_max; d++)
561 /*errorstream<<"checking d="<<d<<" for "
562 <<server->getPlayerName(peer_id)<<std::endl;*/
563 //infostream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
566 If m_nearest_unsent_d was changed by the EmergeThread
567 (it can change it to 0 through SetBlockNotSent),
569 Else update m_nearest_unsent_d
571 /*if(m_nearest_unsent_d != last_nearest_unsent_d)
573 d = m_nearest_unsent_d;
574 last_nearest_unsent_d = m_nearest_unsent_d;
578 Get the border/face dot coordinates of a "d-radiused"
581 core::list<v3s16> list;
582 getFacePositions(list, d);
584 core::list<v3s16>::Iterator li;
585 for(li=list.begin(); li!=list.end(); li++)
587 v3s16 p = *li + center;
591 - Don't allow too many simultaneous transfers
592 - EXCEPT when the blocks are very close
594 Also, don't send blocks that are already flying.
597 // Start with the usual maximum
598 u16 max_simul_dynamic = max_simul_sends_usually;
600 // If block is very close, allow full maximum
601 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
602 max_simul_dynamic = max_simul_sends_setting;
604 // Don't select too many blocks for sending
605 if(num_blocks_selected >= max_simul_dynamic)
607 queue_is_full = true;
608 goto queue_full_break;
611 // Don't send blocks that are currently being transferred
612 if(m_blocks_sending.find(p) != NULL)
618 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
619 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
620 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
621 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
622 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
623 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
626 // If this is true, inexistent block will be made from scratch
627 bool generate = d <= d_max_gen;
630 /*// Limit the generating area vertically to 2/3
631 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
634 // Limit the send area vertically to 1/2
635 if(abs(p.Y - center.Y) > d_max / 2)
641 If block is far away, don't generate it unless it is
647 // Block center y in nodes
648 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
649 // Don't generate if it's very high or very low
650 if(y < -64 || y > 64)
654 v2s16 p2d_nodes_center(
658 // Get ground height in nodes
659 s16 gh = server->m_env->getServerMap().findGroundLevel(
662 // If differs a lot, don't generate
663 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
665 // Actually, don't even send it
671 //infostream<<"d="<<d<<std::endl;
674 Don't generate or send if not in sight
675 FIXME This only works if the client uses a small enough
676 FOV setting. The default of 72 degrees is fine.
679 float camera_fov = (72.0*M_PI/180) * 4./3.;
680 if(isBlockInSight(p, camera_pos, camera_dir, camera_fov, 10000*BS) == false)
686 Don't send already sent blocks
689 if(m_blocks_sent.find(p) != NULL)
696 Check if map has this block
698 MapBlock *block = server->m_env->getMap().getBlockNoCreateNoEx(p);
700 bool surely_not_found_on_disk = false;
701 bool block_is_invalid = false;
704 // Reset usage timer, this block will be of use in the future.
705 block->resetUsageTimer();
707 // Block is dummy if data doesn't exist.
708 // It means it has been not found from disk and not generated
711 surely_not_found_on_disk = true;
714 // Block is valid if lighting is up-to-date and data exists
715 if(block->isValid() == false)
717 block_is_invalid = true;
720 /*if(block->isFullyGenerated() == false)
722 block_is_invalid = true;
727 ServerMap *map = (ServerMap*)(&server->m_env->getMap());
728 v2s16 chunkpos = map->sector_to_chunk(p2d);
729 if(map->chunkNonVolatile(chunkpos) == false)
730 block_is_invalid = true;
732 if(block->isGenerated() == false)
733 block_is_invalid = true;
736 If block is not close, don't send it unless it is near
739 Block is near ground level if night-time mesh
740 differs from day-time mesh.
744 if(block->getDayNightDiff() == false)
751 If block has been marked to not exist on disk (dummy)
752 and generating new ones is not wanted, skip block.
754 if(generate == false && surely_not_found_on_disk == true)
761 Add inexistent block to emerge queue.
763 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
765 //TODO: Get value from somewhere
766 // Allow only one block in emerge queue
767 //if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
768 // Allow two blocks in queue per client
769 //if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
771 // Make it more responsive when needing to generate stuff
772 if(surely_not_found_on_disk)
774 if(server->m_emerge_queue.peerItemCount(peer_id) < max_emerge)
776 //infostream<<"Adding block to emerge queue"<<std::endl;
778 // Add it to the emerge queue and trigger the thread
781 if(generate == false)
782 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
784 server->m_emerge_queue.addBlock(peer_id, p, flags);
785 server->m_emergethread.trigger();
787 if(nearest_emerged_d == -1)
788 nearest_emerged_d = d;
790 if(nearest_emergefull_d == -1)
791 nearest_emergefull_d = d;
798 if(nearest_sent_d == -1)
802 Add block to send queue
805 /*errorstream<<"sending from d="<<d<<" to "
806 <<server->getPlayerName(peer_id)<<std::endl;*/
808 PrioritySortedBlockTransfer q((float)d, p, peer_id);
812 num_blocks_selected += 1;
817 //infostream<<"Stopped at "<<d<<std::endl;
819 // If nothing was found for sending and nothing was queued for
820 // emerging, continue next time browsing from here
821 if(nearest_emerged_d != -1){
822 new_nearest_unsent_d = nearest_emerged_d;
823 } else if(nearest_emergefull_d != -1){
824 new_nearest_unsent_d = nearest_emergefull_d;
826 if(d > g_settings->getS16("max_block_send_distance")){
827 new_nearest_unsent_d = 0;
828 m_nothing_to_send_pause_timer = 2.0;
829 /*infostream<<"GetNextBlocks(): d wrapped around for "
830 <<server->getPlayerName(peer_id)
831 <<"; setting to 0 and pausing"<<std::endl;*/
833 if(nearest_sent_d != -1)
834 new_nearest_unsent_d = nearest_sent_d;
836 new_nearest_unsent_d = d;
840 if(new_nearest_unsent_d != -1)
841 m_nearest_unsent_d = new_nearest_unsent_d;
843 /*timer_result = timer.stop(true);
844 if(timer_result != 0)
845 infostream<<"GetNextBlocks timeout: "<<timer_result<<" (!=0)"<<std::endl;*/
848 void RemoteClient::GotBlock(v3s16 p)
850 if(m_blocks_sending.find(p) != NULL)
851 m_blocks_sending.remove(p);
854 /*infostream<<"RemoteClient::GotBlock(): Didn't find in"
855 " m_blocks_sending"<<std::endl;*/
856 m_excess_gotblocks++;
858 m_blocks_sent.insert(p, true);
861 void RemoteClient::SentBlock(v3s16 p)
863 if(m_blocks_sending.find(p) == NULL)
864 m_blocks_sending.insert(p, 0.0);
866 infostream<<"RemoteClient::SentBlock(): Sent block"
867 " already in m_blocks_sending"<<std::endl;
870 void RemoteClient::SetBlockNotSent(v3s16 p)
872 m_nearest_unsent_d = 0;
874 if(m_blocks_sending.find(p) != NULL)
875 m_blocks_sending.remove(p);
876 if(m_blocks_sent.find(p) != NULL)
877 m_blocks_sent.remove(p);
880 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
882 m_nearest_unsent_d = 0;
884 for(core::map<v3s16, MapBlock*>::Iterator
885 i = blocks.getIterator();
886 i.atEnd()==false; i++)
888 v3s16 p = i.getNode()->getKey();
890 if(m_blocks_sending.find(p) != NULL)
891 m_blocks_sending.remove(p);
892 if(m_blocks_sent.find(p) != NULL)
893 m_blocks_sent.remove(p);
901 PlayerInfo::PlayerInfo()
907 void PlayerInfo::PrintLine(std::ostream *s)
910 (*s)<<"\""<<name<<"\" ("
911 <<(position.X/10)<<","<<(position.Y/10)
912 <<","<<(position.Z/10)<<") ";
914 (*s)<<" avg_rtt="<<avg_rtt;
923 const std::string &path_world,
924 const std::string &path_config,
925 const SubgameSpec &gamespec,
926 bool simple_singleplayer_mode
928 m_path_world(path_world),
929 m_path_config(path_config),
930 m_gamespec(gamespec),
931 m_simple_singleplayer_mode(simple_singleplayer_mode),
932 m_async_fatal_error(""),
934 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
935 m_banmanager(path_world+DIR_DELIM+"ipban.txt"),
937 m_rollback_sink_enabled(true),
938 m_enable_rollback_recording(false),
940 m_itemdef(createItemDefManager()),
941 m_nodedef(createNodeDefManager()),
942 m_craftdef(createCraftDefManager()),
943 m_event(new EventManager()),
945 m_emergethread(this),
946 m_time_of_day_send_timer(0),
948 m_shutdown_requested(false),
949 m_ignore_map_edit_events(false),
950 m_ignore_map_edit_events_peer_id(0)
952 m_liquid_transform_timer = 0.0;
953 m_print_info_timer = 0.0;
954 m_objectdata_timer = 0.0;
955 m_emergethread_trigger_timer = 0.0;
956 m_savemap_timer = 0.0;
960 m_step_dtime_mutex.Init();
964 throw ServerError("Supplied empty world path");
966 if(!gamespec.isValid())
967 throw ServerError("Supplied invalid gamespec");
969 infostream<<"Server created for gameid \""<<m_gamespec.id<<"\"";
970 if(m_simple_singleplayer_mode)
971 infostream<<" in simple singleplayer mode"<<std::endl;
973 infostream<<std::endl;
974 infostream<<"- world: "<<m_path_world<<std::endl;
975 infostream<<"- config: "<<m_path_config<<std::endl;
976 infostream<<"- game: "<<m_gamespec.path<<std::endl;
978 // Create rollback manager
979 std::string rollback_path = m_path_world+DIR_DELIM+"rollback.txt";
980 m_rollback = createRollbackManager(rollback_path, this);
982 // Add world mod search path
983 m_modspaths.push_front(m_path_world + DIR_DELIM + "worldmods");
984 // Add addon mod search path
985 for(std::set<std::string>::const_iterator i = m_gamespec.mods_paths.begin();
986 i != m_gamespec.mods_paths.end(); i++)
987 m_modspaths.push_front((*i));
989 // Print out mod search paths
990 for(core::list<std::string>::Iterator i = m_modspaths.begin();
991 i != m_modspaths.end(); i++){
992 std::string modspath = *i;
993 infostream<<"- mods: "<<modspath<<std::endl;
996 // Path to builtin.lua
997 std::string builtinpath = getBuiltinLuaPath() + DIR_DELIM + "builtin.lua";
999 // Create world if it doesn't exist
1000 if(!initializeWorld(m_path_world, m_gamespec.id))
1001 throw ServerError("Failed to initialize world");
1004 JMutexAutoLock envlock(m_env_mutex);
1005 JMutexAutoLock conlock(m_con_mutex);
1007 // Initialize scripting
1009 infostream<<"Server: Initializing Lua"<<std::endl;
1010 m_lua = script_init();
1013 scriptapi_export(m_lua, this);
1014 // Load and run builtin.lua
1015 infostream<<"Server: Loading builtin.lua [\""
1016 <<builtinpath<<"\"]"<<std::endl;
1017 bool success = scriptapi_loadmod(m_lua, builtinpath, "__builtin");
1019 errorstream<<"Server: Failed to load and run "
1020 <<builtinpath<<std::endl;
1021 throw ModError("Failed to load and run "+builtinpath);
1023 // Find mods in mod search paths
1024 m_mods = getMods(m_modspaths);
1026 infostream<<"Server: Loading mods: ";
1027 for(core::list<ModSpec>::Iterator i = m_mods.begin();
1028 i != m_mods.end(); i++){
1029 const ModSpec &mod = *i;
1030 infostream<<mod.name<<" ";
1032 infostream<<std::endl;
1033 // Load and run "mod" scripts
1034 for(core::list<ModSpec>::Iterator i = m_mods.begin();
1035 i != m_mods.end(); i++){
1036 const ModSpec &mod = *i;
1037 std::string scriptpath = mod.path + DIR_DELIM + "init.lua";
1038 infostream<<" ["<<padStringRight(mod.name, 12)<<"] [\""
1039 <<scriptpath<<"\"]"<<std::endl;
1040 bool success = scriptapi_loadmod(m_lua, scriptpath, mod.name);
1042 errorstream<<"Server: Failed to load and run "
1043 <<scriptpath<<std::endl;
1044 throw ModError("Failed to load and run "+scriptpath);
1048 // Read Textures and calculate sha1 sums
1051 // Apply item aliases in the node definition manager
1052 m_nodedef->updateAliases(m_itemdef);
1054 // Initialize Environment
1056 m_env = new ServerEnvironment(new ServerMap(path_world, this), m_lua,
1059 // Give environment reference to scripting api
1060 scriptapi_add_environment(m_lua, m_env);
1062 // Register us to receive map edit events
1063 m_env->getMap().addEventReceiver(this);
1065 // If file exists, load environment metadata
1066 if(fs::PathExists(m_path_world+DIR_DELIM+"env_meta.txt"))
1068 infostream<<"Server: Loading environment metadata"<<std::endl;
1069 m_env->loadMeta(m_path_world);
1073 infostream<<"Server: Loading players"<<std::endl;
1074 m_env->deSerializePlayers(m_path_world);
1077 Add some test ActiveBlockModifiers to environment
1079 add_legacy_abms(m_env, m_nodedef);
1084 infostream<<"Server destructing"<<std::endl;
1087 Send shutdown message
1090 JMutexAutoLock conlock(m_con_mutex);
1092 std::wstring line = L"*** Server shutting down";
1095 Send the message to clients
1097 for(core::map<u16, RemoteClient*>::Iterator
1098 i = m_clients.getIterator();
1099 i.atEnd() == false; i++)
1101 // Get client and check that it is valid
1102 RemoteClient *client = i.getNode()->getValue();
1103 assert(client->peer_id == i.getNode()->getKey());
1104 if(client->serialization_version == SER_FMT_VER_INVALID)
1108 SendChatMessage(client->peer_id, line);
1110 catch(con::PeerNotFoundException &e)
1116 JMutexAutoLock envlock(m_env_mutex);
1121 infostream<<"Server: Saving players"<<std::endl;
1122 m_env->serializePlayers(m_path_world);
1125 Save environment metadata
1127 infostream<<"Server: Saving environment metadata"<<std::endl;
1128 m_env->saveMeta(m_path_world);
1140 JMutexAutoLock clientslock(m_con_mutex);
1142 for(core::map<u16, RemoteClient*>::Iterator
1143 i = m_clients.getIterator();
1144 i.atEnd() == false; i++)
1147 // NOTE: These are removed by env destructor
1149 u16 peer_id = i.getNode()->getKey();
1150 JMutexAutoLock envlock(m_env_mutex);
1151 m_env->removePlayer(peer_id);
1155 delete i.getNode()->getValue();
1159 // Delete things in the reverse order of creation
1167 // Deinitialize scripting
1168 infostream<<"Server: Deinitializing scripting"<<std::endl;
1169 script_deinit(m_lua);
1171 // Delete detached inventories
1173 for(std::map<std::string, Inventory*>::iterator
1174 i = m_detached_inventories.begin();
1175 i != m_detached_inventories.end(); i++){
1181 void Server::start(unsigned short port)
1183 DSTACK(__FUNCTION_NAME);
1184 infostream<<"Starting server on port "<<port<<"..."<<std::endl;
1186 // Stop thread if already running
1189 // Initialize connection
1190 m_con.SetTimeoutMs(30);
1194 m_thread.setRun(true);
1197 // ASCII art for the win!
1199 <<" .__ __ __ "<<std::endl
1200 <<" _____ |__| ____ _____/ |_ ____ _______/ |_ "<<std::endl
1201 <<" / \\| |/ \\_/ __ \\ __\\/ __ \\ / ___/\\ __\\"<<std::endl
1202 <<"| Y Y \\ | | \\ ___/| | \\ ___/ \\___ \\ | | "<<std::endl
1203 <<"|__|_| /__|___| /\\___ >__| \\___ >____ > |__| "<<std::endl
1204 <<" \\/ \\/ \\/ \\/ \\/ "<<std::endl;
1205 actionstream<<"World at ["<<m_path_world<<"]"<<std::endl;
1206 actionstream<<"Server for gameid=\""<<m_gamespec.id
1207 <<"\" listening on port "<<port<<"."<<std::endl;
1212 DSTACK(__FUNCTION_NAME);
1214 infostream<<"Server: Stopping and waiting threads"<<std::endl;
1216 // Stop threads (set run=false first so both start stopping)
1217 m_thread.setRun(false);
1218 m_emergethread.setRun(false);
1220 m_emergethread.stop();
1222 infostream<<"Server: Threads stopped"<<std::endl;
1225 void Server::step(float dtime)
1227 DSTACK(__FUNCTION_NAME);
1232 JMutexAutoLock lock(m_step_dtime_mutex);
1233 m_step_dtime += dtime;
1235 // Throw if fatal error occurred in thread
1236 std::string async_err = m_async_fatal_error.get();
1237 if(async_err != ""){
1238 throw ServerError(async_err);
1242 void Server::AsyncRunStep()
1244 DSTACK(__FUNCTION_NAME);
1246 g_profiler->add("Server::AsyncRunStep (num)", 1);
1250 JMutexAutoLock lock1(m_step_dtime_mutex);
1251 dtime = m_step_dtime;
1255 // Send blocks to clients
1262 g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
1264 //infostream<<"Server steps "<<dtime<<std::endl;
1265 //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1268 JMutexAutoLock lock1(m_step_dtime_mutex);
1269 m_step_dtime -= dtime;
1276 m_uptime.set(m_uptime.get() + dtime);
1280 // Process connection's timeouts
1281 JMutexAutoLock lock2(m_con_mutex);
1282 ScopeProfiler sp(g_profiler, "Server: connection timeout processing");
1283 m_con.RunTimeouts(dtime);
1287 // This has to be called so that the client list gets synced
1288 // with the peer list of the connection
1289 handlePeerChanges();
1293 Update time of day and overall game time
1296 JMutexAutoLock envlock(m_env_mutex);
1298 m_env->setTimeOfDaySpeed(g_settings->getFloat("time_speed"));
1301 Send to clients at constant intervals
1304 m_time_of_day_send_timer -= dtime;
1305 if(m_time_of_day_send_timer < 0.0)
1307 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
1309 //JMutexAutoLock envlock(m_env_mutex);
1310 JMutexAutoLock conlock(m_con_mutex);
1312 for(core::map<u16, RemoteClient*>::Iterator
1313 i = m_clients.getIterator();
1314 i.atEnd() == false; i++)
1316 RemoteClient *client = i.getNode()->getValue();
1317 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1318 m_env->getTimeOfDay(), g_settings->getFloat("time_speed"));
1320 m_con.Send(client->peer_id, 0, data, true);
1326 JMutexAutoLock lock(m_env_mutex);
1328 ScopeProfiler sp(g_profiler, "SEnv step");
1329 ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
1333 const float map_timer_and_unload_dtime = 2.92;
1334 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
1336 JMutexAutoLock lock(m_env_mutex);
1337 // Run Map's timers and unload unused data
1338 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
1339 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
1340 g_settings->getFloat("server_unload_unused_data_timeout"));
1351 JMutexAutoLock lock(m_env_mutex);
1352 JMutexAutoLock lock2(m_con_mutex);
1354 ScopeProfiler sp(g_profiler, "Server: handle players");
1356 for(core::map<u16, RemoteClient*>::Iterator
1357 i = m_clients.getIterator();
1358 i.atEnd() == false; i++)
1360 RemoteClient *client = i.getNode()->getValue();
1361 PlayerSAO *playersao = getPlayerSAO(client->peer_id);
1362 if(playersao == NULL)
1366 Handle player HPs (die if hp=0)
1368 if(playersao->getHP() == 0 && playersao->m_hp_not_sent)
1369 DiePlayer(client->peer_id);
1372 Send player inventories and HPs if necessary
1374 if(playersao->m_moved){
1375 SendMovePlayer(client->peer_id);
1376 playersao->m_moved = false;
1378 if(playersao->m_inventory_not_sent){
1379 UpdateCrafting(client->peer_id);
1380 SendInventory(client->peer_id);
1382 if(playersao->m_hp_not_sent){
1383 SendPlayerHP(client->peer_id);
1388 /* Transform liquids */
1389 m_liquid_transform_timer += dtime;
1390 if(m_liquid_transform_timer >= 1.00)
1392 m_liquid_transform_timer -= 1.00;
1394 JMutexAutoLock lock(m_env_mutex);
1396 ScopeProfiler sp(g_profiler, "Server: liquid transform");
1398 core::map<v3s16, MapBlock*> modified_blocks;
1399 m_env->getMap().transformLiquids(modified_blocks);
1404 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1405 ServerMap &map = ((ServerMap&)m_env->getMap());
1406 map.updateLighting(modified_blocks, lighting_modified_blocks);
1408 // Add blocks modified by lighting to modified_blocks
1409 for(core::map<v3s16, MapBlock*>::Iterator
1410 i = lighting_modified_blocks.getIterator();
1411 i.atEnd() == false; i++)
1413 MapBlock *block = i.getNode()->getValue();
1414 modified_blocks.insert(block->getPos(), block);
1418 Set the modified blocks unsent for all the clients
1421 JMutexAutoLock lock2(m_con_mutex);
1423 for(core::map<u16, RemoteClient*>::Iterator
1424 i = m_clients.getIterator();
1425 i.atEnd() == false; i++)
1427 RemoteClient *client = i.getNode()->getValue();
1429 if(modified_blocks.size() > 0)
1431 // Remove block from sent history
1432 client->SetBlocksNotSent(modified_blocks);
1437 // Periodically print some info
1439 float &counter = m_print_info_timer;
1445 JMutexAutoLock lock2(m_con_mutex);
1447 if(m_clients.size() != 0)
1448 infostream<<"Players:"<<std::endl;
1449 for(core::map<u16, RemoteClient*>::Iterator
1450 i = m_clients.getIterator();
1451 i.atEnd() == false; i++)
1453 //u16 peer_id = i.getNode()->getKey();
1454 RemoteClient *client = i.getNode()->getValue();
1455 Player *player = m_env->getPlayer(client->peer_id);
1458 infostream<<"* "<<player->getName()<<"\t";
1459 client->PrintInfo(infostream);
1464 //if(g_settings->getBool("enable_experimental"))
1468 Check added and deleted active objects
1471 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
1472 JMutexAutoLock envlock(m_env_mutex);
1473 JMutexAutoLock conlock(m_con_mutex);
1475 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
1477 // Radius inside which objects are active
1478 s16 radius = g_settings->getS16("active_object_send_range_blocks");
1479 radius *= MAP_BLOCKSIZE;
1481 for(core::map<u16, RemoteClient*>::Iterator
1482 i = m_clients.getIterator();
1483 i.atEnd() == false; i++)
1485 RemoteClient *client = i.getNode()->getValue();
1487 // If definitions and textures have not been sent, don't
1488 // send objects either
1489 if(!client->definitions_sent)
1492 Player *player = m_env->getPlayer(client->peer_id);
1495 // This can happen if the client timeouts somehow
1496 /*infostream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
1498 <<" has no associated player"<<std::endl;*/
1501 v3s16 pos = floatToInt(player->getPosition(), BS);
1503 core::map<u16, bool> removed_objects;
1504 core::map<u16, bool> added_objects;
1505 m_env->getRemovedActiveObjects(pos, radius,
1506 client->m_known_objects, removed_objects);
1507 m_env->getAddedActiveObjects(pos, radius,
1508 client->m_known_objects, added_objects);
1510 // Ignore if nothing happened
1511 if(removed_objects.size() == 0 && added_objects.size() == 0)
1513 //infostream<<"active objects: none changed"<<std::endl;
1517 std::string data_buffer;
1521 // Handle removed objects
1522 writeU16((u8*)buf, removed_objects.size());
1523 data_buffer.append(buf, 2);
1524 for(core::map<u16, bool>::Iterator
1525 i = removed_objects.getIterator();
1526 i.atEnd()==false; i++)
1529 u16 id = i.getNode()->getKey();
1530 ServerActiveObject* obj = m_env->getActiveObject(id);
1532 // Add to data buffer for sending
1533 writeU16((u8*)buf, i.getNode()->getKey());
1534 data_buffer.append(buf, 2);
1536 // Remove from known objects
1537 client->m_known_objects.remove(i.getNode()->getKey());
1539 if(obj && obj->m_known_by_count > 0)
1540 obj->m_known_by_count--;
1543 // Handle added objects
1544 writeU16((u8*)buf, added_objects.size());
1545 data_buffer.append(buf, 2);
1546 for(core::map<u16, bool>::Iterator
1547 i = added_objects.getIterator();
1548 i.atEnd()==false; i++)
1551 u16 id = i.getNode()->getKey();
1552 ServerActiveObject* obj = m_env->getActiveObject(id);
1555 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1557 infostream<<"WARNING: "<<__FUNCTION_NAME
1558 <<": NULL object"<<std::endl;
1560 type = obj->getSendType();
1562 // Add to data buffer for sending
1563 writeU16((u8*)buf, id);
1564 data_buffer.append(buf, 2);
1565 writeU8((u8*)buf, type);
1566 data_buffer.append(buf, 1);
1569 data_buffer.append(serializeLongString(
1570 obj->getClientInitializationData()));
1572 data_buffer.append(serializeLongString(""));
1574 // Add to known objects
1575 client->m_known_objects.insert(i.getNode()->getKey(), false);
1578 obj->m_known_by_count++;
1582 SharedBuffer<u8> reply(2 + data_buffer.size());
1583 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1584 memcpy((char*)&reply[2], data_buffer.c_str(),
1585 data_buffer.size());
1587 m_con.Send(client->peer_id, 0, reply, true);
1589 verbosestream<<"Server: Sent object remove/add: "
1590 <<removed_objects.size()<<" removed, "
1591 <<added_objects.size()<<" added, "
1592 <<"packet size is "<<reply.getSize()<<std::endl;
1597 Collect a list of all the objects known by the clients
1598 and report it back to the environment.
1601 core::map<u16, bool> all_known_objects;
1603 for(core::map<u16, RemoteClient*>::Iterator
1604 i = m_clients.getIterator();
1605 i.atEnd() == false; i++)
1607 RemoteClient *client = i.getNode()->getValue();
1608 // Go through all known objects of client
1609 for(core::map<u16, bool>::Iterator
1610 i = client->m_known_objects.getIterator();
1611 i.atEnd()==false; i++)
1613 u16 id = i.getNode()->getKey();
1614 all_known_objects[id] = true;
1618 m_env->setKnownActiveObjects(whatever);
1624 Send object messages
1627 JMutexAutoLock envlock(m_env_mutex);
1628 JMutexAutoLock conlock(m_con_mutex);
1630 ScopeProfiler sp(g_profiler, "Server: sending object messages");
1633 // Value = data sent by object
1634 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1636 // Get active object messages from environment
1639 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
1643 core::list<ActiveObjectMessage>* message_list = NULL;
1644 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1645 n = buffered_messages.find(aom.id);
1648 message_list = new core::list<ActiveObjectMessage>;
1649 buffered_messages.insert(aom.id, message_list);
1653 message_list = n->getValue();
1655 message_list->push_back(aom);
1658 // Route data to every client
1659 for(core::map<u16, RemoteClient*>::Iterator
1660 i = m_clients.getIterator();
1661 i.atEnd()==false; i++)
1663 RemoteClient *client = i.getNode()->getValue();
1664 std::string reliable_data;
1665 std::string unreliable_data;
1666 // Go through all objects in message buffer
1667 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1668 j = buffered_messages.getIterator();
1669 j.atEnd()==false; j++)
1671 // If object is not known by client, skip it
1672 u16 id = j.getNode()->getKey();
1673 if(client->m_known_objects.find(id) == NULL)
1675 // Get message list of object
1676 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1677 // Go through every message
1678 for(core::list<ActiveObjectMessage>::Iterator
1679 k = list->begin(); k != list->end(); k++)
1681 // Compose the full new data with header
1682 ActiveObjectMessage aom = *k;
1683 std::string new_data;
1686 writeU16((u8*)&buf[0], aom.id);
1687 new_data.append(buf, 2);
1689 new_data += serializeString(aom.datastring);
1690 // Add data to buffer
1692 reliable_data += new_data;
1694 unreliable_data += new_data;
1698 reliable_data and unreliable_data are now ready.
1701 if(reliable_data.size() > 0)
1703 SharedBuffer<u8> reply(2 + reliable_data.size());
1704 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1705 memcpy((char*)&reply[2], reliable_data.c_str(),
1706 reliable_data.size());
1708 m_con.Send(client->peer_id, 0, reply, true);
1710 if(unreliable_data.size() > 0)
1712 SharedBuffer<u8> reply(2 + unreliable_data.size());
1713 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1714 memcpy((char*)&reply[2], unreliable_data.c_str(),
1715 unreliable_data.size());
1716 // Send as unreliable
1717 m_con.Send(client->peer_id, 0, reply, false);
1720 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1722 infostream<<"Server: Size of object message data: "
1723 <<"reliable: "<<reliable_data.size()
1724 <<", unreliable: "<<unreliable_data.size()
1729 // Clear buffered_messages
1730 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1731 i = buffered_messages.getIterator();
1732 i.atEnd()==false; i++)
1734 delete i.getNode()->getValue();
1738 } // enable_experimental
1741 Send queued-for-sending map edit events.
1744 // We will be accessing the environment and the connection
1745 JMutexAutoLock lock(m_env_mutex);
1746 JMutexAutoLock conlock(m_con_mutex);
1748 // Don't send too many at a time
1751 // Single change sending is disabled if queue size is not small
1752 bool disable_single_change_sending = false;
1753 if(m_unsent_map_edit_queue.size() >= 4)
1754 disable_single_change_sending = true;
1756 int event_count = m_unsent_map_edit_queue.size();
1758 // We'll log the amount of each
1761 while(m_unsent_map_edit_queue.size() != 0)
1763 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1765 // Players far away from the change are stored here.
1766 // Instead of sending the changes, MapBlocks are set not sent
1768 core::list<u16> far_players;
1770 if(event->type == MEET_ADDNODE)
1772 //infostream<<"Server: MEET_ADDNODE"<<std::endl;
1773 prof.add("MEET_ADDNODE", 1);
1774 if(disable_single_change_sending)
1775 sendAddNode(event->p, event->n, event->already_known_by_peer,
1778 sendAddNode(event->p, event->n, event->already_known_by_peer,
1781 else if(event->type == MEET_REMOVENODE)
1783 //infostream<<"Server: MEET_REMOVENODE"<<std::endl;
1784 prof.add("MEET_REMOVENODE", 1);
1785 if(disable_single_change_sending)
1786 sendRemoveNode(event->p, event->already_known_by_peer,
1789 sendRemoveNode(event->p, event->already_known_by_peer,
1792 else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
1794 infostream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
1795 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
1796 setBlockNotSent(event->p);
1798 else if(event->type == MEET_OTHER)
1800 infostream<<"Server: MEET_OTHER"<<std::endl;
1801 prof.add("MEET_OTHER", 1);
1802 for(core::map<v3s16, bool>::Iterator
1803 i = event->modified_blocks.getIterator();
1804 i.atEnd()==false; i++)
1806 v3s16 p = i.getNode()->getKey();
1812 prof.add("unknown", 1);
1813 infostream<<"WARNING: Server: Unknown MapEditEvent "
1814 <<((u32)event->type)<<std::endl;
1818 Set blocks not sent to far players
1820 if(far_players.size() > 0)
1822 // Convert list format to that wanted by SetBlocksNotSent
1823 core::map<v3s16, MapBlock*> modified_blocks2;
1824 for(core::map<v3s16, bool>::Iterator
1825 i = event->modified_blocks.getIterator();
1826 i.atEnd()==false; i++)
1828 v3s16 p = i.getNode()->getKey();
1829 modified_blocks2.insert(p,
1830 m_env->getMap().getBlockNoCreateNoEx(p));
1832 // Set blocks not sent
1833 for(core::list<u16>::Iterator
1834 i = far_players.begin();
1835 i != far_players.end(); i++)
1838 RemoteClient *client = getClient(peer_id);
1841 client->SetBlocksNotSent(modified_blocks2);
1847 /*// Don't send too many at a time
1849 if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
1853 if(event_count >= 5){
1854 infostream<<"Server: MapEditEvents:"<<std::endl;
1855 prof.print(infostream);
1856 } else if(event_count != 0){
1857 verbosestream<<"Server: MapEditEvents:"<<std::endl;
1858 prof.print(verbosestream);
1864 Trigger emergethread (it somehow gets to a non-triggered but
1865 bysy state sometimes)
1868 float &counter = m_emergethread_trigger_timer;
1874 m_emergethread.trigger();
1876 // Update m_enable_rollback_recording here too
1877 m_enable_rollback_recording =
1878 g_settings->getBool("enable_rollback_recording");
1882 // Save map, players and auth stuff
1884 float &counter = m_savemap_timer;
1886 if(counter >= g_settings->getFloat("server_map_save_interval"))
1889 JMutexAutoLock lock(m_env_mutex);
1891 ScopeProfiler sp(g_profiler, "Server: saving stuff");
1894 if(m_banmanager.isModified())
1895 m_banmanager.save();
1897 // Save changed parts of map
1898 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
1901 m_env->serializePlayers(m_path_world);
1903 // Save environment metadata
1904 m_env->saveMeta(m_path_world);
1909 void Server::Receive()
1911 DSTACK(__FUNCTION_NAME);
1912 SharedBuffer<u8> data;
1917 JMutexAutoLock conlock(m_con_mutex);
1918 datasize = m_con.Receive(peer_id, data);
1921 // This has to be called so that the client list gets synced
1922 // with the peer list of the connection
1923 handlePeerChanges();
1925 ProcessData(*data, datasize, peer_id);
1927 catch(con::InvalidIncomingDataException &e)
1929 infostream<<"Server::Receive(): "
1930 "InvalidIncomingDataException: what()="
1931 <<e.what()<<std::endl;
1933 catch(con::PeerNotFoundException &e)
1935 //NOTE: This is not needed anymore
1937 // The peer has been disconnected.
1938 // Find the associated player and remove it.
1940 /*JMutexAutoLock envlock(m_env_mutex);
1942 infostream<<"ServerThread: peer_id="<<peer_id
1943 <<" has apparently closed connection. "
1944 <<"Removing player."<<std::endl;
1946 m_env->removePlayer(peer_id);*/
1950 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1952 DSTACK(__FUNCTION_NAME);
1953 // Environment is locked first.
1954 JMutexAutoLock envlock(m_env_mutex);
1955 JMutexAutoLock conlock(m_con_mutex);
1957 ScopeProfiler sp(g_profiler, "Server::ProcessData");
1960 Address address = m_con.GetPeerAddress(peer_id);
1961 std::string addr_s = address.serializeString();
1963 // drop player if is ip is banned
1964 if(m_banmanager.isIpBanned(addr_s)){
1965 infostream<<"Server: A banned client tried to connect from "
1966 <<addr_s<<"; banned name was "
1967 <<m_banmanager.getBanName(addr_s)<<std::endl;
1968 // This actually doesn't seem to transfer to the client
1969 SendAccessDenied(m_con, peer_id,
1970 L"Your ip is banned. Banned name was "
1971 +narrow_to_wide(m_banmanager.getBanName(addr_s)));
1972 m_con.DeletePeer(peer_id);
1976 catch(con::PeerNotFoundException &e)
1978 infostream<<"Server::ProcessData(): Cancelling: peer "
1979 <<peer_id<<" not found"<<std::endl;
1983 std::string addr_s = m_con.GetPeerAddress(peer_id).serializeString();
1985 u8 peer_ser_ver = getClient(peer_id)->serialization_version;
1993 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1995 if(command == TOSERVER_INIT)
1997 // [0] u16 TOSERVER_INIT
1998 // [2] u8 SER_FMT_VER_HIGHEST
1999 // [3] u8[20] player_name
2000 // [23] u8[28] password <--- can be sent without this, from old versions
2002 if(datasize < 2+1+PLAYERNAME_SIZE)
2005 verbosestream<<"Server: Got TOSERVER_INIT from "
2006 <<peer_id<<std::endl;
2008 // First byte after command is maximum supported
2009 // serialization version
2010 u8 client_max = data[2];
2011 u8 our_max = SER_FMT_VER_HIGHEST;
2012 // Use the highest version supported by both
2013 u8 deployed = core::min_(client_max, our_max);
2014 // If it's lower than the lowest supported, give up.
2015 if(deployed < SER_FMT_VER_LOWEST)
2016 deployed = SER_FMT_VER_INVALID;
2018 //peer->serialization_version = deployed;
2019 getClient(peer_id)->pending_serialization_version = deployed;
2021 if(deployed == SER_FMT_VER_INVALID)
2023 actionstream<<"Server: A mismatched client tried to connect from "
2024 <<addr_s<<std::endl;
2025 infostream<<"Server: Cannot negotiate "
2026 "serialization version with peer "
2027 <<peer_id<<std::endl;
2028 SendAccessDenied(m_con, peer_id, std::wstring(
2029 L"Your client's version is not supported.\n"
2030 L"Server version is ")
2031 + narrow_to_wide(VERSION_STRING) + L"."
2037 Read and check network protocol version
2040 u16 net_proto_version = 0;
2041 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
2043 net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
2046 getClient(peer_id)->net_proto_version = net_proto_version;
2048 if(net_proto_version == 0)
2050 actionstream<<"Server: An old tried to connect from "<<addr_s
2052 SendAccessDenied(m_con, peer_id, std::wstring(
2053 L"Your client's version is not supported.\n"
2054 L"Server version is ")
2055 + narrow_to_wide(VERSION_STRING) + L"."
2060 if(g_settings->getBool("strict_protocol_version_checking"))
2062 if(net_proto_version != PROTOCOL_VERSION)
2064 actionstream<<"Server: A mismatched client tried to connect"
2065 <<" from "<<addr_s<<std::endl;
2066 SendAccessDenied(m_con, peer_id, std::wstring(
2067 L"Your client's version is not supported.\n"
2068 L"Server version is ")
2069 + narrow_to_wide(VERSION_STRING) + L",\n"
2070 + L"server's PROTOCOL_VERSION is "
2071 + narrow_to_wide(itos(PROTOCOL_VERSION))
2072 + L", client's PROTOCOL_VERSION is "
2073 + narrow_to_wide(itos(net_proto_version))
2084 char playername[PLAYERNAME_SIZE];
2085 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
2087 playername[i] = data[3+i];
2089 playername[PLAYERNAME_SIZE-1] = 0;
2091 if(playername[0]=='\0')
2093 actionstream<<"Server: Player with an empty name "
2094 <<"tried to connect from "<<addr_s<<std::endl;
2095 SendAccessDenied(m_con, peer_id,
2100 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
2102 actionstream<<"Server: Player with an invalid name "
2103 <<"tried to connect from "<<addr_s<<std::endl;
2104 SendAccessDenied(m_con, peer_id,
2105 L"Name contains unallowed characters");
2109 infostream<<"Server: New connection: \""<<playername<<"\" from "
2110 <<m_con.GetPeerAddress(peer_id).serializeString()<<std::endl;
2113 char given_password[PASSWORD_SIZE];
2114 if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
2116 // old version - assume blank password
2117 given_password[0] = 0;
2121 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2123 given_password[i] = data[23+i];
2125 given_password[PASSWORD_SIZE-1] = 0;
2128 if(!base64_is_valid(given_password)){
2129 infostream<<"Server: "<<playername
2130 <<" supplied invalid password hash"<<std::endl;
2131 SendAccessDenied(m_con, peer_id, L"Invalid password hash");
2135 std::string checkpwd; // Password hash to check against
2136 bool has_auth = scriptapi_get_auth(m_lua, playername, &checkpwd, NULL);
2138 // If no authentication info exists for user, create it
2140 if(!isSingleplayer() &&
2141 g_settings->getBool("disallow_empty_password") &&
2142 std::string(given_password) == ""){
2143 SendAccessDenied(m_con, peer_id, L"Empty passwords are "
2144 L"disallowed. Set a password and try again.");
2147 std::wstring raw_default_password =
2148 narrow_to_wide(g_settings->get("default_password"));
2149 std::string initial_password =
2150 translatePassword(playername, raw_default_password);
2152 // If default_password is empty, allow any initial password
2153 if (raw_default_password.length() == 0)
2154 initial_password = given_password;
2156 scriptapi_create_auth(m_lua, playername, initial_password);
2159 has_auth = scriptapi_get_auth(m_lua, playername, &checkpwd, NULL);
2162 SendAccessDenied(m_con, peer_id, L"Not allowed to login");
2166 if(given_password != checkpwd){
2167 infostream<<"Server: peer_id="<<peer_id
2168 <<": supplied invalid password for "
2169 <<playername<<std::endl;
2170 SendAccessDenied(m_con, peer_id, L"Invalid password");
2174 // Do not allow multiple players in simple singleplayer mode.
2175 // This isn't a perfect way to do it, but will suffice for now.
2176 if(m_simple_singleplayer_mode && m_clients.size() > 1){
2177 infostream<<"Server: Not allowing another client to connect in"
2178 <<" simple singleplayer mode"<<std::endl;
2179 SendAccessDenied(m_con, peer_id,
2180 L"Running in simple singleplayer mode.");
2184 // Enforce user limit.
2185 // Don't enforce for users that have some admin right
2186 if(m_clients.size() >= g_settings->getU16("max_users") &&
2187 !checkPriv(playername, "server") &&
2188 !checkPriv(playername, "ban") &&
2189 !checkPriv(playername, "privs") &&
2190 !checkPriv(playername, "password") &&
2191 playername != g_settings->get("name"))
2193 actionstream<<"Server: "<<playername<<" tried to join, but there"
2194 <<" are already max_users="
2195 <<g_settings->getU16("max_users")<<" players."<<std::endl;
2196 SendAccessDenied(m_con, peer_id, L"Too many users.");
2201 PlayerSAO *playersao = emergePlayer(playername, peer_id);
2203 // If failed, cancel
2204 if(playersao == NULL)
2206 errorstream<<"Server: peer_id="<<peer_id
2207 <<": failed to emerge player"<<std::endl;
2212 Answer with a TOCLIENT_INIT
2215 SharedBuffer<u8> reply(2+1+6+8);
2216 writeU16(&reply[0], TOCLIENT_INIT);
2217 writeU8(&reply[2], deployed);
2218 writeV3S16(&reply[2+1], floatToInt(playersao->getPlayer()->getPosition()+v3f(0,BS/2,0), BS));
2219 writeU64(&reply[2+1+6], m_env->getServerMap().getSeed());
2222 m_con.Send(peer_id, 0, reply, true);
2226 Send complete position information
2228 SendMovePlayer(peer_id);
2233 if(command == TOSERVER_INIT2)
2235 verbosestream<<"Server: Got TOSERVER_INIT2 from "
2236 <<peer_id<<std::endl;
2238 Player *player = m_env->getPlayer(peer_id);
2240 verbosestream<<"Server: TOSERVER_INIT2: "
2241 <<"Player not found; ignoring."<<std::endl;
2245 getClient(peer_id)->serialization_version
2246 = getClient(peer_id)->pending_serialization_version;
2249 Send some initialization data
2252 infostream<<"Server: Sending content to "
2253 <<getPlayerName(peer_id)<<std::endl;
2255 // Send item definitions
2256 SendItemDef(m_con, peer_id, m_itemdef);
2258 // Send node definitions
2259 SendNodeDef(m_con, peer_id, m_nodedef);
2261 // Send media announcement
2262 sendMediaAnnouncement(peer_id);
2265 SendPlayerPrivileges(peer_id);
2267 // Send inventory formspec
2268 SendPlayerInventoryFormspec(peer_id);
2271 UpdateCrafting(peer_id);
2272 SendInventory(peer_id);
2275 SendPlayerHP(peer_id);
2277 // Send detached inventories
2278 sendDetachedInventories(peer_id);
2280 // Show death screen if necessary
2282 SendDeathscreen(m_con, peer_id, false, v3f(0,0,0));
2286 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2287 m_env->getTimeOfDay(), g_settings->getFloat("time_speed"));
2288 m_con.Send(peer_id, 0, data, true);
2291 // Note things in chat if not in simple singleplayer mode
2292 if(!m_simple_singleplayer_mode)
2294 // Send information about server to player in chat
2295 SendChatMessage(peer_id, getStatusString());
2297 // Send information about joining in chat
2299 std::wstring name = L"unknown";
2300 Player *player = m_env->getPlayer(peer_id);
2302 name = narrow_to_wide(player->getName());
2304 std::wstring message;
2307 message += L" joined the game.";
2308 BroadcastChatMessage(message);
2312 // Warnings about protocol version can be issued here
2313 if(getClient(peer_id)->net_proto_version < PROTOCOL_VERSION)
2315 SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT IS OLD AND MAY WORK PROPERLY WITH THIS SERVER!");
2322 std::ostringstream os(std::ios_base::binary);
2323 for(core::map<u16, RemoteClient*>::Iterator
2324 i = m_clients.getIterator();
2325 i.atEnd() == false; i++)
2327 RemoteClient *client = i.getNode()->getValue();
2328 assert(client->peer_id == i.getNode()->getKey());
2329 if(client->serialization_version == SER_FMT_VER_INVALID)
2332 Player *player = m_env->getPlayer(client->peer_id);
2335 // Get name of player
2336 os<<player->getName()<<" ";
2339 actionstream<<player->getName()<<" joins game. List of players: "
2340 <<os.str()<<std::endl;
2346 if(peer_ser_ver == SER_FMT_VER_INVALID)
2348 infostream<<"Server::ProcessData(): Cancelling: Peer"
2349 " serialization format invalid or not initialized."
2350 " Skipping incoming command="<<command<<std::endl;
2354 Player *player = m_env->getPlayer(peer_id);
2356 infostream<<"Server::ProcessData(): Cancelling: "
2357 "No player for peer_id="<<peer_id
2362 PlayerSAO *playersao = player->getPlayerSAO();
2363 if(playersao == NULL){
2364 infostream<<"Server::ProcessData(): Cancelling: "
2365 "No player object for peer_id="<<peer_id
2370 if(command == TOSERVER_PLAYERPOS)
2372 if(datasize < 2+12+12+4+4+4)
2376 v3s32 ps = readV3S32(&data[start+2]);
2377 v3s32 ss = readV3S32(&data[start+2+12]);
2378 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2379 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2380 u32 keyPressed = (u32)readU32(&data[2+12+12+4+4]);
2381 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2382 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2383 pitch = wrapDegrees(pitch);
2384 yaw = wrapDegrees(yaw);
2386 player->setPosition(position);
2387 player->setSpeed(speed);
2388 player->setPitch(pitch);
2389 player->setYaw(yaw);
2390 player->keyPressed=keyPressed;
2391 player->control.up = (bool)(keyPressed&1);
2392 player->control.down = (bool)(keyPressed&2);
2393 player->control.left = (bool)(keyPressed&4);
2394 player->control.right = (bool)(keyPressed&8);
2395 player->control.jump = (bool)(keyPressed&16);
2396 player->control.aux1 = (bool)(keyPressed&32);
2397 player->control.sneak = (bool)(keyPressed&64);
2398 player->control.LMB = (bool)(keyPressed&128);
2399 player->control.RMB = (bool)(keyPressed&256);
2401 /*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2402 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2403 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2405 else if(command == TOSERVER_GOTBLOCKS)
2418 u16 count = data[2];
2419 for(u16 i=0; i<count; i++)
2421 if((s16)datasize < 2+1+(i+1)*6)
2422 throw con::InvalidIncomingDataException
2423 ("GOTBLOCKS length is too short");
2424 v3s16 p = readV3S16(&data[2+1+i*6]);
2425 /*infostream<<"Server: GOTBLOCKS ("
2426 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2427 RemoteClient *client = getClient(peer_id);
2428 client->GotBlock(p);
2431 else if(command == TOSERVER_DELETEDBLOCKS)
2444 u16 count = data[2];
2445 for(u16 i=0; i<count; i++)
2447 if((s16)datasize < 2+1+(i+1)*6)
2448 throw con::InvalidIncomingDataException
2449 ("DELETEDBLOCKS length is too short");
2450 v3s16 p = readV3S16(&data[2+1+i*6]);
2451 /*infostream<<"Server: DELETEDBLOCKS ("
2452 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2453 RemoteClient *client = getClient(peer_id);
2454 client->SetBlockNotSent(p);
2457 else if(command == TOSERVER_CLICK_OBJECT)
2459 infostream<<"Server: CLICK_OBJECT not supported anymore"<<std::endl;
2462 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2464 infostream<<"Server: CLICK_ACTIVEOBJECT not supported anymore"<<std::endl;
2467 else if(command == TOSERVER_GROUND_ACTION)
2469 infostream<<"Server: GROUND_ACTION not supported anymore"<<std::endl;
2473 else if(command == TOSERVER_RELEASE)
2475 infostream<<"Server: RELEASE not supported anymore"<<std::endl;
2478 else if(command == TOSERVER_SIGNTEXT)
2480 infostream<<"Server: SIGNTEXT not supported anymore"
2484 else if(command == TOSERVER_SIGNNODETEXT)
2486 infostream<<"Server: SIGNNODETEXT not supported anymore"
2490 else if(command == TOSERVER_INVENTORY_ACTION)
2492 // Strip command and create a stream
2493 std::string datastring((char*)&data[2], datasize-2);
2494 verbosestream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2495 std::istringstream is(datastring, std::ios_base::binary);
2497 InventoryAction *a = InventoryAction::deSerialize(is);
2500 infostream<<"TOSERVER_INVENTORY_ACTION: "
2501 <<"InventoryAction::deSerialize() returned NULL"
2506 // If something goes wrong, this player is to blame
2507 RollbackScopeActor rollback_scope(m_rollback,
2508 std::string("player:")+player->getName());
2511 Note: Always set inventory not sent, to repair cases
2512 where the client made a bad prediction.
2516 Handle restrictions and special cases of the move action
2518 if(a->getType() == IACTION_MOVE)
2520 IMoveAction *ma = (IMoveAction*)a;
2522 ma->from_inv.applyCurrentPlayer(player->getName());
2523 ma->to_inv.applyCurrentPlayer(player->getName());
2525 setInventoryModified(ma->from_inv);
2526 setInventoryModified(ma->to_inv);
2528 bool from_inv_is_current_player =
2529 (ma->from_inv.type == InventoryLocation::PLAYER) &&
2530 (ma->from_inv.name == player->getName());
2532 bool to_inv_is_current_player =
2533 (ma->to_inv.type == InventoryLocation::PLAYER) &&
2534 (ma->to_inv.name == player->getName());
2537 Disable moving items out of craftpreview
2539 if(ma->from_list == "craftpreview")
2541 infostream<<"Ignoring IMoveAction from "
2542 <<(ma->from_inv.dump())<<":"<<ma->from_list
2543 <<" to "<<(ma->to_inv.dump())<<":"<<ma->to_list
2544 <<" because src is "<<ma->from_list<<std::endl;
2550 Disable moving items into craftresult and craftpreview
2552 if(ma->to_list == "craftpreview" || ma->to_list == "craftresult")
2554 infostream<<"Ignoring IMoveAction from "
2555 <<(ma->from_inv.dump())<<":"<<ma->from_list
2556 <<" to "<<(ma->to_inv.dump())<<":"<<ma->to_list
2557 <<" because dst is "<<ma->to_list<<std::endl;
2562 // Disallow moving items in elsewhere than player's inventory
2563 // if not allowed to interact
2564 if(!checkPriv(player->getName(), "interact") &&
2565 (!from_inv_is_current_player ||
2566 !to_inv_is_current_player))
2568 infostream<<"Cannot move outside of player's inventory: "
2569 <<"No interact privilege"<<std::endl;
2575 Handle restrictions and special cases of the drop action
2577 else if(a->getType() == IACTION_DROP)
2579 IDropAction *da = (IDropAction*)a;
2581 da->from_inv.applyCurrentPlayer(player->getName());
2583 setInventoryModified(da->from_inv);
2585 // Disallow dropping items if not allowed to interact
2586 if(!checkPriv(player->getName(), "interact"))
2593 Handle restrictions and special cases of the craft action
2595 else if(a->getType() == IACTION_CRAFT)
2597 ICraftAction *ca = (ICraftAction*)a;
2599 ca->craft_inv.applyCurrentPlayer(player->getName());
2601 setInventoryModified(ca->craft_inv);
2603 //bool craft_inv_is_current_player =
2604 // (ca->craft_inv.type == InventoryLocation::PLAYER) &&
2605 // (ca->craft_inv.name == player->getName());
2607 // Disallow crafting if not allowed to interact
2608 if(!checkPriv(player->getName(), "interact"))
2610 infostream<<"Cannot craft: "
2611 <<"No interact privilege"<<std::endl;
2618 a->apply(this, playersao, this);
2622 else if(command == TOSERVER_CHAT_MESSAGE)
2630 std::string datastring((char*)&data[2], datasize-2);
2631 std::istringstream is(datastring, std::ios_base::binary);
2634 is.read((char*)buf, 2);
2635 u16 len = readU16(buf);
2637 std::wstring message;
2638 for(u16 i=0; i<len; i++)
2640 is.read((char*)buf, 2);
2641 message += (wchar_t)readU16(buf);
2644 // If something goes wrong, this player is to blame
2645 RollbackScopeActor rollback_scope(m_rollback,
2646 std::string("player:")+player->getName());
2648 // Get player name of this client
2649 std::wstring name = narrow_to_wide(player->getName());
2652 bool ate = scriptapi_on_chat_message(m_lua, player->getName(),
2653 wide_to_narrow(message));
2654 // If script ate the message, don't proceed
2658 // Line to send to players
2660 // Whether to send to the player that sent the line
2661 bool send_to_sender = false;
2662 // Whether to send to other players
2663 bool send_to_others = false;
2665 // Commands are implemented in Lua, so only catch invalid
2666 // commands that were not "eaten" and send an error back
2667 if(message[0] == L'/')
2669 message = message.substr(1);
2670 send_to_sender = true;
2671 if(message.length() == 0)
2672 line += L"-!- Empty command";
2674 line += L"-!- Invalid command: " + str_split(message, L' ')[0];
2678 if(checkPriv(player->getName(), "shout")){
2683 send_to_others = true;
2685 line += L"-!- You don't have permission to shout.";
2686 send_to_sender = true;
2693 actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2696 Send the message to clients
2698 for(core::map<u16, RemoteClient*>::Iterator
2699 i = m_clients.getIterator();
2700 i.atEnd() == false; i++)
2702 // Get client and check that it is valid
2703 RemoteClient *client = i.getNode()->getValue();
2704 assert(client->peer_id == i.getNode()->getKey());
2705 if(client->serialization_version == SER_FMT_VER_INVALID)
2709 bool sender_selected = (peer_id == client->peer_id);
2710 if(sender_selected == true && send_to_sender == false)
2712 if(sender_selected == false && send_to_others == false)
2715 SendChatMessage(client->peer_id, line);
2719 else if(command == TOSERVER_DAMAGE)
2721 std::string datastring((char*)&data[2], datasize-2);
2722 std::istringstream is(datastring, std::ios_base::binary);
2723 u8 damage = readU8(is);
2725 actionstream<<player->getName()<<" damaged by "
2726 <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
2729 playersao->setHP(playersao->getHP() - damage);
2731 if(playersao->getHP() == 0 && playersao->m_hp_not_sent)
2734 if(playersao->m_hp_not_sent)
2735 SendPlayerHP(peer_id);
2737 else if(command == TOSERVER_PASSWORD)
2740 [0] u16 TOSERVER_PASSWORD
2741 [2] u8[28] old password
2742 [30] u8[28] new password
2745 if(datasize != 2+PASSWORD_SIZE*2)
2747 /*char password[PASSWORD_SIZE];
2748 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2749 password[i] = data[2+i];
2750 password[PASSWORD_SIZE-1] = 0;*/
2752 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2760 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2762 char c = data[2+PASSWORD_SIZE+i];
2768 if(!base64_is_valid(newpwd)){
2769 infostream<<"Server: "<<player->getName()<<" supplied invalid password hash"<<std::endl;
2770 // Wrong old password supplied!!
2771 SendChatMessage(peer_id, L"Invalid new password hash supplied. Password NOT changed.");
2775 infostream<<"Server: Client requests a password change from "
2776 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
2778 std::string playername = player->getName();
2780 std::string checkpwd;
2781 scriptapi_get_auth(m_lua, playername, &checkpwd, NULL);
2783 if(oldpwd != checkpwd)
2785 infostream<<"Server: invalid old password"<<std::endl;
2786 // Wrong old password supplied!!
2787 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
2791 bool success = scriptapi_set_password(m_lua, playername, newpwd);
2793 actionstream<<player->getName()<<" changes password"<<std::endl;
2794 SendChatMessage(peer_id, L"Password change successful.");
2796 actionstream<<player->getName()<<" tries to change password but "
2797 <<"it fails"<<std::endl;
2798 SendChatMessage(peer_id, L"Password change failed or inavailable.");
2801 else if(command == TOSERVER_PLAYERITEM)
2806 u16 item = readU16(&data[2]);
2807 playersao->setWieldIndex(item);
2809 else if(command == TOSERVER_RESPAWN)
2814 RespawnPlayer(peer_id);
2816 actionstream<<player->getName()<<" respawns at "
2817 <<PP(player->getPosition()/BS)<<std::endl;
2819 // ActiveObject is added to environment in AsyncRunStep after
2820 // the previous addition has been succesfully removed
2822 else if(command == TOSERVER_REQUEST_MEDIA) {
2823 std::string datastring((char*)&data[2], datasize-2);
2824 std::istringstream is(datastring, std::ios_base::binary);
2826 core::list<MediaRequest> tosend;
2827 u16 numfiles = readU16(is);
2829 infostream<<"Sending "<<numfiles<<" files to "
2830 <<getPlayerName(peer_id)<<std::endl;
2831 verbosestream<<"TOSERVER_REQUEST_MEDIA: "<<std::endl;
2833 for(int i = 0; i < numfiles; i++) {
2834 std::string name = deSerializeString(is);
2835 tosend.push_back(MediaRequest(name));
2836 verbosestream<<"TOSERVER_REQUEST_MEDIA: requested file "
2840 sendRequestedMedia(peer_id, tosend);
2842 // Now the client should know about everything
2843 // (definitions and files)
2844 getClient(peer_id)->definitions_sent = true;
2846 else if(command == TOSERVER_INTERACT)
2848 std::string datastring((char*)&data[2], datasize-2);
2849 std::istringstream is(datastring, std::ios_base::binary);
2855 [5] u32 length of the next item
2856 [9] serialized PointedThing
2858 0: start digging (from undersurface) or use
2859 1: stop digging (all parameters ignored)
2860 2: digging completed
2861 3: place block or item (to abovesurface)
2864 u8 action = readU8(is);
2865 u16 item_i = readU16(is);
2866 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
2867 PointedThing pointed;
2868 pointed.deSerialize(tmp_is);
2870 verbosestream<<"TOSERVER_INTERACT: action="<<(int)action<<", item="
2871 <<item_i<<", pointed="<<pointed.dump()<<std::endl;
2875 verbosestream<<"TOSERVER_INTERACT: "<<player->getName()
2876 <<" tried to interact, but is dead!"<<std::endl;
2880 v3f player_pos = playersao->getLastGoodPosition();
2882 // Update wielded item
2883 playersao->setWieldIndex(item_i);
2885 // Get pointed to node (undefined if not POINTEDTYPE_NODE)
2886 v3s16 p_under = pointed.node_undersurface;
2887 v3s16 p_above = pointed.node_abovesurface;
2889 // Get pointed to object (NULL if not POINTEDTYPE_OBJECT)
2890 ServerActiveObject *pointed_object = NULL;
2891 if(pointed.type == POINTEDTHING_OBJECT)
2893 pointed_object = m_env->getActiveObject(pointed.object_id);
2894 if(pointed_object == NULL)
2896 verbosestream<<"TOSERVER_INTERACT: "
2897 "pointed object is NULL"<<std::endl;
2903 v3f pointed_pos_under = player_pos;
2904 v3f pointed_pos_above = player_pos;
2905 if(pointed.type == POINTEDTHING_NODE)
2907 pointed_pos_under = intToFloat(p_under, BS);
2908 pointed_pos_above = intToFloat(p_above, BS);
2910 else if(pointed.type == POINTEDTHING_OBJECT)
2912 pointed_pos_under = pointed_object->getBasePosition();
2913 pointed_pos_above = pointed_pos_under;
2917 Check that target is reasonably close
2918 (only when digging or placing things)
2920 if(action == 0 || action == 2 || action == 3)
2922 float d = player_pos.getDistanceFrom(pointed_pos_under);
2923 float max_d = BS * 14; // Just some large enough value
2925 actionstream<<"Player "<<player->getName()
2926 <<" tried to access "<<pointed.dump()
2928 <<"d="<<d<<", max_d="<<max_d
2929 <<". ignoring."<<std::endl;
2930 // Re-send block to revert change on client-side
2931 RemoteClient *client = getClient(peer_id);
2932 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2933 client->SetBlockNotSent(blockpos);
2940 Make sure the player is allowed to do it
2942 if(!checkPriv(player->getName(), "interact"))
2944 actionstream<<player->getName()<<" attempted to interact with "
2945 <<pointed.dump()<<" without 'interact' privilege"
2947 // Re-send block to revert change on client-side
2948 RemoteClient *client = getClient(peer_id);
2949 // Digging completed -> under
2951 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2952 client->SetBlockNotSent(blockpos);
2954 // Placement -> above
2956 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
2957 client->SetBlockNotSent(blockpos);
2963 If something goes wrong, this player is to blame
2965 RollbackScopeActor rollback_scope(m_rollback,
2966 std::string("player:")+player->getName());
2969 0: start digging or punch object
2973 if(pointed.type == POINTEDTHING_NODE)
2976 NOTE: This can be used in the future to check if
2977 somebody is cheating, by checking the timing.
2979 MapNode n(CONTENT_IGNORE);
2982 n = m_env->getMap().getNode(p_under);
2984 catch(InvalidPositionException &e)
2986 infostream<<"Server: Not punching: Node not found."
2987 <<" Adding block to emerge queue."
2989 m_emerge_queue.addBlock(peer_id,
2990 getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
2992 if(n.getContent() != CONTENT_IGNORE)
2993 scriptapi_node_on_punch(m_lua, p_under, n, playersao);
2995 playersao->noCheatDigStart(p_under);
2997 else if(pointed.type == POINTEDTHING_OBJECT)
2999 // Skip if object has been removed
3000 if(pointed_object->m_removed)
3003 actionstream<<player->getName()<<" punches object "
3004 <<pointed.object_id<<": "
3005 <<pointed_object->getDescription()<<std::endl;
3007 ItemStack punchitem = playersao->getWieldedItem();
3008 ToolCapabilities toolcap =
3009 punchitem.getToolCapabilities(m_itemdef);
3010 v3f dir = (pointed_object->getBasePosition() -
3011 (player->getPosition() + player->getEyeOffset())
3013 float time_from_last_punch =
3014 playersao->resetTimeFromLastPunch();
3015 pointed_object->punch(dir, &toolcap, playersao,
3016 time_from_last_punch);
3024 else if(action == 1)
3029 2: Digging completed
3031 else if(action == 2)
3033 // Only digging of nodes
3034 if(pointed.type == POINTEDTHING_NODE)
3036 MapNode n(CONTENT_IGNORE);
3039 n = m_env->getMap().getNode(p_under);
3041 catch(InvalidPositionException &e)
3043 infostream<<"Server: Not finishing digging: Node not found."
3044 <<" Adding block to emerge queue."
3046 m_emerge_queue.addBlock(peer_id,
3047 getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
3050 /* Cheat prevention */
3051 bool is_valid_dig = true;
3052 if(!isSingleplayer() && !g_settings->getBool("disable_anticheat"))
3054 v3s16 nocheat_p = playersao->getNoCheatDigPos();
3055 float nocheat_t = playersao->getNoCheatDigTime();
3056 playersao->noCheatDigEnd();
3057 // If player didn't start digging this, ignore dig
3058 if(nocheat_p != p_under){
3059 infostream<<"Server: NoCheat: "<<player->getName()
3060 <<" started digging "
3061 <<PP(nocheat_p)<<" and completed digging "
3062 <<PP(p_under)<<"; not digging."<<std::endl;
3063 is_valid_dig = false;
3065 // Get player's wielded item
3066 ItemStack playeritem;
3067 InventoryList *mlist = playersao->getInventory()->getList("main");
3069 playeritem = mlist->getItem(playersao->getWieldIndex());
3070 ToolCapabilities playeritem_toolcap =
3071 playeritem.getToolCapabilities(m_itemdef);
3072 // Get diggability and expected digging time
3073 DigParams params = getDigParams(m_nodedef->get(n).groups,
3074 &playeritem_toolcap);
3075 // If can't dig, try hand
3076 if(!params.diggable){
3077 const ItemDefinition &hand = m_itemdef->get("");
3078 const ToolCapabilities *tp = hand.tool_capabilities;
3080 params = getDigParams(m_nodedef->get(n).groups, tp);
3082 // If can't dig, ignore dig
3083 if(!params.diggable){
3084 infostream<<"Server: NoCheat: "<<player->getName()
3085 <<" completed digging "<<PP(p_under)
3086 <<", which is not diggable with tool. not digging."
3088 is_valid_dig = false;
3090 // If time is considerably too short, ignore dig
3091 // Check time only for medium and slow timed digs
3092 if(params.diggable && params.time > 0.3 && nocheat_t < 0.5 * params.time){
3093 infostream<<"Server: NoCheat: "<<player->getName()
3094 <<" completed digging "
3095 <<PP(p_under)<<" in "<<nocheat_t<<"s; expected "
3096 <<params.time<<"s; not digging."<<std::endl;
3097 is_valid_dig = false;
3101 /* Actually dig node */
3103 if(is_valid_dig && n.getContent() != CONTENT_IGNORE)
3104 scriptapi_node_on_dig(m_lua, p_under, n, playersao);
3106 // Send unusual result (that is, node not being removed)
3107 if(m_env->getMap().getNodeNoEx(p_under).getContent() != CONTENT_AIR)
3109 // Re-send block to revert change on client-side
3110 RemoteClient *client = getClient(peer_id);
3111 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
3112 client->SetBlockNotSent(blockpos);
3118 3: place block or right-click object
3120 else if(action == 3)
3122 ItemStack item = playersao->getWieldedItem();
3124 // Reset build time counter
3125 if(pointed.type == POINTEDTHING_NODE &&
3126 item.getDefinition(m_itemdef).type == ITEM_NODE)
3127 getClient(peer_id)->m_time_from_building = 0.0;
3129 if(pointed.type == POINTEDTHING_OBJECT)
3131 // Right click object
3133 // Skip if object has been removed
3134 if(pointed_object->m_removed)
3137 actionstream<<player->getName()<<" right-clicks object "
3138 <<pointed.object_id<<": "
3139 <<pointed_object->getDescription()<<std::endl;
3142 pointed_object->rightClick(playersao);
3144 else if(scriptapi_item_on_place(m_lua,
3145 item, playersao, pointed))
3147 // Placement was handled in lua
3149 // Apply returned ItemStack
3150 playersao->setWieldedItem(item);
3153 // If item has node placement prediction, always send the above
3154 // node to make sure the client knows what exactly happened
3155 if(item.getDefinition(m_itemdef).node_placement_prediction != ""){
3156 RemoteClient *client = getClient(peer_id);
3157 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_above, BS));
3158 client->SetBlockNotSent(blockpos);
3165 else if(action == 4)
3167 ItemStack item = playersao->getWieldedItem();
3169 actionstream<<player->getName()<<" uses "<<item.name
3170 <<", pointing at "<<pointed.dump()<<std::endl;
3172 if(scriptapi_item_on_use(m_lua,
3173 item, playersao, pointed))
3175 // Apply returned ItemStack
3176 playersao->setWieldedItem(item);
3183 Catch invalid actions
3187 infostream<<"WARNING: Server: Invalid action "
3188 <<action<<std::endl;
3191 else if(command == TOSERVER_REMOVED_SOUNDS)
3193 std::string datastring((char*)&data[2], datasize-2);
3194 std::istringstream is(datastring, std::ios_base::binary);
3196 int num = readU16(is);
3197 for(int k=0; k<num; k++){
3198 s32 id = readS32(is);
3199 std::map<s32, ServerPlayingSound>::iterator i =
3200 m_playing_sounds.find(id);
3201 if(i == m_playing_sounds.end())
3203 ServerPlayingSound &psound = i->second;
3204 psound.clients.erase(peer_id);
3205 if(psound.clients.size() == 0)
3206 m_playing_sounds.erase(i++);
3209 else if(command == TOSERVER_NODEMETA_FIELDS)
3211 std::string datastring((char*)&data[2], datasize-2);
3212 std::istringstream is(datastring, std::ios_base::binary);
3214 v3s16 p = readV3S16(is);
3215 std::string formname = deSerializeString(is);
3216 int num = readU16(is);
3217 std::map<std::string, std::string> fields;
3218 for(int k=0; k<num; k++){
3219 std::string fieldname = deSerializeString(is);
3220 std::string fieldvalue = deSerializeLongString(is);
3221 fields[fieldname] = fieldvalue;
3224 // If something goes wrong, this player is to blame
3225 RollbackScopeActor rollback_scope(m_rollback,
3226 std::string("player:")+player->getName());
3228 // Check the target node for rollback data; leave others unnoticed
3229 RollbackNode rn_old(&m_env->getMap(), p, this);
3231 scriptapi_node_on_receive_fields(m_lua, p, formname, fields,
3234 // Report rollback data
3235 RollbackNode rn_new(&m_env->getMap(), p, this);
3236 if(rollback() && rn_new != rn_old){
3237 RollbackAction action;
3238 action.setSetNode(p, rn_old, rn_new);
3239 rollback()->reportAction(action);
3242 else if(command == TOSERVER_INVENTORY_FIELDS)
3244 std::string datastring((char*)&data[2], datasize-2);
3245 std::istringstream is(datastring, std::ios_base::binary);
3247 std::string formname = deSerializeString(is);
3248 int num = readU16(is);
3249 std::map<std::string, std::string> fields;
3250 for(int k=0; k<num; k++){
3251 std::string fieldname = deSerializeString(is);
3252 std::string fieldvalue = deSerializeLongString(is);
3253 fields[fieldname] = fieldvalue;
3256 scriptapi_on_player_receive_fields(m_lua, playersao, formname, fields);
3260 infostream<<"Server::ProcessData(): Ignoring "
3261 "unknown command "<<command<<std::endl;
3265 catch(SendFailedException &e)
3267 errorstream<<"Server::ProcessData(): SendFailedException: "
3273 void Server::onMapEditEvent(MapEditEvent *event)
3275 //infostream<<"Server::onMapEditEvent()"<<std::endl;
3276 if(m_ignore_map_edit_events)
3278 if(m_ignore_map_edit_events_area.contains(event->getArea()))
3280 MapEditEvent *e = event->clone();
3281 m_unsent_map_edit_queue.push_back(e);
3284 Inventory* Server::getInventory(const InventoryLocation &loc)
3287 case InventoryLocation::UNDEFINED:
3290 case InventoryLocation::CURRENT_PLAYER:
3293 case InventoryLocation::PLAYER:
3295 Player *player = m_env->getPlayer(loc.name.c_str());
3298 PlayerSAO *playersao = player->getPlayerSAO();
3301 return playersao->getInventory();
3304 case InventoryLocation::NODEMETA:
3306 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
3309 return meta->getInventory();
3312 case InventoryLocation::DETACHED:
3314 if(m_detached_inventories.count(loc.name) == 0)
3316 return m_detached_inventories[loc.name];
3324 void Server::setInventoryModified(const InventoryLocation &loc)
3327 case InventoryLocation::UNDEFINED:
3330 case InventoryLocation::PLAYER:
3332 Player *player = m_env->getPlayer(loc.name.c_str());
3335 PlayerSAO *playersao = player->getPlayerSAO();
3338 playersao->m_inventory_not_sent = true;
3339 playersao->m_wielded_item_not_sent = true;
3342 case InventoryLocation::NODEMETA:
3344 v3s16 blockpos = getNodeBlockPos(loc.p);
3346 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3348 block->raiseModified(MOD_STATE_WRITE_NEEDED);
3350 setBlockNotSent(blockpos);
3353 case InventoryLocation::DETACHED:
3355 sendDetachedInventoryToAll(loc.name);
3363 core::list<PlayerInfo> Server::getPlayerInfo()
3365 DSTACK(__FUNCTION_NAME);
3366 JMutexAutoLock envlock(m_env_mutex);
3367 JMutexAutoLock conlock(m_con_mutex);
3369 core::list<PlayerInfo> list;
3371 core::list<Player*> players = m_env->getPlayers();
3373 core::list<Player*>::Iterator i;
3374 for(i = players.begin();
3375 i != players.end(); i++)
3379 Player *player = *i;
3382 // Copy info from connection to info struct
3383 info.id = player->peer_id;
3384 info.address = m_con.GetPeerAddress(player->peer_id);
3385 info.avg_rtt = m_con.GetPeerAvgRTT(player->peer_id);
3387 catch(con::PeerNotFoundException &e)
3389 // Set dummy peer info
3391 info.address = Address(0,0,0,0,0);
3395 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3396 info.position = player->getPosition();
3398 list.push_back(info);
3405 void Server::peerAdded(con::Peer *peer)
3407 DSTACK(__FUNCTION_NAME);
3408 verbosestream<<"Server::peerAdded(): peer->id="
3409 <<peer->id<<std::endl;
3412 c.type = PEER_ADDED;
3413 c.peer_id = peer->id;
3415 m_peer_change_queue.push_back(c);
3418 void Server::deletingPeer(con::Peer *peer, bool timeout)
3420 DSTACK(__FUNCTION_NAME);
3421 verbosestream<<"Server::deletingPeer(): peer->id="
3422 <<peer->id<<", timeout="<<timeout<<std::endl;
3425 c.type = PEER_REMOVED;
3426 c.peer_id = peer->id;
3427 c.timeout = timeout;
3428 m_peer_change_queue.push_back(c);
3435 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3437 DSTACK(__FUNCTION_NAME);
3438 std::ostringstream os(std::ios_base::binary);
3440 writeU16(os, TOCLIENT_HP);
3444 std::string s = os.str();
3445 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3447 con.Send(peer_id, 0, data, true);
3450 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3451 const std::wstring &reason)
3453 DSTACK(__FUNCTION_NAME);
3454 std::ostringstream os(std::ios_base::binary);
3456 writeU16(os, TOCLIENT_ACCESS_DENIED);
3457 os<<serializeWideString(reason);
3460 std::string s = os.str();
3461 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3463 con.Send(peer_id, 0, data, true);
3466 void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
3467 bool set_camera_point_target, v3f camera_point_target)
3469 DSTACK(__FUNCTION_NAME);
3470 std::ostringstream os(std::ios_base::binary);
3472 writeU16(os, TOCLIENT_DEATHSCREEN);
3473 writeU8(os, set_camera_point_target);
3474 writeV3F1000(os, camera_point_target);
3477 std::string s = os.str();
3478 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3480 con.Send(peer_id, 0, data, true);
3483 void Server::SendItemDef(con::Connection &con, u16 peer_id,
3484 IItemDefManager *itemdef)
3486 DSTACK(__FUNCTION_NAME);
3487 std::ostringstream os(std::ios_base::binary);
3491 u32 length of the next item
3492 zlib-compressed serialized ItemDefManager
3494 writeU16(os, TOCLIENT_ITEMDEF);
3495 std::ostringstream tmp_os(std::ios::binary);
3496 itemdef->serialize(tmp_os);
3497 std::ostringstream tmp_os2(std::ios::binary);
3498 compressZlib(tmp_os.str(), tmp_os2);
3499 os<<serializeLongString(tmp_os2.str());
3502 std::string s = os.str();
3503 verbosestream<<"Server: Sending item definitions to id("<<peer_id
3504 <<"): size="<<s.size()<<std::endl;
3505 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3507 con.Send(peer_id, 0, data, true);
3510 void Server::SendNodeDef(con::Connection &con, u16 peer_id,
3511 INodeDefManager *nodedef)
3513 DSTACK(__FUNCTION_NAME);
3514 std::ostringstream os(std::ios_base::binary);
3518 u32 length of the next item
3519 zlib-compressed serialized NodeDefManager
3521 writeU16(os, TOCLIENT_NODEDEF);
3522 std::ostringstream tmp_os(std::ios::binary);
3523 nodedef->serialize(tmp_os);
3524 std::ostringstream tmp_os2(std::ios::binary);
3525 compressZlib(tmp_os.str(), tmp_os2);
3526 os<<serializeLongString(tmp_os2.str());
3529 std::string s = os.str();
3530 verbosestream<<"Server: Sending node definitions to id("<<peer_id
3531 <<"): size="<<s.size()<<std::endl;
3532 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3534 con.Send(peer_id, 0, data, true);
3538 Non-static send methods
3541 void Server::SendInventory(u16 peer_id)
3543 DSTACK(__FUNCTION_NAME);
3545 PlayerSAO *playersao = getPlayerSAO(peer_id);
3548 playersao->m_inventory_not_sent = false;
3554 std::ostringstream os;
3555 playersao->getInventory()->serialize(os);
3557 std::string s = os.str();
3559 SharedBuffer<u8> data(s.size()+2);
3560 writeU16(&data[0], TOCLIENT_INVENTORY);
3561 memcpy(&data[2], s.c_str(), s.size());
3564 m_con.Send(peer_id, 0, data, true);
3567 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3569 DSTACK(__FUNCTION_NAME);
3571 std::ostringstream os(std::ios_base::binary);
3575 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3576 os.write((char*)buf, 2);
3579 writeU16(buf, message.size());
3580 os.write((char*)buf, 2);
3583 for(u32 i=0; i<message.size(); i++)
3587 os.write((char*)buf, 2);
3591 std::string s = os.str();
3592 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3594 m_con.Send(peer_id, 0, data, true);
3597 void Server::BroadcastChatMessage(const std::wstring &message)
3599 for(core::map<u16, RemoteClient*>::Iterator
3600 i = m_clients.getIterator();
3601 i.atEnd() == false; i++)
3603 // Get client and check that it is valid
3604 RemoteClient *client = i.getNode()->getValue();
3605 assert(client->peer_id == i.getNode()->getKey());
3606 if(client->serialization_version == SER_FMT_VER_INVALID)
3609 SendChatMessage(client->peer_id, message);
3613 void Server::SendPlayerHP(u16 peer_id)
3615 DSTACK(__FUNCTION_NAME);
3616 PlayerSAO *playersao = getPlayerSAO(peer_id);
3618 playersao->m_hp_not_sent = false;
3619 SendHP(m_con, peer_id, playersao->getHP());
3622 void Server::SendMovePlayer(u16 peer_id)
3624 DSTACK(__FUNCTION_NAME);
3625 Player *player = m_env->getPlayer(peer_id);
3628 std::ostringstream os(std::ios_base::binary);
3629 writeU16(os, TOCLIENT_MOVE_PLAYER);
3630 writeV3F1000(os, player->getPosition());
3631 writeF1000(os, player->getPitch());
3632 writeF1000(os, player->getYaw());
3635 v3f pos = player->getPosition();
3636 f32 pitch = player->getPitch();
3637 f32 yaw = player->getYaw();
3638 verbosestream<<"Server: Sending TOCLIENT_MOVE_PLAYER"
3639 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3646 std::string s = os.str();
3647 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3649 m_con.Send(peer_id, 0, data, true);
3652 void Server::SendPlayerPrivileges(u16 peer_id)
3654 Player *player = m_env->getPlayer(peer_id);
3656 if(player->peer_id == PEER_ID_INEXISTENT)
3659 std::set<std::string> privs;
3660 scriptapi_get_auth(m_lua, player->getName(), NULL, &privs);
3662 std::ostringstream os(std::ios_base::binary);
3663 writeU16(os, TOCLIENT_PRIVILEGES);
3664 writeU16(os, privs.size());
3665 for(std::set<std::string>::const_iterator i = privs.begin();
3666 i != privs.end(); i++){
3667 os<<serializeString(*i);
3671 std::string s = os.str();
3672 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3674 m_con.Send(peer_id, 0, data, true);
3677 void Server::SendPlayerInventoryFormspec(u16 peer_id)
3679 Player *player = m_env->getPlayer(peer_id);
3681 if(player->peer_id == PEER_ID_INEXISTENT)
3684 std::ostringstream os(std::ios_base::binary);
3685 writeU16(os, TOCLIENT_INVENTORY_FORMSPEC);
3686 os<<serializeLongString(player->inventory_formspec);
3689 std::string s = os.str();
3690 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3692 m_con.Send(peer_id, 0, data, true);
3695 s32 Server::playSound(const SimpleSoundSpec &spec,
3696 const ServerSoundParams ¶ms)
3698 // Find out initial position of sound
3699 bool pos_exists = false;
3700 v3f pos = params.getPos(m_env, &pos_exists);
3701 // If position is not found while it should be, cancel sound
3702 if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
3704 // Filter destination clients
3705 std::set<RemoteClient*> dst_clients;
3706 if(params.to_player != "")
3708 Player *player = m_env->getPlayer(params.to_player.c_str());
3710 infostream<<"Server::playSound: Player \""<<params.to_player
3711 <<"\" not found"<<std::endl;
3714 if(player->peer_id == PEER_ID_INEXISTENT){
3715 infostream<<"Server::playSound: Player \""<<params.to_player
3716 <<"\" not connected"<<std::endl;
3719 RemoteClient *client = getClient(player->peer_id);
3720 dst_clients.insert(client);
3724 for(core::map<u16, RemoteClient*>::Iterator
3725 i = m_clients.getIterator(); i.atEnd() == false; i++)
3727 RemoteClient *client = i.getNode()->getValue();
3728 Player *player = m_env->getPlayer(client->peer_id);
3732 if(player->getPosition().getDistanceFrom(pos) >
3733 params.max_hear_distance)
3736 dst_clients.insert(client);
3739 if(dst_clients.size() == 0)
3742 s32 id = m_next_sound_id++;
3743 // The sound will exist as a reference in m_playing_sounds
3744 m_playing_sounds[id] = ServerPlayingSound();
3745 ServerPlayingSound &psound = m_playing_sounds[id];
3746 psound.params = params;
3747 for(std::set<RemoteClient*>::iterator i = dst_clients.begin();
3748 i != dst_clients.end(); i++)
3749 psound.clients.insert((*i)->peer_id);
3751 std::ostringstream os(std::ios_base::binary);
3752 writeU16(os, TOCLIENT_PLAY_SOUND);
3754 os<<serializeString(spec.name);
3755 writeF1000(os, spec.gain * params.gain);
3756 writeU8(os, params.type);
3757 writeV3F1000(os, pos);
3758 writeU16(os, params.object);
3759 writeU8(os, params.loop);
3761 std::string s = os.str();
3762 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3764 for(std::set<RemoteClient*>::iterator i = dst_clients.begin();
3765 i != dst_clients.end(); i++){
3767 m_con.Send((*i)->peer_id, 0, data, true);
3771 void Server::stopSound(s32 handle)
3773 // Get sound reference
3774 std::map<s32, ServerPlayingSound>::iterator i =
3775 m_playing_sounds.find(handle);
3776 if(i == m_playing_sounds.end())
3778 ServerPlayingSound &psound = i->second;
3780 std::ostringstream os(std::ios_base::binary);
3781 writeU16(os, TOCLIENT_STOP_SOUND);
3782 writeS32(os, handle);
3784 std::string s = os.str();
3785 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3787 for(std::set<u16>::iterator i = psound.clients.begin();
3788 i != psound.clients.end(); i++){
3790 m_con.Send(*i, 0, data, true);
3792 // Remove sound reference
3793 m_playing_sounds.erase(i);
3796 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3797 core::list<u16> *far_players, float far_d_nodes)
3799 float maxd = far_d_nodes*BS;
3800 v3f p_f = intToFloat(p, BS);
3804 SharedBuffer<u8> reply(replysize);
3805 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3806 writeS16(&reply[2], p.X);
3807 writeS16(&reply[4], p.Y);
3808 writeS16(&reply[6], p.Z);
3810 for(core::map<u16, RemoteClient*>::Iterator
3811 i = m_clients.getIterator();
3812 i.atEnd() == false; i++)
3814 // Get client and check that it is valid
3815 RemoteClient *client = i.getNode()->getValue();
3816 assert(client->peer_id == i.getNode()->getKey());
3817 if(client->serialization_version == SER_FMT_VER_INVALID)
3820 // Don't send if it's the same one
3821 if(client->peer_id == ignore_id)
3827 Player *player = m_env->getPlayer(client->peer_id);
3830 // If player is far away, only set modified blocks not sent
3831 v3f player_pos = player->getPosition();
3832 if(player_pos.getDistanceFrom(p_f) > maxd)
3834 far_players->push_back(client->peer_id);
3841 m_con.Send(client->peer_id, 0, reply, true);
3845 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3846 core::list<u16> *far_players, float far_d_nodes)
3848 float maxd = far_d_nodes*BS;
3849 v3f p_f = intToFloat(p, BS);
3851 for(core::map<u16, RemoteClient*>::Iterator
3852 i = m_clients.getIterator();
3853 i.atEnd() == false; i++)
3855 // Get client and check that it is valid
3856 RemoteClient *client = i.getNode()->getValue();
3857 assert(client->peer_id == i.getNode()->getKey());
3858 if(client->serialization_version == SER_FMT_VER_INVALID)
3861 // Don't send if it's the same one
3862 if(client->peer_id == ignore_id)
3868 Player *player = m_env->getPlayer(client->peer_id);
3871 // If player is far away, only set modified blocks not sent
3872 v3f player_pos = player->getPosition();
3873 if(player_pos.getDistanceFrom(p_f) > maxd)
3875 far_players->push_back(client->peer_id);
3882 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3883 SharedBuffer<u8> reply(replysize);
3884 writeU16(&reply[0], TOCLIENT_ADDNODE);
3885 writeS16(&reply[2], p.X);
3886 writeS16(&reply[4], p.Y);
3887 writeS16(&reply[6], p.Z);
3888 n.serialize(&reply[8], client->serialization_version);
3891 m_con.Send(client->peer_id, 0, reply, true);
3895 void Server::setBlockNotSent(v3s16 p)
3897 for(core::map<u16, RemoteClient*>::Iterator
3898 i = m_clients.getIterator();
3899 i.atEnd()==false; i++)
3901 RemoteClient *client = i.getNode()->getValue();
3902 client->SetBlockNotSent(p);
3906 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3908 DSTACK(__FUNCTION_NAME);
3910 v3s16 p = block->getPos();
3914 bool completely_air = true;
3915 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3916 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3917 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3919 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
3921 completely_air = false;
3922 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
3927 infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
3929 infostream<<"[completely air] ";
3930 infostream<<std::endl;
3934 Create a packet with the block in the right format
3937 std::ostringstream os(std::ios_base::binary);
3938 block->serialize(os, ver, false);
3939 std::string s = os.str();
3940 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3942 u32 replysize = 8 + blockdata.getSize();
3943 SharedBuffer<u8> reply(replysize);
3944 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3945 writeS16(&reply[2], p.X);
3946 writeS16(&reply[4], p.Y);
3947 writeS16(&reply[6], p.Z);
3948 memcpy(&reply[8], *blockdata, blockdata.getSize());
3950 /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3951 <<": \tpacket size: "<<replysize<<std::endl;*/
3956 m_con.Send(peer_id, 1, reply, true);
3959 void Server::SendBlocks(float dtime)
3961 DSTACK(__FUNCTION_NAME);
3963 JMutexAutoLock envlock(m_env_mutex);
3964 JMutexAutoLock conlock(m_con_mutex);
3966 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
3968 core::array<PrioritySortedBlockTransfer> queue;
3970 s32 total_sending = 0;
3973 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
3975 for(core::map<u16, RemoteClient*>::Iterator
3976 i = m_clients.getIterator();
3977 i.atEnd() == false; i++)
3979 RemoteClient *client = i.getNode()->getValue();
3980 assert(client->peer_id == i.getNode()->getKey());
3982 // If definitions and textures have not been sent, don't
3983 // send MapBlocks either
3984 if(!client->definitions_sent)
3987 total_sending += client->SendingCount();
3989 if(client->serialization_version == SER_FMT_VER_INVALID)
3992 client->GetNextBlocks(this, dtime, queue);
3997 // Lowest priority number comes first.
3998 // Lowest is most important.
4001 for(u32 i=0; i<queue.size(); i++)
4003 //TODO: Calculate limit dynamically
4004 if(total_sending >= g_settings->getS32
4005 ("max_simultaneous_block_sends_server_total"))
4008 PrioritySortedBlockTransfer q = queue[i];
4010 MapBlock *block = NULL;
4013 block = m_env->getMap().getBlockNoCreate(q.pos);
4015 catch(InvalidPositionException &e)
4020 RemoteClient *client = getClient(q.peer_id);
4022 SendBlockNoLock(q.peer_id, block, client->serialization_version);
4024 client->SentBlock(q.pos);
4030 void Server::fillMediaCache()
4032 DSTACK(__FUNCTION_NAME);
4034 infostream<<"Server: Calculating media file checksums"<<std::endl;
4036 // Collect all media file paths
4037 std::list<std::string> paths;
4038 for(core::list<ModSpec>::Iterator i = m_mods.begin();
4039 i != m_mods.end(); i++){
4040 const ModSpec &mod = *i;
4041 paths.push_back(mod.path + DIR_DELIM + "textures");
4042 paths.push_back(mod.path + DIR_DELIM + "sounds");
4043 paths.push_back(mod.path + DIR_DELIM + "media");
4044 paths.push_back(mod.path + DIR_DELIM + "models");
4046 std::string path_all = "textures";
4047 paths.push_back(path_all + DIR_DELIM + "all");
4049 // Collect media file information from paths into cache
4050 for(std::list<std::string>::iterator i = paths.begin();
4051 i != paths.end(); i++)
4053 std::string mediapath = *i;
4054 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
4055 for(u32 j=0; j<dirlist.size(); j++){
4056 if(dirlist[j].dir) // Ignode dirs
4058 std::string filename = dirlist[j].name;
4059 // If name contains illegal characters, ignore the file
4060 if(!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)){
4061 infostream<<"Server: ignoring illegal file name: \""
4062 <<filename<<"\""<<std::endl;
4065 // If name is not in a supported format, ignore it
4066 const char *supported_ext[] = {
4067 ".png", ".jpg", ".bmp", ".tga",
4068 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
4070 ".x", ".b3d", ".md2", ".obj",
4073 if(removeStringEnd(filename, supported_ext) == ""){
4074 infostream<<"Server: ignoring unsupported file extension: \""
4075 <<filename<<"\""<<std::endl;
4078 // Ok, attempt to load the file and add to cache
4079 std::string filepath = mediapath + DIR_DELIM + filename;
4081 std::ifstream fis(filepath.c_str(), std::ios_base::binary);
4082 if(fis.good() == false){
4083 errorstream<<"Server::fillMediaCache(): Could not open \""
4084 <<filename<<"\" for reading"<<std::endl;
4087 std::ostringstream tmp_os(std::ios_base::binary);
4091 fis.read(buf, 1024);
4092 std::streamsize len = fis.gcount();
4093 tmp_os.write(buf, len);
4102 errorstream<<"Server::fillMediaCache(): Failed to read \""
4103 <<filename<<"\""<<std::endl;
4106 if(tmp_os.str().length() == 0){
4107 errorstream<<"Server::fillMediaCache(): Empty file \""
4108 <<filepath<<"\""<<std::endl;
4113 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
4115 unsigned char *digest = sha1.getDigest();
4116 std::string sha1_base64 = base64_encode(digest, 20);
4117 std::string sha1_hex = hex_encode((char*)digest, 20);
4121 this->m_media[filename] = MediaInfo(filepath, sha1_base64);
4122 verbosestream<<"Server: "<<sha1_hex<<" is "<<filename<<std::endl;
4127 struct SendableMediaAnnouncement
4130 std::string sha1_digest;
4132 SendableMediaAnnouncement(const std::string name_="",
4133 const std::string sha1_digest_=""):
4135 sha1_digest(sha1_digest_)
4139 void Server::sendMediaAnnouncement(u16 peer_id)
4141 DSTACK(__FUNCTION_NAME);
4143 verbosestream<<"Server: Announcing files to id("<<peer_id<<")"
4146 core::list<SendableMediaAnnouncement> file_announcements;
4148 for(std::map<std::string, MediaInfo>::iterator i = m_media.begin();
4149 i != m_media.end(); i++){
4151 file_announcements.push_back(
4152 SendableMediaAnnouncement(i->first, i->second.sha1_digest));
4156 std::ostringstream os(std::ios_base::binary);
4164 u16 length of sha1_digest
4169 writeU16(os, TOCLIENT_ANNOUNCE_MEDIA);
4170 writeU16(os, file_announcements.size());
4172 for(core::list<SendableMediaAnnouncement>::Iterator
4173 j = file_announcements.begin();
4174 j != file_announcements.end(); j++){
4175 os<<serializeString(j->name);
4176 os<<serializeString(j->sha1_digest);
4180 std::string s = os.str();
4181 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4184 m_con.Send(peer_id, 0, data, true);
4188 struct SendableMedia
4194 SendableMedia(const std::string &name_="", const std::string path_="",
4195 const std::string &data_=""):
4202 void Server::sendRequestedMedia(u16 peer_id,
4203 const core::list<MediaRequest> &tosend)
4205 DSTACK(__FUNCTION_NAME);
4207 verbosestream<<"Server::sendRequestedMedia(): "
4208 <<"Sending files to client"<<std::endl;
4212 // Put 5kB in one bunch (this is not accurate)
4213 u32 bytes_per_bunch = 5000;
4215 core::array< core::list<SendableMedia> > file_bunches;
4216 file_bunches.push_back(core::list<SendableMedia>());
4218 u32 file_size_bunch_total = 0;
4220 for(core::list<MediaRequest>::ConstIterator i = tosend.begin();
4221 i != tosend.end(); i++)
4223 if(m_media.find(i->name) == m_media.end()){
4224 errorstream<<"Server::sendRequestedMedia(): Client asked for "
4225 <<"unknown file \""<<(i->name)<<"\""<<std::endl;
4229 //TODO get path + name
4230 std::string tpath = m_media[(*i).name].path;
4233 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
4234 if(fis.good() == false){
4235 errorstream<<"Server::sendRequestedMedia(): Could not open \""
4236 <<tpath<<"\" for reading"<<std::endl;
4239 std::ostringstream tmp_os(std::ios_base::binary);
4243 fis.read(buf, 1024);
4244 std::streamsize len = fis.gcount();
4245 tmp_os.write(buf, len);
4246 file_size_bunch_total += len;
4255 errorstream<<"Server::sendRequestedMedia(): Failed to read \""
4256 <<(*i).name<<"\""<<std::endl;
4259 /*infostream<<"Server::sendRequestedMedia(): Loaded \""
4260 <<tname<<"\""<<std::endl;*/
4262 file_bunches[file_bunches.size()-1].push_back(
4263 SendableMedia((*i).name, tpath, tmp_os.str()));
4265 // Start next bunch if got enough data
4266 if(file_size_bunch_total >= bytes_per_bunch){
4267 file_bunches.push_back(core::list<SendableMedia>());
4268 file_size_bunch_total = 0;
4273 /* Create and send packets */
4275 u32 num_bunches = file_bunches.size();
4276 for(u32 i=0; i<num_bunches; i++)
4278 std::ostringstream os(std::ios_base::binary);
4282 u16 total number of texture bunches
4283 u16 index of this bunch
4284 u32 number of files in this bunch
4293 writeU16(os, TOCLIENT_MEDIA);
4294 writeU16(os, num_bunches);
4296 writeU32(os, file_bunches[i].size());
4298 for(core::list<SendableMedia>::Iterator
4299 j = file_bunches[i].begin();
4300 j != file_bunches[i].end(); j++){
4301 os<<serializeString(j->name);
4302 os<<serializeLongString(j->data);
4306 std::string s = os.str();
4307 verbosestream<<"Server::sendRequestedMedia(): bunch "
4308 <<i<<"/"<<num_bunches
4309 <<" files="<<file_bunches[i].size()
4310 <<" size=" <<s.size()<<std::endl;
4311 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4313 m_con.Send(peer_id, 0, data, true);
4317 void Server::sendDetachedInventory(const std::string &name, u16 peer_id)
4319 if(m_detached_inventories.count(name) == 0){
4320 errorstream<<__FUNCTION_NAME<<": \""<<name<<"\" not found"<<std::endl;
4323 Inventory *inv = m_detached_inventories[name];
4325 std::ostringstream os(std::ios_base::binary);
4326 writeU16(os, TOCLIENT_DETACHED_INVENTORY);
4327 os<<serializeString(name);
4331 std::string s = os.str();
4332 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4334 m_con.Send(peer_id, 0, data, true);
4337 void Server::sendDetachedInventoryToAll(const std::string &name)
4339 DSTACK(__FUNCTION_NAME);
4341 for(core::map<u16, RemoteClient*>::Iterator
4342 i = m_clients.getIterator();
4343 i.atEnd() == false; i++){
4344 RemoteClient *client = i.getNode()->getValue();
4345 sendDetachedInventory(name, client->peer_id);
4349 void Server::sendDetachedInventories(u16 peer_id)
4351 DSTACK(__FUNCTION_NAME);
4353 for(std::map<std::string, Inventory*>::iterator
4354 i = m_detached_inventories.begin();
4355 i != m_detached_inventories.end(); i++){
4356 const std::string &name = i->first;
4357 //Inventory *inv = i->second;
4358 sendDetachedInventory(name, peer_id);
4366 void Server::DiePlayer(u16 peer_id)
4368 DSTACK(__FUNCTION_NAME);
4370 PlayerSAO *playersao = getPlayerSAO(peer_id);
4373 infostream<<"Server::DiePlayer(): Player "
4374 <<playersao->getPlayer()->getName()
4375 <<" dies"<<std::endl;
4377 playersao->setHP(0);
4379 // Trigger scripted stuff
4380 scriptapi_on_dieplayer(m_lua, playersao);
4382 SendPlayerHP(peer_id);
4383 SendDeathscreen(m_con, peer_id, false, v3f(0,0,0));
4386 void Server::RespawnPlayer(u16 peer_id)
4388 DSTACK(__FUNCTION_NAME);
4390 PlayerSAO *playersao = getPlayerSAO(peer_id);
4393 infostream<<"Server::RespawnPlayer(): Player "
4394 <<playersao->getPlayer()->getName()
4395 <<" respawns"<<std::endl;
4397 playersao->setHP(PLAYER_MAX_HP);
4399 bool repositioned = scriptapi_on_respawnplayer(m_lua, playersao);
4401 v3f pos = findSpawnPos(m_env->getServerMap());
4402 playersao->setPos(pos);
4406 void Server::UpdateCrafting(u16 peer_id)
4408 DSTACK(__FUNCTION_NAME);
4410 Player* player = m_env->getPlayer(peer_id);
4413 // Get a preview for crafting
4415 getCraftingResult(&player->inventory, preview, false, this);
4417 // Put the new preview in
4418 InventoryList *plist = player->inventory.getList("craftpreview");
4420 assert(plist->getSize() >= 1);
4421 plist->changeItem(0, preview);
4424 RemoteClient* Server::getClient(u16 peer_id)
4426 DSTACK(__FUNCTION_NAME);
4427 //JMutexAutoLock lock(m_con_mutex);
4428 core::map<u16, RemoteClient*>::Node *n;
4429 n = m_clients.find(peer_id);
4430 // A client should exist for all peers
4432 return n->getValue();
4435 std::wstring Server::getStatusString()
4437 std::wostringstream os(std::ios_base::binary);
4440 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4442 os<<L", uptime="<<m_uptime.get();
4443 // Information about clients
4444 core::map<u16, RemoteClient*>::Iterator i;
4447 for(i = m_clients.getIterator(), first = true;
4448 i.atEnd() == false; i++)
4450 // Get client and check that it is valid
4451 RemoteClient *client = i.getNode()->getValue();
4452 assert(client->peer_id == i.getNode()->getKey());
4453 if(client->serialization_version == SER_FMT_VER_INVALID)
4456 Player *player = m_env->getPlayer(client->peer_id);
4457 // Get name of player
4458 std::wstring name = L"unknown";
4460 name = narrow_to_wide(player->getName());
4461 // Add name to information string
4469 if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
4470 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4471 if(g_settings->get("motd") != "")
4472 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4476 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
4478 std::set<std::string> privs;
4479 scriptapi_get_auth(m_lua, name, NULL, &privs);
4483 bool Server::checkPriv(const std::string &name, const std::string &priv)
4485 std::set<std::string> privs = getPlayerEffectivePrivs(name);
4486 return (privs.count(priv) != 0);
4489 void Server::reportPrivsModified(const std::string &name)
4492 for(core::map<u16, RemoteClient*>::Iterator
4493 i = m_clients.getIterator();
4494 i.atEnd() == false; i++){
4495 RemoteClient *client = i.getNode()->getValue();
4496 Player *player = m_env->getPlayer(client->peer_id);
4497 reportPrivsModified(player->getName());
4500 Player *player = m_env->getPlayer(name.c_str());
4503 SendPlayerPrivileges(player->peer_id);
4504 PlayerSAO *sao = player->getPlayerSAO();
4507 sao->updatePrivileges(
4508 getPlayerEffectivePrivs(name),
4513 void Server::reportInventoryFormspecModified(const std::string &name)
4515 Player *player = m_env->getPlayer(name.c_str());
4518 SendPlayerInventoryFormspec(player->peer_id);
4521 // Saves g_settings to configpath given at initialization
4522 void Server::saveConfig()
4524 if(m_path_config != "")
4525 g_settings->updateConfigFile(m_path_config.c_str());
4528 void Server::notifyPlayer(const char *name, const std::wstring msg)
4530 Player *player = m_env->getPlayer(name);
4533 SendChatMessage(player->peer_id, std::wstring(L"Server: -!- ")+msg);
4536 void Server::notifyPlayers(const std::wstring msg)
4538 BroadcastChatMessage(msg);
4541 void Server::queueBlockEmerge(v3s16 blockpos, bool allow_generate)
4545 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
4546 m_emerge_queue.addBlock(PEER_ID_INEXISTENT, blockpos, flags);
4549 Inventory* Server::createDetachedInventory(const std::string &name)
4551 if(m_detached_inventories.count(name) > 0){
4552 infostream<<"Server clearing detached inventory \""<<name<<"\""<<std::endl;
4553 delete m_detached_inventories[name];
4555 infostream<<"Server creating detached inventory \""<<name<<"\""<<std::endl;
4557 Inventory *inv = new Inventory(m_itemdef);
4559 m_detached_inventories[name] = inv;
4560 sendDetachedInventoryToAll(name);
4567 BoolScopeSet(bool *dst, bool val):
4570 m_orig_state = *m_dst;
4575 *m_dst = m_orig_state;
4582 // actions: time-reversed list
4583 // Return value: success/failure
4584 bool Server::rollbackRevertActions(const std::list<RollbackAction> &actions,
4585 std::list<std::string> *log)
4587 infostream<<"Server::rollbackRevertActions(len="<<actions.size()<<")"<<std::endl;
4588 ServerMap *map = (ServerMap*)(&m_env->getMap());
4589 // Disable rollback report sink while reverting
4590 BoolScopeSet rollback_scope_disable(&m_rollback_sink_enabled, false);
4592 // Fail if no actions to handle
4593 if(actions.empty()){
4594 log->push_back("Nothing to do.");
4601 for(std::list<RollbackAction>::const_iterator
4602 i = actions.begin();
4603 i != actions.end(); i++)
4605 const RollbackAction &action = *i;
4607 bool success = action.applyRevert(map, this, this);
4610 std::ostringstream os;
4611 os<<"Revert of step ("<<num_tried<<") "<<action.toString()<<" failed";
4612 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
4614 log->push_back(os.str());
4616 std::ostringstream os;
4617 os<<"Succesfully reverted step ("<<num_tried<<") "<<action.toString();
4618 infostream<<"Map::rollbackRevertActions(): "<<os.str()<<std::endl;
4620 log->push_back(os.str());
4624 infostream<<"Map::rollbackRevertActions(): "<<num_failed<<"/"<<num_tried
4625 <<" failed"<<std::endl;
4627 // Call it done if less than half failed
4628 return num_failed <= num_tried/2;
4631 // IGameDef interface
4633 IItemDefManager* Server::getItemDefManager()
4637 INodeDefManager* Server::getNodeDefManager()
4641 ICraftDefManager* Server::getCraftDefManager()
4645 ITextureSource* Server::getTextureSource()
4649 u16 Server::allocateUnknownNodeId(const std::string &name)
4651 return m_nodedef->allocateDummy(name);
4653 ISoundManager* Server::getSoundManager()
4655 return &dummySoundManager;
4657 MtEventManager* Server::getEventManager()
4661 IRollbackReportSink* Server::getRollbackReportSink()
4663 if(!m_enable_rollback_recording)
4665 if(!m_rollback_sink_enabled)
4670 IWritableItemDefManager* Server::getWritableItemDefManager()
4674 IWritableNodeDefManager* Server::getWritableNodeDefManager()
4678 IWritableCraftDefManager* Server::getWritableCraftDefManager()
4683 const ModSpec* Server::getModSpec(const std::string &modname)
4685 for(core::list<ModSpec>::Iterator i = m_mods.begin();
4686 i != m_mods.end(); i++){
4687 const ModSpec &mod = *i;
4688 if(mod.name == modname)
4693 void Server::getModNames(core::list<std::string> &modlist)
4695 for(core::list<ModSpec>::Iterator i = m_mods.begin(); i != m_mods.end(); i++)
4697 modlist.push_back((*i).name);
4700 std::string Server::getBuiltinLuaPath()
4702 return porting::path_share + DIR_DELIM + "builtin";
4705 v3f findSpawnPos(ServerMap &map)
4707 //return v3f(50,50,50)*BS;
4712 nodepos = v2s16(0,0);
4717 // Try to find a good place a few times
4718 for(s32 i=0; i<1000; i++)
4721 // We're going to try to throw the player to this position
4722 v2s16 nodepos2d = v2s16(-range + (myrand()%(range*2)),
4723 -range + (myrand()%(range*2)));
4724 //v2s16 sectorpos = getNodeSectorPos(nodepos2d);
4725 // Get ground height at point (fallbacks to heightmap function)
4726 s16 groundheight = map.findGroundLevel(nodepos2d);
4727 // Don't go underwater
4728 if(groundheight < WATER_LEVEL)
4730 //infostream<<"-> Underwater"<<std::endl;
4733 // Don't go to high places
4734 if(groundheight > WATER_LEVEL + 4)
4736 //infostream<<"-> Underwater"<<std::endl;
4740 nodepos = v3s16(nodepos2d.X, groundheight-2, nodepos2d.Y);
4741 bool is_good = false;
4743 for(s32 i=0; i<10; i++){
4744 v3s16 blockpos = getNodeBlockPos(nodepos);
4745 map.emergeBlock(blockpos, true);
4746 MapNode n = map.getNodeNoEx(nodepos);
4747 if(n.getContent() == CONTENT_AIR){
4758 // Found a good place
4759 //infostream<<"Searched through "<<i<<" places."<<std::endl;
4765 return intToFloat(nodepos, BS);
4768 PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id)
4770 RemotePlayer *player = NULL;
4771 bool newplayer = false;
4774 Try to get an existing player
4776 player = static_cast<RemotePlayer*>(m_env->getPlayer(name));
4778 // If player is already connected, cancel
4779 if(player != NULL && player->peer_id != 0)
4781 infostream<<"emergePlayer(): Player already connected"<<std::endl;
4786 If player with the wanted peer_id already exists, cancel.
4788 if(m_env->getPlayer(peer_id) != NULL)
4790 infostream<<"emergePlayer(): Player with wrong name but same"
4791 " peer_id already exists"<<std::endl;
4796 Create a new player if it doesn't exist yet
4801 player = new RemotePlayer(this);
4802 player->updateName(name);
4804 /* Set player position */
4805 infostream<<"Server: Finding spawn place for player \""
4806 <<name<<"\""<<std::endl;
4807 v3f pos = findSpawnPos(m_env->getServerMap());
4808 player->setPosition(pos);
4810 /* Add player to environment */
4811 m_env->addPlayer(player);
4815 Create a new player active object
4817 PlayerSAO *playersao = new PlayerSAO(m_env, player, peer_id,
4818 getPlayerEffectivePrivs(player->getName()),
4821 /* Add object to environment */
4822 m_env->addActiveObject(playersao);
4826 scriptapi_on_newplayer(m_lua, playersao);
4828 scriptapi_on_joinplayer(m_lua, playersao);
4833 void Server::handlePeerChange(PeerChange &c)
4835 JMutexAutoLock envlock(m_env_mutex);
4836 JMutexAutoLock conlock(m_con_mutex);
4838 if(c.type == PEER_ADDED)
4845 core::map<u16, RemoteClient*>::Node *n;
4846 n = m_clients.find(c.peer_id);
4847 // The client shouldn't already exist
4851 RemoteClient *client = new RemoteClient();
4852 client->peer_id = c.peer_id;
4853 m_clients.insert(client->peer_id, client);
4856 else if(c.type == PEER_REMOVED)
4863 core::map<u16, RemoteClient*>::Node *n;
4864 n = m_clients.find(c.peer_id);
4865 // The client should exist
4869 Mark objects to be not known by the client
4871 RemoteClient *client = n->getValue();
4873 for(core::map<u16, bool>::Iterator
4874 i = client->m_known_objects.getIterator();
4875 i.atEnd()==false; i++)
4878 u16 id = i.getNode()->getKey();
4879 ServerActiveObject* obj = m_env->getActiveObject(id);
4881 if(obj && obj->m_known_by_count > 0)
4882 obj->m_known_by_count--;
4886 Clear references to playing sounds
4888 for(std::map<s32, ServerPlayingSound>::iterator
4889 i = m_playing_sounds.begin();
4890 i != m_playing_sounds.end();)
4892 ServerPlayingSound &psound = i->second;
4893 psound.clients.erase(c.peer_id);
4894 if(psound.clients.size() == 0)
4895 m_playing_sounds.erase(i++);
4900 Player *player = m_env->getPlayer(c.peer_id);
4902 // Collect information about leaving in chat
4903 std::wstring message;
4907 std::wstring name = narrow_to_wide(player->getName());
4910 message += L" left the game.";
4912 message += L" (timed out)";
4916 /* Run scripts and remove from environment */
4920 PlayerSAO *playersao = player->getPlayerSAO();
4923 scriptapi_on_leaveplayer(m_lua, playersao);
4925 playersao->disconnected();
4935 std::ostringstream os(std::ios_base::binary);
4936 for(core::map<u16, RemoteClient*>::Iterator
4937 i = m_clients.getIterator();
4938 i.atEnd() == false; i++)
4940 RemoteClient *client = i.getNode()->getValue();
4941 assert(client->peer_id == i.getNode()->getKey());
4942 if(client->serialization_version == SER_FMT_VER_INVALID)
4945 Player *player = m_env->getPlayer(client->peer_id);
4948 // Get name of player
4949 os<<player->getName()<<" ";
4952 actionstream<<player->getName()<<" "
4953 <<(c.timeout?"times out.":"leaves game.")
4954 <<" List of players: "
4955 <<os.str()<<std::endl;
4960 delete m_clients[c.peer_id];
4961 m_clients.remove(c.peer_id);
4963 // Send player info to all remaining clients
4964 //SendPlayerInfos();
4966 // Send leave chat message to all remaining clients
4967 if(message.length() != 0)
4968 BroadcastChatMessage(message);
4977 void Server::handlePeerChanges()
4979 while(m_peer_change_queue.size() > 0)
4981 PeerChange c = m_peer_change_queue.pop_front();
4983 verbosestream<<"Server: Handling peer change: "
4984 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4987 handlePeerChange(c);
4991 void dedicated_server_loop(Server &server, bool &kill)
4993 DSTACK(__FUNCTION_NAME);
4995 verbosestream<<"dedicated_server_loop()"<<std::endl;
4997 IntervalLimiter m_profiler_interval;
5001 float steplen = g_settings->getFloat("dedicated_server_step");
5002 // This is kind of a hack but can be done like this
5003 // because server.step() is very light
5005 ScopeProfiler sp(g_profiler, "dedicated server sleep");
5006 sleep_ms((int)(steplen*1000.0));
5008 server.step(steplen);
5010 if(server.getShutdownRequested() || kill)
5012 infostream<<"Dedicated server quitting"<<std::endl;
5019 float profiler_print_interval =
5020 g_settings->getFloat("profiler_print_interval");
5021 if(profiler_print_interval != 0)
5023 if(m_profiler_interval.step(steplen, profiler_print_interval))
5025 infostream<<"Profiler:"<<std::endl;
5026 g_profiler->print(infostream);
5027 g_profiler->clear();