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 General Public License as published by
7 the Free Software Foundation; either version 2 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 General Public License for more details.
15 You should have received a copy of the GNU 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.
24 #include "clientserver.h"
26 #include "jmutexautolock.h"
28 #include "constants.h"
31 #include "servercommand.h"
34 #include "serverobject.h"
39 #include "scriptapi.h"
44 #include "content_mapnode.h"
45 #include "content_nodemeta.h"
46 #include "content_abm.h"
47 #include "content_sao.h"
52 #include "utility_string.h"
53 #include "sound.h" // dummySoundManager
54 #include "event_manager.h"
57 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
59 #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
61 class MapEditEventIgnorer
64 MapEditEventIgnorer(bool *flag):
73 ~MapEditEventIgnorer()
86 class MapEditEventAreaIgnorer
89 MapEditEventAreaIgnorer(VoxelArea *ignorevariable, const VoxelArea &a):
90 m_ignorevariable(ignorevariable)
92 if(m_ignorevariable->getVolume() == 0)
93 *m_ignorevariable = a;
95 m_ignorevariable = NULL;
98 ~MapEditEventAreaIgnorer()
102 assert(m_ignorevariable->getVolume() != 0);
103 *m_ignorevariable = VoxelArea();
108 VoxelArea *m_ignorevariable;
111 void * ServerThread::Thread()
115 log_register_thread("ServerThread");
117 DSTACK(__FUNCTION_NAME);
119 BEGIN_DEBUG_EXCEPTION_HANDLER
124 //TimeTaker timer("AsyncRunStep() + Receive()");
127 //TimeTaker timer("AsyncRunStep()");
128 m_server->AsyncRunStep();
131 //infostream<<"Running m_server->Receive()"<<std::endl;
134 catch(con::NoIncomingDataException &e)
137 catch(con::PeerNotFoundException &e)
139 infostream<<"Server: PeerNotFoundException"<<std::endl;
141 catch(con::ConnectionBindFailed &e)
143 m_server->setAsyncFatalError(e.what());
147 m_server->setAsyncFatalError(e.what());
151 END_DEBUG_EXCEPTION_HANDLER(errorstream)
156 void * EmergeThread::Thread()
160 log_register_thread("EmergeThread");
162 DSTACK(__FUNCTION_NAME);
164 BEGIN_DEBUG_EXCEPTION_HANDLER
166 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
168 v3s16 last_tried_pos(-32768,-32768,-32768); // For error output
171 Get block info from queue, emerge them and send them
174 After queue is empty, exit.
178 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
182 SharedPtr<QueuedBlockEmerge> q(qptr);
190 Do not generate over-limit
192 if(blockpos_over_limit(p))
195 //infostream<<"EmergeThread::Thread(): running"<<std::endl;
197 //TimeTaker timer("block emerge");
200 Try to emerge it from somewhere.
202 If it is only wanted as optional, only loading from disk
207 Check if any peer wants it as non-optional. In that case it
210 Also decrement the emerge queue count in clients.
213 bool only_from_disk = true;
216 core::map<u16, u8>::Iterator i;
217 for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
219 //u16 peer_id = i.getNode()->getKey();
222 u8 flags = i.getNode()->getValue();
223 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
224 only_from_disk = false;
229 if(enable_mapgen_debug_info)
230 infostream<<"EmergeThread: p="
231 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
232 <<"only_from_disk="<<only_from_disk<<std::endl;
234 ServerMap &map = ((ServerMap&)m_server->m_env->getMap());
236 MapBlock *block = NULL;
237 bool got_block = true;
238 core::map<v3s16, MapBlock*> modified_blocks;
241 Try to fetch block from memory or disk.
242 If not found and asked to generate, initialize generator.
245 bool started_generate = false;
246 mapgen::BlockMakeData data;
249 JMutexAutoLock envlock(m_server->m_env_mutex);
251 // Load sector if it isn't loaded
252 if(map.getSectorNoGenerateNoEx(p2d) == NULL)
253 map.loadSectorMeta(p2d);
255 // Attempt to load block
256 block = map.getBlockNoCreateNoEx(p);
257 if(!block || block->isDummy() || !block->isGenerated())
259 if(enable_mapgen_debug_info)
260 infostream<<"EmergeThread: not in memory, "
261 <<"attempting to load from disk"<<std::endl;
263 block = map.loadBlock(p);
266 // If could not load and allowed to generate, start generation
267 // inside this same envlock
268 if(only_from_disk == false &&
269 (block == NULL || block->isGenerated() == false)){
270 if(enable_mapgen_debug_info)
271 infostream<<"EmergeThread: generating"<<std::endl;
272 started_generate = true;
274 map.initBlockMake(&data, p);
279 If generator was initialized, generate now when envlock is free.
284 ScopeProfiler sp(g_profiler, "EmergeThread: mapgen::make_block",
286 TimeTaker t("mapgen::make_block()");
288 mapgen::make_block(&data);
290 if(enable_mapgen_debug_info == false)
291 t.stop(true); // Hide output
295 // Lock environment again to access the map
296 JMutexAutoLock envlock(m_server->m_env_mutex);
298 ScopeProfiler sp(g_profiler, "EmergeThread: after "
299 "mapgen::make_block (envlock)", SPT_AVG);
301 // Blit data back on map, update lighting, add mobs and
302 // whatever this does
303 map.finishBlockMake(&data, modified_blocks);
306 block = map.getBlockNoCreateNoEx(p);
308 // If block doesn't exist, don't try doing anything with it
309 // This happens if the block is not in generation boundaries
314 Do some post-generate stuff
317 v3s16 minp = data.blockpos_min*MAP_BLOCKSIZE;
318 v3s16 maxp = data.blockpos_max*MAP_BLOCKSIZE +
319 v3s16(1,1,1)*(MAP_BLOCKSIZE-1);
322 Ignore map edit events, they will not need to be
323 sent to anybody because the block hasn't been sent
326 //MapEditEventIgnorer ign(&m_server->m_ignore_map_edit_events);
327 MapEditEventAreaIgnorer ign(
328 &m_server->m_ignore_map_edit_events_area,
329 VoxelArea(minp, maxp));
331 TimeTaker timer("on_generated");
332 scriptapi_environment_on_generated(m_server->m_lua,
333 minp, maxp, mapgen::get_blockseed(data.seed, minp));
334 /*int t = timer.stop(true);
335 dstream<<"on_generated took "<<t<<"ms"<<std::endl;*/
338 if(enable_mapgen_debug_info)
339 infostream<<"EmergeThread: ended up with: "
340 <<analyze_block(block)<<std::endl;
342 // Activate objects and stuff
343 m_server->m_env->activateBlock(block, 0);
351 Set sent status of modified blocks on clients
354 // NOTE: Server's clients are also behind the connection mutex
355 JMutexAutoLock lock(m_server->m_con_mutex);
358 Add the originally fetched block to the modified list
362 modified_blocks.insert(p, block);
366 Set the modified blocks unsent for all the clients
369 for(core::map<u16, RemoteClient*>::Iterator
370 i = m_server->m_clients.getIterator();
371 i.atEnd() == false; i++)
373 RemoteClient *client = i.getNode()->getValue();
375 if(modified_blocks.size() > 0)
377 // Remove block from sent history
378 client->SetBlocksNotSent(modified_blocks);
382 catch(VersionMismatchException &e)
384 std::ostringstream err;
385 err<<"World data version mismatch in MapBlock "<<PP(last_tried_pos)<<std::endl;
386 err<<"----"<<std::endl;
387 err<<"\""<<e.what()<<"\""<<std::endl;
388 err<<"See debug.txt."<<std::endl;
389 err<<"World probably saved by a newer version of Minetest."<<std::endl;
390 m_server->setAsyncFatalError(err.str());
392 catch(SerializationError &e)
394 std::ostringstream err;
395 err<<"Invalid data in MapBlock "<<PP(last_tried_pos)<<std::endl;
396 err<<"----"<<std::endl;
397 err<<"\""<<e.what()<<"\""<<std::endl;
398 err<<"See debug.txt."<<std::endl;
399 err<<"You can ignore this using [ignore_world_load_errors = true]."<<std::endl;
400 m_server->setAsyncFatalError(err.str());
403 END_DEBUG_EXCEPTION_HANDLER(errorstream)
405 log_deregister_thread();
410 v3f ServerSoundParams::getPos(ServerEnvironment *env, bool *pos_exists) const
412 if(pos_exists) *pos_exists = false;
417 if(pos_exists) *pos_exists = true;
422 ServerActiveObject *sao = env->getActiveObject(object);
425 if(pos_exists) *pos_exists = true;
426 return sao->getBasePosition(); }
431 void RemoteClient::GetNextBlocks(Server *server, float dtime,
432 core::array<PrioritySortedBlockTransfer> &dest)
434 DSTACK(__FUNCTION_NAME);
437 TimeTaker timer("RemoteClient::GetNextBlocks", &timer_result);*/
440 m_nothing_to_send_pause_timer -= dtime;
441 m_nearest_unsent_reset_timer += dtime;
443 if(m_nothing_to_send_pause_timer >= 0)
448 // Won't send anything if already sending
449 if(m_blocks_sending.size() >= g_settings->getU16
450 ("max_simultaneous_block_sends_per_client"))
452 //infostream<<"Not sending any blocks, Queue full."<<std::endl;
456 //TimeTaker timer("RemoteClient::GetNextBlocks");
458 Player *player = server->m_env->getPlayer(peer_id);
460 assert(player != NULL);
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*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 duration: "<<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_itemdef(createItemDefManager()),
938 m_nodedef(createNodeDefManager()),
939 m_craftdef(createCraftDefManager()),
940 m_event(new EventManager()),
942 m_emergethread(this),
943 m_time_of_day_send_timer(0),
945 m_shutdown_requested(false),
946 m_ignore_map_edit_events(false),
947 m_ignore_map_edit_events_peer_id(0)
949 m_liquid_transform_timer = 0.0;
950 m_print_info_timer = 0.0;
951 m_objectdata_timer = 0.0;
952 m_emergethread_trigger_timer = 0.0;
953 m_savemap_timer = 0.0;
957 m_step_dtime_mutex.Init();
961 throw ServerError("Supplied empty world path");
963 if(!gamespec.isValid())
964 throw ServerError("Supplied invalid gamespec");
966 infostream<<"Server created for gameid \""<<m_gamespec.id<<"\"";
967 if(m_simple_singleplayer_mode)
968 infostream<<" in simple singleplayer mode"<<std::endl;
970 infostream<<std::endl;
971 infostream<<"- world: "<<m_path_world<<std::endl;
972 infostream<<"- config: "<<m_path_config<<std::endl;
973 infostream<<"- game: "<<m_gamespec.path<<std::endl;
975 // Add world mod search path
976 m_modspaths.push_front(m_path_world + DIR_DELIM + "worldmods");
977 // Add addon mod search path
978 for(std::set<std::string>::const_iterator i = m_gamespec.mods_paths.begin();
979 i != m_gamespec.mods_paths.end(); i++)
980 m_modspaths.push_front((*i));
982 // Print out mod search paths
983 for(core::list<std::string>::Iterator i = m_modspaths.begin();
984 i != m_modspaths.end(); i++){
985 std::string modspath = *i;
986 infostream<<"- mods: "<<modspath<<std::endl;
989 // Path to builtin.lua
990 std::string builtinpath = getBuiltinLuaPath() + DIR_DELIM + "builtin.lua";
992 // Create world if it doesn't exist
993 if(!initializeWorld(m_path_world, m_gamespec.id))
994 throw ServerError("Failed to initialize world");
997 JMutexAutoLock envlock(m_env_mutex);
998 JMutexAutoLock conlock(m_con_mutex);
1000 // Initialize scripting
1002 infostream<<"Server: Initializing Lua"<<std::endl;
1003 m_lua = script_init();
1006 scriptapi_export(m_lua, this);
1007 // Load and run builtin.lua
1008 infostream<<"Server: Loading builtin.lua [\""
1009 <<builtinpath<<"\"]"<<std::endl;
1010 bool success = scriptapi_loadmod(m_lua, builtinpath, "__builtin");
1012 errorstream<<"Server: Failed to load and run "
1013 <<builtinpath<<std::endl;
1014 throw ModError("Failed to load and run "+builtinpath);
1016 // Find mods in mod search paths
1017 m_mods = getMods(m_modspaths);
1019 infostream<<"Server: Loading mods: ";
1020 for(core::list<ModSpec>::Iterator i = m_mods.begin();
1021 i != m_mods.end(); i++){
1022 const ModSpec &mod = *i;
1023 infostream<<mod.name<<" ";
1025 infostream<<std::endl;
1026 // Load and run "mod" scripts
1027 for(core::list<ModSpec>::Iterator i = m_mods.begin();
1028 i != m_mods.end(); i++){
1029 const ModSpec &mod = *i;
1030 std::string scriptpath = mod.path + DIR_DELIM + "init.lua";
1031 infostream<<" ["<<padStringRight(mod.name, 12)<<"] [\""
1032 <<scriptpath<<"\"]"<<std::endl;
1033 bool success = scriptapi_loadmod(m_lua, scriptpath, mod.name);
1035 errorstream<<"Server: Failed to load and run "
1036 <<scriptpath<<std::endl;
1037 throw ModError("Failed to load and run "+scriptpath);
1041 // Read Textures and calculate sha1 sums
1044 // Apply item aliases in the node definition manager
1045 m_nodedef->updateAliases(m_itemdef);
1047 // Initialize Environment
1049 m_env = new ServerEnvironment(new ServerMap(path_world, this), m_lua,
1052 // Give environment reference to scripting api
1053 scriptapi_add_environment(m_lua, m_env);
1055 // Register us to receive map edit events
1056 m_env->getMap().addEventReceiver(this);
1058 // If file exists, load environment metadata
1059 if(fs::PathExists(m_path_world+DIR_DELIM+"env_meta.txt"))
1061 infostream<<"Server: Loading environment metadata"<<std::endl;
1062 m_env->loadMeta(m_path_world);
1066 infostream<<"Server: Loading players"<<std::endl;
1067 m_env->deSerializePlayers(m_path_world);
1070 Add some test ActiveBlockModifiers to environment
1072 add_legacy_abms(m_env, m_nodedef);
1077 infostream<<"Server destructing"<<std::endl;
1080 Send shutdown message
1083 JMutexAutoLock conlock(m_con_mutex);
1085 std::wstring line = L"*** Server shutting down";
1088 Send the message to clients
1090 for(core::map<u16, RemoteClient*>::Iterator
1091 i = m_clients.getIterator();
1092 i.atEnd() == false; i++)
1094 // Get client and check that it is valid
1095 RemoteClient *client = i.getNode()->getValue();
1096 assert(client->peer_id == i.getNode()->getKey());
1097 if(client->serialization_version == SER_FMT_VER_INVALID)
1101 SendChatMessage(client->peer_id, line);
1103 catch(con::PeerNotFoundException &e)
1109 JMutexAutoLock envlock(m_env_mutex);
1114 infostream<<"Server: Saving players"<<std::endl;
1115 m_env->serializePlayers(m_path_world);
1118 Save environment metadata
1120 infostream<<"Server: Saving environment metadata"<<std::endl;
1121 m_env->saveMeta(m_path_world);
1133 JMutexAutoLock clientslock(m_con_mutex);
1135 for(core::map<u16, RemoteClient*>::Iterator
1136 i = m_clients.getIterator();
1137 i.atEnd() == false; i++)
1140 // NOTE: These are removed by env destructor
1142 u16 peer_id = i.getNode()->getKey();
1143 JMutexAutoLock envlock(m_env_mutex);
1144 m_env->removePlayer(peer_id);
1148 delete i.getNode()->getValue();
1152 // Delete things in the reverse order of creation
1159 // Deinitialize scripting
1160 infostream<<"Server: Deinitializing scripting"<<std::endl;
1161 script_deinit(m_lua);
1164 void Server::start(unsigned short port)
1166 DSTACK(__FUNCTION_NAME);
1167 infostream<<"Starting server on port "<<port<<"..."<<std::endl;
1169 // Stop thread if already running
1172 // Initialize connection
1173 m_con.SetTimeoutMs(30);
1177 m_thread.setRun(true);
1180 // ASCII art for the win!
1182 <<" .__ __ __ "<<std::endl
1183 <<" _____ |__| ____ _____/ |_ ____ _______/ |_ "<<std::endl
1184 <<" / \\| |/ \\_/ __ \\ __\\/ __ \\ / ___/\\ __\\"<<std::endl
1185 <<"| Y Y \\ | | \\ ___/| | \\ ___/ \\___ \\ | | "<<std::endl
1186 <<"|__|_| /__|___| /\\___ >__| \\___ >____ > |__| "<<std::endl
1187 <<" \\/ \\/ \\/ \\/ \\/ "<<std::endl;
1188 actionstream<<"World at ["<<m_path_world<<"]"<<std::endl;
1189 actionstream<<"Server for gameid=\""<<m_gamespec.id
1190 <<"\" listening on port "<<port<<"."<<std::endl;
1195 DSTACK(__FUNCTION_NAME);
1197 infostream<<"Server: Stopping and waiting threads"<<std::endl;
1199 // Stop threads (set run=false first so both start stopping)
1200 m_thread.setRun(false);
1201 m_emergethread.setRun(false);
1203 m_emergethread.stop();
1205 infostream<<"Server: Threads stopped"<<std::endl;
1208 void Server::step(float dtime)
1210 DSTACK(__FUNCTION_NAME);
1215 JMutexAutoLock lock(m_step_dtime_mutex);
1216 m_step_dtime += dtime;
1218 // Throw if fatal error occurred in thread
1219 std::string async_err = m_async_fatal_error.get();
1220 if(async_err != ""){
1221 throw ServerError(async_err);
1225 void Server::AsyncRunStep()
1227 DSTACK(__FUNCTION_NAME);
1229 g_profiler->add("Server::AsyncRunStep (num)", 1);
1233 JMutexAutoLock lock1(m_step_dtime_mutex);
1234 dtime = m_step_dtime;
1238 // Send blocks to clients
1245 g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
1247 //infostream<<"Server steps "<<dtime<<std::endl;
1248 //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1251 JMutexAutoLock lock1(m_step_dtime_mutex);
1252 m_step_dtime -= dtime;
1259 m_uptime.set(m_uptime.get() + dtime);
1263 // Process connection's timeouts
1264 JMutexAutoLock lock2(m_con_mutex);
1265 ScopeProfiler sp(g_profiler, "Server: connection timeout processing");
1266 m_con.RunTimeouts(dtime);
1270 // This has to be called so that the client list gets synced
1271 // with the peer list of the connection
1272 handlePeerChanges();
1276 Update time of day and overall game time
1279 JMutexAutoLock envlock(m_env_mutex);
1281 m_env->setTimeOfDaySpeed(g_settings->getFloat("time_speed"));
1284 Send to clients at constant intervals
1287 m_time_of_day_send_timer -= dtime;
1288 if(m_time_of_day_send_timer < 0.0)
1290 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
1292 //JMutexAutoLock envlock(m_env_mutex);
1293 JMutexAutoLock conlock(m_con_mutex);
1295 for(core::map<u16, RemoteClient*>::Iterator
1296 i = m_clients.getIterator();
1297 i.atEnd() == false; i++)
1299 RemoteClient *client = i.getNode()->getValue();
1300 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1301 m_env->getTimeOfDay(), g_settings->getFloat("time_speed"));
1303 m_con.Send(client->peer_id, 0, data, true);
1309 JMutexAutoLock lock(m_env_mutex);
1311 ScopeProfiler sp(g_profiler, "SEnv step");
1312 ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
1316 const float map_timer_and_unload_dtime = 2.92;
1317 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
1319 JMutexAutoLock lock(m_env_mutex);
1320 // Run Map's timers and unload unused data
1321 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
1322 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
1323 g_settings->getFloat("server_unload_unused_data_timeout"));
1334 JMutexAutoLock lock(m_env_mutex);
1335 JMutexAutoLock lock2(m_con_mutex);
1337 ScopeProfiler sp(g_profiler, "Server: handle players");
1339 for(core::map<u16, RemoteClient*>::Iterator
1340 i = m_clients.getIterator();
1341 i.atEnd() == false; i++)
1343 RemoteClient *client = i.getNode()->getValue();
1344 PlayerSAO *playersao = getPlayerSAO(client->peer_id);
1345 if(playersao == NULL)
1349 Handle player HPs (die if hp=0)
1351 if(playersao->getHP() == 0 && playersao->m_hp_not_sent)
1352 DiePlayer(client->peer_id);
1355 Send player inventories and HPs if necessary
1357 if(playersao->m_teleported){
1358 SendMovePlayer(client->peer_id);
1359 playersao->m_teleported = false;
1361 if(playersao->m_inventory_not_sent){
1362 UpdateCrafting(client->peer_id);
1363 SendInventory(client->peer_id);
1365 if(playersao->m_hp_not_sent){
1366 SendPlayerHP(client->peer_id);
1371 /* Transform liquids */
1372 m_liquid_transform_timer += dtime;
1373 if(m_liquid_transform_timer >= 1.00)
1375 m_liquid_transform_timer -= 1.00;
1377 JMutexAutoLock lock(m_env_mutex);
1379 ScopeProfiler sp(g_profiler, "Server: liquid transform");
1381 core::map<v3s16, MapBlock*> modified_blocks;
1382 m_env->getMap().transformLiquids(modified_blocks);
1387 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1388 ServerMap &map = ((ServerMap&)m_env->getMap());
1389 map.updateLighting(modified_blocks, lighting_modified_blocks);
1391 // Add blocks modified by lighting to modified_blocks
1392 for(core::map<v3s16, MapBlock*>::Iterator
1393 i = lighting_modified_blocks.getIterator();
1394 i.atEnd() == false; i++)
1396 MapBlock *block = i.getNode()->getValue();
1397 modified_blocks.insert(block->getPos(), block);
1401 Set the modified blocks unsent for all the clients
1404 JMutexAutoLock lock2(m_con_mutex);
1406 for(core::map<u16, RemoteClient*>::Iterator
1407 i = m_clients.getIterator();
1408 i.atEnd() == false; i++)
1410 RemoteClient *client = i.getNode()->getValue();
1412 if(modified_blocks.size() > 0)
1414 // Remove block from sent history
1415 client->SetBlocksNotSent(modified_blocks);
1420 // Periodically print some info
1422 float &counter = m_print_info_timer;
1428 JMutexAutoLock lock2(m_con_mutex);
1430 if(m_clients.size() != 0)
1431 infostream<<"Players:"<<std::endl;
1432 for(core::map<u16, RemoteClient*>::Iterator
1433 i = m_clients.getIterator();
1434 i.atEnd() == false; i++)
1436 //u16 peer_id = i.getNode()->getKey();
1437 RemoteClient *client = i.getNode()->getValue();
1438 Player *player = m_env->getPlayer(client->peer_id);
1441 infostream<<"* "<<player->getName()<<"\t";
1442 client->PrintInfo(infostream);
1447 //if(g_settings->getBool("enable_experimental"))
1451 Check added and deleted active objects
1454 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
1455 JMutexAutoLock envlock(m_env_mutex);
1456 JMutexAutoLock conlock(m_con_mutex);
1458 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
1460 // Radius inside which objects are active
1461 s16 radius = g_settings->getS16("active_object_send_range_blocks");
1462 radius *= MAP_BLOCKSIZE;
1464 for(core::map<u16, RemoteClient*>::Iterator
1465 i = m_clients.getIterator();
1466 i.atEnd() == false; i++)
1468 RemoteClient *client = i.getNode()->getValue();
1470 // If definitions and textures have not been sent, don't
1471 // send objects either
1472 if(!client->definitions_sent)
1475 Player *player = m_env->getPlayer(client->peer_id);
1478 // This can happen if the client timeouts somehow
1479 /*infostream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
1481 <<" has no associated player"<<std::endl;*/
1484 v3s16 pos = floatToInt(player->getPosition(), BS);
1486 core::map<u16, bool> removed_objects;
1487 core::map<u16, bool> added_objects;
1488 m_env->getRemovedActiveObjects(pos, radius,
1489 client->m_known_objects, removed_objects);
1490 m_env->getAddedActiveObjects(pos, radius,
1491 client->m_known_objects, added_objects);
1493 // Ignore if nothing happened
1494 if(removed_objects.size() == 0 && added_objects.size() == 0)
1496 //infostream<<"active objects: none changed"<<std::endl;
1500 std::string data_buffer;
1504 // Handle removed objects
1505 writeU16((u8*)buf, removed_objects.size());
1506 data_buffer.append(buf, 2);
1507 for(core::map<u16, bool>::Iterator
1508 i = removed_objects.getIterator();
1509 i.atEnd()==false; i++)
1512 u16 id = i.getNode()->getKey();
1513 ServerActiveObject* obj = m_env->getActiveObject(id);
1515 // Add to data buffer for sending
1516 writeU16((u8*)buf, i.getNode()->getKey());
1517 data_buffer.append(buf, 2);
1519 // Remove from known objects
1520 client->m_known_objects.remove(i.getNode()->getKey());
1522 if(obj && obj->m_known_by_count > 0)
1523 obj->m_known_by_count--;
1526 // Handle added objects
1527 writeU16((u8*)buf, added_objects.size());
1528 data_buffer.append(buf, 2);
1529 for(core::map<u16, bool>::Iterator
1530 i = added_objects.getIterator();
1531 i.atEnd()==false; i++)
1534 u16 id = i.getNode()->getKey();
1535 ServerActiveObject* obj = m_env->getActiveObject(id);
1538 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1540 infostream<<"WARNING: "<<__FUNCTION_NAME
1541 <<": NULL object"<<std::endl;
1543 type = obj->getSendType();
1545 // Add to data buffer for sending
1546 writeU16((u8*)buf, id);
1547 data_buffer.append(buf, 2);
1548 writeU8((u8*)buf, type);
1549 data_buffer.append(buf, 1);
1552 data_buffer.append(serializeLongString(
1553 obj->getClientInitializationData()));
1555 data_buffer.append(serializeLongString(""));
1557 // Add to known objects
1558 client->m_known_objects.insert(i.getNode()->getKey(), false);
1561 obj->m_known_by_count++;
1565 SharedBuffer<u8> reply(2 + data_buffer.size());
1566 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1567 memcpy((char*)&reply[2], data_buffer.c_str(),
1568 data_buffer.size());
1570 m_con.Send(client->peer_id, 0, reply, true);
1572 verbosestream<<"Server: Sent object remove/add: "
1573 <<removed_objects.size()<<" removed, "
1574 <<added_objects.size()<<" added, "
1575 <<"packet size is "<<reply.getSize()<<std::endl;
1580 Collect a list of all the objects known by the clients
1581 and report it back to the environment.
1584 core::map<u16, bool> all_known_objects;
1586 for(core::map<u16, RemoteClient*>::Iterator
1587 i = m_clients.getIterator();
1588 i.atEnd() == false; i++)
1590 RemoteClient *client = i.getNode()->getValue();
1591 // Go through all known objects of client
1592 for(core::map<u16, bool>::Iterator
1593 i = client->m_known_objects.getIterator();
1594 i.atEnd()==false; i++)
1596 u16 id = i.getNode()->getKey();
1597 all_known_objects[id] = true;
1601 m_env->setKnownActiveObjects(whatever);
1607 Send object messages
1610 JMutexAutoLock envlock(m_env_mutex);
1611 JMutexAutoLock conlock(m_con_mutex);
1613 ScopeProfiler sp(g_profiler, "Server: sending object messages");
1616 // Value = data sent by object
1617 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1619 // Get active object messages from environment
1622 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
1626 core::list<ActiveObjectMessage>* message_list = NULL;
1627 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1628 n = buffered_messages.find(aom.id);
1631 message_list = new core::list<ActiveObjectMessage>;
1632 buffered_messages.insert(aom.id, message_list);
1636 message_list = n->getValue();
1638 message_list->push_back(aom);
1641 // Route data to every client
1642 for(core::map<u16, RemoteClient*>::Iterator
1643 i = m_clients.getIterator();
1644 i.atEnd()==false; i++)
1646 RemoteClient *client = i.getNode()->getValue();
1647 std::string reliable_data;
1648 std::string unreliable_data;
1649 // Go through all objects in message buffer
1650 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1651 j = buffered_messages.getIterator();
1652 j.atEnd()==false; j++)
1654 // If object is not known by client, skip it
1655 u16 id = j.getNode()->getKey();
1656 if(client->m_known_objects.find(id) == NULL)
1658 // Get message list of object
1659 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1660 // Go through every message
1661 for(core::list<ActiveObjectMessage>::Iterator
1662 k = list->begin(); k != list->end(); k++)
1664 // Compose the full new data with header
1665 ActiveObjectMessage aom = *k;
1666 std::string new_data;
1669 writeU16((u8*)&buf[0], aom.id);
1670 new_data.append(buf, 2);
1672 new_data += serializeString(aom.datastring);
1673 // Add data to buffer
1675 reliable_data += new_data;
1677 unreliable_data += new_data;
1681 reliable_data and unreliable_data are now ready.
1684 if(reliable_data.size() > 0)
1686 SharedBuffer<u8> reply(2 + reliable_data.size());
1687 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1688 memcpy((char*)&reply[2], reliable_data.c_str(),
1689 reliable_data.size());
1691 m_con.Send(client->peer_id, 0, reply, true);
1693 if(unreliable_data.size() > 0)
1695 SharedBuffer<u8> reply(2 + unreliable_data.size());
1696 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1697 memcpy((char*)&reply[2], unreliable_data.c_str(),
1698 unreliable_data.size());
1699 // Send as unreliable
1700 m_con.Send(client->peer_id, 0, reply, false);
1703 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1705 infostream<<"Server: Size of object message data: "
1706 <<"reliable: "<<reliable_data.size()
1707 <<", unreliable: "<<unreliable_data.size()
1712 // Clear buffered_messages
1713 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1714 i = buffered_messages.getIterator();
1715 i.atEnd()==false; i++)
1717 delete i.getNode()->getValue();
1721 } // enable_experimental
1724 Send queued-for-sending map edit events.
1727 // We will be accessing the environment and the connection
1728 JMutexAutoLock lock(m_env_mutex);
1729 JMutexAutoLock conlock(m_con_mutex);
1731 // Don't send too many at a time
1734 // Single change sending is disabled if queue size is not small
1735 bool disable_single_change_sending = false;
1736 if(m_unsent_map_edit_queue.size() >= 4)
1737 disable_single_change_sending = true;
1739 int event_count = m_unsent_map_edit_queue.size();
1741 // We'll log the amount of each
1744 while(m_unsent_map_edit_queue.size() != 0)
1746 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1748 // Players far away from the change are stored here.
1749 // Instead of sending the changes, MapBlocks are set not sent
1751 core::list<u16> far_players;
1753 if(event->type == MEET_ADDNODE)
1755 //infostream<<"Server: MEET_ADDNODE"<<std::endl;
1756 prof.add("MEET_ADDNODE", 1);
1757 if(disable_single_change_sending)
1758 sendAddNode(event->p, event->n, event->already_known_by_peer,
1761 sendAddNode(event->p, event->n, event->already_known_by_peer,
1764 else if(event->type == MEET_REMOVENODE)
1766 //infostream<<"Server: MEET_REMOVENODE"<<std::endl;
1767 prof.add("MEET_REMOVENODE", 1);
1768 if(disable_single_change_sending)
1769 sendRemoveNode(event->p, event->already_known_by_peer,
1772 sendRemoveNode(event->p, event->already_known_by_peer,
1775 else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
1777 infostream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
1778 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
1779 setBlockNotSent(event->p);
1781 else if(event->type == MEET_OTHER)
1783 infostream<<"Server: MEET_OTHER"<<std::endl;
1784 prof.add("MEET_OTHER", 1);
1785 for(core::map<v3s16, bool>::Iterator
1786 i = event->modified_blocks.getIterator();
1787 i.atEnd()==false; i++)
1789 v3s16 p = i.getNode()->getKey();
1795 prof.add("unknown", 1);
1796 infostream<<"WARNING: Server: Unknown MapEditEvent "
1797 <<((u32)event->type)<<std::endl;
1801 Set blocks not sent to far players
1803 if(far_players.size() > 0)
1805 // Convert list format to that wanted by SetBlocksNotSent
1806 core::map<v3s16, MapBlock*> modified_blocks2;
1807 for(core::map<v3s16, bool>::Iterator
1808 i = event->modified_blocks.getIterator();
1809 i.atEnd()==false; i++)
1811 v3s16 p = i.getNode()->getKey();
1812 modified_blocks2.insert(p,
1813 m_env->getMap().getBlockNoCreateNoEx(p));
1815 // Set blocks not sent
1816 for(core::list<u16>::Iterator
1817 i = far_players.begin();
1818 i != far_players.end(); i++)
1821 RemoteClient *client = getClient(peer_id);
1824 client->SetBlocksNotSent(modified_blocks2);
1830 /*// Don't send too many at a time
1832 if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
1836 if(event_count >= 5){
1837 infostream<<"Server: MapEditEvents:"<<std::endl;
1838 prof.print(infostream);
1839 } else if(event_count != 0){
1840 verbosestream<<"Server: MapEditEvents:"<<std::endl;
1841 prof.print(verbosestream);
1847 Trigger emergethread (it somehow gets to a non-triggered but
1848 bysy state sometimes)
1851 float &counter = m_emergethread_trigger_timer;
1857 m_emergethread.trigger();
1861 // Save map, players and auth stuff
1863 float &counter = m_savemap_timer;
1865 if(counter >= g_settings->getFloat("server_map_save_interval"))
1868 JMutexAutoLock lock(m_env_mutex);
1870 ScopeProfiler sp(g_profiler, "Server: saving stuff");
1873 if(m_banmanager.isModified())
1874 m_banmanager.save();
1876 // Save changed parts of map
1877 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
1880 m_env->serializePlayers(m_path_world);
1882 // Save environment metadata
1883 m_env->saveMeta(m_path_world);
1888 void Server::Receive()
1890 DSTACK(__FUNCTION_NAME);
1891 SharedBuffer<u8> data;
1896 JMutexAutoLock conlock(m_con_mutex);
1897 datasize = m_con.Receive(peer_id, data);
1900 // This has to be called so that the client list gets synced
1901 // with the peer list of the connection
1902 handlePeerChanges();
1904 ProcessData(*data, datasize, peer_id);
1906 catch(con::InvalidIncomingDataException &e)
1908 infostream<<"Server::Receive(): "
1909 "InvalidIncomingDataException: what()="
1910 <<e.what()<<std::endl;
1912 catch(con::PeerNotFoundException &e)
1914 //NOTE: This is not needed anymore
1916 // The peer has been disconnected.
1917 // Find the associated player and remove it.
1919 /*JMutexAutoLock envlock(m_env_mutex);
1921 infostream<<"ServerThread: peer_id="<<peer_id
1922 <<" has apparently closed connection. "
1923 <<"Removing player."<<std::endl;
1925 m_env->removePlayer(peer_id);*/
1929 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1931 DSTACK(__FUNCTION_NAME);
1932 // Environment is locked first.
1933 JMutexAutoLock envlock(m_env_mutex);
1934 JMutexAutoLock conlock(m_con_mutex);
1936 ScopeProfiler sp(g_profiler, "Server::ProcessData");
1939 Address address = m_con.GetPeerAddress(peer_id);
1940 std::string addr_s = address.serializeString();
1942 // drop player if is ip is banned
1943 if(m_banmanager.isIpBanned(addr_s)){
1944 infostream<<"Server: A banned client tried to connect from "
1945 <<addr_s<<"; banned name was "
1946 <<m_banmanager.getBanName(addr_s)<<std::endl;
1947 // This actually doesn't seem to transfer to the client
1948 SendAccessDenied(m_con, peer_id,
1949 L"Your ip is banned. Banned name was "
1950 +narrow_to_wide(m_banmanager.getBanName(addr_s)));
1951 m_con.DeletePeer(peer_id);
1955 catch(con::PeerNotFoundException &e)
1957 infostream<<"Server::ProcessData(): Cancelling: peer "
1958 <<peer_id<<" not found"<<std::endl;
1962 std::string addr_s = m_con.GetPeerAddress(peer_id).serializeString();
1964 u8 peer_ser_ver = getClient(peer_id)->serialization_version;
1972 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1974 if(command == TOSERVER_INIT)
1976 // [0] u16 TOSERVER_INIT
1977 // [2] u8 SER_FMT_VER_HIGHEST
1978 // [3] u8[20] player_name
1979 // [23] u8[28] password <--- can be sent without this, from old versions
1981 if(datasize < 2+1+PLAYERNAME_SIZE)
1984 verbosestream<<"Server: Got TOSERVER_INIT from "
1985 <<peer_id<<std::endl;
1987 // First byte after command is maximum supported
1988 // serialization version
1989 u8 client_max = data[2];
1990 u8 our_max = SER_FMT_VER_HIGHEST;
1991 // Use the highest version supported by both
1992 u8 deployed = core::min_(client_max, our_max);
1993 // If it's lower than the lowest supported, give up.
1994 if(deployed < SER_FMT_VER_LOWEST)
1995 deployed = SER_FMT_VER_INVALID;
1997 //peer->serialization_version = deployed;
1998 getClient(peer_id)->pending_serialization_version = deployed;
2000 if(deployed == SER_FMT_VER_INVALID)
2002 actionstream<<"Server: A mismatched client tried to connect from "
2003 <<addr_s<<std::endl;
2004 infostream<<"Server: Cannot negotiate "
2005 "serialization version with peer "
2006 <<peer_id<<std::endl;
2007 SendAccessDenied(m_con, peer_id, std::wstring(
2008 L"Your client's version is not supported.\n"
2009 L"Server version is ")
2010 + narrow_to_wide(VERSION_STRING) + L"."
2016 Read and check network protocol version
2019 u16 net_proto_version = 0;
2020 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
2022 net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
2025 getClient(peer_id)->net_proto_version = net_proto_version;
2027 if(net_proto_version == 0)
2029 actionstream<<"Server: An old tried to connect from "<<addr_s
2031 SendAccessDenied(m_con, peer_id, std::wstring(
2032 L"Your client's version is not supported.\n"
2033 L"Server version is ")
2034 + narrow_to_wide(VERSION_STRING) + L"."
2039 if(g_settings->getBool("strict_protocol_version_checking"))
2041 if(net_proto_version != PROTOCOL_VERSION)
2043 actionstream<<"Server: A mismatched client tried to connect"
2044 <<" from "<<addr_s<<std::endl;
2045 SendAccessDenied(m_con, peer_id, std::wstring(
2046 L"Your client's version is not supported.\n"
2047 L"Server version is ")
2048 + narrow_to_wide(VERSION_STRING) + L",\n"
2049 + L"server's PROTOCOL_VERSION is "
2050 + narrow_to_wide(itos(PROTOCOL_VERSION))
2051 + L", client's PROTOCOL_VERSION is "
2052 + narrow_to_wide(itos(net_proto_version))
2063 char playername[PLAYERNAME_SIZE];
2064 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
2066 playername[i] = data[3+i];
2068 playername[PLAYERNAME_SIZE-1] = 0;
2070 if(playername[0]=='\0')
2072 actionstream<<"Server: Player with an empty name "
2073 <<"tried to connect from "<<addr_s<<std::endl;
2074 SendAccessDenied(m_con, peer_id,
2079 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
2081 actionstream<<"Server: Player with an invalid name "
2082 <<"tried to connect from "<<addr_s<<std::endl;
2083 SendAccessDenied(m_con, peer_id,
2084 L"Name contains unallowed characters");
2088 infostream<<"Server: New connection: \""<<playername<<"\" from "
2089 <<m_con.GetPeerAddress(peer_id).serializeString()<<std::endl;
2092 char password[PASSWORD_SIZE];
2093 if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
2095 // old version - assume blank password
2100 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2102 password[i] = data[23+i];
2104 password[PASSWORD_SIZE-1] = 0;
2107 if(!base64_is_valid(password)){
2108 infostream<<"Server: "<<playername<<" supplied invalid password hash"<<std::endl;
2109 SendAccessDenied(m_con, peer_id, L"Invalid password hash");
2113 std::string checkpwd;
2114 bool has_auth = scriptapi_get_auth(m_lua, playername, &checkpwd, NULL);
2117 std::wstring raw_default_password =
2118 narrow_to_wide(g_settings->get("default_password"));
2119 std::string use_password =
2120 translatePassword(playername, raw_default_password);
2122 // If default_password is empty, allow any initial password
2123 if (raw_default_password.length() == 0)
2124 use_password = password;
2126 scriptapi_create_auth(m_lua, playername, use_password);
2129 has_auth = scriptapi_get_auth(m_lua, playername, &checkpwd, NULL);
2132 SendAccessDenied(m_con, peer_id, L"Not allowed to login");
2136 if(password != checkpwd){
2137 infostream<<"Server: peer_id="<<peer_id
2138 <<": supplied invalid password for "
2139 <<playername<<std::endl;
2140 SendAccessDenied(m_con, peer_id, L"Invalid password");
2144 // Do not allow multiple players in simple singleplayer mode.
2145 // This isn't a perfect way to do it, but will suffice for now.
2146 if(m_simple_singleplayer_mode && m_clients.size() > 1){
2147 infostream<<"Server: Not allowing another client to connect in"
2148 <<" simple singleplayer mode"<<std::endl;
2149 SendAccessDenied(m_con, peer_id,
2150 L"Running in simple singleplayer mode.");
2154 // Enforce user limit.
2155 // Don't enforce for users that have some admin right
2156 if(m_clients.size() >= g_settings->getU16("max_users") &&
2157 !checkPriv(playername, "server") &&
2158 !checkPriv(playername, "ban") &&
2159 !checkPriv(playername, "privs") &&
2160 !checkPriv(playername, "password") &&
2161 playername != g_settings->get("name"))
2163 actionstream<<"Server: "<<playername<<" tried to join, but there"
2164 <<" are already max_users="
2165 <<g_settings->getU16("max_users")<<" players."<<std::endl;
2166 SendAccessDenied(m_con, peer_id, L"Too many users.");
2171 PlayerSAO *playersao = emergePlayer(playername, peer_id);
2173 // If failed, cancel
2174 if(playersao == NULL)
2176 errorstream<<"Server: peer_id="<<peer_id
2177 <<": failed to emerge player"<<std::endl;
2182 Answer with a TOCLIENT_INIT
2185 SharedBuffer<u8> reply(2+1+6+8);
2186 writeU16(&reply[0], TOCLIENT_INIT);
2187 writeU8(&reply[2], deployed);
2188 writeV3S16(&reply[2+1], floatToInt(playersao->getPlayer()->getPosition()+v3f(0,BS/2,0), BS));
2189 writeU64(&reply[2+1+6], m_env->getServerMap().getSeed());
2192 m_con.Send(peer_id, 0, reply, true);
2196 Send complete position information
2198 SendMovePlayer(peer_id);
2203 if(command == TOSERVER_INIT2)
2205 verbosestream<<"Server: Got TOSERVER_INIT2 from "
2206 <<peer_id<<std::endl;
2208 Player *player = m_env->getPlayer(peer_id);
2210 verbosestream<<"Server: TOSERVER_INIT2: "
2211 <<"Player not found; ignoring."<<std::endl;
2215 getClient(peer_id)->serialization_version
2216 = getClient(peer_id)->pending_serialization_version;
2219 Send some initialization data
2222 infostream<<"Server: Sending content to "
2223 <<getPlayerName(peer_id)<<std::endl;
2225 // Send item definitions
2226 SendItemDef(m_con, peer_id, m_itemdef);
2228 // Send node definitions
2229 SendNodeDef(m_con, peer_id, m_nodedef);
2231 // Send media announcement
2232 sendMediaAnnouncement(peer_id);
2235 SendPlayerPrivileges(peer_id);
2238 UpdateCrafting(peer_id);
2239 SendInventory(peer_id);
2242 SendPlayerHP(peer_id);
2244 // Show death screen if necessary
2246 SendDeathscreen(m_con, peer_id, false, v3f(0,0,0));
2250 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2251 m_env->getTimeOfDay(), g_settings->getFloat("time_speed"));
2252 m_con.Send(peer_id, 0, data, true);
2255 // Note things in chat if not in simple singleplayer mode
2256 if(!m_simple_singleplayer_mode)
2258 // Send information about server to player in chat
2259 SendChatMessage(peer_id, getStatusString());
2261 // Send information about joining in chat
2263 std::wstring name = L"unknown";
2264 Player *player = m_env->getPlayer(peer_id);
2266 name = narrow_to_wide(player->getName());
2268 std::wstring message;
2271 message += L" joined game";
2272 BroadcastChatMessage(message);
2276 // Warnings about protocol version can be issued here
2277 if(getClient(peer_id)->net_proto_version < PROTOCOL_VERSION)
2279 SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT IS OLD AND MAY WORK PROPERLY WITH THIS SERVER");
2286 std::ostringstream os(std::ios_base::binary);
2287 for(core::map<u16, RemoteClient*>::Iterator
2288 i = m_clients.getIterator();
2289 i.atEnd() == false; i++)
2291 RemoteClient *client = i.getNode()->getValue();
2292 assert(client->peer_id == i.getNode()->getKey());
2293 if(client->serialization_version == SER_FMT_VER_INVALID)
2296 Player *player = m_env->getPlayer(client->peer_id);
2299 // Get name of player
2300 os<<player->getName()<<" ";
2303 actionstream<<player->getName()<<" joins game. List of players: "
2304 <<os.str()<<std::endl;
2310 if(peer_ser_ver == SER_FMT_VER_INVALID)
2312 infostream<<"Server::ProcessData(): Cancelling: Peer"
2313 " serialization format invalid or not initialized."
2314 " Skipping incoming command="<<command<<std::endl;
2318 Player *player = m_env->getPlayer(peer_id);
2320 infostream<<"Server::ProcessData(): Cancelling: "
2321 "No player for peer_id="<<peer_id
2326 PlayerSAO *playersao = player->getPlayerSAO();
2327 if(playersao == NULL){
2328 infostream<<"Server::ProcessData(): Cancelling: "
2329 "No player object for peer_id="<<peer_id
2334 if(command == TOSERVER_PLAYERPOS)
2336 if(datasize < 2+12+12+4+4)
2340 v3s32 ps = readV3S32(&data[start+2]);
2341 v3s32 ss = readV3S32(&data[start+2+12]);
2342 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2343 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2344 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2345 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2346 pitch = wrapDegrees(pitch);
2347 yaw = wrapDegrees(yaw);
2349 player->setPosition(position);
2350 player->setSpeed(speed);
2351 player->setPitch(pitch);
2352 player->setYaw(yaw);
2354 /*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2355 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2356 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2358 else if(command == TOSERVER_GOTBLOCKS)
2371 u16 count = data[2];
2372 for(u16 i=0; i<count; i++)
2374 if((s16)datasize < 2+1+(i+1)*6)
2375 throw con::InvalidIncomingDataException
2376 ("GOTBLOCKS length is too short");
2377 v3s16 p = readV3S16(&data[2+1+i*6]);
2378 /*infostream<<"Server: GOTBLOCKS ("
2379 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2380 RemoteClient *client = getClient(peer_id);
2381 client->GotBlock(p);
2384 else if(command == TOSERVER_DELETEDBLOCKS)
2397 u16 count = data[2];
2398 for(u16 i=0; i<count; i++)
2400 if((s16)datasize < 2+1+(i+1)*6)
2401 throw con::InvalidIncomingDataException
2402 ("DELETEDBLOCKS length is too short");
2403 v3s16 p = readV3S16(&data[2+1+i*6]);
2404 /*infostream<<"Server: DELETEDBLOCKS ("
2405 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2406 RemoteClient *client = getClient(peer_id);
2407 client->SetBlockNotSent(p);
2410 else if(command == TOSERVER_CLICK_OBJECT)
2412 infostream<<"Server: CLICK_OBJECT not supported anymore"<<std::endl;
2415 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2417 infostream<<"Server: CLICK_ACTIVEOBJECT not supported anymore"<<std::endl;
2420 else if(command == TOSERVER_GROUND_ACTION)
2422 infostream<<"Server: GROUND_ACTION not supported anymore"<<std::endl;
2426 else if(command == TOSERVER_RELEASE)
2428 infostream<<"Server: RELEASE not supported anymore"<<std::endl;
2431 else if(command == TOSERVER_SIGNTEXT)
2433 infostream<<"Server: SIGNTEXT not supported anymore"
2437 else if(command == TOSERVER_SIGNNODETEXT)
2439 infostream<<"Server: SIGNNODETEXT not supported anymore"
2443 else if(command == TOSERVER_INVENTORY_ACTION)
2445 // Strip command and create a stream
2446 std::string datastring((char*)&data[2], datasize-2);
2447 verbosestream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2448 std::istringstream is(datastring, std::ios_base::binary);
2450 InventoryAction *a = InventoryAction::deSerialize(is);
2453 infostream<<"TOSERVER_INVENTORY_ACTION: "
2454 <<"InventoryAction::deSerialize() returned NULL"
2460 Note: Always set inventory not sent, to repair cases
2461 where the client made a bad prediction.
2465 Handle restrictions and special cases of the move action
2467 if(a->getType() == IACTION_MOVE)
2469 IMoveAction *ma = (IMoveAction*)a;
2471 ma->from_inv.applyCurrentPlayer(player->getName());
2472 ma->to_inv.applyCurrentPlayer(player->getName());
2474 setInventoryModified(ma->from_inv);
2475 setInventoryModified(ma->to_inv);
2477 bool from_inv_is_current_player =
2478 (ma->from_inv.type == InventoryLocation::PLAYER) &&
2479 (ma->from_inv.name == player->getName());
2481 bool to_inv_is_current_player =
2482 (ma->to_inv.type == InventoryLocation::PLAYER) &&
2483 (ma->to_inv.name == player->getName());
2486 Disable moving items out of craftpreview
2488 if(ma->from_list == "craftpreview")
2490 infostream<<"Ignoring IMoveAction from "
2491 <<(ma->from_inv.dump())<<":"<<ma->from_list
2492 <<" to "<<(ma->to_inv.dump())<<":"<<ma->to_list
2493 <<" because src is "<<ma->from_list<<std::endl;
2499 Disable moving items into craftresult and craftpreview
2501 if(ma->to_list == "craftpreview" || ma->to_list == "craftresult")
2503 infostream<<"Ignoring IMoveAction from "
2504 <<(ma->from_inv.dump())<<":"<<ma->from_list
2505 <<" to "<<(ma->to_inv.dump())<<":"<<ma->to_list
2506 <<" because dst is "<<ma->to_list<<std::endl;
2511 // Disallow moving items in elsewhere than player's inventory
2512 // if not allowed to interact
2513 if(!checkPriv(player->getName(), "interact") &&
2514 (!from_inv_is_current_player ||
2515 !to_inv_is_current_player))
2517 infostream<<"Cannot move outside of player's inventory: "
2518 <<"No interact privilege"<<std::endl;
2523 // If player is not an admin, check for ownership of src and dst
2524 /*if(!checkPriv(player->getName(), "server"))
2526 std::string owner_from = getInventoryOwner(ma->from_inv);
2527 if(owner_from != "" && owner_from != player->getName())
2529 infostream<<"WARNING: "<<player->getName()
2530 <<" tried to access an inventory that"
2531 <<" belongs to "<<owner_from<<std::endl;
2536 std::string owner_to = getInventoryOwner(ma->to_inv);
2537 if(owner_to != "" && owner_to != player->getName())
2539 infostream<<"WARNING: "<<player->getName()
2540 <<" tried to access an inventory that"
2541 <<" belongs to "<<owner_to<<std::endl;
2548 Handle restrictions and special cases of the drop action
2550 else if(a->getType() == IACTION_DROP)
2552 IDropAction *da = (IDropAction*)a;
2554 da->from_inv.applyCurrentPlayer(player->getName());
2556 setInventoryModified(da->from_inv);
2558 // Disallow dropping items if not allowed to interact
2559 if(!checkPriv(player->getName(), "interact"))
2564 // If player is not an admin, check for ownership
2565 /*else if(!checkPriv(player->getName(), "server"))
2567 std::string owner_from = getInventoryOwner(da->from_inv);
2568 if(owner_from != "" && owner_from != player->getName())
2570 infostream<<"WARNING: "<<player->getName()
2571 <<" tried to access an inventory that"
2572 <<" belongs to "<<owner_from<<std::endl;
2579 Handle restrictions and special cases of the craft action
2581 else if(a->getType() == IACTION_CRAFT)
2583 ICraftAction *ca = (ICraftAction*)a;
2585 ca->craft_inv.applyCurrentPlayer(player->getName());
2587 setInventoryModified(ca->craft_inv);
2589 //bool craft_inv_is_current_player =
2590 // (ca->craft_inv.type == InventoryLocation::PLAYER) &&
2591 // (ca->craft_inv.name == player->getName());
2593 // Disallow crafting if not allowed to interact
2594 if(!checkPriv(player->getName(), "interact"))
2596 infostream<<"Cannot craft: "
2597 <<"No interact privilege"<<std::endl;
2602 // If player is not an admin, check for ownership of inventory
2603 /*if(!checkPriv(player->getName(), "server"))
2605 std::string owner_craft = getInventoryOwner(ca->craft_inv);
2606 if(owner_craft != "" && owner_craft != player->getName())
2608 infostream<<"WARNING: "<<player->getName()
2609 <<" tried to access an inventory that"
2610 <<" belongs to "<<owner_craft<<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 // Get player name of this client
2645 std::wstring name = narrow_to_wide(player->getName());
2648 bool ate = scriptapi_on_chat_message(m_lua, player->getName(),
2649 wide_to_narrow(message));
2650 // If script ate the message, don't proceed
2654 // Line to send to players
2656 // Whether to send to the player that sent the line
2657 bool send_to_sender = false;
2658 // Whether to send to other players
2659 bool send_to_others = false;
2662 if(message[0] == L'/')
2664 size_t strip_size = 1;
2665 if (message[1] == L'#') // support old-style commans
2667 message = message.substr(strip_size);
2669 WStrfnd f1(message);
2670 f1.next(L" "); // Skip over /#whatever
2671 std::wstring paramstring = f1.next(L"");
2673 ServerCommandContext *ctx = new ServerCommandContext(
2674 str_split(message, L' '),
2680 std::wstring reply(processServerCommand(ctx));
2681 send_to_sender = ctx->flags & SEND_TO_SENDER;
2682 send_to_others = ctx->flags & SEND_TO_OTHERS;
2684 if (ctx->flags & SEND_NO_PREFIX)
2687 line += L"Server: " + reply;
2694 if(checkPriv(player->getName(), "shout")){
2699 send_to_others = true;
2701 line += L"Server: You are not allowed to shout";
2702 send_to_sender = true;
2709 actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2712 Send the message to clients
2714 for(core::map<u16, RemoteClient*>::Iterator
2715 i = m_clients.getIterator();
2716 i.atEnd() == false; i++)
2718 // Get client and check that it is valid
2719 RemoteClient *client = i.getNode()->getValue();
2720 assert(client->peer_id == i.getNode()->getKey());
2721 if(client->serialization_version == SER_FMT_VER_INVALID)
2725 bool sender_selected = (peer_id == client->peer_id);
2726 if(sender_selected == true && send_to_sender == false)
2728 if(sender_selected == false && send_to_others == false)
2731 SendChatMessage(client->peer_id, line);
2735 else if(command == TOSERVER_DAMAGE)
2737 std::string datastring((char*)&data[2], datasize-2);
2738 std::istringstream is(datastring, std::ios_base::binary);
2739 u8 damage = readU8(is);
2741 actionstream<<player->getName()<<" damaged by "
2742 <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
2745 playersao->setHP(playersao->getHP() - damage);
2747 if(playersao->getHP() == 0 && playersao->m_hp_not_sent)
2750 if(playersao->m_hp_not_sent)
2751 SendPlayerHP(peer_id);
2753 else if(command == TOSERVER_PASSWORD)
2756 [0] u16 TOSERVER_PASSWORD
2757 [2] u8[28] old password
2758 [30] u8[28] new password
2761 if(datasize != 2+PASSWORD_SIZE*2)
2763 /*char password[PASSWORD_SIZE];
2764 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2765 password[i] = data[2+i];
2766 password[PASSWORD_SIZE-1] = 0;*/
2768 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2776 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2778 char c = data[2+PASSWORD_SIZE+i];
2784 if(!base64_is_valid(newpwd)){
2785 infostream<<"Server: "<<player->getName()<<" supplied invalid password hash"<<std::endl;
2786 // Wrong old password supplied!!
2787 SendChatMessage(peer_id, L"Invalid new password hash supplied. Password NOT changed.");
2791 infostream<<"Server: Client requests a password change from "
2792 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
2794 std::string playername = player->getName();
2796 std::string checkpwd;
2797 scriptapi_get_auth(m_lua, playername, &checkpwd, NULL);
2799 if(oldpwd != checkpwd)
2801 infostream<<"Server: invalid old password"<<std::endl;
2802 // Wrong old password supplied!!
2803 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
2807 bool success = scriptapi_set_password(m_lua, playername, newpwd);
2809 actionstream<<player->getName()<<" changes password"<<std::endl;
2810 SendChatMessage(peer_id, L"Password change successful");
2812 actionstream<<player->getName()<<" tries to change password but "
2813 <<"it fails"<<std::endl;
2814 SendChatMessage(peer_id, L"Password change failed or inavailable");
2817 else if(command == TOSERVER_PLAYERITEM)
2822 u16 item = readU16(&data[2]);
2823 playersao->setWieldIndex(item);
2825 else if(command == TOSERVER_RESPAWN)
2830 RespawnPlayer(peer_id);
2832 actionstream<<player->getName()<<" respawns at "
2833 <<PP(player->getPosition()/BS)<<std::endl;
2835 // ActiveObject is added to environment in AsyncRunStep after
2836 // the previous addition has been succesfully removed
2838 else if(command == TOSERVER_REQUEST_MEDIA) {
2839 std::string datastring((char*)&data[2], datasize-2);
2840 std::istringstream is(datastring, std::ios_base::binary);
2842 core::list<MediaRequest> tosend;
2843 u16 numfiles = readU16(is);
2845 infostream<<"Sending "<<numfiles<<" files to "
2846 <<getPlayerName(peer_id)<<std::endl;
2847 verbosestream<<"TOSERVER_REQUEST_MEDIA: "<<std::endl;
2849 for(int i = 0; i < numfiles; i++) {
2850 std::string name = deSerializeString(is);
2851 tosend.push_back(MediaRequest(name));
2852 verbosestream<<"TOSERVER_REQUEST_MEDIA: requested file "
2856 sendRequestedMedia(peer_id, tosend);
2858 // Now the client should know about everything
2859 // (definitions and files)
2860 getClient(peer_id)->definitions_sent = true;
2862 else if(command == TOSERVER_INTERACT)
2864 std::string datastring((char*)&data[2], datasize-2);
2865 std::istringstream is(datastring, std::ios_base::binary);
2871 [5] u32 length of the next item
2872 [9] serialized PointedThing
2874 0: start digging (from undersurface) or use
2875 1: stop digging (all parameters ignored)
2876 2: digging completed
2877 3: place block or item (to abovesurface)
2880 u8 action = readU8(is);
2881 u16 item_i = readU16(is);
2882 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
2883 PointedThing pointed;
2884 pointed.deSerialize(tmp_is);
2886 verbosestream<<"TOSERVER_INTERACT: action="<<(int)action<<", item="
2887 <<item_i<<", pointed="<<pointed.dump()<<std::endl;
2891 verbosestream<<"TOSERVER_INTERACT: "<<player->getName()
2892 <<" tried to interact, but is dead!"<<std::endl;
2896 v3f player_pos = playersao->getLastGoodPosition();
2898 // Update wielded item
2899 playersao->setWieldIndex(item_i);
2901 // Get pointed to node (undefined if not POINTEDTYPE_NODE)
2902 v3s16 p_under = pointed.node_undersurface;
2903 v3s16 p_above = pointed.node_abovesurface;
2905 // Get pointed to object (NULL if not POINTEDTYPE_OBJECT)
2906 ServerActiveObject *pointed_object = NULL;
2907 if(pointed.type == POINTEDTHING_OBJECT)
2909 pointed_object = m_env->getActiveObject(pointed.object_id);
2910 if(pointed_object == NULL)
2912 verbosestream<<"TOSERVER_INTERACT: "
2913 "pointed object is NULL"<<std::endl;
2919 v3f pointed_pos_under = player_pos;
2920 v3f pointed_pos_above = player_pos;
2921 if(pointed.type == POINTEDTHING_NODE)
2923 pointed_pos_under = intToFloat(p_under, BS);
2924 pointed_pos_above = intToFloat(p_above, BS);
2926 else if(pointed.type == POINTEDTHING_OBJECT)
2928 pointed_pos_under = pointed_object->getBasePosition();
2929 pointed_pos_above = pointed_pos_under;
2933 Check that target is reasonably close
2934 (only when digging or placing things)
2936 if(action == 0 || action == 2 || action == 3)
2938 float d = player_pos.getDistanceFrom(pointed_pos_under);
2939 float max_d = BS * 14; // Just some large enough value
2941 actionstream<<"Player "<<player->getName()
2942 <<" tried to access "<<pointed.dump()
2944 <<"d="<<d<<", max_d="<<max_d
2945 <<". ignoring."<<std::endl;
2946 // Re-send block to revert change on client-side
2947 RemoteClient *client = getClient(peer_id);
2948 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2949 client->SetBlockNotSent(blockpos);
2956 Make sure the player is allowed to do it
2958 if(!checkPriv(player->getName(), "interact"))
2960 actionstream<<player->getName()<<" attempted to interact with "
2961 <<pointed.dump()<<" without 'interact' privilege"
2963 // Re-send block to revert change on client-side
2964 RemoteClient *client = getClient(peer_id);
2965 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2966 client->SetBlockNotSent(blockpos);
2971 0: start digging or punch object
2975 if(pointed.type == POINTEDTHING_NODE)
2978 NOTE: This can be used in the future to check if
2979 somebody is cheating, by checking the timing.
2981 MapNode n(CONTENT_IGNORE);
2984 n = m_env->getMap().getNode(p_under);
2986 catch(InvalidPositionException &e)
2988 infostream<<"Server: Not punching: Node not found."
2989 <<" Adding block to emerge queue."
2991 m_emerge_queue.addBlock(peer_id,
2992 getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
2994 if(n.getContent() != CONTENT_IGNORE)
2995 scriptapi_node_on_punch(m_lua, p_under, n, playersao);
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 complete 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);
3049 if(n.getContent() != CONTENT_IGNORE)
3050 scriptapi_node_on_dig(m_lua, p_under, n, playersao);
3052 if (m_env->getMap().getNodeNoEx(p_under).getContent() != CONTENT_AIR)
3054 // Re-send block to revert change on client-side
3055 RemoteClient *client = getClient(peer_id);
3056 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
3057 client->SetBlockNotSent(blockpos);
3063 3: place block or right-click object
3065 else if(action == 3)
3067 ItemStack item = playersao->getWieldedItem();
3069 // Reset build time counter
3070 if(pointed.type == POINTEDTHING_NODE &&
3071 item.getDefinition(m_itemdef).type == ITEM_NODE)
3072 getClient(peer_id)->m_time_from_building = 0.0;
3074 if(pointed.type == POINTEDTHING_OBJECT)
3076 // Right click object
3078 // Skip if object has been removed
3079 if(pointed_object->m_removed)
3082 actionstream<<player->getName()<<" right-clicks object "
3083 <<pointed.object_id<<": "
3084 <<pointed_object->getDescription()<<std::endl;
3087 pointed_object->rightClick(playersao);
3089 else if(scriptapi_item_on_place(m_lua,
3090 item, playersao, pointed))
3092 // Placement was handled in lua
3094 // Apply returned ItemStack
3095 if(g_settings->getBool("creative_mode") == false)
3096 playersao->setWieldedItem(item);
3104 else if(action == 4)
3106 ItemStack item = playersao->getWieldedItem();
3108 actionstream<<player->getName()<<" uses "<<item.name
3109 <<", pointing at "<<pointed.dump()<<std::endl;
3111 if(scriptapi_item_on_use(m_lua,
3112 item, playersao, pointed))
3114 // Apply returned ItemStack
3115 if(g_settings->getBool("creative_mode") == false)
3116 playersao->setWieldedItem(item);
3122 Catch invalid actions
3126 infostream<<"WARNING: Server: Invalid action "
3127 <<action<<std::endl;
3130 else if(command == TOSERVER_REMOVED_SOUNDS)
3132 std::string datastring((char*)&data[2], datasize-2);
3133 std::istringstream is(datastring, std::ios_base::binary);
3135 int num = readU16(is);
3136 for(int k=0; k<num; k++){
3137 s32 id = readS32(is);
3138 std::map<s32, ServerPlayingSound>::iterator i =
3139 m_playing_sounds.find(id);
3140 if(i == m_playing_sounds.end())
3142 ServerPlayingSound &psound = i->second;
3143 psound.clients.erase(peer_id);
3144 if(psound.clients.size() == 0)
3145 m_playing_sounds.erase(i++);
3148 else if(command == TOSERVER_NODEMETA_FIELDS)
3150 std::string datastring((char*)&data[2], datasize-2);
3151 std::istringstream is(datastring, std::ios_base::binary);
3153 v3s16 p = readV3S16(is);
3154 std::string formname = deSerializeString(is);
3155 int num = readU16(is);
3156 std::map<std::string, std::string> fields;
3157 for(int k=0; k<num; k++){
3158 std::string fieldname = deSerializeString(is);
3159 std::string fieldvalue = deSerializeLongString(is);
3160 fields[fieldname] = fieldvalue;
3163 scriptapi_node_on_receive_fields(m_lua, p, formname, fields,
3168 infostream<<"Server::ProcessData(): Ignoring "
3169 "unknown command "<<command<<std::endl;
3173 catch(SendFailedException &e)
3175 errorstream<<"Server::ProcessData(): SendFailedException: "
3181 void Server::onMapEditEvent(MapEditEvent *event)
3183 //infostream<<"Server::onMapEditEvent()"<<std::endl;
3184 if(m_ignore_map_edit_events)
3186 if(m_ignore_map_edit_events_area.contains(event->getArea()))
3188 MapEditEvent *e = event->clone();
3189 m_unsent_map_edit_queue.push_back(e);
3192 Inventory* Server::getInventory(const InventoryLocation &loc)
3195 case InventoryLocation::UNDEFINED:
3198 case InventoryLocation::CURRENT_PLAYER:
3201 case InventoryLocation::PLAYER:
3203 Player *player = m_env->getPlayer(loc.name.c_str());
3206 PlayerSAO *playersao = player->getPlayerSAO();
3209 return playersao->getInventory();
3212 case InventoryLocation::NODEMETA:
3214 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
3217 return meta->getInventory();
3225 void Server::setInventoryModified(const InventoryLocation &loc)
3228 case InventoryLocation::UNDEFINED:
3231 case InventoryLocation::PLAYER:
3233 Player *player = m_env->getPlayer(loc.name.c_str());
3236 PlayerSAO *playersao = player->getPlayerSAO();
3239 playersao->m_inventory_not_sent = true;
3240 playersao->m_wielded_item_not_sent = true;
3243 case InventoryLocation::NODEMETA:
3245 v3s16 blockpos = getNodeBlockPos(loc.p);
3247 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3249 block->raiseModified(MOD_STATE_WRITE_NEEDED);
3251 setBlockNotSent(blockpos);
3259 core::list<PlayerInfo> Server::getPlayerInfo()
3261 DSTACK(__FUNCTION_NAME);
3262 JMutexAutoLock envlock(m_env_mutex);
3263 JMutexAutoLock conlock(m_con_mutex);
3265 core::list<PlayerInfo> list;
3267 core::list<Player*> players = m_env->getPlayers();
3269 core::list<Player*>::Iterator i;
3270 for(i = players.begin();
3271 i != players.end(); i++)
3275 Player *player = *i;
3278 // Copy info from connection to info struct
3279 info.id = player->peer_id;
3280 info.address = m_con.GetPeerAddress(player->peer_id);
3281 info.avg_rtt = m_con.GetPeerAvgRTT(player->peer_id);
3283 catch(con::PeerNotFoundException &e)
3285 // Set dummy peer info
3287 info.address = Address(0,0,0,0,0);
3291 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3292 info.position = player->getPosition();
3294 list.push_back(info);
3301 void Server::peerAdded(con::Peer *peer)
3303 DSTACK(__FUNCTION_NAME);
3304 verbosestream<<"Server::peerAdded(): peer->id="
3305 <<peer->id<<std::endl;
3308 c.type = PEER_ADDED;
3309 c.peer_id = peer->id;
3311 m_peer_change_queue.push_back(c);
3314 void Server::deletingPeer(con::Peer *peer, bool timeout)
3316 DSTACK(__FUNCTION_NAME);
3317 verbosestream<<"Server::deletingPeer(): peer->id="
3318 <<peer->id<<", timeout="<<timeout<<std::endl;
3321 c.type = PEER_REMOVED;
3322 c.peer_id = peer->id;
3323 c.timeout = timeout;
3324 m_peer_change_queue.push_back(c);
3331 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3333 DSTACK(__FUNCTION_NAME);
3334 std::ostringstream os(std::ios_base::binary);
3336 writeU16(os, TOCLIENT_HP);
3340 std::string s = os.str();
3341 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3343 con.Send(peer_id, 0, data, true);
3346 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3347 const std::wstring &reason)
3349 DSTACK(__FUNCTION_NAME);
3350 std::ostringstream os(std::ios_base::binary);
3352 writeU16(os, TOCLIENT_ACCESS_DENIED);
3353 os<<serializeWideString(reason);
3356 std::string s = os.str();
3357 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3359 con.Send(peer_id, 0, data, true);
3362 void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
3363 bool set_camera_point_target, v3f camera_point_target)
3365 DSTACK(__FUNCTION_NAME);
3366 std::ostringstream os(std::ios_base::binary);
3368 writeU16(os, TOCLIENT_DEATHSCREEN);
3369 writeU8(os, set_camera_point_target);
3370 writeV3F1000(os, camera_point_target);
3373 std::string s = os.str();
3374 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3376 con.Send(peer_id, 0, data, true);
3379 void Server::SendItemDef(con::Connection &con, u16 peer_id,
3380 IItemDefManager *itemdef)
3382 DSTACK(__FUNCTION_NAME);
3383 std::ostringstream os(std::ios_base::binary);
3387 u32 length of the next item
3388 zlib-compressed serialized ItemDefManager
3390 writeU16(os, TOCLIENT_ITEMDEF);
3391 std::ostringstream tmp_os(std::ios::binary);
3392 itemdef->serialize(tmp_os);
3393 std::ostringstream tmp_os2(std::ios::binary);
3394 compressZlib(tmp_os.str(), tmp_os2);
3395 os<<serializeLongString(tmp_os2.str());
3398 std::string s = os.str();
3399 verbosestream<<"Server: Sending item definitions to id("<<peer_id
3400 <<"): size="<<s.size()<<std::endl;
3401 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3403 con.Send(peer_id, 0, data, true);
3406 void Server::SendNodeDef(con::Connection &con, u16 peer_id,
3407 INodeDefManager *nodedef)
3409 DSTACK(__FUNCTION_NAME);
3410 std::ostringstream os(std::ios_base::binary);
3414 u32 length of the next item
3415 zlib-compressed serialized NodeDefManager
3417 writeU16(os, TOCLIENT_NODEDEF);
3418 std::ostringstream tmp_os(std::ios::binary);
3419 nodedef->serialize(tmp_os);
3420 std::ostringstream tmp_os2(std::ios::binary);
3421 compressZlib(tmp_os.str(), tmp_os2);
3422 os<<serializeLongString(tmp_os2.str());
3425 std::string s = os.str();
3426 verbosestream<<"Server: Sending node definitions to id("<<peer_id
3427 <<"): size="<<s.size()<<std::endl;
3428 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3430 con.Send(peer_id, 0, data, true);
3434 Non-static send methods
3437 void Server::SendInventory(u16 peer_id)
3439 DSTACK(__FUNCTION_NAME);
3441 PlayerSAO *playersao = getPlayerSAO(peer_id);
3444 playersao->m_inventory_not_sent = false;
3450 std::ostringstream os;
3451 playersao->getInventory()->serialize(os);
3453 std::string s = os.str();
3455 SharedBuffer<u8> data(s.size()+2);
3456 writeU16(&data[0], TOCLIENT_INVENTORY);
3457 memcpy(&data[2], s.c_str(), s.size());
3460 m_con.Send(peer_id, 0, data, true);
3463 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3465 DSTACK(__FUNCTION_NAME);
3467 std::ostringstream os(std::ios_base::binary);
3471 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3472 os.write((char*)buf, 2);
3475 writeU16(buf, message.size());
3476 os.write((char*)buf, 2);
3479 for(u32 i=0; i<message.size(); i++)
3483 os.write((char*)buf, 2);
3487 std::string s = os.str();
3488 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3490 m_con.Send(peer_id, 0, data, true);
3493 void Server::BroadcastChatMessage(const std::wstring &message)
3495 for(core::map<u16, RemoteClient*>::Iterator
3496 i = m_clients.getIterator();
3497 i.atEnd() == false; i++)
3499 // Get client and check that it is valid
3500 RemoteClient *client = i.getNode()->getValue();
3501 assert(client->peer_id == i.getNode()->getKey());
3502 if(client->serialization_version == SER_FMT_VER_INVALID)
3505 SendChatMessage(client->peer_id, message);
3509 void Server::SendPlayerHP(u16 peer_id)
3511 DSTACK(__FUNCTION_NAME);
3512 PlayerSAO *playersao = getPlayerSAO(peer_id);
3514 playersao->m_hp_not_sent = false;
3515 SendHP(m_con, peer_id, playersao->getHP());
3518 void Server::SendMovePlayer(u16 peer_id)
3520 DSTACK(__FUNCTION_NAME);
3521 Player *player = m_env->getPlayer(peer_id);
3524 std::ostringstream os(std::ios_base::binary);
3525 writeU16(os, TOCLIENT_MOVE_PLAYER);
3526 writeV3F1000(os, player->getPosition());
3527 writeF1000(os, player->getPitch());
3528 writeF1000(os, player->getYaw());
3531 v3f pos = player->getPosition();
3532 f32 pitch = player->getPitch();
3533 f32 yaw = player->getYaw();
3534 verbosestream<<"Server: Sending TOCLIENT_MOVE_PLAYER"
3535 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3542 std::string s = os.str();
3543 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3545 m_con.Send(peer_id, 0, data, true);
3548 void Server::SendPlayerPrivileges(u16 peer_id)
3550 Player *player = m_env->getPlayer(peer_id);
3552 std::set<std::string> privs;
3553 scriptapi_get_auth(m_lua, player->getName(), NULL, &privs);
3555 std::ostringstream os(std::ios_base::binary);
3556 writeU16(os, TOCLIENT_PRIVILEGES);
3557 writeU16(os, privs.size());
3558 for(std::set<std::string>::const_iterator i = privs.begin();
3559 i != privs.end(); i++){
3560 os<<serializeString(*i);
3564 std::string s = os.str();
3565 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3567 m_con.Send(peer_id, 0, data, true);
3570 s32 Server::playSound(const SimpleSoundSpec &spec,
3571 const ServerSoundParams ¶ms)
3573 // Find out initial position of sound
3574 bool pos_exists = false;
3575 v3f pos = params.getPos(m_env, &pos_exists);
3576 // If position is not found while it should be, cancel sound
3577 if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
3579 // Filter destination clients
3580 std::set<RemoteClient*> dst_clients;
3581 if(params.to_player != "")
3583 Player *player = m_env->getPlayer(params.to_player.c_str());
3585 infostream<<"Server::playSound: Player \""<<params.to_player
3586 <<"\" not found"<<std::endl;
3589 if(player->peer_id == PEER_ID_INEXISTENT){
3590 infostream<<"Server::playSound: Player \""<<params.to_player
3591 <<"\" not connected"<<std::endl;
3594 RemoteClient *client = getClient(player->peer_id);
3595 dst_clients.insert(client);
3599 for(core::map<u16, RemoteClient*>::Iterator
3600 i = m_clients.getIterator(); i.atEnd() == false; i++)
3602 RemoteClient *client = i.getNode()->getValue();
3603 Player *player = m_env->getPlayer(client->peer_id);
3607 if(player->getPosition().getDistanceFrom(pos) >
3608 params.max_hear_distance)
3611 dst_clients.insert(client);
3614 if(dst_clients.size() == 0)
3617 s32 id = m_next_sound_id++;
3618 // The sound will exist as a reference in m_playing_sounds
3619 m_playing_sounds[id] = ServerPlayingSound();
3620 ServerPlayingSound &psound = m_playing_sounds[id];
3621 psound.params = params;
3622 for(std::set<RemoteClient*>::iterator i = dst_clients.begin();
3623 i != dst_clients.end(); i++)
3624 psound.clients.insert((*i)->peer_id);
3626 std::ostringstream os(std::ios_base::binary);
3627 writeU16(os, TOCLIENT_PLAY_SOUND);
3629 os<<serializeString(spec.name);
3630 writeF1000(os, spec.gain * params.gain);
3631 writeU8(os, params.type);
3632 writeV3F1000(os, pos);
3633 writeU16(os, params.object);
3634 writeU8(os, params.loop);
3636 std::string s = os.str();
3637 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3639 for(std::set<RemoteClient*>::iterator i = dst_clients.begin();
3640 i != dst_clients.end(); i++){
3642 m_con.Send((*i)->peer_id, 0, data, true);
3646 void Server::stopSound(s32 handle)
3648 // Get sound reference
3649 std::map<s32, ServerPlayingSound>::iterator i =
3650 m_playing_sounds.find(handle);
3651 if(i == m_playing_sounds.end())
3653 ServerPlayingSound &psound = i->second;
3655 std::ostringstream os(std::ios_base::binary);
3656 writeU16(os, TOCLIENT_STOP_SOUND);
3657 writeS32(os, handle);
3659 std::string s = os.str();
3660 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3662 for(std::set<u16>::iterator i = psound.clients.begin();
3663 i != psound.clients.end(); i++){
3665 m_con.Send(*i, 0, data, true);
3667 // Remove sound reference
3668 m_playing_sounds.erase(i);
3671 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3672 core::list<u16> *far_players, float far_d_nodes)
3674 float maxd = far_d_nodes*BS;
3675 v3f p_f = intToFloat(p, BS);
3679 SharedBuffer<u8> reply(replysize);
3680 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3681 writeS16(&reply[2], p.X);
3682 writeS16(&reply[4], p.Y);
3683 writeS16(&reply[6], p.Z);
3685 for(core::map<u16, RemoteClient*>::Iterator
3686 i = m_clients.getIterator();
3687 i.atEnd() == false; i++)
3689 // Get client and check that it is valid
3690 RemoteClient *client = i.getNode()->getValue();
3691 assert(client->peer_id == i.getNode()->getKey());
3692 if(client->serialization_version == SER_FMT_VER_INVALID)
3695 // Don't send if it's the same one
3696 if(client->peer_id == ignore_id)
3702 Player *player = m_env->getPlayer(client->peer_id);
3705 // If player is far away, only set modified blocks not sent
3706 v3f player_pos = player->getPosition();
3707 if(player_pos.getDistanceFrom(p_f) > maxd)
3709 far_players->push_back(client->peer_id);
3716 m_con.Send(client->peer_id, 0, reply, true);
3720 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3721 core::list<u16> *far_players, float far_d_nodes)
3723 float maxd = far_d_nodes*BS;
3724 v3f p_f = intToFloat(p, BS);
3726 for(core::map<u16, RemoteClient*>::Iterator
3727 i = m_clients.getIterator();
3728 i.atEnd() == false; i++)
3730 // Get client and check that it is valid
3731 RemoteClient *client = i.getNode()->getValue();
3732 assert(client->peer_id == i.getNode()->getKey());
3733 if(client->serialization_version == SER_FMT_VER_INVALID)
3736 // Don't send if it's the same one
3737 if(client->peer_id == ignore_id)
3743 Player *player = m_env->getPlayer(client->peer_id);
3746 // If player is far away, only set modified blocks not sent
3747 v3f player_pos = player->getPosition();
3748 if(player_pos.getDistanceFrom(p_f) > maxd)
3750 far_players->push_back(client->peer_id);
3757 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3758 SharedBuffer<u8> reply(replysize);
3759 writeU16(&reply[0], TOCLIENT_ADDNODE);
3760 writeS16(&reply[2], p.X);
3761 writeS16(&reply[4], p.Y);
3762 writeS16(&reply[6], p.Z);
3763 n.serialize(&reply[8], client->serialization_version);
3766 m_con.Send(client->peer_id, 0, reply, true);
3770 void Server::setBlockNotSent(v3s16 p)
3772 for(core::map<u16, RemoteClient*>::Iterator
3773 i = m_clients.getIterator();
3774 i.atEnd()==false; i++)
3776 RemoteClient *client = i.getNode()->getValue();
3777 client->SetBlockNotSent(p);
3781 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3783 DSTACK(__FUNCTION_NAME);
3785 v3s16 p = block->getPos();
3789 bool completely_air = true;
3790 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3791 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3792 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3794 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
3796 completely_air = false;
3797 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
3802 infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
3804 infostream<<"[completely air] ";
3805 infostream<<std::endl;
3809 Create a packet with the block in the right format
3812 std::ostringstream os(std::ios_base::binary);
3813 block->serialize(os, ver, false);
3814 std::string s = os.str();
3815 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3817 u32 replysize = 8 + blockdata.getSize();
3818 SharedBuffer<u8> reply(replysize);
3819 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3820 writeS16(&reply[2], p.X);
3821 writeS16(&reply[4], p.Y);
3822 writeS16(&reply[6], p.Z);
3823 memcpy(&reply[8], *blockdata, blockdata.getSize());
3825 /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3826 <<": \tpacket size: "<<replysize<<std::endl;*/
3831 m_con.Send(peer_id, 1, reply, true);
3834 void Server::SendBlocks(float dtime)
3836 DSTACK(__FUNCTION_NAME);
3838 JMutexAutoLock envlock(m_env_mutex);
3839 JMutexAutoLock conlock(m_con_mutex);
3841 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
3843 core::array<PrioritySortedBlockTransfer> queue;
3845 s32 total_sending = 0;
3848 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
3850 for(core::map<u16, RemoteClient*>::Iterator
3851 i = m_clients.getIterator();
3852 i.atEnd() == false; i++)
3854 RemoteClient *client = i.getNode()->getValue();
3855 assert(client->peer_id == i.getNode()->getKey());
3857 // If definitions and textures have not been sent, don't
3858 // send MapBlocks either
3859 if(!client->definitions_sent)
3862 total_sending += client->SendingCount();
3864 if(client->serialization_version == SER_FMT_VER_INVALID)
3867 client->GetNextBlocks(this, dtime, queue);
3872 // Lowest priority number comes first.
3873 // Lowest is most important.
3876 for(u32 i=0; i<queue.size(); i++)
3878 //TODO: Calculate limit dynamically
3879 if(total_sending >= g_settings->getS32
3880 ("max_simultaneous_block_sends_server_total"))
3883 PrioritySortedBlockTransfer q = queue[i];
3885 MapBlock *block = NULL;
3888 block = m_env->getMap().getBlockNoCreate(q.pos);
3890 catch(InvalidPositionException &e)
3895 RemoteClient *client = getClient(q.peer_id);
3897 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3899 client->SentBlock(q.pos);
3905 void Server::fillMediaCache()
3907 DSTACK(__FUNCTION_NAME);
3909 infostream<<"Server: Calculating media file checksums"<<std::endl;
3911 // Collect all media file paths
3912 std::list<std::string> paths;
3913 for(core::list<ModSpec>::Iterator i = m_mods.begin();
3914 i != m_mods.end(); i++){
3915 const ModSpec &mod = *i;
3916 paths.push_back(mod.path + DIR_DELIM + "textures");
3917 paths.push_back(mod.path + DIR_DELIM + "sounds");
3918 paths.push_back(mod.path + DIR_DELIM + "media");
3921 // Collect media file information from paths into cache
3922 for(std::list<std::string>::iterator i = paths.begin();
3923 i != paths.end(); i++)
3925 std::string mediapath = *i;
3926 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
3927 for(u32 j=0; j<dirlist.size(); j++){
3928 if(dirlist[j].dir) // Ignode dirs
3930 std::string filename = dirlist[j].name;
3931 // If name contains illegal characters, ignore the file
3932 if(!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)){
3933 infostream<<"Server: ignoring illegal file name: \""
3934 <<filename<<"\""<<std::endl;
3937 // If name is not in a supported format, ignore it
3938 const char *supported_ext[] = {
3939 ".png", ".jpg", ".bmp", ".tga",
3940 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
3944 if(removeStringEnd(filename, supported_ext) == ""){
3945 infostream<<"Server: ignoring unsupported file extension: \""
3946 <<filename<<"\""<<std::endl;
3949 // Ok, attempt to load the file and add to cache
3950 std::string filepath = mediapath + DIR_DELIM + filename;
3952 std::ifstream fis(filepath.c_str(), std::ios_base::binary);
3953 if(fis.good() == false){
3954 errorstream<<"Server::fillMediaCache(): Could not open \""
3955 <<filename<<"\" for reading"<<std::endl;
3958 std::ostringstream tmp_os(std::ios_base::binary);
3962 fis.read(buf, 1024);
3963 std::streamsize len = fis.gcount();
3964 tmp_os.write(buf, len);
3973 errorstream<<"Server::fillMediaCache(): Failed to read \""
3974 <<filename<<"\""<<std::endl;
3977 if(tmp_os.str().length() == 0){
3978 errorstream<<"Server::fillMediaCache(): Empty file \""
3979 <<filepath<<"\""<<std::endl;
3984 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
3986 unsigned char *digest = sha1.getDigest();
3987 std::string sha1_base64 = base64_encode(digest, 20);
3988 std::string sha1_hex = hex_encode((char*)digest, 20);
3992 this->m_media[filename] = MediaInfo(filepath, sha1_base64);
3993 verbosestream<<"Server: "<<sha1_hex<<" is "<<filename<<std::endl;
3998 struct SendableMediaAnnouncement
4001 std::string sha1_digest;
4003 SendableMediaAnnouncement(const std::string name_="",
4004 const std::string sha1_digest_=""):
4006 sha1_digest(sha1_digest_)
4010 void Server::sendMediaAnnouncement(u16 peer_id)
4012 DSTACK(__FUNCTION_NAME);
4014 verbosestream<<"Server: Announcing files to id("<<peer_id<<")"
4017 core::list<SendableMediaAnnouncement> file_announcements;
4019 for(std::map<std::string, MediaInfo>::iterator i = m_media.begin();
4020 i != m_media.end(); i++){
4022 file_announcements.push_back(
4023 SendableMediaAnnouncement(i->first, i->second.sha1_digest));
4027 std::ostringstream os(std::ios_base::binary);
4035 u16 length of sha1_digest
4040 writeU16(os, TOCLIENT_ANNOUNCE_MEDIA);
4041 writeU16(os, file_announcements.size());
4043 for(core::list<SendableMediaAnnouncement>::Iterator
4044 j = file_announcements.begin();
4045 j != file_announcements.end(); j++){
4046 os<<serializeString(j->name);
4047 os<<serializeString(j->sha1_digest);
4051 std::string s = os.str();
4052 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4055 m_con.Send(peer_id, 0, data, true);
4059 struct SendableMedia
4065 SendableMedia(const std::string &name_="", const std::string path_="",
4066 const std::string &data_=""):
4073 void Server::sendRequestedMedia(u16 peer_id,
4074 const core::list<MediaRequest> &tosend)
4076 DSTACK(__FUNCTION_NAME);
4078 verbosestream<<"Server::sendRequestedMedia(): "
4079 <<"Sending files to client"<<std::endl;
4083 // Put 5kB in one bunch (this is not accurate)
4084 u32 bytes_per_bunch = 5000;
4086 core::array< core::list<SendableMedia> > file_bunches;
4087 file_bunches.push_back(core::list<SendableMedia>());
4089 u32 file_size_bunch_total = 0;
4091 for(core::list<MediaRequest>::ConstIterator i = tosend.begin();
4092 i != tosend.end(); i++)
4094 if(m_media.find(i->name) == m_media.end()){
4095 errorstream<<"Server::sendRequestedMedia(): Client asked for "
4096 <<"unknown file \""<<(i->name)<<"\""<<std::endl;
4100 //TODO get path + name
4101 std::string tpath = m_media[(*i).name].path;
4104 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
4105 if(fis.good() == false){
4106 errorstream<<"Server::sendRequestedMedia(): Could not open \""
4107 <<tpath<<"\" for reading"<<std::endl;
4110 std::ostringstream tmp_os(std::ios_base::binary);
4114 fis.read(buf, 1024);
4115 std::streamsize len = fis.gcount();
4116 tmp_os.write(buf, len);
4117 file_size_bunch_total += len;
4126 errorstream<<"Server::sendRequestedMedia(): Failed to read \""
4127 <<(*i).name<<"\""<<std::endl;
4130 /*infostream<<"Server::sendRequestedMedia(): Loaded \""
4131 <<tname<<"\""<<std::endl;*/
4133 file_bunches[file_bunches.size()-1].push_back(
4134 SendableMedia((*i).name, tpath, tmp_os.str()));
4136 // Start next bunch if got enough data
4137 if(file_size_bunch_total >= bytes_per_bunch){
4138 file_bunches.push_back(core::list<SendableMedia>());
4139 file_size_bunch_total = 0;
4144 /* Create and send packets */
4146 u32 num_bunches = file_bunches.size();
4147 for(u32 i=0; i<num_bunches; i++)
4149 std::ostringstream os(std::ios_base::binary);
4153 u16 total number of texture bunches
4154 u16 index of this bunch
4155 u32 number of files in this bunch
4164 writeU16(os, TOCLIENT_MEDIA);
4165 writeU16(os, num_bunches);
4167 writeU32(os, file_bunches[i].size());
4169 for(core::list<SendableMedia>::Iterator
4170 j = file_bunches[i].begin();
4171 j != file_bunches[i].end(); j++){
4172 os<<serializeString(j->name);
4173 os<<serializeLongString(j->data);
4177 std::string s = os.str();
4178 verbosestream<<"Server::sendRequestedMedia(): bunch "
4179 <<i<<"/"<<num_bunches
4180 <<" files="<<file_bunches[i].size()
4181 <<" size=" <<s.size()<<std::endl;
4182 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4184 m_con.Send(peer_id, 0, data, true);
4192 void Server::DiePlayer(u16 peer_id)
4194 DSTACK(__FUNCTION_NAME);
4196 PlayerSAO *playersao = getPlayerSAO(peer_id);
4199 infostream<<"Server::DiePlayer(): Player "
4200 <<playersao->getPlayer()->getName()
4201 <<" dies"<<std::endl;
4203 playersao->setHP(0);
4205 // Trigger scripted stuff
4206 scriptapi_on_dieplayer(m_lua, playersao);
4208 SendPlayerHP(peer_id);
4209 SendDeathscreen(m_con, peer_id, false, v3f(0,0,0));
4212 void Server::RespawnPlayer(u16 peer_id)
4214 DSTACK(__FUNCTION_NAME);
4216 PlayerSAO *playersao = getPlayerSAO(peer_id);
4219 infostream<<"Server::RespawnPlayer(): Player "
4220 <<playersao->getPlayer()->getName()
4221 <<" respawns"<<std::endl;
4223 playersao->setHP(PLAYER_MAX_HP);
4225 bool repositioned = scriptapi_on_respawnplayer(m_lua, playersao);
4227 v3f pos = findSpawnPos(m_env->getServerMap());
4228 playersao->setPos(pos);
4232 void Server::UpdateCrafting(u16 peer_id)
4234 DSTACK(__FUNCTION_NAME);
4236 Player* player = m_env->getPlayer(peer_id);
4239 // Get a preview for crafting
4241 // No crafting in creative mode
4242 if(g_settings->getBool("creative_mode") == false)
4243 getCraftingResult(&player->inventory, preview, false, this);
4245 // Put the new preview in
4246 InventoryList *plist = player->inventory.getList("craftpreview");
4248 assert(plist->getSize() >= 1);
4249 plist->changeItem(0, preview);
4252 RemoteClient* Server::getClient(u16 peer_id)
4254 DSTACK(__FUNCTION_NAME);
4255 //JMutexAutoLock lock(m_con_mutex);
4256 core::map<u16, RemoteClient*>::Node *n;
4257 n = m_clients.find(peer_id);
4258 // A client should exist for all peers
4260 return n->getValue();
4263 std::wstring Server::getStatusString()
4265 std::wostringstream os(std::ios_base::binary);
4268 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4270 os<<L", uptime="<<m_uptime.get();
4271 // Information about clients
4273 for(core::map<u16, RemoteClient*>::Iterator
4274 i = m_clients.getIterator();
4275 i.atEnd() == false; i++)
4277 // Get client and check that it is valid
4278 RemoteClient *client = i.getNode()->getValue();
4279 assert(client->peer_id == i.getNode()->getKey());
4280 if(client->serialization_version == SER_FMT_VER_INVALID)
4283 Player *player = m_env->getPlayer(client->peer_id);
4284 // Get name of player
4285 std::wstring name = L"unknown";
4287 name = narrow_to_wide(player->getName());
4288 // Add name to information string
4292 if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
4293 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4294 if(g_settings->get("motd") != "")
4295 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4299 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
4301 std::set<std::string> privs;
4302 scriptapi_get_auth(m_lua, name, NULL, &privs);
4306 bool Server::checkPriv(const std::string &name, const std::string &priv)
4308 std::set<std::string> privs = getPlayerEffectivePrivs(name);
4309 return (privs.count(priv) != 0);
4312 void Server::reportPrivsModified(const std::string &name)
4315 for(core::map<u16, RemoteClient*>::Iterator
4316 i = m_clients.getIterator();
4317 i.atEnd() == false; i++){
4318 RemoteClient *client = i.getNode()->getValue();
4319 Player *player = m_env->getPlayer(client->peer_id);
4320 reportPrivsModified(player->getName());
4323 Player *player = m_env->getPlayer(name.c_str());
4326 SendPlayerPrivileges(player->peer_id);
4327 PlayerSAO *sao = player->getPlayerSAO();
4330 sao->updatePrivileges(
4331 getPlayerEffectivePrivs(name),
4336 // Saves g_settings to configpath given at initialization
4337 void Server::saveConfig()
4339 if(m_path_config != "")
4340 g_settings->updateConfigFile(m_path_config.c_str());
4343 void Server::notifyPlayer(const char *name, const std::wstring msg)
4345 Player *player = m_env->getPlayer(name);
4348 SendChatMessage(player->peer_id, std::wstring(L"Server: -!- ")+msg);
4351 void Server::notifyPlayers(const std::wstring msg)
4353 BroadcastChatMessage(msg);
4356 void Server::queueBlockEmerge(v3s16 blockpos, bool allow_generate)
4360 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
4361 m_emerge_queue.addBlock(PEER_ID_INEXISTENT, blockpos, flags);
4364 // IGameDef interface
4366 IItemDefManager* Server::getItemDefManager()
4370 INodeDefManager* Server::getNodeDefManager()
4374 ICraftDefManager* Server::getCraftDefManager()
4378 ITextureSource* Server::getTextureSource()
4382 u16 Server::allocateUnknownNodeId(const std::string &name)
4384 return m_nodedef->allocateDummy(name);
4386 ISoundManager* Server::getSoundManager()
4388 return &dummySoundManager;
4390 MtEventManager* Server::getEventManager()
4395 IWritableItemDefManager* Server::getWritableItemDefManager()
4399 IWritableNodeDefManager* Server::getWritableNodeDefManager()
4403 IWritableCraftDefManager* Server::getWritableCraftDefManager()
4408 const ModSpec* Server::getModSpec(const std::string &modname)
4410 for(core::list<ModSpec>::Iterator i = m_mods.begin();
4411 i != m_mods.end(); i++){
4412 const ModSpec &mod = *i;
4413 if(mod.name == modname)
4418 std::string Server::getBuiltinLuaPath()
4420 return porting::path_share + DIR_DELIM + "builtin";
4423 v3f findSpawnPos(ServerMap &map)
4425 //return v3f(50,50,50)*BS;
4430 nodepos = v2s16(0,0);
4435 // Try to find a good place a few times
4436 for(s32 i=0; i<1000; i++)
4439 // We're going to try to throw the player to this position
4440 v2s16 nodepos2d = v2s16(-range + (myrand()%(range*2)),
4441 -range + (myrand()%(range*2)));
4442 //v2s16 sectorpos = getNodeSectorPos(nodepos2d);
4443 // Get ground height at point (fallbacks to heightmap function)
4444 s16 groundheight = map.findGroundLevel(nodepos2d);
4445 // Don't go underwater
4446 if(groundheight < WATER_LEVEL)
4448 //infostream<<"-> Underwater"<<std::endl;
4451 // Don't go to high places
4452 if(groundheight > WATER_LEVEL + 4)
4454 //infostream<<"-> Underwater"<<std::endl;
4458 nodepos = v3s16(nodepos2d.X, groundheight-2, nodepos2d.Y);
4459 bool is_good = false;
4461 for(s32 i=0; i<10; i++){
4462 v3s16 blockpos = getNodeBlockPos(nodepos);
4463 map.emergeBlock(blockpos, true);
4464 MapNode n = map.getNodeNoEx(nodepos);
4465 if(n.getContent() == CONTENT_AIR){
4476 // Found a good place
4477 //infostream<<"Searched through "<<i<<" places."<<std::endl;
4483 return intToFloat(nodepos, BS);
4486 PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id)
4488 RemotePlayer *player = NULL;
4489 bool newplayer = false;
4492 Try to get an existing player
4494 player = static_cast<RemotePlayer*>(m_env->getPlayer(name));
4496 // If player is already connected, cancel
4497 if(player != NULL && player->peer_id != 0)
4499 infostream<<"emergePlayer(): Player already connected"<<std::endl;
4504 If player with the wanted peer_id already exists, cancel.
4506 if(m_env->getPlayer(peer_id) != NULL)
4508 infostream<<"emergePlayer(): Player with wrong name but same"
4509 " peer_id already exists"<<std::endl;
4514 Create a new player if it doesn't exist yet
4519 player = new RemotePlayer(this);
4520 player->updateName(name);
4522 /* Set player position */
4523 infostream<<"Server: Finding spawn place for player \""
4524 <<name<<"\""<<std::endl;
4525 v3f pos = findSpawnPos(m_env->getServerMap());
4526 player->setPosition(pos);
4528 /* Add player to environment */
4529 m_env->addPlayer(player);
4533 Create a new player active object
4535 PlayerSAO *playersao = new PlayerSAO(m_env, player, peer_id,
4536 getPlayerEffectivePrivs(player->getName()),
4539 /* Add object to environment */
4540 m_env->addActiveObject(playersao);
4544 scriptapi_on_newplayer(m_lua, playersao);
4546 scriptapi_on_joinplayer(m_lua, playersao);
4549 if(g_settings->getBool("creative_mode"))
4550 playersao->createCreativeInventory();
4555 void Server::handlePeerChange(PeerChange &c)
4557 JMutexAutoLock envlock(m_env_mutex);
4558 JMutexAutoLock conlock(m_con_mutex);
4560 if(c.type == PEER_ADDED)
4567 core::map<u16, RemoteClient*>::Node *n;
4568 n = m_clients.find(c.peer_id);
4569 // The client shouldn't already exist
4573 RemoteClient *client = new RemoteClient();
4574 client->peer_id = c.peer_id;
4575 m_clients.insert(client->peer_id, client);
4578 else if(c.type == PEER_REMOVED)
4585 core::map<u16, RemoteClient*>::Node *n;
4586 n = m_clients.find(c.peer_id);
4587 // The client should exist
4591 Mark objects to be not known by the client
4593 RemoteClient *client = n->getValue();
4595 for(core::map<u16, bool>::Iterator
4596 i = client->m_known_objects.getIterator();
4597 i.atEnd()==false; i++)
4600 u16 id = i.getNode()->getKey();
4601 ServerActiveObject* obj = m_env->getActiveObject(id);
4603 if(obj && obj->m_known_by_count > 0)
4604 obj->m_known_by_count--;
4608 Clear references to playing sounds
4610 for(std::map<s32, ServerPlayingSound>::iterator
4611 i = m_playing_sounds.begin();
4612 i != m_playing_sounds.end();)
4614 ServerPlayingSound &psound = i->second;
4615 psound.clients.erase(c.peer_id);
4616 if(psound.clients.size() == 0)
4617 m_playing_sounds.erase(i++);
4622 Player *player = m_env->getPlayer(c.peer_id);
4624 // Collect information about leaving in chat
4625 std::wstring message;
4629 std::wstring name = narrow_to_wide(player->getName());
4632 message += L" left game";
4634 message += L" (timed out)";
4638 /* Run scripts and remove from environment */
4642 PlayerSAO *playersao = player->getPlayerSAO();
4645 scriptapi_on_leaveplayer(m_lua, playersao);
4647 playersao->disconnected();
4657 std::ostringstream os(std::ios_base::binary);
4658 for(core::map<u16, RemoteClient*>::Iterator
4659 i = m_clients.getIterator();
4660 i.atEnd() == false; i++)
4662 RemoteClient *client = i.getNode()->getValue();
4663 assert(client->peer_id == i.getNode()->getKey());
4664 if(client->serialization_version == SER_FMT_VER_INVALID)
4667 Player *player = m_env->getPlayer(client->peer_id);
4670 // Get name of player
4671 os<<player->getName()<<" ";
4674 actionstream<<player->getName()<<" "
4675 <<(c.timeout?"times out.":"leaves game.")
4676 <<" List of players: "
4677 <<os.str()<<std::endl;
4682 delete m_clients[c.peer_id];
4683 m_clients.remove(c.peer_id);
4685 // Send player info to all remaining clients
4686 //SendPlayerInfos();
4688 // Send leave chat message to all remaining clients
4689 if(message.length() != 0)
4690 BroadcastChatMessage(message);
4699 void Server::handlePeerChanges()
4701 while(m_peer_change_queue.size() > 0)
4703 PeerChange c = m_peer_change_queue.pop_front();
4705 verbosestream<<"Server: Handling peer change: "
4706 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4709 handlePeerChange(c);
4713 void dedicated_server_loop(Server &server, bool &kill)
4715 DSTACK(__FUNCTION_NAME);
4717 verbosestream<<"dedicated_server_loop()"<<std::endl;
4719 IntervalLimiter m_profiler_interval;
4723 float steplen = g_settings->getFloat("dedicated_server_step");
4724 // This is kind of a hack but can be done like this
4725 // because server.step() is very light
4727 ScopeProfiler sp(g_profiler, "dedicated server sleep");
4728 sleep_ms((int)(steplen*1000.0));
4730 server.step(steplen);
4732 if(server.getShutdownRequested() || kill)
4734 infostream<<"Dedicated server quitting"<<std::endl;
4741 float profiler_print_interval =
4742 g_settings->getFloat("profiler_print_interval");
4743 if(profiler_print_interval != 0)
4745 if(m_profiler_interval.step(steplen, profiler_print_interval))
4747 infostream<<"Profiler:"<<std::endl;
4748 g_profiler->print(infostream);
4749 g_profiler->clear();