3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU Lesser General Public License as published by
7 the Free Software Foundation; either version 2.1 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU Lesser General Public License for more details.
15 You should have received a copy of the GNU Lesser General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
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 given_password[PASSWORD_SIZE];
2093 if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
2095 // old version - assume blank password
2096 given_password[0] = 0;
2100 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2102 given_password[i] = data[23+i];
2104 given_password[PASSWORD_SIZE-1] = 0;
2107 if(!base64_is_valid(given_password)){
2108 infostream<<"Server: "<<playername
2109 <<" supplied invalid password hash"<<std::endl;
2110 SendAccessDenied(m_con, peer_id, L"Invalid password hash");
2114 std::string checkpwd; // Password hash to check against
2115 bool has_auth = scriptapi_get_auth(m_lua, playername, &checkpwd, NULL);
2117 // If no authentication info exists for user, create it
2119 if(!isSingleplayer() &&
2120 g_settings->getBool("disallow_empty_password") &&
2121 std::string(given_password) == ""){
2122 SendAccessDenied(m_con, peer_id, L"Empty passwords are "
2123 L"disallowed. Set a password and try again.");
2126 std::wstring raw_default_password =
2127 narrow_to_wide(g_settings->get("default_password"));
2128 std::string initial_password =
2129 translatePassword(playername, raw_default_password);
2131 // If default_password is empty, allow any initial password
2132 if (raw_default_password.length() == 0)
2133 initial_password = given_password;
2135 scriptapi_create_auth(m_lua, playername, initial_password);
2138 has_auth = scriptapi_get_auth(m_lua, playername, &checkpwd, NULL);
2141 SendAccessDenied(m_con, peer_id, L"Not allowed to login");
2145 if(given_password != checkpwd){
2146 infostream<<"Server: peer_id="<<peer_id
2147 <<": supplied invalid password for "
2148 <<playername<<std::endl;
2149 SendAccessDenied(m_con, peer_id, L"Invalid password");
2153 // Do not allow multiple players in simple singleplayer mode.
2154 // This isn't a perfect way to do it, but will suffice for now.
2155 if(m_simple_singleplayer_mode && m_clients.size() > 1){
2156 infostream<<"Server: Not allowing another client to connect in"
2157 <<" simple singleplayer mode"<<std::endl;
2158 SendAccessDenied(m_con, peer_id,
2159 L"Running in simple singleplayer mode.");
2163 // Enforce user limit.
2164 // Don't enforce for users that have some admin right
2165 if(m_clients.size() >= g_settings->getU16("max_users") &&
2166 !checkPriv(playername, "server") &&
2167 !checkPriv(playername, "ban") &&
2168 !checkPriv(playername, "privs") &&
2169 !checkPriv(playername, "password") &&
2170 playername != g_settings->get("name"))
2172 actionstream<<"Server: "<<playername<<" tried to join, but there"
2173 <<" are already max_users="
2174 <<g_settings->getU16("max_users")<<" players."<<std::endl;
2175 SendAccessDenied(m_con, peer_id, L"Too many users.");
2180 PlayerSAO *playersao = emergePlayer(playername, peer_id);
2182 // If failed, cancel
2183 if(playersao == NULL)
2185 errorstream<<"Server: peer_id="<<peer_id
2186 <<": failed to emerge player"<<std::endl;
2191 Answer with a TOCLIENT_INIT
2194 SharedBuffer<u8> reply(2+1+6+8);
2195 writeU16(&reply[0], TOCLIENT_INIT);
2196 writeU8(&reply[2], deployed);
2197 writeV3S16(&reply[2+1], floatToInt(playersao->getPlayer()->getPosition()+v3f(0,BS/2,0), BS));
2198 writeU64(&reply[2+1+6], m_env->getServerMap().getSeed());
2201 m_con.Send(peer_id, 0, reply, true);
2205 Send complete position information
2207 SendMovePlayer(peer_id);
2212 if(command == TOSERVER_INIT2)
2214 verbosestream<<"Server: Got TOSERVER_INIT2 from "
2215 <<peer_id<<std::endl;
2217 Player *player = m_env->getPlayer(peer_id);
2219 verbosestream<<"Server: TOSERVER_INIT2: "
2220 <<"Player not found; ignoring."<<std::endl;
2224 getClient(peer_id)->serialization_version
2225 = getClient(peer_id)->pending_serialization_version;
2228 Send some initialization data
2231 infostream<<"Server: Sending content to "
2232 <<getPlayerName(peer_id)<<std::endl;
2234 // Send item definitions
2235 SendItemDef(m_con, peer_id, m_itemdef);
2237 // Send node definitions
2238 SendNodeDef(m_con, peer_id, m_nodedef);
2240 // Send media announcement
2241 sendMediaAnnouncement(peer_id);
2244 SendPlayerPrivileges(peer_id);
2247 UpdateCrafting(peer_id);
2248 SendInventory(peer_id);
2251 SendPlayerHP(peer_id);
2253 // Show death screen if necessary
2255 SendDeathscreen(m_con, peer_id, false, v3f(0,0,0));
2259 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2260 m_env->getTimeOfDay(), g_settings->getFloat("time_speed"));
2261 m_con.Send(peer_id, 0, data, true);
2264 // Note things in chat if not in simple singleplayer mode
2265 if(!m_simple_singleplayer_mode)
2267 // Send information about server to player in chat
2268 SendChatMessage(peer_id, getStatusString());
2270 // Send information about joining in chat
2272 std::wstring name = L"unknown";
2273 Player *player = m_env->getPlayer(peer_id);
2275 name = narrow_to_wide(player->getName());
2277 std::wstring message;
2280 message += L" joined game";
2281 BroadcastChatMessage(message);
2285 // Warnings about protocol version can be issued here
2286 if(getClient(peer_id)->net_proto_version < PROTOCOL_VERSION)
2288 SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT IS OLD AND MAY WORK PROPERLY WITH THIS SERVER");
2295 std::ostringstream os(std::ios_base::binary);
2296 for(core::map<u16, RemoteClient*>::Iterator
2297 i = m_clients.getIterator();
2298 i.atEnd() == false; i++)
2300 RemoteClient *client = i.getNode()->getValue();
2301 assert(client->peer_id == i.getNode()->getKey());
2302 if(client->serialization_version == SER_FMT_VER_INVALID)
2305 Player *player = m_env->getPlayer(client->peer_id);
2308 // Get name of player
2309 os<<player->getName()<<" ";
2312 actionstream<<player->getName()<<" joins game. List of players: "
2313 <<os.str()<<std::endl;
2319 if(peer_ser_ver == SER_FMT_VER_INVALID)
2321 infostream<<"Server::ProcessData(): Cancelling: Peer"
2322 " serialization format invalid or not initialized."
2323 " Skipping incoming command="<<command<<std::endl;
2327 Player *player = m_env->getPlayer(peer_id);
2329 infostream<<"Server::ProcessData(): Cancelling: "
2330 "No player for peer_id="<<peer_id
2335 PlayerSAO *playersao = player->getPlayerSAO();
2336 if(playersao == NULL){
2337 infostream<<"Server::ProcessData(): Cancelling: "
2338 "No player object for peer_id="<<peer_id
2343 if(command == TOSERVER_PLAYERPOS)
2345 if(datasize < 2+12+12+4+4)
2349 v3s32 ps = readV3S32(&data[start+2]);
2350 v3s32 ss = readV3S32(&data[start+2+12]);
2351 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2352 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2353 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2354 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2355 pitch = wrapDegrees(pitch);
2356 yaw = wrapDegrees(yaw);
2358 player->setPosition(position);
2359 player->setSpeed(speed);
2360 player->setPitch(pitch);
2361 player->setYaw(yaw);
2363 /*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2364 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2365 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2367 else if(command == TOSERVER_GOTBLOCKS)
2380 u16 count = data[2];
2381 for(u16 i=0; i<count; i++)
2383 if((s16)datasize < 2+1+(i+1)*6)
2384 throw con::InvalidIncomingDataException
2385 ("GOTBLOCKS length is too short");
2386 v3s16 p = readV3S16(&data[2+1+i*6]);
2387 /*infostream<<"Server: GOTBLOCKS ("
2388 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2389 RemoteClient *client = getClient(peer_id);
2390 client->GotBlock(p);
2393 else if(command == TOSERVER_DELETEDBLOCKS)
2406 u16 count = data[2];
2407 for(u16 i=0; i<count; i++)
2409 if((s16)datasize < 2+1+(i+1)*6)
2410 throw con::InvalidIncomingDataException
2411 ("DELETEDBLOCKS length is too short");
2412 v3s16 p = readV3S16(&data[2+1+i*6]);
2413 /*infostream<<"Server: DELETEDBLOCKS ("
2414 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2415 RemoteClient *client = getClient(peer_id);
2416 client->SetBlockNotSent(p);
2419 else if(command == TOSERVER_CLICK_OBJECT)
2421 infostream<<"Server: CLICK_OBJECT not supported anymore"<<std::endl;
2424 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2426 infostream<<"Server: CLICK_ACTIVEOBJECT not supported anymore"<<std::endl;
2429 else if(command == TOSERVER_GROUND_ACTION)
2431 infostream<<"Server: GROUND_ACTION not supported anymore"<<std::endl;
2435 else if(command == TOSERVER_RELEASE)
2437 infostream<<"Server: RELEASE not supported anymore"<<std::endl;
2440 else if(command == TOSERVER_SIGNTEXT)
2442 infostream<<"Server: SIGNTEXT not supported anymore"
2446 else if(command == TOSERVER_SIGNNODETEXT)
2448 infostream<<"Server: SIGNNODETEXT not supported anymore"
2452 else if(command == TOSERVER_INVENTORY_ACTION)
2454 // Strip command and create a stream
2455 std::string datastring((char*)&data[2], datasize-2);
2456 verbosestream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2457 std::istringstream is(datastring, std::ios_base::binary);
2459 InventoryAction *a = InventoryAction::deSerialize(is);
2462 infostream<<"TOSERVER_INVENTORY_ACTION: "
2463 <<"InventoryAction::deSerialize() returned NULL"
2469 Note: Always set inventory not sent, to repair cases
2470 where the client made a bad prediction.
2474 Handle restrictions and special cases of the move action
2476 if(a->getType() == IACTION_MOVE)
2478 IMoveAction *ma = (IMoveAction*)a;
2480 ma->from_inv.applyCurrentPlayer(player->getName());
2481 ma->to_inv.applyCurrentPlayer(player->getName());
2483 setInventoryModified(ma->from_inv);
2484 setInventoryModified(ma->to_inv);
2486 bool from_inv_is_current_player =
2487 (ma->from_inv.type == InventoryLocation::PLAYER) &&
2488 (ma->from_inv.name == player->getName());
2490 bool to_inv_is_current_player =
2491 (ma->to_inv.type == InventoryLocation::PLAYER) &&
2492 (ma->to_inv.name == player->getName());
2495 Disable moving items out of craftpreview
2497 if(ma->from_list == "craftpreview")
2499 infostream<<"Ignoring IMoveAction from "
2500 <<(ma->from_inv.dump())<<":"<<ma->from_list
2501 <<" to "<<(ma->to_inv.dump())<<":"<<ma->to_list
2502 <<" because src is "<<ma->from_list<<std::endl;
2508 Disable moving items into craftresult and craftpreview
2510 if(ma->to_list == "craftpreview" || ma->to_list == "craftresult")
2512 infostream<<"Ignoring IMoveAction from "
2513 <<(ma->from_inv.dump())<<":"<<ma->from_list
2514 <<" to "<<(ma->to_inv.dump())<<":"<<ma->to_list
2515 <<" because dst is "<<ma->to_list<<std::endl;
2520 // Disallow moving items in elsewhere than player's inventory
2521 // if not allowed to interact
2522 if(!checkPriv(player->getName(), "interact") &&
2523 (!from_inv_is_current_player ||
2524 !to_inv_is_current_player))
2526 infostream<<"Cannot move outside of player's inventory: "
2527 <<"No interact privilege"<<std::endl;
2532 // If player is not an admin, check for ownership of src and dst
2533 /*if(!checkPriv(player->getName(), "server"))
2535 std::string owner_from = getInventoryOwner(ma->from_inv);
2536 if(owner_from != "" && owner_from != player->getName())
2538 infostream<<"WARNING: "<<player->getName()
2539 <<" tried to access an inventory that"
2540 <<" belongs to "<<owner_from<<std::endl;
2545 std::string owner_to = getInventoryOwner(ma->to_inv);
2546 if(owner_to != "" && owner_to != player->getName())
2548 infostream<<"WARNING: "<<player->getName()
2549 <<" tried to access an inventory that"
2550 <<" belongs to "<<owner_to<<std::endl;
2557 Handle restrictions and special cases of the drop action
2559 else if(a->getType() == IACTION_DROP)
2561 IDropAction *da = (IDropAction*)a;
2563 da->from_inv.applyCurrentPlayer(player->getName());
2565 setInventoryModified(da->from_inv);
2567 // Disallow dropping items if not allowed to interact
2568 if(!checkPriv(player->getName(), "interact"))
2573 // If player is not an admin, check for ownership
2574 /*else if(!checkPriv(player->getName(), "server"))
2576 std::string owner_from = getInventoryOwner(da->from_inv);
2577 if(owner_from != "" && owner_from != player->getName())
2579 infostream<<"WARNING: "<<player->getName()
2580 <<" tried to access an inventory that"
2581 <<" belongs to "<<owner_from<<std::endl;
2588 Handle restrictions and special cases of the craft action
2590 else if(a->getType() == IACTION_CRAFT)
2592 ICraftAction *ca = (ICraftAction*)a;
2594 ca->craft_inv.applyCurrentPlayer(player->getName());
2596 setInventoryModified(ca->craft_inv);
2598 //bool craft_inv_is_current_player =
2599 // (ca->craft_inv.type == InventoryLocation::PLAYER) &&
2600 // (ca->craft_inv.name == player->getName());
2602 // Disallow crafting if not allowed to interact
2603 if(!checkPriv(player->getName(), "interact"))
2605 infostream<<"Cannot craft: "
2606 <<"No interact privilege"<<std::endl;
2611 // If player is not an admin, check for ownership of inventory
2612 /*if(!checkPriv(player->getName(), "server"))
2614 std::string owner_craft = getInventoryOwner(ca->craft_inv);
2615 if(owner_craft != "" && owner_craft != player->getName())
2617 infostream<<"WARNING: "<<player->getName()
2618 <<" tried to access an inventory that"
2619 <<" belongs to "<<owner_craft<<std::endl;
2627 a->apply(this, playersao, this);
2631 else if(command == TOSERVER_CHAT_MESSAGE)
2639 std::string datastring((char*)&data[2], datasize-2);
2640 std::istringstream is(datastring, std::ios_base::binary);
2643 is.read((char*)buf, 2);
2644 u16 len = readU16(buf);
2646 std::wstring message;
2647 for(u16 i=0; i<len; i++)
2649 is.read((char*)buf, 2);
2650 message += (wchar_t)readU16(buf);
2653 // Get player name of this client
2654 std::wstring name = narrow_to_wide(player->getName());
2657 bool ate = scriptapi_on_chat_message(m_lua, player->getName(),
2658 wide_to_narrow(message));
2659 // If script ate the message, don't proceed
2663 // Line to send to players
2665 // Whether to send to the player that sent the line
2666 bool send_to_sender = false;
2667 // Whether to send to other players
2668 bool send_to_others = false;
2671 if(message[0] == L'/')
2673 size_t strip_size = 1;
2674 if (message[1] == L'#') // support old-style commans
2676 message = message.substr(strip_size);
2678 WStrfnd f1(message);
2679 f1.next(L" "); // Skip over /#whatever
2680 std::wstring paramstring = f1.next(L"");
2682 ServerCommandContext *ctx = new ServerCommandContext(
2683 str_split(message, L' '),
2689 std::wstring reply(processServerCommand(ctx));
2690 send_to_sender = ctx->flags & SEND_TO_SENDER;
2691 send_to_others = ctx->flags & SEND_TO_OTHERS;
2693 if (ctx->flags & SEND_NO_PREFIX)
2696 line += L"Server: " + reply;
2703 if(checkPriv(player->getName(), "shout")){
2708 send_to_others = true;
2710 line += L"Server: You are not allowed to shout";
2711 send_to_sender = true;
2718 actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2721 Send the message to clients
2723 for(core::map<u16, RemoteClient*>::Iterator
2724 i = m_clients.getIterator();
2725 i.atEnd() == false; i++)
2727 // Get client and check that it is valid
2728 RemoteClient *client = i.getNode()->getValue();
2729 assert(client->peer_id == i.getNode()->getKey());
2730 if(client->serialization_version == SER_FMT_VER_INVALID)
2734 bool sender_selected = (peer_id == client->peer_id);
2735 if(sender_selected == true && send_to_sender == false)
2737 if(sender_selected == false && send_to_others == false)
2740 SendChatMessage(client->peer_id, line);
2744 else if(command == TOSERVER_DAMAGE)
2746 std::string datastring((char*)&data[2], datasize-2);
2747 std::istringstream is(datastring, std::ios_base::binary);
2748 u8 damage = readU8(is);
2750 actionstream<<player->getName()<<" damaged by "
2751 <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
2754 playersao->setHP(playersao->getHP() - damage);
2756 if(playersao->getHP() == 0 && playersao->m_hp_not_sent)
2759 if(playersao->m_hp_not_sent)
2760 SendPlayerHP(peer_id);
2762 else if(command == TOSERVER_PASSWORD)
2765 [0] u16 TOSERVER_PASSWORD
2766 [2] u8[28] old password
2767 [30] u8[28] new password
2770 if(datasize != 2+PASSWORD_SIZE*2)
2772 /*char password[PASSWORD_SIZE];
2773 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2774 password[i] = data[2+i];
2775 password[PASSWORD_SIZE-1] = 0;*/
2777 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2785 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2787 char c = data[2+PASSWORD_SIZE+i];
2793 if(!base64_is_valid(newpwd)){
2794 infostream<<"Server: "<<player->getName()<<" supplied invalid password hash"<<std::endl;
2795 // Wrong old password supplied!!
2796 SendChatMessage(peer_id, L"Invalid new password hash supplied. Password NOT changed.");
2800 infostream<<"Server: Client requests a password change from "
2801 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
2803 std::string playername = player->getName();
2805 std::string checkpwd;
2806 scriptapi_get_auth(m_lua, playername, &checkpwd, NULL);
2808 if(oldpwd != checkpwd)
2810 infostream<<"Server: invalid old password"<<std::endl;
2811 // Wrong old password supplied!!
2812 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
2816 bool success = scriptapi_set_password(m_lua, playername, newpwd);
2818 actionstream<<player->getName()<<" changes password"<<std::endl;
2819 SendChatMessage(peer_id, L"Password change successful");
2821 actionstream<<player->getName()<<" tries to change password but "
2822 <<"it fails"<<std::endl;
2823 SendChatMessage(peer_id, L"Password change failed or inavailable");
2826 else if(command == TOSERVER_PLAYERITEM)
2831 u16 item = readU16(&data[2]);
2832 playersao->setWieldIndex(item);
2834 else if(command == TOSERVER_RESPAWN)
2839 RespawnPlayer(peer_id);
2841 actionstream<<player->getName()<<" respawns at "
2842 <<PP(player->getPosition()/BS)<<std::endl;
2844 // ActiveObject is added to environment in AsyncRunStep after
2845 // the previous addition has been succesfully removed
2847 else if(command == TOSERVER_REQUEST_MEDIA) {
2848 std::string datastring((char*)&data[2], datasize-2);
2849 std::istringstream is(datastring, std::ios_base::binary);
2851 core::list<MediaRequest> tosend;
2852 u16 numfiles = readU16(is);
2854 infostream<<"Sending "<<numfiles<<" files to "
2855 <<getPlayerName(peer_id)<<std::endl;
2856 verbosestream<<"TOSERVER_REQUEST_MEDIA: "<<std::endl;
2858 for(int i = 0; i < numfiles; i++) {
2859 std::string name = deSerializeString(is);
2860 tosend.push_back(MediaRequest(name));
2861 verbosestream<<"TOSERVER_REQUEST_MEDIA: requested file "
2865 sendRequestedMedia(peer_id, tosend);
2867 // Now the client should know about everything
2868 // (definitions and files)
2869 getClient(peer_id)->definitions_sent = true;
2871 else if(command == TOSERVER_INTERACT)
2873 std::string datastring((char*)&data[2], datasize-2);
2874 std::istringstream is(datastring, std::ios_base::binary);
2880 [5] u32 length of the next item
2881 [9] serialized PointedThing
2883 0: start digging (from undersurface) or use
2884 1: stop digging (all parameters ignored)
2885 2: digging completed
2886 3: place block or item (to abovesurface)
2889 u8 action = readU8(is);
2890 u16 item_i = readU16(is);
2891 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
2892 PointedThing pointed;
2893 pointed.deSerialize(tmp_is);
2895 verbosestream<<"TOSERVER_INTERACT: action="<<(int)action<<", item="
2896 <<item_i<<", pointed="<<pointed.dump()<<std::endl;
2900 verbosestream<<"TOSERVER_INTERACT: "<<player->getName()
2901 <<" tried to interact, but is dead!"<<std::endl;
2905 v3f player_pos = playersao->getLastGoodPosition();
2907 // Update wielded item
2908 playersao->setWieldIndex(item_i);
2910 // Get pointed to node (undefined if not POINTEDTYPE_NODE)
2911 v3s16 p_under = pointed.node_undersurface;
2912 v3s16 p_above = pointed.node_abovesurface;
2914 // Get pointed to object (NULL if not POINTEDTYPE_OBJECT)
2915 ServerActiveObject *pointed_object = NULL;
2916 if(pointed.type == POINTEDTHING_OBJECT)
2918 pointed_object = m_env->getActiveObject(pointed.object_id);
2919 if(pointed_object == NULL)
2921 verbosestream<<"TOSERVER_INTERACT: "
2922 "pointed object is NULL"<<std::endl;
2928 v3f pointed_pos_under = player_pos;
2929 v3f pointed_pos_above = player_pos;
2930 if(pointed.type == POINTEDTHING_NODE)
2932 pointed_pos_under = intToFloat(p_under, BS);
2933 pointed_pos_above = intToFloat(p_above, BS);
2935 else if(pointed.type == POINTEDTHING_OBJECT)
2937 pointed_pos_under = pointed_object->getBasePosition();
2938 pointed_pos_above = pointed_pos_under;
2942 Check that target is reasonably close
2943 (only when digging or placing things)
2945 if(action == 0 || action == 2 || action == 3)
2947 float d = player_pos.getDistanceFrom(pointed_pos_under);
2948 float max_d = BS * 14; // Just some large enough value
2950 actionstream<<"Player "<<player->getName()
2951 <<" tried to access "<<pointed.dump()
2953 <<"d="<<d<<", max_d="<<max_d
2954 <<". ignoring."<<std::endl;
2955 // Re-send block to revert change on client-side
2956 RemoteClient *client = getClient(peer_id);
2957 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2958 client->SetBlockNotSent(blockpos);
2965 Make sure the player is allowed to do it
2967 if(!checkPriv(player->getName(), "interact"))
2969 actionstream<<player->getName()<<" attempted to interact with "
2970 <<pointed.dump()<<" without 'interact' privilege"
2972 // Re-send block to revert change on client-side
2973 RemoteClient *client = getClient(peer_id);
2974 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
2975 client->SetBlockNotSent(blockpos);
2980 0: start digging or punch object
2984 if(pointed.type == POINTEDTHING_NODE)
2987 NOTE: This can be used in the future to check if
2988 somebody is cheating, by checking the timing.
2990 MapNode n(CONTENT_IGNORE);
2993 n = m_env->getMap().getNode(p_under);
2995 catch(InvalidPositionException &e)
2997 infostream<<"Server: Not punching: Node not found."
2998 <<" Adding block to emerge queue."
3000 m_emerge_queue.addBlock(peer_id,
3001 getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
3003 if(n.getContent() != CONTENT_IGNORE)
3004 scriptapi_node_on_punch(m_lua, p_under, n, playersao);
3006 else if(pointed.type == POINTEDTHING_OBJECT)
3008 // Skip if object has been removed
3009 if(pointed_object->m_removed)
3012 actionstream<<player->getName()<<" punches object "
3013 <<pointed.object_id<<": "
3014 <<pointed_object->getDescription()<<std::endl;
3016 ItemStack punchitem = playersao->getWieldedItem();
3017 ToolCapabilities toolcap =
3018 punchitem.getToolCapabilities(m_itemdef);
3019 v3f dir = (pointed_object->getBasePosition() -
3020 (player->getPosition() + player->getEyeOffset())
3022 float time_from_last_punch =
3023 playersao->resetTimeFromLastPunch();
3024 pointed_object->punch(dir, &toolcap, playersao,
3025 time_from_last_punch);
3033 else if(action == 1)
3038 2: Digging completed
3040 else if(action == 2)
3042 // Only complete digging of nodes
3043 if(pointed.type == POINTEDTHING_NODE)
3045 MapNode n(CONTENT_IGNORE);
3048 n = m_env->getMap().getNode(p_under);
3050 catch(InvalidPositionException &e)
3052 infostream<<"Server: Not finishing digging: Node not found."
3053 <<" Adding block to emerge queue."
3055 m_emerge_queue.addBlock(peer_id,
3056 getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
3058 if(n.getContent() != CONTENT_IGNORE)
3059 scriptapi_node_on_dig(m_lua, p_under, n, playersao);
3061 if (m_env->getMap().getNodeNoEx(p_under).getContent() != CONTENT_AIR)
3063 // Re-send block to revert change on client-side
3064 RemoteClient *client = getClient(peer_id);
3065 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos_under, BS));
3066 client->SetBlockNotSent(blockpos);
3072 3: place block or right-click object
3074 else if(action == 3)
3076 ItemStack item = playersao->getWieldedItem();
3078 // Reset build time counter
3079 if(pointed.type == POINTEDTHING_NODE &&
3080 item.getDefinition(m_itemdef).type == ITEM_NODE)
3081 getClient(peer_id)->m_time_from_building = 0.0;
3083 if(pointed.type == POINTEDTHING_OBJECT)
3085 // Right click object
3087 // Skip if object has been removed
3088 if(pointed_object->m_removed)
3091 actionstream<<player->getName()<<" right-clicks object "
3092 <<pointed.object_id<<": "
3093 <<pointed_object->getDescription()<<std::endl;
3096 pointed_object->rightClick(playersao);
3098 else if(scriptapi_item_on_place(m_lua,
3099 item, playersao, pointed))
3101 // Placement was handled in lua
3103 // Apply returned ItemStack
3104 if(g_settings->getBool("creative_mode") == false)
3105 playersao->setWieldedItem(item);
3113 else if(action == 4)
3115 ItemStack item = playersao->getWieldedItem();
3117 actionstream<<player->getName()<<" uses "<<item.name
3118 <<", pointing at "<<pointed.dump()<<std::endl;
3120 if(scriptapi_item_on_use(m_lua,
3121 item, playersao, pointed))
3123 // Apply returned ItemStack
3124 if(g_settings->getBool("creative_mode") == false)
3125 playersao->setWieldedItem(item);
3131 Catch invalid actions
3135 infostream<<"WARNING: Server: Invalid action "
3136 <<action<<std::endl;
3139 else if(command == TOSERVER_REMOVED_SOUNDS)
3141 std::string datastring((char*)&data[2], datasize-2);
3142 std::istringstream is(datastring, std::ios_base::binary);
3144 int num = readU16(is);
3145 for(int k=0; k<num; k++){
3146 s32 id = readS32(is);
3147 std::map<s32, ServerPlayingSound>::iterator i =
3148 m_playing_sounds.find(id);
3149 if(i == m_playing_sounds.end())
3151 ServerPlayingSound &psound = i->second;
3152 psound.clients.erase(peer_id);
3153 if(psound.clients.size() == 0)
3154 m_playing_sounds.erase(i++);
3157 else if(command == TOSERVER_NODEMETA_FIELDS)
3159 std::string datastring((char*)&data[2], datasize-2);
3160 std::istringstream is(datastring, std::ios_base::binary);
3162 v3s16 p = readV3S16(is);
3163 std::string formname = deSerializeString(is);
3164 int num = readU16(is);
3165 std::map<std::string, std::string> fields;
3166 for(int k=0; k<num; k++){
3167 std::string fieldname = deSerializeString(is);
3168 std::string fieldvalue = deSerializeLongString(is);
3169 fields[fieldname] = fieldvalue;
3172 scriptapi_node_on_receive_fields(m_lua, p, formname, fields,
3177 infostream<<"Server::ProcessData(): Ignoring "
3178 "unknown command "<<command<<std::endl;
3182 catch(SendFailedException &e)
3184 errorstream<<"Server::ProcessData(): SendFailedException: "
3190 void Server::onMapEditEvent(MapEditEvent *event)
3192 //infostream<<"Server::onMapEditEvent()"<<std::endl;
3193 if(m_ignore_map_edit_events)
3195 if(m_ignore_map_edit_events_area.contains(event->getArea()))
3197 MapEditEvent *e = event->clone();
3198 m_unsent_map_edit_queue.push_back(e);
3201 Inventory* Server::getInventory(const InventoryLocation &loc)
3204 case InventoryLocation::UNDEFINED:
3207 case InventoryLocation::CURRENT_PLAYER:
3210 case InventoryLocation::PLAYER:
3212 Player *player = m_env->getPlayer(loc.name.c_str());
3215 PlayerSAO *playersao = player->getPlayerSAO();
3218 return playersao->getInventory();
3221 case InventoryLocation::NODEMETA:
3223 NodeMetadata *meta = m_env->getMap().getNodeMetadata(loc.p);
3226 return meta->getInventory();
3234 void Server::setInventoryModified(const InventoryLocation &loc)
3237 case InventoryLocation::UNDEFINED:
3240 case InventoryLocation::PLAYER:
3242 Player *player = m_env->getPlayer(loc.name.c_str());
3245 PlayerSAO *playersao = player->getPlayerSAO();
3248 playersao->m_inventory_not_sent = true;
3249 playersao->m_wielded_item_not_sent = true;
3252 case InventoryLocation::NODEMETA:
3254 v3s16 blockpos = getNodeBlockPos(loc.p);
3256 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3258 block->raiseModified(MOD_STATE_WRITE_NEEDED);
3260 setBlockNotSent(blockpos);
3268 core::list<PlayerInfo> Server::getPlayerInfo()
3270 DSTACK(__FUNCTION_NAME);
3271 JMutexAutoLock envlock(m_env_mutex);
3272 JMutexAutoLock conlock(m_con_mutex);
3274 core::list<PlayerInfo> list;
3276 core::list<Player*> players = m_env->getPlayers();
3278 core::list<Player*>::Iterator i;
3279 for(i = players.begin();
3280 i != players.end(); i++)
3284 Player *player = *i;
3287 // Copy info from connection to info struct
3288 info.id = player->peer_id;
3289 info.address = m_con.GetPeerAddress(player->peer_id);
3290 info.avg_rtt = m_con.GetPeerAvgRTT(player->peer_id);
3292 catch(con::PeerNotFoundException &e)
3294 // Set dummy peer info
3296 info.address = Address(0,0,0,0,0);
3300 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3301 info.position = player->getPosition();
3303 list.push_back(info);
3310 void Server::peerAdded(con::Peer *peer)
3312 DSTACK(__FUNCTION_NAME);
3313 verbosestream<<"Server::peerAdded(): peer->id="
3314 <<peer->id<<std::endl;
3317 c.type = PEER_ADDED;
3318 c.peer_id = peer->id;
3320 m_peer_change_queue.push_back(c);
3323 void Server::deletingPeer(con::Peer *peer, bool timeout)
3325 DSTACK(__FUNCTION_NAME);
3326 verbosestream<<"Server::deletingPeer(): peer->id="
3327 <<peer->id<<", timeout="<<timeout<<std::endl;
3330 c.type = PEER_REMOVED;
3331 c.peer_id = peer->id;
3332 c.timeout = timeout;
3333 m_peer_change_queue.push_back(c);
3340 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3342 DSTACK(__FUNCTION_NAME);
3343 std::ostringstream os(std::ios_base::binary);
3345 writeU16(os, TOCLIENT_HP);
3349 std::string s = os.str();
3350 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3352 con.Send(peer_id, 0, data, true);
3355 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3356 const std::wstring &reason)
3358 DSTACK(__FUNCTION_NAME);
3359 std::ostringstream os(std::ios_base::binary);
3361 writeU16(os, TOCLIENT_ACCESS_DENIED);
3362 os<<serializeWideString(reason);
3365 std::string s = os.str();
3366 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3368 con.Send(peer_id, 0, data, true);
3371 void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
3372 bool set_camera_point_target, v3f camera_point_target)
3374 DSTACK(__FUNCTION_NAME);
3375 std::ostringstream os(std::ios_base::binary);
3377 writeU16(os, TOCLIENT_DEATHSCREEN);
3378 writeU8(os, set_camera_point_target);
3379 writeV3F1000(os, camera_point_target);
3382 std::string s = os.str();
3383 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3385 con.Send(peer_id, 0, data, true);
3388 void Server::SendItemDef(con::Connection &con, u16 peer_id,
3389 IItemDefManager *itemdef)
3391 DSTACK(__FUNCTION_NAME);
3392 std::ostringstream os(std::ios_base::binary);
3396 u32 length of the next item
3397 zlib-compressed serialized ItemDefManager
3399 writeU16(os, TOCLIENT_ITEMDEF);
3400 std::ostringstream tmp_os(std::ios::binary);
3401 itemdef->serialize(tmp_os);
3402 std::ostringstream tmp_os2(std::ios::binary);
3403 compressZlib(tmp_os.str(), tmp_os2);
3404 os<<serializeLongString(tmp_os2.str());
3407 std::string s = os.str();
3408 verbosestream<<"Server: Sending item definitions to id("<<peer_id
3409 <<"): size="<<s.size()<<std::endl;
3410 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3412 con.Send(peer_id, 0, data, true);
3415 void Server::SendNodeDef(con::Connection &con, u16 peer_id,
3416 INodeDefManager *nodedef)
3418 DSTACK(__FUNCTION_NAME);
3419 std::ostringstream os(std::ios_base::binary);
3423 u32 length of the next item
3424 zlib-compressed serialized NodeDefManager
3426 writeU16(os, TOCLIENT_NODEDEF);
3427 std::ostringstream tmp_os(std::ios::binary);
3428 nodedef->serialize(tmp_os);
3429 std::ostringstream tmp_os2(std::ios::binary);
3430 compressZlib(tmp_os.str(), tmp_os2);
3431 os<<serializeLongString(tmp_os2.str());
3434 std::string s = os.str();
3435 verbosestream<<"Server: Sending node definitions to id("<<peer_id
3436 <<"): size="<<s.size()<<std::endl;
3437 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3439 con.Send(peer_id, 0, data, true);
3443 Non-static send methods
3446 void Server::SendInventory(u16 peer_id)
3448 DSTACK(__FUNCTION_NAME);
3450 PlayerSAO *playersao = getPlayerSAO(peer_id);
3453 playersao->m_inventory_not_sent = false;
3459 std::ostringstream os;
3460 playersao->getInventory()->serialize(os);
3462 std::string s = os.str();
3464 SharedBuffer<u8> data(s.size()+2);
3465 writeU16(&data[0], TOCLIENT_INVENTORY);
3466 memcpy(&data[2], s.c_str(), s.size());
3469 m_con.Send(peer_id, 0, data, true);
3472 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3474 DSTACK(__FUNCTION_NAME);
3476 std::ostringstream os(std::ios_base::binary);
3480 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3481 os.write((char*)buf, 2);
3484 writeU16(buf, message.size());
3485 os.write((char*)buf, 2);
3488 for(u32 i=0; i<message.size(); i++)
3492 os.write((char*)buf, 2);
3496 std::string s = os.str();
3497 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3499 m_con.Send(peer_id, 0, data, true);
3502 void Server::BroadcastChatMessage(const std::wstring &message)
3504 for(core::map<u16, RemoteClient*>::Iterator
3505 i = m_clients.getIterator();
3506 i.atEnd() == false; i++)
3508 // Get client and check that it is valid
3509 RemoteClient *client = i.getNode()->getValue();
3510 assert(client->peer_id == i.getNode()->getKey());
3511 if(client->serialization_version == SER_FMT_VER_INVALID)
3514 SendChatMessage(client->peer_id, message);
3518 void Server::SendPlayerHP(u16 peer_id)
3520 DSTACK(__FUNCTION_NAME);
3521 PlayerSAO *playersao = getPlayerSAO(peer_id);
3523 playersao->m_hp_not_sent = false;
3524 SendHP(m_con, peer_id, playersao->getHP());
3527 void Server::SendMovePlayer(u16 peer_id)
3529 DSTACK(__FUNCTION_NAME);
3530 Player *player = m_env->getPlayer(peer_id);
3533 std::ostringstream os(std::ios_base::binary);
3534 writeU16(os, TOCLIENT_MOVE_PLAYER);
3535 writeV3F1000(os, player->getPosition());
3536 writeF1000(os, player->getPitch());
3537 writeF1000(os, player->getYaw());
3540 v3f pos = player->getPosition();
3541 f32 pitch = player->getPitch();
3542 f32 yaw = player->getYaw();
3543 verbosestream<<"Server: Sending TOCLIENT_MOVE_PLAYER"
3544 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3551 std::string s = os.str();
3552 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3554 m_con.Send(peer_id, 0, data, true);
3557 void Server::SendPlayerPrivileges(u16 peer_id)
3559 Player *player = m_env->getPlayer(peer_id);
3561 std::set<std::string> privs;
3562 scriptapi_get_auth(m_lua, player->getName(), NULL, &privs);
3564 std::ostringstream os(std::ios_base::binary);
3565 writeU16(os, TOCLIENT_PRIVILEGES);
3566 writeU16(os, privs.size());
3567 for(std::set<std::string>::const_iterator i = privs.begin();
3568 i != privs.end(); i++){
3569 os<<serializeString(*i);
3573 std::string s = os.str();
3574 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3576 m_con.Send(peer_id, 0, data, true);
3579 s32 Server::playSound(const SimpleSoundSpec &spec,
3580 const ServerSoundParams ¶ms)
3582 // Find out initial position of sound
3583 bool pos_exists = false;
3584 v3f pos = params.getPos(m_env, &pos_exists);
3585 // If position is not found while it should be, cancel sound
3586 if(pos_exists != (params.type != ServerSoundParams::SSP_LOCAL))
3588 // Filter destination clients
3589 std::set<RemoteClient*> dst_clients;
3590 if(params.to_player != "")
3592 Player *player = m_env->getPlayer(params.to_player.c_str());
3594 infostream<<"Server::playSound: Player \""<<params.to_player
3595 <<"\" not found"<<std::endl;
3598 if(player->peer_id == PEER_ID_INEXISTENT){
3599 infostream<<"Server::playSound: Player \""<<params.to_player
3600 <<"\" not connected"<<std::endl;
3603 RemoteClient *client = getClient(player->peer_id);
3604 dst_clients.insert(client);
3608 for(core::map<u16, RemoteClient*>::Iterator
3609 i = m_clients.getIterator(); i.atEnd() == false; i++)
3611 RemoteClient *client = i.getNode()->getValue();
3612 Player *player = m_env->getPlayer(client->peer_id);
3616 if(player->getPosition().getDistanceFrom(pos) >
3617 params.max_hear_distance)
3620 dst_clients.insert(client);
3623 if(dst_clients.size() == 0)
3626 s32 id = m_next_sound_id++;
3627 // The sound will exist as a reference in m_playing_sounds
3628 m_playing_sounds[id] = ServerPlayingSound();
3629 ServerPlayingSound &psound = m_playing_sounds[id];
3630 psound.params = params;
3631 for(std::set<RemoteClient*>::iterator i = dst_clients.begin();
3632 i != dst_clients.end(); i++)
3633 psound.clients.insert((*i)->peer_id);
3635 std::ostringstream os(std::ios_base::binary);
3636 writeU16(os, TOCLIENT_PLAY_SOUND);
3638 os<<serializeString(spec.name);
3639 writeF1000(os, spec.gain * params.gain);
3640 writeU8(os, params.type);
3641 writeV3F1000(os, pos);
3642 writeU16(os, params.object);
3643 writeU8(os, params.loop);
3645 std::string s = os.str();
3646 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3648 for(std::set<RemoteClient*>::iterator i = dst_clients.begin();
3649 i != dst_clients.end(); i++){
3651 m_con.Send((*i)->peer_id, 0, data, true);
3655 void Server::stopSound(s32 handle)
3657 // Get sound reference
3658 std::map<s32, ServerPlayingSound>::iterator i =
3659 m_playing_sounds.find(handle);
3660 if(i == m_playing_sounds.end())
3662 ServerPlayingSound &psound = i->second;
3664 std::ostringstream os(std::ios_base::binary);
3665 writeU16(os, TOCLIENT_STOP_SOUND);
3666 writeS32(os, handle);
3668 std::string s = os.str();
3669 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3671 for(std::set<u16>::iterator i = psound.clients.begin();
3672 i != psound.clients.end(); i++){
3674 m_con.Send(*i, 0, data, true);
3676 // Remove sound reference
3677 m_playing_sounds.erase(i);
3680 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3681 core::list<u16> *far_players, float far_d_nodes)
3683 float maxd = far_d_nodes*BS;
3684 v3f p_f = intToFloat(p, BS);
3688 SharedBuffer<u8> reply(replysize);
3689 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3690 writeS16(&reply[2], p.X);
3691 writeS16(&reply[4], p.Y);
3692 writeS16(&reply[6], p.Z);
3694 for(core::map<u16, RemoteClient*>::Iterator
3695 i = m_clients.getIterator();
3696 i.atEnd() == false; i++)
3698 // Get client and check that it is valid
3699 RemoteClient *client = i.getNode()->getValue();
3700 assert(client->peer_id == i.getNode()->getKey());
3701 if(client->serialization_version == SER_FMT_VER_INVALID)
3704 // Don't send if it's the same one
3705 if(client->peer_id == ignore_id)
3711 Player *player = m_env->getPlayer(client->peer_id);
3714 // If player is far away, only set modified blocks not sent
3715 v3f player_pos = player->getPosition();
3716 if(player_pos.getDistanceFrom(p_f) > maxd)
3718 far_players->push_back(client->peer_id);
3725 m_con.Send(client->peer_id, 0, reply, true);
3729 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3730 core::list<u16> *far_players, float far_d_nodes)
3732 float maxd = far_d_nodes*BS;
3733 v3f p_f = intToFloat(p, BS);
3735 for(core::map<u16, RemoteClient*>::Iterator
3736 i = m_clients.getIterator();
3737 i.atEnd() == false; i++)
3739 // Get client and check that it is valid
3740 RemoteClient *client = i.getNode()->getValue();
3741 assert(client->peer_id == i.getNode()->getKey());
3742 if(client->serialization_version == SER_FMT_VER_INVALID)
3745 // Don't send if it's the same one
3746 if(client->peer_id == ignore_id)
3752 Player *player = m_env->getPlayer(client->peer_id);
3755 // If player is far away, only set modified blocks not sent
3756 v3f player_pos = player->getPosition();
3757 if(player_pos.getDistanceFrom(p_f) > maxd)
3759 far_players->push_back(client->peer_id);
3766 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3767 SharedBuffer<u8> reply(replysize);
3768 writeU16(&reply[0], TOCLIENT_ADDNODE);
3769 writeS16(&reply[2], p.X);
3770 writeS16(&reply[4], p.Y);
3771 writeS16(&reply[6], p.Z);
3772 n.serialize(&reply[8], client->serialization_version);
3775 m_con.Send(client->peer_id, 0, reply, true);
3779 void Server::setBlockNotSent(v3s16 p)
3781 for(core::map<u16, RemoteClient*>::Iterator
3782 i = m_clients.getIterator();
3783 i.atEnd()==false; i++)
3785 RemoteClient *client = i.getNode()->getValue();
3786 client->SetBlockNotSent(p);
3790 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3792 DSTACK(__FUNCTION_NAME);
3794 v3s16 p = block->getPos();
3798 bool completely_air = true;
3799 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3800 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3801 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3803 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
3805 completely_air = false;
3806 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
3811 infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
3813 infostream<<"[completely air] ";
3814 infostream<<std::endl;
3818 Create a packet with the block in the right format
3821 std::ostringstream os(std::ios_base::binary);
3822 block->serialize(os, ver, false);
3823 std::string s = os.str();
3824 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3826 u32 replysize = 8 + blockdata.getSize();
3827 SharedBuffer<u8> reply(replysize);
3828 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3829 writeS16(&reply[2], p.X);
3830 writeS16(&reply[4], p.Y);
3831 writeS16(&reply[6], p.Z);
3832 memcpy(&reply[8], *blockdata, blockdata.getSize());
3834 /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3835 <<": \tpacket size: "<<replysize<<std::endl;*/
3840 m_con.Send(peer_id, 1, reply, true);
3843 void Server::SendBlocks(float dtime)
3845 DSTACK(__FUNCTION_NAME);
3847 JMutexAutoLock envlock(m_env_mutex);
3848 JMutexAutoLock conlock(m_con_mutex);
3850 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
3852 core::array<PrioritySortedBlockTransfer> queue;
3854 s32 total_sending = 0;
3857 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
3859 for(core::map<u16, RemoteClient*>::Iterator
3860 i = m_clients.getIterator();
3861 i.atEnd() == false; i++)
3863 RemoteClient *client = i.getNode()->getValue();
3864 assert(client->peer_id == i.getNode()->getKey());
3866 // If definitions and textures have not been sent, don't
3867 // send MapBlocks either
3868 if(!client->definitions_sent)
3871 total_sending += client->SendingCount();
3873 if(client->serialization_version == SER_FMT_VER_INVALID)
3876 client->GetNextBlocks(this, dtime, queue);
3881 // Lowest priority number comes first.
3882 // Lowest is most important.
3885 for(u32 i=0; i<queue.size(); i++)
3887 //TODO: Calculate limit dynamically
3888 if(total_sending >= g_settings->getS32
3889 ("max_simultaneous_block_sends_server_total"))
3892 PrioritySortedBlockTransfer q = queue[i];
3894 MapBlock *block = NULL;
3897 block = m_env->getMap().getBlockNoCreate(q.pos);
3899 catch(InvalidPositionException &e)
3904 RemoteClient *client = getClient(q.peer_id);
3906 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3908 client->SentBlock(q.pos);
3914 void Server::fillMediaCache()
3916 DSTACK(__FUNCTION_NAME);
3918 infostream<<"Server: Calculating media file checksums"<<std::endl;
3920 // Collect all media file paths
3921 std::list<std::string> paths;
3922 for(core::list<ModSpec>::Iterator i = m_mods.begin();
3923 i != m_mods.end(); i++){
3924 const ModSpec &mod = *i;
3925 paths.push_back(mod.path + DIR_DELIM + "textures");
3926 paths.push_back(mod.path + DIR_DELIM + "sounds");
3927 paths.push_back(mod.path + DIR_DELIM + "media");
3930 // Collect media file information from paths into cache
3931 for(std::list<std::string>::iterator i = paths.begin();
3932 i != paths.end(); i++)
3934 std::string mediapath = *i;
3935 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(mediapath);
3936 for(u32 j=0; j<dirlist.size(); j++){
3937 if(dirlist[j].dir) // Ignode dirs
3939 std::string filename = dirlist[j].name;
3940 // If name contains illegal characters, ignore the file
3941 if(!string_allowed(filename, TEXTURENAME_ALLOWED_CHARS)){
3942 infostream<<"Server: ignoring illegal file name: \""
3943 <<filename<<"\""<<std::endl;
3946 // If name is not in a supported format, ignore it
3947 const char *supported_ext[] = {
3948 ".png", ".jpg", ".bmp", ".tga",
3949 ".pcx", ".ppm", ".psd", ".wal", ".rgb",
3953 if(removeStringEnd(filename, supported_ext) == ""){
3954 infostream<<"Server: ignoring unsupported file extension: \""
3955 <<filename<<"\""<<std::endl;
3958 // Ok, attempt to load the file and add to cache
3959 std::string filepath = mediapath + DIR_DELIM + filename;
3961 std::ifstream fis(filepath.c_str(), std::ios_base::binary);
3962 if(fis.good() == false){
3963 errorstream<<"Server::fillMediaCache(): Could not open \""
3964 <<filename<<"\" for reading"<<std::endl;
3967 std::ostringstream tmp_os(std::ios_base::binary);
3971 fis.read(buf, 1024);
3972 std::streamsize len = fis.gcount();
3973 tmp_os.write(buf, len);
3982 errorstream<<"Server::fillMediaCache(): Failed to read \""
3983 <<filename<<"\""<<std::endl;
3986 if(tmp_os.str().length() == 0){
3987 errorstream<<"Server::fillMediaCache(): Empty file \""
3988 <<filepath<<"\""<<std::endl;
3993 sha1.addBytes(tmp_os.str().c_str(), tmp_os.str().length());
3995 unsigned char *digest = sha1.getDigest();
3996 std::string sha1_base64 = base64_encode(digest, 20);
3997 std::string sha1_hex = hex_encode((char*)digest, 20);
4001 this->m_media[filename] = MediaInfo(filepath, sha1_base64);
4002 verbosestream<<"Server: "<<sha1_hex<<" is "<<filename<<std::endl;
4007 struct SendableMediaAnnouncement
4010 std::string sha1_digest;
4012 SendableMediaAnnouncement(const std::string name_="",
4013 const std::string sha1_digest_=""):
4015 sha1_digest(sha1_digest_)
4019 void Server::sendMediaAnnouncement(u16 peer_id)
4021 DSTACK(__FUNCTION_NAME);
4023 verbosestream<<"Server: Announcing files to id("<<peer_id<<")"
4026 core::list<SendableMediaAnnouncement> file_announcements;
4028 for(std::map<std::string, MediaInfo>::iterator i = m_media.begin();
4029 i != m_media.end(); i++){
4031 file_announcements.push_back(
4032 SendableMediaAnnouncement(i->first, i->second.sha1_digest));
4036 std::ostringstream os(std::ios_base::binary);
4044 u16 length of sha1_digest
4049 writeU16(os, TOCLIENT_ANNOUNCE_MEDIA);
4050 writeU16(os, file_announcements.size());
4052 for(core::list<SendableMediaAnnouncement>::Iterator
4053 j = file_announcements.begin();
4054 j != file_announcements.end(); j++){
4055 os<<serializeString(j->name);
4056 os<<serializeString(j->sha1_digest);
4060 std::string s = os.str();
4061 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4064 m_con.Send(peer_id, 0, data, true);
4068 struct SendableMedia
4074 SendableMedia(const std::string &name_="", const std::string path_="",
4075 const std::string &data_=""):
4082 void Server::sendRequestedMedia(u16 peer_id,
4083 const core::list<MediaRequest> &tosend)
4085 DSTACK(__FUNCTION_NAME);
4087 verbosestream<<"Server::sendRequestedMedia(): "
4088 <<"Sending files to client"<<std::endl;
4092 // Put 5kB in one bunch (this is not accurate)
4093 u32 bytes_per_bunch = 5000;
4095 core::array< core::list<SendableMedia> > file_bunches;
4096 file_bunches.push_back(core::list<SendableMedia>());
4098 u32 file_size_bunch_total = 0;
4100 for(core::list<MediaRequest>::ConstIterator i = tosend.begin();
4101 i != tosend.end(); i++)
4103 if(m_media.find(i->name) == m_media.end()){
4104 errorstream<<"Server::sendRequestedMedia(): Client asked for "
4105 <<"unknown file \""<<(i->name)<<"\""<<std::endl;
4109 //TODO get path + name
4110 std::string tpath = m_media[(*i).name].path;
4113 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
4114 if(fis.good() == false){
4115 errorstream<<"Server::sendRequestedMedia(): Could not open \""
4116 <<tpath<<"\" for reading"<<std::endl;
4119 std::ostringstream tmp_os(std::ios_base::binary);
4123 fis.read(buf, 1024);
4124 std::streamsize len = fis.gcount();
4125 tmp_os.write(buf, len);
4126 file_size_bunch_total += len;
4135 errorstream<<"Server::sendRequestedMedia(): Failed to read \""
4136 <<(*i).name<<"\""<<std::endl;
4139 /*infostream<<"Server::sendRequestedMedia(): Loaded \""
4140 <<tname<<"\""<<std::endl;*/
4142 file_bunches[file_bunches.size()-1].push_back(
4143 SendableMedia((*i).name, tpath, tmp_os.str()));
4145 // Start next bunch if got enough data
4146 if(file_size_bunch_total >= bytes_per_bunch){
4147 file_bunches.push_back(core::list<SendableMedia>());
4148 file_size_bunch_total = 0;
4153 /* Create and send packets */
4155 u32 num_bunches = file_bunches.size();
4156 for(u32 i=0; i<num_bunches; i++)
4158 std::ostringstream os(std::ios_base::binary);
4162 u16 total number of texture bunches
4163 u16 index of this bunch
4164 u32 number of files in this bunch
4173 writeU16(os, TOCLIENT_MEDIA);
4174 writeU16(os, num_bunches);
4176 writeU32(os, file_bunches[i].size());
4178 for(core::list<SendableMedia>::Iterator
4179 j = file_bunches[i].begin();
4180 j != file_bunches[i].end(); j++){
4181 os<<serializeString(j->name);
4182 os<<serializeLongString(j->data);
4186 std::string s = os.str();
4187 verbosestream<<"Server::sendRequestedMedia(): bunch "
4188 <<i<<"/"<<num_bunches
4189 <<" files="<<file_bunches[i].size()
4190 <<" size=" <<s.size()<<std::endl;
4191 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4193 m_con.Send(peer_id, 0, data, true);
4201 void Server::DiePlayer(u16 peer_id)
4203 DSTACK(__FUNCTION_NAME);
4205 PlayerSAO *playersao = getPlayerSAO(peer_id);
4208 infostream<<"Server::DiePlayer(): Player "
4209 <<playersao->getPlayer()->getName()
4210 <<" dies"<<std::endl;
4212 playersao->setHP(0);
4214 // Trigger scripted stuff
4215 scriptapi_on_dieplayer(m_lua, playersao);
4217 SendPlayerHP(peer_id);
4218 SendDeathscreen(m_con, peer_id, false, v3f(0,0,0));
4221 void Server::RespawnPlayer(u16 peer_id)
4223 DSTACK(__FUNCTION_NAME);
4225 PlayerSAO *playersao = getPlayerSAO(peer_id);
4228 infostream<<"Server::RespawnPlayer(): Player "
4229 <<playersao->getPlayer()->getName()
4230 <<" respawns"<<std::endl;
4232 playersao->setHP(PLAYER_MAX_HP);
4234 bool repositioned = scriptapi_on_respawnplayer(m_lua, playersao);
4236 v3f pos = findSpawnPos(m_env->getServerMap());
4237 playersao->setPos(pos);
4241 void Server::UpdateCrafting(u16 peer_id)
4243 DSTACK(__FUNCTION_NAME);
4245 Player* player = m_env->getPlayer(peer_id);
4248 // Get a preview for crafting
4250 // No crafting in creative mode
4251 if(g_settings->getBool("creative_mode") == false)
4252 getCraftingResult(&player->inventory, preview, false, this);
4254 // Put the new preview in
4255 InventoryList *plist = player->inventory.getList("craftpreview");
4257 assert(plist->getSize() >= 1);
4258 plist->changeItem(0, preview);
4261 RemoteClient* Server::getClient(u16 peer_id)
4263 DSTACK(__FUNCTION_NAME);
4264 //JMutexAutoLock lock(m_con_mutex);
4265 core::map<u16, RemoteClient*>::Node *n;
4266 n = m_clients.find(peer_id);
4267 // A client should exist for all peers
4269 return n->getValue();
4272 std::wstring Server::getStatusString()
4274 std::wostringstream os(std::ios_base::binary);
4277 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4279 os<<L", uptime="<<m_uptime.get();
4280 // Information about clients
4282 for(core::map<u16, RemoteClient*>::Iterator
4283 i = m_clients.getIterator();
4284 i.atEnd() == false; i++)
4286 // Get client and check that it is valid
4287 RemoteClient *client = i.getNode()->getValue();
4288 assert(client->peer_id == i.getNode()->getKey());
4289 if(client->serialization_version == SER_FMT_VER_INVALID)
4292 Player *player = m_env->getPlayer(client->peer_id);
4293 // Get name of player
4294 std::wstring name = L"unknown";
4296 name = narrow_to_wide(player->getName());
4297 // Add name to information string
4301 if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
4302 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4303 if(g_settings->get("motd") != "")
4304 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4308 std::set<std::string> Server::getPlayerEffectivePrivs(const std::string &name)
4310 std::set<std::string> privs;
4311 scriptapi_get_auth(m_lua, name, NULL, &privs);
4315 bool Server::checkPriv(const std::string &name, const std::string &priv)
4317 std::set<std::string> privs = getPlayerEffectivePrivs(name);
4318 return (privs.count(priv) != 0);
4321 void Server::reportPrivsModified(const std::string &name)
4324 for(core::map<u16, RemoteClient*>::Iterator
4325 i = m_clients.getIterator();
4326 i.atEnd() == false; i++){
4327 RemoteClient *client = i.getNode()->getValue();
4328 Player *player = m_env->getPlayer(client->peer_id);
4329 reportPrivsModified(player->getName());
4332 Player *player = m_env->getPlayer(name.c_str());
4335 SendPlayerPrivileges(player->peer_id);
4336 PlayerSAO *sao = player->getPlayerSAO();
4339 sao->updatePrivileges(
4340 getPlayerEffectivePrivs(name),
4345 // Saves g_settings to configpath given at initialization
4346 void Server::saveConfig()
4348 if(m_path_config != "")
4349 g_settings->updateConfigFile(m_path_config.c_str());
4352 void Server::notifyPlayer(const char *name, const std::wstring msg)
4354 Player *player = m_env->getPlayer(name);
4357 SendChatMessage(player->peer_id, std::wstring(L"Server: -!- ")+msg);
4360 void Server::notifyPlayers(const std::wstring msg)
4362 BroadcastChatMessage(msg);
4365 void Server::queueBlockEmerge(v3s16 blockpos, bool allow_generate)
4369 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
4370 m_emerge_queue.addBlock(PEER_ID_INEXISTENT, blockpos, flags);
4373 // IGameDef interface
4375 IItemDefManager* Server::getItemDefManager()
4379 INodeDefManager* Server::getNodeDefManager()
4383 ICraftDefManager* Server::getCraftDefManager()
4387 ITextureSource* Server::getTextureSource()
4391 u16 Server::allocateUnknownNodeId(const std::string &name)
4393 return m_nodedef->allocateDummy(name);
4395 ISoundManager* Server::getSoundManager()
4397 return &dummySoundManager;
4399 MtEventManager* Server::getEventManager()
4404 IWritableItemDefManager* Server::getWritableItemDefManager()
4408 IWritableNodeDefManager* Server::getWritableNodeDefManager()
4412 IWritableCraftDefManager* Server::getWritableCraftDefManager()
4417 const ModSpec* Server::getModSpec(const std::string &modname)
4419 for(core::list<ModSpec>::Iterator i = m_mods.begin();
4420 i != m_mods.end(); i++){
4421 const ModSpec &mod = *i;
4422 if(mod.name == modname)
4427 std::string Server::getBuiltinLuaPath()
4429 return porting::path_share + DIR_DELIM + "builtin";
4432 v3f findSpawnPos(ServerMap &map)
4434 //return v3f(50,50,50)*BS;
4439 nodepos = v2s16(0,0);
4444 // Try to find a good place a few times
4445 for(s32 i=0; i<1000; i++)
4448 // We're going to try to throw the player to this position
4449 v2s16 nodepos2d = v2s16(-range + (myrand()%(range*2)),
4450 -range + (myrand()%(range*2)));
4451 //v2s16 sectorpos = getNodeSectorPos(nodepos2d);
4452 // Get ground height at point (fallbacks to heightmap function)
4453 s16 groundheight = map.findGroundLevel(nodepos2d);
4454 // Don't go underwater
4455 if(groundheight < WATER_LEVEL)
4457 //infostream<<"-> Underwater"<<std::endl;
4460 // Don't go to high places
4461 if(groundheight > WATER_LEVEL + 4)
4463 //infostream<<"-> Underwater"<<std::endl;
4467 nodepos = v3s16(nodepos2d.X, groundheight-2, nodepos2d.Y);
4468 bool is_good = false;
4470 for(s32 i=0; i<10; i++){
4471 v3s16 blockpos = getNodeBlockPos(nodepos);
4472 map.emergeBlock(blockpos, true);
4473 MapNode n = map.getNodeNoEx(nodepos);
4474 if(n.getContent() == CONTENT_AIR){
4485 // Found a good place
4486 //infostream<<"Searched through "<<i<<" places."<<std::endl;
4492 return intToFloat(nodepos, BS);
4495 PlayerSAO* Server::emergePlayer(const char *name, u16 peer_id)
4497 RemotePlayer *player = NULL;
4498 bool newplayer = false;
4501 Try to get an existing player
4503 player = static_cast<RemotePlayer*>(m_env->getPlayer(name));
4505 // If player is already connected, cancel
4506 if(player != NULL && player->peer_id != 0)
4508 infostream<<"emergePlayer(): Player already connected"<<std::endl;
4513 If player with the wanted peer_id already exists, cancel.
4515 if(m_env->getPlayer(peer_id) != NULL)
4517 infostream<<"emergePlayer(): Player with wrong name but same"
4518 " peer_id already exists"<<std::endl;
4523 Create a new player if it doesn't exist yet
4528 player = new RemotePlayer(this);
4529 player->updateName(name);
4531 /* Set player position */
4532 infostream<<"Server: Finding spawn place for player \""
4533 <<name<<"\""<<std::endl;
4534 v3f pos = findSpawnPos(m_env->getServerMap());
4535 player->setPosition(pos);
4537 /* Add player to environment */
4538 m_env->addPlayer(player);
4542 Create a new player active object
4544 PlayerSAO *playersao = new PlayerSAO(m_env, player, peer_id,
4545 getPlayerEffectivePrivs(player->getName()),
4548 /* Add object to environment */
4549 m_env->addActiveObject(playersao);
4553 scriptapi_on_newplayer(m_lua, playersao);
4555 scriptapi_on_joinplayer(m_lua, playersao);
4558 if(g_settings->getBool("creative_mode"))
4559 playersao->createCreativeInventory();
4564 void Server::handlePeerChange(PeerChange &c)
4566 JMutexAutoLock envlock(m_env_mutex);
4567 JMutexAutoLock conlock(m_con_mutex);
4569 if(c.type == PEER_ADDED)
4576 core::map<u16, RemoteClient*>::Node *n;
4577 n = m_clients.find(c.peer_id);
4578 // The client shouldn't already exist
4582 RemoteClient *client = new RemoteClient();
4583 client->peer_id = c.peer_id;
4584 m_clients.insert(client->peer_id, client);
4587 else if(c.type == PEER_REMOVED)
4594 core::map<u16, RemoteClient*>::Node *n;
4595 n = m_clients.find(c.peer_id);
4596 // The client should exist
4600 Mark objects to be not known by the client
4602 RemoteClient *client = n->getValue();
4604 for(core::map<u16, bool>::Iterator
4605 i = client->m_known_objects.getIterator();
4606 i.atEnd()==false; i++)
4609 u16 id = i.getNode()->getKey();
4610 ServerActiveObject* obj = m_env->getActiveObject(id);
4612 if(obj && obj->m_known_by_count > 0)
4613 obj->m_known_by_count--;
4617 Clear references to playing sounds
4619 for(std::map<s32, ServerPlayingSound>::iterator
4620 i = m_playing_sounds.begin();
4621 i != m_playing_sounds.end();)
4623 ServerPlayingSound &psound = i->second;
4624 psound.clients.erase(c.peer_id);
4625 if(psound.clients.size() == 0)
4626 m_playing_sounds.erase(i++);
4631 Player *player = m_env->getPlayer(c.peer_id);
4633 // Collect information about leaving in chat
4634 std::wstring message;
4638 std::wstring name = narrow_to_wide(player->getName());
4641 message += L" left game";
4643 message += L" (timed out)";
4647 /* Run scripts and remove from environment */
4651 PlayerSAO *playersao = player->getPlayerSAO();
4654 scriptapi_on_leaveplayer(m_lua, playersao);
4656 playersao->disconnected();
4666 std::ostringstream os(std::ios_base::binary);
4667 for(core::map<u16, RemoteClient*>::Iterator
4668 i = m_clients.getIterator();
4669 i.atEnd() == false; i++)
4671 RemoteClient *client = i.getNode()->getValue();
4672 assert(client->peer_id == i.getNode()->getKey());
4673 if(client->serialization_version == SER_FMT_VER_INVALID)
4676 Player *player = m_env->getPlayer(client->peer_id);
4679 // Get name of player
4680 os<<player->getName()<<" ";
4683 actionstream<<player->getName()<<" "
4684 <<(c.timeout?"times out.":"leaves game.")
4685 <<" List of players: "
4686 <<os.str()<<std::endl;
4691 delete m_clients[c.peer_id];
4692 m_clients.remove(c.peer_id);
4694 // Send player info to all remaining clients
4695 //SendPlayerInfos();
4697 // Send leave chat message to all remaining clients
4698 if(message.length() != 0)
4699 BroadcastChatMessage(message);
4708 void Server::handlePeerChanges()
4710 while(m_peer_change_queue.size() > 0)
4712 PeerChange c = m_peer_change_queue.pop_front();
4714 verbosestream<<"Server: Handling peer change: "
4715 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4718 handlePeerChange(c);
4722 void dedicated_server_loop(Server &server, bool &kill)
4724 DSTACK(__FUNCTION_NAME);
4726 verbosestream<<"dedicated_server_loop()"<<std::endl;
4728 IntervalLimiter m_profiler_interval;
4732 float steplen = g_settings->getFloat("dedicated_server_step");
4733 // This is kind of a hack but can be done like this
4734 // because server.step() is very light
4736 ScopeProfiler sp(g_profiler, "dedicated server sleep");
4737 sleep_ms((int)(steplen*1000.0));
4739 server.step(steplen);
4741 if(server.getShutdownRequested() || kill)
4743 infostream<<"Dedicated server quitting"<<std::endl;
4750 float profiler_print_interval =
4751 g_settings->getFloat("profiler_print_interval");
4752 if(profiler_print_interval != 0)
4754 if(m_profiler_interval.step(steplen, profiler_print_interval))
4756 infostream<<"Profiler:"<<std::endl;
4757 g_profiler->print(infostream);
4758 g_profiler->clear();