3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 #include "clientserver.h"
26 #include "jmutexautolock.h"
28 #include "constants.h"
30 #include "materials.h"
33 #include "servercommand.h"
35 #include "content_mapnode.h"
36 #include "content_nodemeta.h"
38 #include "serverobject.h"
43 #include "scriptapi.h"
47 #include "craftitemdef.h"
49 #include "content_abm.h"
52 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
54 #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
56 class MapEditEventIgnorer
59 MapEditEventIgnorer(bool *flag):
68 ~MapEditEventIgnorer()
81 void * ServerThread::Thread()
85 log_register_thread("ServerThread");
87 DSTACK(__FUNCTION_NAME);
89 BEGIN_DEBUG_EXCEPTION_HANDLER
94 //TimeTaker timer("AsyncRunStep() + Receive()");
97 //TimeTaker timer("AsyncRunStep()");
98 m_server->AsyncRunStep();
101 //infostream<<"Running m_server->Receive()"<<std::endl;
104 catch(con::NoIncomingDataException &e)
107 catch(con::PeerNotFoundException &e)
109 infostream<<"Server: PeerNotFoundException"<<std::endl;
113 END_DEBUG_EXCEPTION_HANDLER(errorstream)
118 void * EmergeThread::Thread()
122 log_register_thread("EmergeThread");
124 DSTACK(__FUNCTION_NAME);
126 BEGIN_DEBUG_EXCEPTION_HANDLER
128 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
131 Get block info from queue, emerge them and send them
134 After queue is empty, exit.
138 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
142 SharedPtr<QueuedBlockEmerge> q(qptr);
148 Do not generate over-limit
150 if(blockpos_over_limit(p))
153 //infostream<<"EmergeThread::Thread(): running"<<std::endl;
155 //TimeTaker timer("block emerge");
158 Try to emerge it from somewhere.
160 If it is only wanted as optional, only loading from disk
165 Check if any peer wants it as non-optional. In that case it
168 Also decrement the emerge queue count in clients.
171 bool only_from_disk = true;
174 core::map<u16, u8>::Iterator i;
175 for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
177 //u16 peer_id = i.getNode()->getKey();
180 u8 flags = i.getNode()->getValue();
181 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
182 only_from_disk = false;
187 if(enable_mapgen_debug_info)
188 infostream<<"EmergeThread: p="
189 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
190 <<"only_from_disk="<<only_from_disk<<std::endl;
192 ServerMap &map = ((ServerMap&)m_server->m_env->getMap());
194 MapBlock *block = NULL;
195 bool got_block = true;
196 core::map<v3s16, MapBlock*> modified_blocks;
199 Try to fetch block from memory or disk.
200 If not found and asked to generate, initialize generator.
203 bool started_generate = false;
204 mapgen::BlockMakeData data;
207 JMutexAutoLock envlock(m_server->m_env_mutex);
209 // Load sector if it isn't loaded
210 if(map.getSectorNoGenerateNoEx(p2d) == NULL)
211 map.loadSectorMeta(p2d);
213 // Attempt to load block
214 block = map.getBlockNoCreateNoEx(p);
215 if(!block || block->isDummy() || !block->isGenerated())
217 if(enable_mapgen_debug_info)
218 infostream<<"EmergeThread: not in memory, "
219 <<"attempting to load from disk"<<std::endl;
221 block = map.loadBlock(p);
224 // If could not load and allowed to generate, start generation
225 // inside this same envlock
226 if(only_from_disk == false &&
227 (block == NULL || block->isGenerated() == false)){
228 if(enable_mapgen_debug_info)
229 infostream<<"EmergeThread: generating"<<std::endl;
230 started_generate = true;
232 map.initBlockMake(&data, p);
237 If generator was initialized, generate now when envlock is free.
242 ScopeProfiler sp(g_profiler, "EmergeThread: mapgen::make_block",
244 TimeTaker t("mapgen::make_block()");
246 mapgen::make_block(&data);
248 if(enable_mapgen_debug_info == false)
249 t.stop(true); // Hide output
253 // Lock environment again to access the map
254 JMutexAutoLock envlock(m_server->m_env_mutex);
256 ScopeProfiler sp(g_profiler, "EmergeThread: after "
257 "mapgen::make_block (envlock)", SPT_AVG);
259 // Blit data back on map, update lighting, add mobs and
260 // whatever this does
261 map.finishBlockMake(&data, modified_blocks);
264 block = map.getBlockNoCreateNoEx(p);
266 // If block doesn't exist, don't try doing anything with it
267 // This happens if the block is not in generation boundaries
272 Do some post-generate stuff
275 v3s16 minp = block->getPos()*MAP_BLOCKSIZE;
276 v3s16 maxp = minp + v3s16(1,1,1)*(MAP_BLOCKSIZE-1);
277 scriptapi_environment_on_generated(m_server->m_lua,
280 if(enable_mapgen_debug_info)
281 infostream<<"EmergeThread: ended up with: "
282 <<analyze_block(block)<<std::endl;
285 Ignore map edit events, they will not need to be
286 sent to anybody because the block hasn't been sent
289 MapEditEventIgnorer ign(&m_server->m_ignore_map_edit_events);
291 // Activate objects and stuff
292 m_server->m_env->activateBlock(block, 0);
300 Set sent status of modified blocks on clients
303 // NOTE: Server's clients are also behind the connection mutex
304 JMutexAutoLock lock(m_server->m_con_mutex);
307 Add the originally fetched block to the modified list
311 modified_blocks.insert(p, block);
315 Set the modified blocks unsent for all the clients
318 for(core::map<u16, RemoteClient*>::Iterator
319 i = m_server->m_clients.getIterator();
320 i.atEnd() == false; i++)
322 RemoteClient *client = i.getNode()->getValue();
324 if(modified_blocks.size() > 0)
326 // Remove block from sent history
327 client->SetBlocksNotSent(modified_blocks);
333 END_DEBUG_EXCEPTION_HANDLER(errorstream)
335 log_deregister_thread();
340 void RemoteClient::GetNextBlocks(Server *server, float dtime,
341 core::array<PrioritySortedBlockTransfer> &dest)
343 DSTACK(__FUNCTION_NAME);
346 TimeTaker timer("RemoteClient::GetNextBlocks", &timer_result);*/
349 m_nothing_to_send_pause_timer -= dtime;
350 m_nearest_unsent_reset_timer += dtime;
352 if(m_nothing_to_send_pause_timer >= 0)
357 // Won't send anything if already sending
358 if(m_blocks_sending.size() >= g_settings->getU16
359 ("max_simultaneous_block_sends_per_client"))
361 //infostream<<"Not sending any blocks, Queue full."<<std::endl;
365 //TimeTaker timer("RemoteClient::GetNextBlocks");
367 Player *player = server->m_env->getPlayer(peer_id);
369 assert(player != NULL);
371 v3f playerpos = player->getPosition();
372 v3f playerspeed = player->getSpeed();
373 v3f playerspeeddir(0,0,0);
374 if(playerspeed.getLength() > 1.0*BS)
375 playerspeeddir = playerspeed / playerspeed.getLength();
376 // Predict to next block
377 v3f playerpos_predicted = playerpos + playerspeeddir*MAP_BLOCKSIZE*BS;
379 v3s16 center_nodepos = floatToInt(playerpos_predicted, BS);
381 v3s16 center = getNodeBlockPos(center_nodepos);
383 // Camera position and direction
384 v3f camera_pos = player->getEyePosition();
385 v3f camera_dir = v3f(0,0,1);
386 camera_dir.rotateYZBy(player->getPitch());
387 camera_dir.rotateXZBy(player->getYaw());
389 /*infostream<<"camera_dir=("<<camera_dir.X<<","<<camera_dir.Y<<","
390 <<camera_dir.Z<<")"<<std::endl;*/
393 Get the starting value of the block finder radius.
396 if(m_last_center != center)
398 m_nearest_unsent_d = 0;
399 m_last_center = center;
402 /*infostream<<"m_nearest_unsent_reset_timer="
403 <<m_nearest_unsent_reset_timer<<std::endl;*/
405 // Reset periodically to workaround for some bugs or stuff
406 if(m_nearest_unsent_reset_timer > 20.0)
408 m_nearest_unsent_reset_timer = 0;
409 m_nearest_unsent_d = 0;
410 //infostream<<"Resetting m_nearest_unsent_d for "
411 // <<server->getPlayerName(peer_id)<<std::endl;
414 //s16 last_nearest_unsent_d = m_nearest_unsent_d;
415 s16 d_start = m_nearest_unsent_d;
417 //infostream<<"d_start="<<d_start<<std::endl;
419 u16 max_simul_sends_setting = g_settings->getU16
420 ("max_simultaneous_block_sends_per_client");
421 u16 max_simul_sends_usually = max_simul_sends_setting;
424 Check the time from last addNode/removeNode.
426 Decrease send rate if player is building stuff.
428 m_time_from_building += dtime;
429 if(m_time_from_building < g_settings->getFloat(
430 "full_block_send_enable_min_time_from_building"))
432 max_simul_sends_usually
433 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
437 Number of blocks sending + number of blocks selected for sending
439 u32 num_blocks_selected = m_blocks_sending.size();
442 next time d will be continued from the d from which the nearest
443 unsent block was found this time.
445 This is because not necessarily any of the blocks found this
446 time are actually sent.
448 s32 new_nearest_unsent_d = -1;
450 s16 d_max = g_settings->getS16("max_block_send_distance");
451 s16 d_max_gen = g_settings->getS16("max_block_generate_distance");
453 // Don't loop very much at a time
454 s16 max_d_increment_at_time = 2;
455 if(d_max > d_start + max_d_increment_at_time)
456 d_max = d_start + max_d_increment_at_time;
457 /*if(d_max_gen > d_start+2)
458 d_max_gen = d_start+2;*/
460 //infostream<<"Starting from "<<d_start<<std::endl;
462 s32 nearest_emerged_d = -1;
463 s32 nearest_emergefull_d = -1;
464 s32 nearest_sent_d = -1;
465 bool queue_is_full = false;
468 for(d = d_start; d <= d_max; d++)
470 /*errorstream<<"checking d="<<d<<" for "
471 <<server->getPlayerName(peer_id)<<std::endl;*/
472 //infostream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
475 If m_nearest_unsent_d was changed by the EmergeThread
476 (it can change it to 0 through SetBlockNotSent),
478 Else update m_nearest_unsent_d
480 /*if(m_nearest_unsent_d != last_nearest_unsent_d)
482 d = m_nearest_unsent_d;
483 last_nearest_unsent_d = m_nearest_unsent_d;
487 Get the border/face dot coordinates of a "d-radiused"
490 core::list<v3s16> list;
491 getFacePositions(list, d);
493 core::list<v3s16>::Iterator li;
494 for(li=list.begin(); li!=list.end(); li++)
496 v3s16 p = *li + center;
500 - Don't allow too many simultaneous transfers
501 - EXCEPT when the blocks are very close
503 Also, don't send blocks that are already flying.
506 // Start with the usual maximum
507 u16 max_simul_dynamic = max_simul_sends_usually;
509 // If block is very close, allow full maximum
510 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
511 max_simul_dynamic = max_simul_sends_setting;
513 // Don't select too many blocks for sending
514 if(num_blocks_selected >= max_simul_dynamic)
516 queue_is_full = true;
517 goto queue_full_break;
520 // Don't send blocks that are currently being transferred
521 if(m_blocks_sending.find(p) != NULL)
527 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
528 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
529 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
530 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
531 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
532 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
535 // If this is true, inexistent block will be made from scratch
536 bool generate = d <= d_max_gen;
539 /*// Limit the generating area vertically to 2/3
540 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
543 // Limit the send area vertically to 1/2
544 if(abs(p.Y - center.Y) > d_max / 2)
550 If block is far away, don't generate it unless it is
556 // Block center y in nodes
557 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
558 // Don't generate if it's very high or very low
559 if(y < -64 || y > 64)
563 v2s16 p2d_nodes_center(
567 // Get ground height in nodes
568 s16 gh = server->m_env->getServerMap().findGroundLevel(
571 // If differs a lot, don't generate
572 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
574 // Actually, don't even send it
580 //infostream<<"d="<<d<<std::endl;
583 Don't generate or send if not in sight
584 FIXME This only works if the client uses a small enough
585 FOV setting. The default of 72 degrees is fine.
588 float camera_fov = (72.0*PI/180) * 4./3.;
589 if(isBlockInSight(p, camera_pos, camera_dir, camera_fov, 10000*BS) == false)
595 Don't send already sent blocks
598 if(m_blocks_sent.find(p) != NULL)
605 Check if map has this block
607 MapBlock *block = server->m_env->getMap().getBlockNoCreateNoEx(p);
609 bool surely_not_found_on_disk = false;
610 bool block_is_invalid = false;
613 // Reset usage timer, this block will be of use in the future.
614 block->resetUsageTimer();
616 // Block is dummy if data doesn't exist.
617 // It means it has been not found from disk and not generated
620 surely_not_found_on_disk = true;
623 // Block is valid if lighting is up-to-date and data exists
624 if(block->isValid() == false)
626 block_is_invalid = true;
629 /*if(block->isFullyGenerated() == false)
631 block_is_invalid = true;
636 ServerMap *map = (ServerMap*)(&server->m_env->getMap());
637 v2s16 chunkpos = map->sector_to_chunk(p2d);
638 if(map->chunkNonVolatile(chunkpos) == false)
639 block_is_invalid = true;
641 if(block->isGenerated() == false)
642 block_is_invalid = true;
645 If block is not close, don't send it unless it is near
648 Block is near ground level if night-time mesh
649 differs from day-time mesh.
653 if(block->dayNightDiffed() == false)
660 If block has been marked to not exist on disk (dummy)
661 and generating new ones is not wanted, skip block.
663 if(generate == false && surely_not_found_on_disk == true)
670 Add inexistent block to emerge queue.
672 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
674 //TODO: Get value from somewhere
675 // Allow only one block in emerge queue
676 //if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
677 // Allow two blocks in queue per client
678 //if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
680 // Make it more responsive when needing to generate stuff
681 if(surely_not_found_on_disk)
683 if(server->m_emerge_queue.peerItemCount(peer_id) < max_emerge)
685 //infostream<<"Adding block to emerge queue"<<std::endl;
687 // Add it to the emerge queue and trigger the thread
690 if(generate == false)
691 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
693 server->m_emerge_queue.addBlock(peer_id, p, flags);
694 server->m_emergethread.trigger();
696 if(nearest_emerged_d == -1)
697 nearest_emerged_d = d;
699 if(nearest_emergefull_d == -1)
700 nearest_emergefull_d = d;
707 if(nearest_sent_d == -1)
711 Add block to send queue
714 /*errorstream<<"sending from d="<<d<<" to "
715 <<server->getPlayerName(peer_id)<<std::endl;*/
717 PrioritySortedBlockTransfer q((float)d, p, peer_id);
721 num_blocks_selected += 1;
726 //infostream<<"Stopped at "<<d<<std::endl;
728 // If nothing was found for sending and nothing was queued for
729 // emerging, continue next time browsing from here
730 if(nearest_emerged_d != -1){
731 new_nearest_unsent_d = nearest_emerged_d;
732 } else if(nearest_emergefull_d != -1){
733 new_nearest_unsent_d = nearest_emergefull_d;
735 if(d > g_settings->getS16("max_block_send_distance")){
736 new_nearest_unsent_d = 0;
737 m_nothing_to_send_pause_timer = 2.0;
738 /*infostream<<"GetNextBlocks(): d wrapped around for "
739 <<server->getPlayerName(peer_id)
740 <<"; setting to 0 and pausing"<<std::endl;*/
742 if(nearest_sent_d != -1)
743 new_nearest_unsent_d = nearest_sent_d;
745 new_nearest_unsent_d = d;
749 if(new_nearest_unsent_d != -1)
750 m_nearest_unsent_d = new_nearest_unsent_d;
752 /*timer_result = timer.stop(true);
753 if(timer_result != 0)
754 infostream<<"GetNextBlocks duration: "<<timer_result<<" (!=0)"<<std::endl;*/
757 void RemoteClient::GotBlock(v3s16 p)
759 if(m_blocks_sending.find(p) != NULL)
760 m_blocks_sending.remove(p);
763 /*infostream<<"RemoteClient::GotBlock(): Didn't find in"
764 " m_blocks_sending"<<std::endl;*/
765 m_excess_gotblocks++;
767 m_blocks_sent.insert(p, true);
770 void RemoteClient::SentBlock(v3s16 p)
772 if(m_blocks_sending.find(p) == NULL)
773 m_blocks_sending.insert(p, 0.0);
775 infostream<<"RemoteClient::SentBlock(): Sent block"
776 " already in m_blocks_sending"<<std::endl;
779 void RemoteClient::SetBlockNotSent(v3s16 p)
781 m_nearest_unsent_d = 0;
783 if(m_blocks_sending.find(p) != NULL)
784 m_blocks_sending.remove(p);
785 if(m_blocks_sent.find(p) != NULL)
786 m_blocks_sent.remove(p);
789 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
791 m_nearest_unsent_d = 0;
793 for(core::map<v3s16, MapBlock*>::Iterator
794 i = blocks.getIterator();
795 i.atEnd()==false; i++)
797 v3s16 p = i.getNode()->getKey();
799 if(m_blocks_sending.find(p) != NULL)
800 m_blocks_sending.remove(p);
801 if(m_blocks_sent.find(p) != NULL)
802 m_blocks_sent.remove(p);
810 PlayerInfo::PlayerInfo()
816 void PlayerInfo::PrintLine(std::ostream *s)
819 (*s)<<"\""<<name<<"\" ("
820 <<(position.X/10)<<","<<(position.Y/10)
821 <<","<<(position.Z/10)<<") ";
823 (*s)<<" avg_rtt="<<avg_rtt;
827 u32 PIChecksum(core::list<PlayerInfo> &l)
829 core::list<PlayerInfo>::Iterator i;
832 for(i=l.begin(); i!=l.end(); i++)
834 checksum += a * (i->id+1);
835 checksum ^= 0x435aafcd;
846 std::string mapsavedir,
847 std::string configpath
850 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
851 m_authmanager(mapsavedir+DIR_DELIM+"auth.txt"),
852 m_banmanager(mapsavedir+DIR_DELIM+"ipban.txt"),
854 m_toolmgr(createToolDefManager()),
855 m_nodedef(createNodeDefManager()),
856 m_craftdef(createCraftDefManager()),
857 m_craftitemdef(createCraftItemDefManager()),
859 m_emergethread(this),
861 m_time_of_day_send_timer(0),
863 m_mapsavedir(mapsavedir),
864 m_configpath(configpath),
865 m_shutdown_requested(false),
866 m_ignore_map_edit_events(false),
867 m_ignore_map_edit_events_peer_id(0)
869 m_liquid_transform_timer = 0.0;
870 m_print_info_timer = 0.0;
871 m_objectdata_timer = 0.0;
872 m_emergethread_trigger_timer = 0.0;
873 m_savemap_timer = 0.0;
877 m_step_dtime_mutex.Init();
880 JMutexAutoLock envlock(m_env_mutex);
881 JMutexAutoLock conlock(m_con_mutex);
883 // Path to builtin.lua
884 std::string builtinpath = porting::path_data + DIR_DELIM + "builtin.lua";
886 // Add default global mod search path
887 m_modspaths.push_front(porting::path_data + DIR_DELIM + "mods");
888 // Add world mod search path
889 m_modspaths.push_front(mapsavedir + DIR_DELIM + "worldmods");
890 // Add user mod search path
891 m_modspaths.push_front(porting::path_userdata + DIR_DELIM + "usermods");
893 // Print out mod search paths
894 infostream<<"Mod search paths:"<<std::endl;
895 for(core::list<std::string>::Iterator i = m_modspaths.begin();
896 i != m_modspaths.end(); i++){
897 std::string modspath = *i;
898 infostream<<" "<<modspath<<std::endl;
901 // Initialize scripting
903 infostream<<"Server: Initializing scripting"<<std::endl;
904 m_lua = script_init();
907 scriptapi_export(m_lua, this);
908 // Load and run builtin.lua
909 infostream<<"Server: Loading builtin Lua stuff from \""<<builtinpath
911 bool success = scriptapi_loadmod(m_lua, builtinpath, "__builtin");
913 errorstream<<"Server: Failed to load and run "
914 <<builtinpath<<std::endl;
915 throw ModError("Failed to load and run "+builtinpath);
917 // Load and run "mod" scripts
918 m_mods = getMods(m_modspaths);
919 for(core::list<ModSpec>::Iterator i = m_mods.begin();
920 i != m_mods.end(); i++){
921 const ModSpec &mod = *i;
922 infostream<<"Server: Loading mod \""<<mod.name<<"\""<<std::endl;
923 std::string scriptpath = mod.path + DIR_DELIM + "init.lua";
924 bool success = scriptapi_loadmod(m_lua, scriptpath, mod.name);
926 errorstream<<"Server: Failed to load and run "
927 <<scriptpath<<std::endl;
928 throw ModError("Failed to load and run "+scriptpath);
932 // Initialize Environment
934 m_env = new ServerEnvironment(new ServerMap(mapsavedir, this), m_lua,
937 // Give environment reference to scripting api
938 scriptapi_add_environment(m_lua, m_env);
940 // Register us to receive map edit events
941 m_env->getMap().addEventReceiver(this);
943 // If file exists, load environment metadata
944 if(fs::PathExists(m_mapsavedir+DIR_DELIM+"env_meta.txt"))
946 infostream<<"Server: Loading environment metadata"<<std::endl;
947 m_env->loadMeta(m_mapsavedir);
951 infostream<<"Server: Loading players"<<std::endl;
952 m_env->deSerializePlayers(m_mapsavedir);
955 Add some test ActiveBlockModifiers to environment
957 add_legacy_abms(m_env, m_nodedef);
962 infostream<<"Server::~Server()"<<std::endl;
965 Send shutdown message
968 JMutexAutoLock conlock(m_con_mutex);
970 std::wstring line = L"*** Server shutting down";
973 Send the message to clients
975 for(core::map<u16, RemoteClient*>::Iterator
976 i = m_clients.getIterator();
977 i.atEnd() == false; i++)
979 // Get client and check that it is valid
980 RemoteClient *client = i.getNode()->getValue();
981 assert(client->peer_id == i.getNode()->getKey());
982 if(client->serialization_version == SER_FMT_VER_INVALID)
986 SendChatMessage(client->peer_id, line);
988 catch(con::PeerNotFoundException &e)
994 JMutexAutoLock envlock(m_env_mutex);
999 infostream<<"Server: Saving players"<<std::endl;
1000 m_env->serializePlayers(m_mapsavedir);
1003 Save environment metadata
1005 infostream<<"Server: Saving environment metadata"<<std::endl;
1006 m_env->saveMeta(m_mapsavedir);
1018 JMutexAutoLock clientslock(m_con_mutex);
1020 for(core::map<u16, RemoteClient*>::Iterator
1021 i = m_clients.getIterator();
1022 i.atEnd() == false; i++)
1025 // NOTE: These are removed by env destructor
1027 u16 peer_id = i.getNode()->getKey();
1028 JMutexAutoLock envlock(m_env_mutex);
1029 m_env->removePlayer(peer_id);
1033 delete i.getNode()->getValue();
1037 // Delete Environment
1043 delete m_craftitemdef;
1045 // Deinitialize scripting
1046 infostream<<"Server: Deinitializing scripting"<<std::endl;
1047 script_deinit(m_lua);
1050 void Server::start(unsigned short port)
1052 DSTACK(__FUNCTION_NAME);
1053 // Stop thread if already running
1056 // Initialize connection
1057 m_con.SetTimeoutMs(30);
1061 m_thread.setRun(true);
1064 infostream<<"Server: Started on port "<<port<<std::endl;
1069 DSTACK(__FUNCTION_NAME);
1071 infostream<<"Server: Stopping and waiting threads"<<std::endl;
1073 // Stop threads (set run=false first so both start stopping)
1074 m_thread.setRun(false);
1075 m_emergethread.setRun(false);
1077 m_emergethread.stop();
1079 infostream<<"Server: Threads stopped"<<std::endl;
1082 void Server::step(float dtime)
1084 DSTACK(__FUNCTION_NAME);
1089 JMutexAutoLock lock(m_step_dtime_mutex);
1090 m_step_dtime += dtime;
1094 void Server::AsyncRunStep()
1096 DSTACK(__FUNCTION_NAME);
1098 g_profiler->add("Server::AsyncRunStep (num)", 1);
1102 JMutexAutoLock lock1(m_step_dtime_mutex);
1103 dtime = m_step_dtime;
1107 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
1108 // Send blocks to clients
1115 g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
1117 //infostream<<"Server steps "<<dtime<<std::endl;
1118 //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1121 JMutexAutoLock lock1(m_step_dtime_mutex);
1122 m_step_dtime -= dtime;
1129 m_uptime.set(m_uptime.get() + dtime);
1133 // Process connection's timeouts
1134 JMutexAutoLock lock2(m_con_mutex);
1135 ScopeProfiler sp(g_profiler, "Server: connection timeout processing");
1136 m_con.RunTimeouts(dtime);
1140 // This has to be called so that the client list gets synced
1141 // with the peer list of the connection
1142 handlePeerChanges();
1146 Update m_time_of_day and overall game time
1149 JMutexAutoLock envlock(m_env_mutex);
1151 m_time_counter += dtime;
1152 f32 speed = g_settings->getFloat("time_speed") * 24000./(24.*3600);
1153 u32 units = (u32)(m_time_counter*speed);
1154 m_time_counter -= (f32)units / speed;
1156 m_env->setTimeOfDay((m_env->getTimeOfDay() + units) % 24000);
1158 //infostream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1161 Send to clients at constant intervals
1164 m_time_of_day_send_timer -= dtime;
1165 if(m_time_of_day_send_timer < 0.0)
1167 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
1169 //JMutexAutoLock envlock(m_env_mutex);
1170 JMutexAutoLock conlock(m_con_mutex);
1172 for(core::map<u16, RemoteClient*>::Iterator
1173 i = m_clients.getIterator();
1174 i.atEnd() == false; i++)
1176 RemoteClient *client = i.getNode()->getValue();
1177 //Player *player = m_env->getPlayer(client->peer_id);
1179 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1180 m_env->getTimeOfDay());
1182 m_con.Send(client->peer_id, 0, data, true);
1188 JMutexAutoLock lock(m_env_mutex);
1190 ScopeProfiler sp(g_profiler, "SEnv step");
1191 ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
1195 const float map_timer_and_unload_dtime = 2.92;
1196 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
1198 JMutexAutoLock lock(m_env_mutex);
1199 // Run Map's timers and unload unused data
1200 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
1201 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
1202 g_settings->getFloat("server_unload_unused_data_timeout"));
1213 JMutexAutoLock lock(m_env_mutex);
1214 JMutexAutoLock lock2(m_con_mutex);
1216 ScopeProfiler sp(g_profiler, "Server: handle players");
1218 //float player_max_speed = BS * 4.0; // Normal speed
1219 float player_max_speed = BS * 20; // Fast speed
1220 float player_max_speed_up = BS * 20;
1222 player_max_speed *= 2.5; // Tolerance
1223 player_max_speed_up *= 2.5;
1225 for(core::map<u16, RemoteClient*>::Iterator
1226 i = m_clients.getIterator();
1227 i.atEnd() == false; i++)
1229 RemoteClient *client = i.getNode()->getValue();
1230 ServerRemotePlayer *player =
1231 static_cast<ServerRemotePlayer*>
1232 (m_env->getPlayer(client->peer_id));
1237 Check player movements
1239 NOTE: Actually the server should handle player physics like the
1240 client does and compare player's position to what is calculated
1241 on our side. This is required when eg. players fly due to an
1244 player->m_last_good_position_age += dtime;
1245 if(player->m_last_good_position_age >= 2.0){
1246 float age = player->m_last_good_position_age;
1247 v3f diff = (player->getPosition() - player->m_last_good_position);
1248 float d_vert = diff.Y;
1250 float d_horiz = diff.getLength();
1251 /*infostream<<player->getName()<<"'s horizontal speed is "
1252 <<(d_horiz/age)<<std::endl;*/
1253 if(d_horiz <= age * player_max_speed &&
1254 (d_vert < 0 || d_vert < age * player_max_speed_up)){
1255 player->m_last_good_position = player->getPosition();
1257 actionstream<<"Player "<<player->getName()
1258 <<" moved too fast; resetting position"
1260 player->setPosition(player->m_last_good_position);
1261 SendMovePlayer(player);
1263 player->m_last_good_position_age = 0;
1267 Handle player HPs (die if hp=0)
1269 HandlePlayerHP(player, 0);
1272 Send player inventories and HPs if necessary
1274 if(player->m_inventory_not_sent){
1275 UpdateCrafting(player->peer_id);
1276 SendInventory(player->peer_id);
1278 if(player->m_hp_not_sent){
1279 SendPlayerHP(player);
1283 Add to environment if is not in respawn screen
1285 if(!player->m_is_in_environment && !player->m_respawn_active){
1286 player->m_removed = false;
1288 m_env->addActiveObject(player);
1293 /* Transform liquids */
1294 m_liquid_transform_timer += dtime;
1295 if(m_liquid_transform_timer >= 1.00)
1297 m_liquid_transform_timer -= 1.00;
1299 JMutexAutoLock lock(m_env_mutex);
1301 ScopeProfiler sp(g_profiler, "Server: liquid transform");
1303 core::map<v3s16, MapBlock*> modified_blocks;
1304 m_env->getMap().transformLiquids(modified_blocks);
1309 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1310 ServerMap &map = ((ServerMap&)m_env->getMap());
1311 map.updateLighting(modified_blocks, lighting_modified_blocks);
1313 // Add blocks modified by lighting to modified_blocks
1314 for(core::map<v3s16, MapBlock*>::Iterator
1315 i = lighting_modified_blocks.getIterator();
1316 i.atEnd() == false; i++)
1318 MapBlock *block = i.getNode()->getValue();
1319 modified_blocks.insert(block->getPos(), block);
1323 Set the modified blocks unsent for all the clients
1326 JMutexAutoLock lock2(m_con_mutex);
1328 for(core::map<u16, RemoteClient*>::Iterator
1329 i = m_clients.getIterator();
1330 i.atEnd() == false; i++)
1332 RemoteClient *client = i.getNode()->getValue();
1334 if(modified_blocks.size() > 0)
1336 // Remove block from sent history
1337 client->SetBlocksNotSent(modified_blocks);
1342 // Periodically print some info
1344 float &counter = m_print_info_timer;
1350 JMutexAutoLock lock2(m_con_mutex);
1352 if(m_clients.size() != 0)
1353 infostream<<"Players:"<<std::endl;
1354 for(core::map<u16, RemoteClient*>::Iterator
1355 i = m_clients.getIterator();
1356 i.atEnd() == false; i++)
1358 //u16 peer_id = i.getNode()->getKey();
1359 RemoteClient *client = i.getNode()->getValue();
1360 Player *player = m_env->getPlayer(client->peer_id);
1363 infostream<<"* "<<player->getName()<<"\t";
1364 client->PrintInfo(infostream);
1369 //if(g_settings->getBool("enable_experimental"))
1373 Check added and deleted active objects
1376 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
1377 JMutexAutoLock envlock(m_env_mutex);
1378 JMutexAutoLock conlock(m_con_mutex);
1380 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
1382 // Radius inside which objects are active
1383 s16 radius = g_settings->getS16("active_object_send_range_blocks");
1384 radius *= MAP_BLOCKSIZE;
1386 for(core::map<u16, RemoteClient*>::Iterator
1387 i = m_clients.getIterator();
1388 i.atEnd() == false; i++)
1390 RemoteClient *client = i.getNode()->getValue();
1392 // If definitions and textures have not been sent, don't
1393 // send objects either
1394 if(!client->definitions_sent)
1397 Player *player = m_env->getPlayer(client->peer_id);
1400 // This can happen if the client timeouts somehow
1401 /*infostream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
1403 <<" has no associated player"<<std::endl;*/
1406 v3s16 pos = floatToInt(player->getPosition(), BS);
1408 core::map<u16, bool> removed_objects;
1409 core::map<u16, bool> added_objects;
1410 m_env->getRemovedActiveObjects(pos, radius,
1411 client->m_known_objects, removed_objects);
1412 m_env->getAddedActiveObjects(pos, radius,
1413 client->m_known_objects, added_objects);
1415 // Ignore if nothing happened
1416 if(removed_objects.size() == 0 && added_objects.size() == 0)
1418 //infostream<<"active objects: none changed"<<std::endl;
1422 std::string data_buffer;
1426 // Handle removed objects
1427 writeU16((u8*)buf, removed_objects.size());
1428 data_buffer.append(buf, 2);
1429 for(core::map<u16, bool>::Iterator
1430 i = removed_objects.getIterator();
1431 i.atEnd()==false; i++)
1434 u16 id = i.getNode()->getKey();
1435 ServerActiveObject* obj = m_env->getActiveObject(id);
1437 // Add to data buffer for sending
1438 writeU16((u8*)buf, i.getNode()->getKey());
1439 data_buffer.append(buf, 2);
1441 // Remove from known objects
1442 client->m_known_objects.remove(i.getNode()->getKey());
1444 if(obj && obj->m_known_by_count > 0)
1445 obj->m_known_by_count--;
1448 // Handle added objects
1449 writeU16((u8*)buf, added_objects.size());
1450 data_buffer.append(buf, 2);
1451 for(core::map<u16, bool>::Iterator
1452 i = added_objects.getIterator();
1453 i.atEnd()==false; i++)
1456 u16 id = i.getNode()->getKey();
1457 ServerActiveObject* obj = m_env->getActiveObject(id);
1460 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1462 infostream<<"WARNING: "<<__FUNCTION_NAME
1463 <<": NULL object"<<std::endl;
1465 type = obj->getType();
1467 // Add to data buffer for sending
1468 writeU16((u8*)buf, id);
1469 data_buffer.append(buf, 2);
1470 writeU8((u8*)buf, type);
1471 data_buffer.append(buf, 1);
1474 data_buffer.append(serializeLongString(
1475 obj->getClientInitializationData()));
1477 data_buffer.append(serializeLongString(""));
1479 // Add to known objects
1480 client->m_known_objects.insert(i.getNode()->getKey(), false);
1483 obj->m_known_by_count++;
1487 SharedBuffer<u8> reply(2 + data_buffer.size());
1488 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1489 memcpy((char*)&reply[2], data_buffer.c_str(),
1490 data_buffer.size());
1492 m_con.Send(client->peer_id, 0, reply, true);
1494 infostream<<"Server: Sent object remove/add: "
1495 <<removed_objects.size()<<" removed, "
1496 <<added_objects.size()<<" added, "
1497 <<"packet size is "<<reply.getSize()<<std::endl;
1502 Collect a list of all the objects known by the clients
1503 and report it back to the environment.
1506 core::map<u16, bool> all_known_objects;
1508 for(core::map<u16, RemoteClient*>::Iterator
1509 i = m_clients.getIterator();
1510 i.atEnd() == false; i++)
1512 RemoteClient *client = i.getNode()->getValue();
1513 // Go through all known objects of client
1514 for(core::map<u16, bool>::Iterator
1515 i = client->m_known_objects.getIterator();
1516 i.atEnd()==false; i++)
1518 u16 id = i.getNode()->getKey();
1519 all_known_objects[id] = true;
1523 m_env->setKnownActiveObjects(whatever);
1529 Send object messages
1532 JMutexAutoLock envlock(m_env_mutex);
1533 JMutexAutoLock conlock(m_con_mutex);
1535 ScopeProfiler sp(g_profiler, "Server: sending object messages");
1538 // Value = data sent by object
1539 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1541 // Get active object messages from environment
1544 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
1548 core::list<ActiveObjectMessage>* message_list = NULL;
1549 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1550 n = buffered_messages.find(aom.id);
1553 message_list = new core::list<ActiveObjectMessage>;
1554 buffered_messages.insert(aom.id, message_list);
1558 message_list = n->getValue();
1560 message_list->push_back(aom);
1563 // Route data to every client
1564 for(core::map<u16, RemoteClient*>::Iterator
1565 i = m_clients.getIterator();
1566 i.atEnd()==false; i++)
1568 RemoteClient *client = i.getNode()->getValue();
1569 std::string reliable_data;
1570 std::string unreliable_data;
1571 // Go through all objects in message buffer
1572 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1573 j = buffered_messages.getIterator();
1574 j.atEnd()==false; j++)
1576 // If object is not known by client, skip it
1577 u16 id = j.getNode()->getKey();
1578 if(client->m_known_objects.find(id) == NULL)
1580 // Get message list of object
1581 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1582 // Go through every message
1583 for(core::list<ActiveObjectMessage>::Iterator
1584 k = list->begin(); k != list->end(); k++)
1586 // Compose the full new data with header
1587 ActiveObjectMessage aom = *k;
1588 std::string new_data;
1591 writeU16((u8*)&buf[0], aom.id);
1592 new_data.append(buf, 2);
1594 new_data += serializeString(aom.datastring);
1595 // Add data to buffer
1597 reliable_data += new_data;
1599 unreliable_data += new_data;
1603 reliable_data and unreliable_data are now ready.
1606 if(reliable_data.size() > 0)
1608 SharedBuffer<u8> reply(2 + reliable_data.size());
1609 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1610 memcpy((char*)&reply[2], reliable_data.c_str(),
1611 reliable_data.size());
1613 m_con.Send(client->peer_id, 0, reply, true);
1615 if(unreliable_data.size() > 0)
1617 SharedBuffer<u8> reply(2 + unreliable_data.size());
1618 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1619 memcpy((char*)&reply[2], unreliable_data.c_str(),
1620 unreliable_data.size());
1621 // Send as unreliable
1622 m_con.Send(client->peer_id, 0, reply, false);
1625 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1627 infostream<<"Server: Size of object message data: "
1628 <<"reliable: "<<reliable_data.size()
1629 <<", unreliable: "<<unreliable_data.size()
1634 // Clear buffered_messages
1635 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1636 i = buffered_messages.getIterator();
1637 i.atEnd()==false; i++)
1639 delete i.getNode()->getValue();
1643 } // enable_experimental
1646 Send queued-for-sending map edit events.
1649 // Don't send too many at a time
1652 // Single change sending is disabled if queue size is not small
1653 bool disable_single_change_sending = false;
1654 if(m_unsent_map_edit_queue.size() >= 4)
1655 disable_single_change_sending = true;
1657 bool got_any_events = false;
1659 // We'll log the amount of each
1662 while(m_unsent_map_edit_queue.size() != 0)
1664 got_any_events = true;
1666 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1668 // Players far away from the change are stored here.
1669 // Instead of sending the changes, MapBlocks are set not sent
1671 core::list<u16> far_players;
1673 if(event->type == MEET_ADDNODE)
1675 //infostream<<"Server: MEET_ADDNODE"<<std::endl;
1676 prof.add("MEET_ADDNODE", 1);
1677 if(disable_single_change_sending)
1678 sendAddNode(event->p, event->n, event->already_known_by_peer,
1681 sendAddNode(event->p, event->n, event->already_known_by_peer,
1684 else if(event->type == MEET_REMOVENODE)
1686 //infostream<<"Server: MEET_REMOVENODE"<<std::endl;
1687 prof.add("MEET_REMOVENODE", 1);
1688 if(disable_single_change_sending)
1689 sendRemoveNode(event->p, event->already_known_by_peer,
1692 sendRemoveNode(event->p, event->already_known_by_peer,
1695 else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
1697 infostream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
1698 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
1699 setBlockNotSent(event->p);
1701 else if(event->type == MEET_OTHER)
1703 infostream<<"Server: MEET_OTHER"<<std::endl;
1704 prof.add("MEET_OTHER", 1);
1705 for(core::map<v3s16, bool>::Iterator
1706 i = event->modified_blocks.getIterator();
1707 i.atEnd()==false; i++)
1709 v3s16 p = i.getNode()->getKey();
1715 prof.add("unknown", 1);
1716 infostream<<"WARNING: Server: Unknown MapEditEvent "
1717 <<((u32)event->type)<<std::endl;
1721 Set blocks not sent to far players
1723 if(far_players.size() > 0)
1725 // Convert list format to that wanted by SetBlocksNotSent
1726 core::map<v3s16, MapBlock*> modified_blocks2;
1727 for(core::map<v3s16, bool>::Iterator
1728 i = event->modified_blocks.getIterator();
1729 i.atEnd()==false; i++)
1731 v3s16 p = i.getNode()->getKey();
1732 modified_blocks2.insert(p,
1733 m_env->getMap().getBlockNoCreateNoEx(p));
1735 // Set blocks not sent
1736 for(core::list<u16>::Iterator
1737 i = far_players.begin();
1738 i != far_players.end(); i++)
1741 RemoteClient *client = getClient(peer_id);
1744 client->SetBlocksNotSent(modified_blocks2);
1750 /*// Don't send too many at a time
1752 if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
1758 infostream<<"Server: MapEditEvents:"<<std::endl;
1759 prof.print(infostream);
1765 Trigger emergethread (it somehow gets to a non-triggered but
1766 bysy state sometimes)
1769 float &counter = m_emergethread_trigger_timer;
1775 m_emergethread.trigger();
1779 // Save map, players and auth stuff
1781 float &counter = m_savemap_timer;
1783 if(counter >= g_settings->getFloat("server_map_save_interval"))
1787 ScopeProfiler sp(g_profiler, "Server: saving stuff");
1790 if(m_authmanager.isModified())
1791 m_authmanager.save();
1794 if(m_banmanager.isModified())
1795 m_banmanager.save();
1798 JMutexAutoLock lock(m_env_mutex);
1800 // Save changed parts of map
1801 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
1804 m_env->serializePlayers(m_mapsavedir);
1806 // Save environment metadata
1807 m_env->saveMeta(m_mapsavedir);
1812 void Server::Receive()
1814 DSTACK(__FUNCTION_NAME);
1815 SharedBuffer<u8> data;
1820 JMutexAutoLock conlock(m_con_mutex);
1821 datasize = m_con.Receive(peer_id, data);
1824 // This has to be called so that the client list gets synced
1825 // with the peer list of the connection
1826 handlePeerChanges();
1828 ProcessData(*data, datasize, peer_id);
1830 catch(con::InvalidIncomingDataException &e)
1832 infostream<<"Server::Receive(): "
1833 "InvalidIncomingDataException: what()="
1834 <<e.what()<<std::endl;
1836 catch(con::PeerNotFoundException &e)
1838 //NOTE: This is not needed anymore
1840 // The peer has been disconnected.
1841 // Find the associated player and remove it.
1843 /*JMutexAutoLock envlock(m_env_mutex);
1845 infostream<<"ServerThread: peer_id="<<peer_id
1846 <<" has apparently closed connection. "
1847 <<"Removing player."<<std::endl;
1849 m_env->removePlayer(peer_id);*/
1853 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1855 DSTACK(__FUNCTION_NAME);
1856 // Environment is locked first.
1857 JMutexAutoLock envlock(m_env_mutex);
1858 JMutexAutoLock conlock(m_con_mutex);
1861 Address address = m_con.GetPeerAddress(peer_id);
1863 // drop player if is ip is banned
1864 if(m_banmanager.isIpBanned(address.serializeString())){
1865 SendAccessDenied(m_con, peer_id,
1866 L"Your ip is banned. Banned name was "
1867 +narrow_to_wide(m_banmanager.getBanName(
1868 address.serializeString())));
1869 m_con.DeletePeer(peer_id);
1873 catch(con::PeerNotFoundException &e)
1875 infostream<<"Server::ProcessData(): Cancelling: peer "
1876 <<peer_id<<" not found"<<std::endl;
1880 u8 peer_ser_ver = getClient(peer_id)->serialization_version;
1888 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1890 if(command == TOSERVER_INIT)
1892 // [0] u16 TOSERVER_INIT
1893 // [2] u8 SER_FMT_VER_HIGHEST
1894 // [3] u8[20] player_name
1895 // [23] u8[28] password <--- can be sent without this, from old versions
1897 if(datasize < 2+1+PLAYERNAME_SIZE)
1900 infostream<<"Server: Got TOSERVER_INIT from "
1901 <<peer_id<<std::endl;
1903 // First byte after command is maximum supported
1904 // serialization version
1905 u8 client_max = data[2];
1906 u8 our_max = SER_FMT_VER_HIGHEST;
1907 // Use the highest version supported by both
1908 u8 deployed = core::min_(client_max, our_max);
1909 // If it's lower than the lowest supported, give up.
1910 if(deployed < SER_FMT_VER_LOWEST)
1911 deployed = SER_FMT_VER_INVALID;
1913 //peer->serialization_version = deployed;
1914 getClient(peer_id)->pending_serialization_version = deployed;
1916 if(deployed == SER_FMT_VER_INVALID)
1918 infostream<<"Server: Cannot negotiate "
1919 "serialization version with peer "
1920 <<peer_id<<std::endl;
1921 SendAccessDenied(m_con, peer_id, std::wstring(
1922 L"Your client's version is not supported.\n"
1923 L"Server version is ")
1924 + narrow_to_wide(VERSION_STRING) + L"."
1930 Read and check network protocol version
1933 u16 net_proto_version = 0;
1934 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
1936 net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
1939 getClient(peer_id)->net_proto_version = net_proto_version;
1941 if(net_proto_version == 0)
1943 SendAccessDenied(m_con, peer_id, std::wstring(
1944 L"Your client's version is not supported.\n"
1945 L"Server version is ")
1946 + narrow_to_wide(VERSION_STRING) + L"."
1951 if(g_settings->getBool("strict_protocol_version_checking"))
1953 if(net_proto_version != PROTOCOL_VERSION)
1955 SendAccessDenied(m_con, peer_id, std::wstring(
1956 L"Your client's version is not supported.\n"
1957 L"Server version is ")
1958 + narrow_to_wide(VERSION_STRING) + L",\n"
1959 + L"server's PROTOCOL_VERSION is "
1960 + narrow_to_wide(itos(PROTOCOL_VERSION))
1961 + L", client's PROTOCOL_VERSION is "
1962 + narrow_to_wide(itos(net_proto_version))
1973 char playername[PLAYERNAME_SIZE];
1974 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
1976 playername[i] = data[3+i];
1978 playername[PLAYERNAME_SIZE-1] = 0;
1980 if(playername[0]=='\0')
1982 infostream<<"Server: Player has empty name"<<std::endl;
1983 SendAccessDenied(m_con, peer_id,
1988 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
1990 infostream<<"Server: Player has invalid name"<<std::endl;
1991 SendAccessDenied(m_con, peer_id,
1992 L"Name contains unallowed characters");
1997 char password[PASSWORD_SIZE];
1998 if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
2000 // old version - assume blank password
2005 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2007 password[i] = data[23+i];
2009 password[PASSWORD_SIZE-1] = 0;
2012 // Add player to auth manager
2013 if(m_authmanager.exists(playername) == false)
2015 std::wstring default_password =
2016 narrow_to_wide(g_settings->get("default_password"));
2017 std::string translated_default_password =
2018 translatePassword(playername, default_password);
2020 // If default_password is empty, allow any initial password
2021 if (default_password.length() == 0)
2022 translated_default_password = password;
2024 infostream<<"Server: adding player "<<playername
2025 <<" to auth manager"<<std::endl;
2026 m_authmanager.add(playername);
2027 m_authmanager.setPassword(playername, translated_default_password);
2028 m_authmanager.setPrivs(playername,
2029 stringToPrivs(g_settings->get("default_privs")));
2030 m_authmanager.save();
2033 std::string checkpwd = m_authmanager.getPassword(playername);
2035 /*infostream<<"Server: Client gave password '"<<password
2036 <<"', the correct one is '"<<checkpwd<<"'"<<std::endl;*/
2038 if(password != checkpwd)
2040 infostream<<"Server: peer_id="<<peer_id
2041 <<": supplied invalid password for "
2042 <<playername<<std::endl;
2043 SendAccessDenied(m_con, peer_id, L"Invalid password");
2047 // Enforce user limit.
2048 // Don't enforce for users that have some admin right
2049 if(m_clients.size() >= g_settings->getU16("max_users") &&
2050 (m_authmanager.getPrivs(playername)
2051 & (PRIV_SERVER|PRIV_BAN|PRIV_PRIVS|PRIV_PASSWORD)) == 0 &&
2052 playername != g_settings->get("name"))
2054 SendAccessDenied(m_con, peer_id, L"Too many users.");
2059 ServerRemotePlayer *player = emergePlayer(playername, peer_id);
2061 // If failed, cancel
2064 infostream<<"Server: peer_id="<<peer_id
2065 <<": failed to emerge player"<<std::endl;
2070 Answer with a TOCLIENT_INIT
2073 SharedBuffer<u8> reply(2+1+6+8);
2074 writeU16(&reply[0], TOCLIENT_INIT);
2075 writeU8(&reply[2], deployed);
2076 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
2077 writeU64(&reply[2+1+6], m_env->getServerMap().getSeed());
2080 m_con.Send(peer_id, 0, reply, true);
2084 Send complete position information
2086 SendMovePlayer(player);
2091 if(command == TOSERVER_INIT2)
2093 infostream<<"Server: Got TOSERVER_INIT2 from "
2094 <<peer_id<<std::endl;
2097 getClient(peer_id)->serialization_version
2098 = getClient(peer_id)->pending_serialization_version;
2101 Send some initialization data
2104 // Send tool definitions
2105 SendToolDef(m_con, peer_id, m_toolmgr);
2107 // Send node definitions
2108 SendNodeDef(m_con, peer_id, m_nodedef);
2110 // Send CraftItem definitions
2111 SendCraftItemDef(m_con, peer_id, m_craftitemdef);
2114 SendTextures(peer_id);
2116 // Send player info to all players
2117 //SendPlayerInfos();
2119 // Send inventory to player
2120 UpdateCrafting(peer_id);
2121 SendInventory(peer_id);
2123 // Send player items to all players
2126 Player *player = m_env->getPlayer(peer_id);
2129 SendPlayerHP(player);
2133 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2134 m_env->getTimeOfDay());
2135 m_con.Send(peer_id, 0, data, true);
2138 // Now the client should know about everything
2139 getClient(peer_id)->definitions_sent = true;
2141 // Send information about server to player in chat
2142 SendChatMessage(peer_id, getStatusString());
2144 // Send information about joining in chat
2146 std::wstring name = L"unknown";
2147 Player *player = m_env->getPlayer(peer_id);
2149 name = narrow_to_wide(player->getName());
2151 std::wstring message;
2154 message += L" joined game";
2155 BroadcastChatMessage(message);
2158 // Warnings about protocol version can be issued here
2159 if(getClient(peer_id)->net_proto_version < PROTOCOL_VERSION)
2161 SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT IS OLD AND MAY WORK PROPERLY WITH THIS SERVER");
2165 Check HP, respawn if necessary
2167 HandlePlayerHP(player, 0);
2173 std::ostringstream os(std::ios_base::binary);
2174 for(core::map<u16, RemoteClient*>::Iterator
2175 i = m_clients.getIterator();
2176 i.atEnd() == false; i++)
2178 RemoteClient *client = i.getNode()->getValue();
2179 assert(client->peer_id == i.getNode()->getKey());
2180 if(client->serialization_version == SER_FMT_VER_INVALID)
2183 Player *player = m_env->getPlayer(client->peer_id);
2186 // Get name of player
2187 os<<player->getName()<<" ";
2190 actionstream<<player->getName()<<" joins game. List of players: "
2191 <<os.str()<<std::endl;
2197 if(peer_ser_ver == SER_FMT_VER_INVALID)
2199 infostream<<"Server::ProcessData(): Cancelling: Peer"
2200 " serialization format invalid or not initialized."
2201 " Skipping incoming command="<<command<<std::endl;
2205 Player *player = m_env->getPlayer(peer_id);
2206 ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
2209 infostream<<"Server::ProcessData(): Cancelling: "
2210 "No player for peer_id="<<peer_id
2214 if(command == TOSERVER_PLAYERPOS)
2216 if(datasize < 2+12+12+4+4)
2220 v3s32 ps = readV3S32(&data[start+2]);
2221 v3s32 ss = readV3S32(&data[start+2+12]);
2222 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2223 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2224 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2225 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2226 pitch = wrapDegrees(pitch);
2227 yaw = wrapDegrees(yaw);
2229 player->setPosition(position);
2230 player->setSpeed(speed);
2231 player->setPitch(pitch);
2232 player->setYaw(yaw);
2234 /*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2235 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2236 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2238 else if(command == TOSERVER_GOTBLOCKS)
2251 u16 count = data[2];
2252 for(u16 i=0; i<count; i++)
2254 if((s16)datasize < 2+1+(i+1)*6)
2255 throw con::InvalidIncomingDataException
2256 ("GOTBLOCKS length is too short");
2257 v3s16 p = readV3S16(&data[2+1+i*6]);
2258 /*infostream<<"Server: GOTBLOCKS ("
2259 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2260 RemoteClient *client = getClient(peer_id);
2261 client->GotBlock(p);
2264 else if(command == TOSERVER_DELETEDBLOCKS)
2277 u16 count = data[2];
2278 for(u16 i=0; i<count; i++)
2280 if((s16)datasize < 2+1+(i+1)*6)
2281 throw con::InvalidIncomingDataException
2282 ("DELETEDBLOCKS length is too short");
2283 v3s16 p = readV3S16(&data[2+1+i*6]);
2284 /*infostream<<"Server: DELETEDBLOCKS ("
2285 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2286 RemoteClient *client = getClient(peer_id);
2287 client->SetBlockNotSent(p);
2290 else if(command == TOSERVER_CLICK_OBJECT)
2292 infostream<<"Server: CLICK_OBJECT not supported anymore"<<std::endl;
2295 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2297 infostream<<"Server: CLICK_ACTIVEOBJECT not supported anymore"<<std::endl;
2300 else if(command == TOSERVER_GROUND_ACTION)
2302 infostream<<"Server: GROUND_ACTION not supported anymore"<<std::endl;
2306 else if(command == TOSERVER_RELEASE)
2308 infostream<<"Server: RELEASE not supported anymore"<<std::endl;
2311 else if(command == TOSERVER_SIGNTEXT)
2313 infostream<<"Server: SIGNTEXT not supported anymore"
2317 else if(command == TOSERVER_SIGNNODETEXT)
2319 if((getPlayerPrivs(player) & PRIV_INTERACT) == 0)
2327 std::string datastring((char*)&data[2], datasize-2);
2328 std::istringstream is(datastring, std::ios_base::binary);
2331 is.read((char*)buf, 6);
2332 v3s16 p = readV3S16(buf);
2333 is.read((char*)buf, 2);
2334 u16 textlen = readU16(buf);
2336 for(u16 i=0; i<textlen; i++)
2338 is.read((char*)buf, 1);
2339 text += (char)buf[0];
2342 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
2346 meta->setText(text);
2348 actionstream<<player->getName()<<" writes \""<<text<<"\" to sign"
2349 <<" at "<<PP(p)<<std::endl;
2351 v3s16 blockpos = getNodeBlockPos(p);
2352 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
2355 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2359 setBlockNotSent(blockpos);
2361 else if(command == TOSERVER_INVENTORY_ACTION)
2363 /*// Ignore inventory changes if in creative mode
2364 if(g_settings->getBool("creative_mode") == true)
2366 infostream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2370 // Strip command and create a stream
2371 std::string datastring((char*)&data[2], datasize-2);
2372 infostream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2373 std::istringstream is(datastring, std::ios_base::binary);
2375 InventoryAction *a = InventoryAction::deSerialize(is);
2378 infostream<<"TOSERVER_INVENTORY_ACTION: "
2379 <<"InventoryAction::deSerialize() returned NULL"
2385 c.current_player = player;
2388 Handle restrictions and special cases of the move action
2390 if(a->getType() == IACTION_MOVE
2391 && g_settings->getBool("creative_mode") == false)
2393 InventoryList *rlist = player->inventory.getList("craftresult");
2395 InventoryList *clist = player->inventory.getList("craft");
2397 InventoryList *mlist = player->inventory.getList("main");
2400 IMoveAction *ma = (IMoveAction*)a;
2403 Disable moving items into craftresult from elsewhere
2405 if(ma->to_inv == "current_player"
2406 && ma->to_list == "craftresult"
2407 && (ma->from_inv != "current_player"
2408 || ma->from_list != "craftresult"))
2410 infostream<<"Ignoring IMoveAction from "
2411 <<ma->from_inv<<":"<<ma->from_list
2412 <<" to "<<ma->to_inv<<":"<<ma->to_list
2413 <<" because dst is craftresult"
2414 <<" and src isn't craftresult"<<std::endl;
2420 Handle crafting (source is craftresult, which is preview)
2422 if(ma->from_inv == "current_player"
2423 && ma->from_list == "craftresult"
2424 && player->craftresult_is_preview)
2427 If the craftresult is placed on itself, crafting takes
2428 place and result is moved into main list
2430 if(ma->to_inv == "current_player"
2431 && ma->to_list == "craftresult")
2433 // Except if main list doesn't have free slots
2434 if(mlist->getFreeSlots() == 0){
2435 infostream<<"Cannot craft: Main list doesn't have"
2436 <<" free slots"<<std::endl;
2441 player->craftresult_is_preview = false;
2442 clist->decrementMaterials(1);
2444 InventoryItem *item1 = rlist->changeItem(0, NULL);
2445 mlist->addItem(item1);
2447 srp->m_inventory_not_sent = true;
2453 Disable action if there are no free slots in
2456 If the item is placed on an item that is not of the
2457 same kind, the existing item will be first moved to
2458 craftresult and immediately moved to the free slot.
2461 Inventory *inv_to = getInventory(&c, ma->to_inv);
2463 InventoryList *list_to = inv_to->getList(ma->to_list);
2465 if(list_to->getFreeSlots() == 0){
2466 infostream<<"Cannot craft: Destination doesn't have"
2467 <<" free slots"<<std::endl;
2471 }while(0); // Allow break
2476 player->craftresult_is_preview = false;
2477 clist->decrementMaterials(1);
2479 /* Print out action */
2480 InventoryItem *item = rlist->getItem(0);
2481 std::string itemstring = "NULL";
2483 itemstring = item->getItemString();
2484 actionstream<<player->getName()<<" crafts "
2485 <<itemstring<<std::endl;
2488 a->apply(&c, this, m_env);
2498 // Disallow moving items in elsewhere than player's inventory
2499 // if not allowed to build
2500 if((getPlayerPrivs(player) & PRIV_INTERACT) == 0
2501 && (ma->from_inv != "current_player"
2502 || ma->to_inv != "current_player"))
2504 infostream<<"Cannot move outside of player's inventory: "
2505 <<"No build privilege"<<std::endl;
2510 // If player is not an admin, check for ownership of src
2511 if(ma->from_inv != "current_player"
2512 && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
2514 Strfnd fn(ma->from_inv);
2515 std::string id0 = fn.next(":");
2516 if(id0 == "nodemeta")
2519 p.X = stoi(fn.next(","));
2520 p.Y = stoi(fn.next(","));
2521 p.Z = stoi(fn.next(","));
2522 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
2523 if(meta->getOwner() != "" &&
2524 meta->getOwner() != player->getName())
2526 infostream<<"Cannot move item: "
2527 "not owner of metadata"
2534 // If player is not an admin, check for ownership of dst
2535 if(ma->to_inv != "current_player"
2536 && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
2538 Strfnd fn(ma->to_inv);
2539 std::string id0 = fn.next(":");
2540 if(id0 == "nodemeta")
2543 p.X = stoi(fn.next(","));
2544 p.Y = stoi(fn.next(","));
2545 p.Z = stoi(fn.next(","));
2546 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
2547 if(meta->getOwner() != "" &&
2548 meta->getOwner() != player->getName())
2550 infostream<<"Cannot move item: "
2551 "not owner of metadata"
2560 Handle restrictions and special cases of the drop action
2562 else if(a->getType() == IACTION_DROP)
2564 IDropAction *da = (IDropAction*)a;
2565 // Disallow dropping items if not allowed to build
2566 if((getPlayerPrivs(player) & PRIV_INTERACT) == 0)
2571 // If player is not an admin, check for ownership
2572 else if (da->from_inv != "current_player"
2573 && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
2575 Strfnd fn(da->from_inv);
2576 std::string id0 = fn.next(":");
2577 if(id0 == "nodemeta")
2580 p.X = stoi(fn.next(","));
2581 p.Y = stoi(fn.next(","));
2582 p.Z = stoi(fn.next(","));
2583 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
2584 if(meta->getOwner() != "" &&
2585 meta->getOwner() != player->getName())
2587 infostream<<"Cannot move item: "
2588 "not owner of metadata"
2598 a->apply(&c, this, m_env);
2602 else if(command == TOSERVER_CHAT_MESSAGE)
2610 std::string datastring((char*)&data[2], datasize-2);
2611 std::istringstream is(datastring, std::ios_base::binary);
2614 is.read((char*)buf, 2);
2615 u16 len = readU16(buf);
2617 std::wstring message;
2618 for(u16 i=0; i<len; i++)
2620 is.read((char*)buf, 2);
2621 message += (wchar_t)readU16(buf);
2624 // Get player name of this client
2625 std::wstring name = narrow_to_wide(player->getName());
2628 bool ate = scriptapi_on_chat_message(m_lua, player->getName(),
2629 wide_to_narrow(message));
2630 // If script ate the message, don't proceed
2634 // Line to send to players
2636 // Whether to send to the player that sent the line
2637 bool send_to_sender = false;
2638 // Whether to send to other players
2639 bool send_to_others = false;
2641 // Local player gets all privileges regardless of
2642 // what's set on their account.
2643 u64 privs = getPlayerPrivs(player);
2646 if(message[0] == L'/')
2648 size_t strip_size = 1;
2649 if (message[1] == L'#') // support old-style commans
2651 message = message.substr(strip_size);
2653 WStrfnd f1(message);
2654 f1.next(L" "); // Skip over /#whatever
2655 std::wstring paramstring = f1.next(L"");
2657 ServerCommandContext *ctx = new ServerCommandContext(
2658 str_split(message, L' '),
2665 std::wstring reply(processServerCommand(ctx));
2666 send_to_sender = ctx->flags & SEND_TO_SENDER;
2667 send_to_others = ctx->flags & SEND_TO_OTHERS;
2669 if (ctx->flags & SEND_NO_PREFIX)
2672 line += L"Server: " + reply;
2679 if(privs & PRIV_SHOUT)
2685 send_to_others = true;
2689 line += L"Server: You are not allowed to shout";
2690 send_to_sender = true;
2697 actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2700 Send the message to clients
2702 for(core::map<u16, RemoteClient*>::Iterator
2703 i = m_clients.getIterator();
2704 i.atEnd() == false; i++)
2706 // Get client and check that it is valid
2707 RemoteClient *client = i.getNode()->getValue();
2708 assert(client->peer_id == i.getNode()->getKey());
2709 if(client->serialization_version == SER_FMT_VER_INVALID)
2713 bool sender_selected = (peer_id == client->peer_id);
2714 if(sender_selected == true && send_to_sender == false)
2716 if(sender_selected == false && send_to_others == false)
2719 SendChatMessage(client->peer_id, line);
2723 else if(command == TOSERVER_DAMAGE)
2725 std::string datastring((char*)&data[2], datasize-2);
2726 std::istringstream is(datastring, std::ios_base::binary);
2727 u8 damage = readU8(is);
2729 if(g_settings->getBool("enable_damage"))
2731 actionstream<<player->getName()<<" damaged by "
2732 <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
2735 HandlePlayerHP(player, damage);
2739 SendPlayerHP(player);
2742 else if(command == TOSERVER_PASSWORD)
2745 [0] u16 TOSERVER_PASSWORD
2746 [2] u8[28] old password
2747 [30] u8[28] new password
2750 if(datasize != 2+PASSWORD_SIZE*2)
2752 /*char password[PASSWORD_SIZE];
2753 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2754 password[i] = data[2+i];
2755 password[PASSWORD_SIZE-1] = 0;*/
2757 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2765 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2767 char c = data[2+PASSWORD_SIZE+i];
2773 infostream<<"Server: Client requests a password change from "
2774 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
2776 std::string playername = player->getName();
2778 if(m_authmanager.exists(playername) == false)
2780 infostream<<"Server: playername not found in authmanager"<<std::endl;
2781 // Wrong old password supplied!!
2782 SendChatMessage(peer_id, L"playername not found in authmanager");
2786 std::string checkpwd = m_authmanager.getPassword(playername);
2788 if(oldpwd != checkpwd)
2790 infostream<<"Server: invalid old password"<<std::endl;
2791 // Wrong old password supplied!!
2792 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
2796 actionstream<<player->getName()<<" changes password"<<std::endl;
2798 m_authmanager.setPassword(playername, newpwd);
2800 infostream<<"Server: password change successful for "<<playername
2802 SendChatMessage(peer_id, L"Password change successful");
2804 else if(command == TOSERVER_PLAYERITEM)
2809 u16 item = readU16(&data[2]);
2810 player->wieldItem(item);
2811 SendWieldedItem(player);
2813 else if(command == TOSERVER_RESPAWN)
2818 srp->m_respawn_active = false;
2820 RespawnPlayer(player);
2822 actionstream<<player->getName()<<" respawns at "
2823 <<PP(player->getPosition()/BS)<<std::endl;
2825 // ActiveObject is added to environment in AsyncRunStep after
2826 // the previous addition has been succesfully removed
2828 else if(command == TOSERVER_INTERACT)
2830 std::string datastring((char*)&data[2], datasize-2);
2831 std::istringstream is(datastring, std::ios_base::binary);
2837 [5] u32 length of the next item
2838 [9] serialized PointedThing
2840 0: start digging (from undersurface) or use
2841 1: stop digging (all parameters ignored)
2842 2: digging completed
2843 3: place block or item (to abovesurface)
2846 u8 action = readU8(is);
2847 u16 item_i = readU16(is);
2848 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
2849 PointedThing pointed;
2850 pointed.deSerialize(tmp_is);
2852 infostream<<"TOSERVER_INTERACT: action="<<(int)action<<", item="<<item_i<<", pointed="<<pointed.dump()<<std::endl;
2854 v3f player_pos = srp->m_last_good_position;
2856 // Update wielded item
2857 srp->wieldItem(item_i);
2859 // Get pointed to node (undefined if not POINTEDTYPE_NODE)
2860 v3s16 p_under = pointed.node_undersurface;
2861 v3s16 p_above = pointed.node_abovesurface;
2863 // Get pointed to object (NULL if not POINTEDTYPE_OBJECT)
2864 ServerActiveObject *pointed_object = NULL;
2865 if(pointed.type == POINTEDTHING_OBJECT)
2867 pointed_object = m_env->getActiveObject(pointed.object_id);
2868 if(pointed_object == NULL)
2870 infostream<<"TOSERVER_INTERACT: "
2871 "pointed object is NULL"<<std::endl;
2878 Check that target is reasonably close
2879 (only when digging or placing things)
2881 if(action == 0 || action == 2 || action == 3)
2883 v3f pointed_pos = player_pos;
2884 if(pointed.type == POINTEDTHING_NODE)
2886 pointed_pos = intToFloat(p_under, BS);
2888 else if(pointed.type == POINTEDTHING_OBJECT)
2890 pointed_pos = pointed_object->getBasePosition();
2893 float d = player_pos.getDistanceFrom(pointed_pos);
2894 float max_d = BS * 10; // Just some large enough value
2896 actionstream<<"Player "<<player->getName()
2897 <<" tried to access "<<pointed.dump()
2899 <<"d="<<d<<", max_d="<<max_d
2900 <<". ignoring."<<std::endl;
2901 // Re-send block to revert change on client-side
2902 RemoteClient *client = getClient(peer_id);
2903 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos, BS));
2904 client->SetBlockNotSent(blockpos);
2911 Make sure the player is allowed to do it
2913 bool build_priv = (getPlayerPrivs(player) & PRIV_INTERACT) != 0;
2916 infostream<<"Ignoring interaction from player "<<player->getName()
2917 <<" because privileges are "<<getPlayerPrivs(player)
2919 // NOTE: no return; here, fall through
2923 0: start digging or punch object
2927 if(pointed.type == POINTEDTHING_NODE)
2930 NOTE: This can be used in the future to check if
2931 somebody is cheating, by checking the timing.
2933 bool cannot_punch_node = !build_priv;
2935 MapNode n(CONTENT_IGNORE);
2939 n = m_env->getMap().getNode(p_under);
2941 catch(InvalidPositionException &e)
2943 infostream<<"Server: Not punching: Node not found."
2944 <<" Adding block to emerge queue."
2946 m_emerge_queue.addBlock(peer_id,
2947 getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
2948 cannot_punch_node = true;
2951 if(cannot_punch_node)
2957 scriptapi_environment_on_punchnode(m_lua, p_under, n, srp);
2959 else if(pointed.type == POINTEDTHING_OBJECT)
2964 // Skip if object has been removed
2965 if(pointed_object->m_removed)
2968 actionstream<<player->getName()<<" punches object "
2969 <<pointed.object_id<<std::endl;
2972 pointed_object->punch(srp, srp->m_time_from_last_punch);
2973 srp->m_time_from_last_punch = 0;
2981 else if(action == 1)
2986 2: Digging completed
2988 else if(action == 2)
2990 // Only complete digging of nodes
2991 if(pointed.type != POINTEDTHING_NODE)
2994 // Mandatory parameter; actually used for nothing
2995 core::map<v3s16, MapBlock*> modified_blocks;
2997 content_t material = CONTENT_IGNORE;
2998 u8 mineral = MINERAL_NONE;
3000 bool cannot_remove_node = !build_priv;
3002 MapNode n(CONTENT_IGNORE);
3005 n = m_env->getMap().getNode(p_under);
3007 mineral = n.getMineral(m_nodedef);
3008 // Get material at position
3009 material = n.getContent();
3010 // If not yet cancelled
3011 if(cannot_remove_node == false)
3013 // If it's not diggable, do nothing
3014 if(m_nodedef->get(material).diggable == false)
3016 infostream<<"Server: Not finishing digging: "
3017 <<"Node not diggable"
3019 cannot_remove_node = true;
3022 // If not yet cancelled
3023 if(cannot_remove_node == false)
3025 // Get node metadata
3026 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p_under);
3027 if(meta && meta->nodeRemovalDisabled() == true)
3029 infostream<<"Server: Not finishing digging: "
3030 <<"Node metadata disables removal"
3032 cannot_remove_node = true;
3036 catch(InvalidPositionException &e)
3038 infostream<<"Server: Not finishing digging: Node not found."
3039 <<" Adding block to emerge queue."
3041 m_emerge_queue.addBlock(peer_id,
3042 getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
3043 cannot_remove_node = true;
3047 If node can't be removed, set block to be re-sent to
3050 if(cannot_remove_node)
3052 infostream<<"Server: Not finishing digging."<<std::endl;
3054 // Client probably has wrong data.
3055 // Set block not sent, so that client will get
3057 infostream<<"Client "<<peer_id<<" tried to dig "
3058 <<"node; but node cannot be removed."
3059 <<" setting MapBlock not sent."<<std::endl;
3060 RemoteClient *client = getClient(peer_id);
3061 v3s16 blockpos = getNodeBlockPos(p_under);
3062 client->SetBlockNotSent(blockpos);
3067 actionstream<<player->getName()<<" digs "<<PP(p_under)
3068 <<", gets material "<<(int)material<<", mineral "
3069 <<(int)mineral<<std::endl;
3072 Send the removal to all close-by players.
3073 - If other player is close, send REMOVENODE
3074 - Otherwise set blocks not sent
3076 core::list<u16> far_players;
3077 sendRemoveNode(p_under, peer_id, &far_players, 30);
3080 Update and send inventory
3083 if(g_settings->getBool("creative_mode") == false)
3088 InventoryList *mlist = player->inventory.getList("main");
3091 InventoryItem *item = mlist->getItem(item_i);
3092 if(item && (std::string)item->getName() == "ToolItem")
3094 ToolItem *titem = (ToolItem*)item;
3095 std::string toolname = titem->getToolName();
3097 // Get digging properties for material and tool
3098 ToolDiggingProperties tp =
3099 m_toolmgr->getDiggingProperties(toolname);
3100 DiggingProperties prop =
3101 getDiggingProperties(material, &tp, m_nodedef);
3103 if(prop.diggable == false)
3105 infostream<<"Server: WARNING: Player digged"
3106 <<" with impossible material + tool"
3107 <<" combination"<<std::endl;
3110 bool weared_out = titem->addWear(prop.wear);
3114 mlist->deleteItem(item_i);
3117 srp->m_inventory_not_sent = true;
3122 Add dug item to inventory
3125 InventoryItem *item = NULL;
3127 if(mineral != MINERAL_NONE)
3128 item = getDiggedMineralItem(mineral, this);
3133 const std::string &dug_s = m_nodedef->get(material).dug_item;
3136 std::istringstream is(dug_s, std::ios::binary);
3137 item = InventoryItem::deSerialize(is, this);
3143 // Add a item to inventory
3144 player->inventory.addItem("main", item);
3145 srp->m_inventory_not_sent = true;
3151 const std::string &extra_dug_s = m_nodedef->get(material).extra_dug_item;
3152 s32 extra_rarity = m_nodedef->get(material).extra_dug_item_rarity;
3153 if(extra_dug_s != "" && extra_rarity != 0
3154 && myrand() % extra_rarity == 0)
3156 std::istringstream is(extra_dug_s, std::ios::binary);
3157 item = InventoryItem::deSerialize(is, this);
3163 // Add a item to inventory
3164 player->inventory.addItem("main", item);
3165 srp->m_inventory_not_sent = true;
3171 (this takes some time so it is done after the quick stuff)
3174 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
3176 m_env->getMap().removeNodeAndUpdate(p_under, modified_blocks);
3179 Set blocks not sent to far players
3181 for(core::list<u16>::Iterator
3182 i = far_players.begin();
3183 i != far_players.end(); i++)
3186 RemoteClient *client = getClient(peer_id);
3189 client->SetBlocksNotSent(modified_blocks);
3195 scriptapi_environment_on_dignode(m_lua, p_under, n, srp);
3199 3: place block or right-click object
3201 else if(action == 3)
3203 if(pointed.type == POINTEDTHING_NODE)
3205 InventoryList *ilist = player->inventory.getList("main");
3210 InventoryItem *item = ilist->getItem(item_i);
3212 // If there is no item, it is not possible to add it anywhere
3217 Handle material items
3219 if(std::string("MaterialItem") == item->getName())
3221 bool cannot_place_node = !build_priv;
3224 // Don't add a node if this is not a free space
3225 MapNode n2 = m_env->getMap().getNode(p_above);
3226 if(m_nodedef->get(n2).buildable_to == false)
3228 infostream<<"Client "<<peer_id<<" tried to place"
3229 <<" node in invalid position."<<std::endl;
3230 cannot_place_node = true;
3233 catch(InvalidPositionException &e)
3235 infostream<<"Server: Ignoring ADDNODE: Node not found"
3236 <<" Adding block to emerge queue."
3238 m_emerge_queue.addBlock(peer_id,
3239 getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
3240 cannot_place_node = true;
3243 if(cannot_place_node)
3245 // Client probably has wrong data.
3246 // Set block not sent, so that client will get
3248 RemoteClient *client = getClient(peer_id);
3249 v3s16 blockpos = getNodeBlockPos(p_above);
3250 client->SetBlockNotSent(blockpos);
3254 // Reset build time counter
3255 getClient(peer_id)->m_time_from_building = 0.0;
3258 MaterialItem *mitem = (MaterialItem*)item;
3260 n.setContent(mitem->getMaterial());
3262 actionstream<<player->getName()<<" places material "
3263 <<(int)mitem->getMaterial()
3264 <<" at "<<PP(p_under)<<std::endl;
3266 // Calculate direction for wall mounted stuff
3267 if(m_nodedef->get(n).wall_mounted)
3268 n.param2 = packDir(p_under - p_above);
3270 // Calculate the direction for furnaces and chests and stuff
3271 if(m_nodedef->get(n).param_type == CPT_FACEDIR_SIMPLE)
3273 v3f playerpos = player->getPosition();
3274 v3f blockpos = intToFloat(p_above, BS) - playerpos;
3275 blockpos = blockpos.normalize();
3277 if (fabs(blockpos.X) > fabs(blockpos.Z)) {
3291 Send to all close-by players
3293 core::list<u16> far_players;
3294 sendAddNode(p_above, n, 0, &far_players, 30);
3299 InventoryList *ilist = player->inventory.getList("main");
3300 if(g_settings->getBool("creative_mode") == false && ilist)
3302 // Remove from inventory and send inventory
3303 if(mitem->getCount() <= 1)
3304 ilist->deleteItem(item_i);
3307 srp->m_inventory_not_sent = true;
3313 This takes some time so it is done after the quick stuff
3315 core::map<v3s16, MapBlock*> modified_blocks;
3317 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
3319 std::string p_name = std::string(player->getName());
3320 m_env->getMap().addNodeAndUpdate(p_above, n, modified_blocks, p_name);
3323 Set blocks not sent to far players
3325 for(core::list<u16>::Iterator
3326 i = far_players.begin();
3327 i != far_players.end(); i++)
3330 RemoteClient *client = getClient(peer_id);
3333 client->SetBlocksNotSent(modified_blocks);
3339 scriptapi_environment_on_placenode(m_lua, p_above, n, srp);
3342 Calculate special events
3345 /*if(n.d == LEGN(m_nodedef, "CONTENT_MESE"))
3348 for(s16 z=-1; z<=1; z++)
3349 for(s16 y=-1; y<=1; y++)
3350 for(s16 x=-1; x<=1; x++)
3357 Place other item (not a block)
3363 infostream<<"Not allowing player to place item: "
3364 "no build privileges"<<std::endl;
3368 // Calculate a position for it
3369 v3f pos = player_pos;
3370 if(pointed.type == POINTEDTHING_NOTHING)
3372 infostream<<"Not allowing player to place item: "
3373 "pointing to nothing"<<std::endl;
3376 else if(pointed.type == POINTEDTHING_NODE)
3378 pos = intToFloat(p_above, BS);
3380 else if(pointed.type == POINTEDTHING_OBJECT)
3382 pos = pointed_object->getBasePosition();
3385 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
3386 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
3390 //pos.Y -= BS*0.25; // let it drop a bit
3393 Check that the block is loaded so that the item
3394 can properly be added to the static list too
3396 v3s16 blockpos = getNodeBlockPos(floatToInt(pos, BS));
3397 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3400 infostream<<"Error while placing item: "
3401 "block not found"<<std::endl;
3405 actionstream<<player->getName()<<" places "<<item->getName()
3406 <<" at "<<PP(pos)<<std::endl;
3411 bool remove = item->dropOrPlace(m_env, srp, pos, true, -1);
3412 if(remove && g_settings->getBool("creative_mode") == false)
3414 InventoryList *ilist = player->inventory.getList("main");
3416 // Remove from inventory and send inventory
3417 ilist->deleteItem(item_i);
3418 srp->m_inventory_not_sent = true;
3423 else if(pointed.type == POINTEDTHING_OBJECT)
3425 // Right click object
3430 // Skip if object has been removed
3431 if(pointed_object->m_removed)
3434 actionstream<<player->getName()<<" right-clicks object "
3435 <<pointed.object_id<<std::endl;
3438 pointed_object->rightClick(srp);
3446 else if(action == 4)
3448 InventoryList *ilist = player->inventory.getList("main");
3453 InventoryItem *item = ilist->getItem(item_i);
3455 // If there is no item, it is not possible to add it anywhere
3459 // Requires build privs
3462 infostream<<"Not allowing player to use item: "
3463 "no build privileges"<<std::endl;
3467 actionstream<<player->getName()<<" uses "<<item->getName()
3468 <<", pointing at "<<pointed.dump()<<std::endl;
3470 bool remove = item->use(m_env, srp, pointed);
3472 if(remove && g_settings->getBool("creative_mode") == false)
3474 InventoryList *ilist = player->inventory.getList("main");
3476 // Remove from inventory and send inventory
3477 ilist->deleteItem(item_i);
3478 srp->m_inventory_not_sent = true;
3485 Catch invalid actions
3489 infostream<<"WARNING: Server: Invalid action "
3490 <<action<<std::endl;
3493 // Complete add_to_inventory_later
3494 srp->completeAddToInventoryLater(item_i);
3498 infostream<<"Server::ProcessData(): Ignoring "
3499 "unknown command "<<command<<std::endl;
3503 catch(SendFailedException &e)
3505 errorstream<<"Server::ProcessData(): SendFailedException: "
3511 void Server::onMapEditEvent(MapEditEvent *event)
3513 //infostream<<"Server::onMapEditEvent()"<<std::endl;
3514 if(m_ignore_map_edit_events)
3516 MapEditEvent *e = event->clone();
3517 m_unsent_map_edit_queue.push_back(e);
3520 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3522 if(id == "current_player")
3524 assert(c->current_player);
3525 return &(c->current_player->inventory);
3529 std::string id0 = fn.next(":");
3531 if(id0 == "nodemeta")
3534 p.X = stoi(fn.next(","));
3535 p.Y = stoi(fn.next(","));
3536 p.Z = stoi(fn.next(","));
3537 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3539 return meta->getInventory();
3540 infostream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3541 <<"no metadata found"<<std::endl;
3545 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3548 void Server::inventoryModified(InventoryContext *c, std::string id)
3550 if(id == "current_player")
3552 assert(c->current_player);
3553 ServerRemotePlayer *srp =
3554 static_cast<ServerRemotePlayer*>(c->current_player);
3555 srp->m_inventory_not_sent = true;
3560 std::string id0 = fn.next(":");
3562 if(id0 == "nodemeta")
3565 p.X = stoi(fn.next(","));
3566 p.Y = stoi(fn.next(","));
3567 p.Z = stoi(fn.next(","));
3568 v3s16 blockpos = getNodeBlockPos(p);
3570 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3572 meta->inventoryModified();
3574 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3576 block->raiseModified(MOD_STATE_WRITE_NEEDED);
3578 setBlockNotSent(blockpos);
3583 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3586 core::list<PlayerInfo> Server::getPlayerInfo()
3588 DSTACK(__FUNCTION_NAME);
3589 JMutexAutoLock envlock(m_env_mutex);
3590 JMutexAutoLock conlock(m_con_mutex);
3592 core::list<PlayerInfo> list;
3594 core::list<Player*> players = m_env->getPlayers();
3596 core::list<Player*>::Iterator i;
3597 for(i = players.begin();
3598 i != players.end(); i++)
3602 Player *player = *i;
3605 // Copy info from connection to info struct
3606 info.id = player->peer_id;
3607 info.address = m_con.GetPeerAddress(player->peer_id);
3608 info.avg_rtt = m_con.GetPeerAvgRTT(player->peer_id);
3610 catch(con::PeerNotFoundException &e)
3612 // Set dummy peer info
3614 info.address = Address(0,0,0,0,0);
3618 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3619 info.position = player->getPosition();
3621 list.push_back(info);
3628 void Server::peerAdded(con::Peer *peer)
3630 DSTACK(__FUNCTION_NAME);
3631 infostream<<"Server::peerAdded(): peer->id="
3632 <<peer->id<<std::endl;
3635 c.type = PEER_ADDED;
3636 c.peer_id = peer->id;
3638 m_peer_change_queue.push_back(c);
3641 void Server::deletingPeer(con::Peer *peer, bool timeout)
3643 DSTACK(__FUNCTION_NAME);
3644 infostream<<"Server::deletingPeer(): peer->id="
3645 <<peer->id<<", timeout="<<timeout<<std::endl;
3648 c.type = PEER_REMOVED;
3649 c.peer_id = peer->id;
3650 c.timeout = timeout;
3651 m_peer_change_queue.push_back(c);
3658 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3660 DSTACK(__FUNCTION_NAME);
3661 std::ostringstream os(std::ios_base::binary);
3663 writeU16(os, TOCLIENT_HP);
3667 std::string s = os.str();
3668 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3670 con.Send(peer_id, 0, data, true);
3673 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3674 const std::wstring &reason)
3676 DSTACK(__FUNCTION_NAME);
3677 std::ostringstream os(std::ios_base::binary);
3679 writeU16(os, TOCLIENT_ACCESS_DENIED);
3680 os<<serializeWideString(reason);
3683 std::string s = os.str();
3684 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3686 con.Send(peer_id, 0, data, true);
3689 void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
3690 bool set_camera_point_target, v3f camera_point_target)
3692 DSTACK(__FUNCTION_NAME);
3693 std::ostringstream os(std::ios_base::binary);
3695 writeU16(os, TOCLIENT_DEATHSCREEN);
3696 writeU8(os, set_camera_point_target);
3697 writeV3F1000(os, camera_point_target);
3700 std::string s = os.str();
3701 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3703 con.Send(peer_id, 0, data, true);
3706 void Server::SendToolDef(con::Connection &con, u16 peer_id,
3707 IToolDefManager *tooldef)
3709 DSTACK(__FUNCTION_NAME);
3710 std::ostringstream os(std::ios_base::binary);
3714 u32 length of the next item
3715 serialized ToolDefManager
3717 writeU16(os, TOCLIENT_TOOLDEF);
3718 std::ostringstream tmp_os(std::ios::binary);
3719 tooldef->serialize(tmp_os);
3720 os<<serializeLongString(tmp_os.str());
3723 std::string s = os.str();
3724 infostream<<"Server::SendToolDef(): Sending tool definitions: size="
3725 <<s.size()<<std::endl;
3726 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3728 con.Send(peer_id, 0, data, true);
3731 void Server::SendNodeDef(con::Connection &con, u16 peer_id,
3732 INodeDefManager *nodedef)
3734 DSTACK(__FUNCTION_NAME);
3735 std::ostringstream os(std::ios_base::binary);
3739 u32 length of the next item
3740 serialized NodeDefManager
3742 writeU16(os, TOCLIENT_NODEDEF);
3743 std::ostringstream tmp_os(std::ios::binary);
3744 nodedef->serialize(tmp_os);
3745 os<<serializeLongString(tmp_os.str());
3748 std::string s = os.str();
3749 infostream<<"Server::SendNodeDef(): Sending node definitions: size="
3750 <<s.size()<<std::endl;
3751 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3753 con.Send(peer_id, 0, data, true);
3756 void Server::SendCraftItemDef(con::Connection &con, u16 peer_id,
3757 ICraftItemDefManager *craftitemdef)
3759 DSTACK(__FUNCTION_NAME);
3760 std::ostringstream os(std::ios_base::binary);
3764 u32 length of the next item
3765 serialized CraftItemDefManager
3767 writeU16(os, TOCLIENT_CRAFTITEMDEF);
3768 std::ostringstream tmp_os(std::ios::binary);
3769 craftitemdef->serialize(tmp_os);
3770 os<<serializeLongString(tmp_os.str());
3773 std::string s = os.str();
3774 infostream<<"Server::SendCraftItemDef(): Sending craft item definitions: size="
3775 <<s.size()<<std::endl;
3776 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3778 con.Send(peer_id, 0, data, true);
3782 Non-static send methods
3785 void Server::SendInventory(u16 peer_id)
3787 DSTACK(__FUNCTION_NAME);
3789 ServerRemotePlayer* player =
3790 static_cast<ServerRemotePlayer*>(m_env->getPlayer(peer_id));
3793 player->m_inventory_not_sent = false;
3799 std::ostringstream os;
3800 //os.imbue(std::locale("C"));
3802 player->inventory.serialize(os);
3804 std::string s = os.str();
3806 SharedBuffer<u8> data(s.size()+2);
3807 writeU16(&data[0], TOCLIENT_INVENTORY);
3808 memcpy(&data[2], s.c_str(), s.size());
3811 m_con.Send(peer_id, 0, data, true);
3814 std::string getWieldedItemString(const Player *player)
3816 const InventoryItem *item = player->getWieldItem();
3818 return std::string("");
3819 std::ostringstream os(std::ios_base::binary);
3820 item->serialize(os);
3824 void Server::SendWieldedItem(const Player* player)
3826 DSTACK(__FUNCTION_NAME);
3830 std::ostringstream os(std::ios_base::binary);
3832 writeU16(os, TOCLIENT_PLAYERITEM);
3834 writeU16(os, player->peer_id);
3835 os<<serializeString(getWieldedItemString(player));
3838 std::string s = os.str();
3839 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3841 m_con.SendToAll(0, data, true);
3844 void Server::SendPlayerItems()
3846 DSTACK(__FUNCTION_NAME);
3848 std::ostringstream os(std::ios_base::binary);
3849 core::list<Player *> players = m_env->getPlayers(true);
3851 writeU16(os, TOCLIENT_PLAYERITEM);
3852 writeU16(os, players.size());
3853 core::list<Player *>::Iterator i;
3854 for(i = players.begin(); i != players.end(); ++i)
3857 writeU16(os, p->peer_id);
3858 os<<serializeString(getWieldedItemString(p));
3862 std::string s = os.str();
3863 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3865 m_con.SendToAll(0, data, true);
3868 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3870 DSTACK(__FUNCTION_NAME);
3872 std::ostringstream os(std::ios_base::binary);
3876 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3877 os.write((char*)buf, 2);
3880 writeU16(buf, message.size());
3881 os.write((char*)buf, 2);
3884 for(u32 i=0; i<message.size(); i++)
3888 os.write((char*)buf, 2);
3892 std::string s = os.str();
3893 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3895 m_con.Send(peer_id, 0, data, true);
3898 void Server::BroadcastChatMessage(const std::wstring &message)
3900 for(core::map<u16, RemoteClient*>::Iterator
3901 i = m_clients.getIterator();
3902 i.atEnd() == false; i++)
3904 // Get client and check that it is valid
3905 RemoteClient *client = i.getNode()->getValue();
3906 assert(client->peer_id == i.getNode()->getKey());
3907 if(client->serialization_version == SER_FMT_VER_INVALID)
3910 SendChatMessage(client->peer_id, message);
3914 void Server::SendPlayerHP(Player *player)
3916 SendHP(m_con, player->peer_id, player->hp);
3917 static_cast<ServerRemotePlayer*>(player)->m_hp_not_sent = false;
3920 void Server::SendMovePlayer(Player *player)
3922 DSTACK(__FUNCTION_NAME);
3923 std::ostringstream os(std::ios_base::binary);
3925 writeU16(os, TOCLIENT_MOVE_PLAYER);
3926 writeV3F1000(os, player->getPosition());
3927 writeF1000(os, player->getPitch());
3928 writeF1000(os, player->getYaw());
3931 v3f pos = player->getPosition();
3932 f32 pitch = player->getPitch();
3933 f32 yaw = player->getYaw();
3934 infostream<<"Server sending TOCLIENT_MOVE_PLAYER"
3935 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3942 std::string s = os.str();
3943 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3945 m_con.Send(player->peer_id, 0, data, true);
3948 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3949 core::list<u16> *far_players, float far_d_nodes)
3951 float maxd = far_d_nodes*BS;
3952 v3f p_f = intToFloat(p, BS);
3956 SharedBuffer<u8> reply(replysize);
3957 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3958 writeS16(&reply[2], p.X);
3959 writeS16(&reply[4], p.Y);
3960 writeS16(&reply[6], p.Z);
3962 for(core::map<u16, RemoteClient*>::Iterator
3963 i = m_clients.getIterator();
3964 i.atEnd() == false; i++)
3966 // Get client and check that it is valid
3967 RemoteClient *client = i.getNode()->getValue();
3968 assert(client->peer_id == i.getNode()->getKey());
3969 if(client->serialization_version == SER_FMT_VER_INVALID)
3972 // Don't send if it's the same one
3973 if(client->peer_id == ignore_id)
3979 Player *player = m_env->getPlayer(client->peer_id);
3982 // If player is far away, only set modified blocks not sent
3983 v3f player_pos = player->getPosition();
3984 if(player_pos.getDistanceFrom(p_f) > maxd)
3986 far_players->push_back(client->peer_id);
3993 m_con.Send(client->peer_id, 0, reply, true);
3997 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3998 core::list<u16> *far_players, float far_d_nodes)
4000 float maxd = far_d_nodes*BS;
4001 v3f p_f = intToFloat(p, BS);
4003 for(core::map<u16, RemoteClient*>::Iterator
4004 i = m_clients.getIterator();
4005 i.atEnd() == false; i++)
4007 // Get client and check that it is valid
4008 RemoteClient *client = i.getNode()->getValue();
4009 assert(client->peer_id == i.getNode()->getKey());
4010 if(client->serialization_version == SER_FMT_VER_INVALID)
4013 // Don't send if it's the same one
4014 if(client->peer_id == ignore_id)
4020 Player *player = m_env->getPlayer(client->peer_id);
4023 // If player is far away, only set modified blocks not sent
4024 v3f player_pos = player->getPosition();
4025 if(player_pos.getDistanceFrom(p_f) > maxd)
4027 far_players->push_back(client->peer_id);
4034 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
4035 SharedBuffer<u8> reply(replysize);
4036 writeU16(&reply[0], TOCLIENT_ADDNODE);
4037 writeS16(&reply[2], p.X);
4038 writeS16(&reply[4], p.Y);
4039 writeS16(&reply[6], p.Z);
4040 n.serialize(&reply[8], client->serialization_version);
4043 m_con.Send(client->peer_id, 0, reply, true);
4047 void Server::setBlockNotSent(v3s16 p)
4049 for(core::map<u16, RemoteClient*>::Iterator
4050 i = m_clients.getIterator();
4051 i.atEnd()==false; i++)
4053 RemoteClient *client = i.getNode()->getValue();
4054 client->SetBlockNotSent(p);
4058 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
4060 DSTACK(__FUNCTION_NAME);
4062 v3s16 p = block->getPos();
4066 bool completely_air = true;
4067 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4068 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4069 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4071 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
4073 completely_air = false;
4074 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
4079 infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
4081 infostream<<"[completely air] ";
4082 infostream<<std::endl;
4086 Create a packet with the block in the right format
4089 std::ostringstream os(std::ios_base::binary);
4090 block->serialize(os, ver);
4091 std::string s = os.str();
4092 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
4094 u32 replysize = 8 + blockdata.getSize();
4095 SharedBuffer<u8> reply(replysize);
4096 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
4097 writeS16(&reply[2], p.X);
4098 writeS16(&reply[4], p.Y);
4099 writeS16(&reply[6], p.Z);
4100 memcpy(&reply[8], *blockdata, blockdata.getSize());
4102 /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4103 <<": \tpacket size: "<<replysize<<std::endl;*/
4108 m_con.Send(peer_id, 1, reply, true);
4111 void Server::SendBlocks(float dtime)
4113 DSTACK(__FUNCTION_NAME);
4115 JMutexAutoLock envlock(m_env_mutex);
4116 JMutexAutoLock conlock(m_con_mutex);
4118 //TimeTaker timer("Server::SendBlocks");
4120 core::array<PrioritySortedBlockTransfer> queue;
4122 s32 total_sending = 0;
4125 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
4127 for(core::map<u16, RemoteClient*>::Iterator
4128 i = m_clients.getIterator();
4129 i.atEnd() == false; i++)
4131 RemoteClient *client = i.getNode()->getValue();
4132 assert(client->peer_id == i.getNode()->getKey());
4134 // If definitions and textures have not been sent, don't
4135 // send MapBlocks either
4136 if(!client->definitions_sent)
4139 total_sending += client->SendingCount();
4141 if(client->serialization_version == SER_FMT_VER_INVALID)
4144 client->GetNextBlocks(this, dtime, queue);
4149 // Lowest priority number comes first.
4150 // Lowest is most important.
4153 for(u32 i=0; i<queue.size(); i++)
4155 //TODO: Calculate limit dynamically
4156 if(total_sending >= g_settings->getS32
4157 ("max_simultaneous_block_sends_server_total"))
4160 PrioritySortedBlockTransfer q = queue[i];
4162 MapBlock *block = NULL;
4165 block = m_env->getMap().getBlockNoCreate(q.pos);
4167 catch(InvalidPositionException &e)
4172 RemoteClient *client = getClient(q.peer_id);
4174 SendBlockNoLock(q.peer_id, block, client->serialization_version);
4176 client->SentBlock(q.pos);
4182 struct SendableTexture
4188 SendableTexture(const std::string &name_="", const std::string path_="",
4189 const std::string &data_=""):
4196 void Server::SendTextures(u16 peer_id)
4198 DSTACK(__FUNCTION_NAME);
4200 infostream<<"Server::SendTextures(): Sending textures to client"<<std::endl;
4204 // Put 5kB in one bunch (this is not accurate)
4205 u32 bytes_per_bunch = 5000;
4207 core::array< core::list<SendableTexture> > texture_bunches;
4208 texture_bunches.push_back(core::list<SendableTexture>());
4210 u32 texture_size_bunch_total = 0;
4211 for(core::list<ModSpec>::Iterator i = m_mods.begin();
4212 i != m_mods.end(); i++){
4213 const ModSpec &mod = *i;
4214 std::string texturepath = mod.path + DIR_DELIM + "textures";
4215 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(texturepath);
4216 for(u32 j=0; j<dirlist.size(); j++){
4217 if(dirlist[j].dir) // Ignode dirs
4219 std::string tname = dirlist[j].name;
4220 std::string tpath = texturepath + DIR_DELIM + tname;
4222 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
4223 if(fis.good() == false){
4224 errorstream<<"Server::SendTextures(): Could not open \""
4225 <<tname<<"\" for reading"<<std::endl;
4228 std::ostringstream tmp_os(std::ios_base::binary);
4232 fis.read(buf, 1024);
4233 std::streamsize len = fis.gcount();
4234 tmp_os.write(buf, len);
4235 texture_size_bunch_total += len;
4244 errorstream<<"Server::SendTextures(): Failed to read \""
4245 <<tname<<"\""<<std::endl;
4248 /*infostream<<"Server::SendTextures(): Loaded \""
4249 <<tname<<"\""<<std::endl;*/
4251 texture_bunches[texture_bunches.size()-1].push_back(
4252 SendableTexture(tname, tpath, tmp_os.str()));
4254 // Start next bunch if got enough data
4255 if(texture_size_bunch_total >= bytes_per_bunch){
4256 texture_bunches.push_back(core::list<SendableTexture>());
4257 texture_size_bunch_total = 0;
4262 /* Create and send packets */
4264 u32 num_bunches = texture_bunches.size();
4265 for(u32 i=0; i<num_bunches; i++)
4269 u16 total number of texture bunches
4270 u16 index of this bunch
4271 u32 number of textures in this bunch
4279 std::ostringstream os(std::ios_base::binary);
4281 writeU16(os, TOCLIENT_TEXTURES);
4282 writeU16(os, num_bunches);
4284 writeU32(os, texture_bunches[i].size());
4286 for(core::list<SendableTexture>::Iterator
4287 j = texture_bunches[i].begin();
4288 j != texture_bunches[i].end(); j++){
4289 os<<serializeString(j->name);
4290 os<<serializeLongString(j->data);
4294 std::string s = os.str();
4295 infostream<<"Server::SendTextures(): bunch "<<i<<"/"<<num_bunches
4296 <<" textures="<<texture_bunches[i].size()
4297 <<" size=" <<s.size()<<std::endl;
4298 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4300 m_con.Send(peer_id, 0, data, true);
4308 void Server::HandlePlayerHP(Player *player, s16 damage)
4310 ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
4312 if(srp->m_respawn_active)
4315 if(player->hp > damage)
4318 player->hp -= damage;
4319 SendPlayerHP(player);
4324 infostream<<"Server::HandlePlayerHP(): Player "
4325 <<player->getName()<<" dies"<<std::endl;
4329 //TODO: Throw items around
4331 // Handle players that are not connected
4332 if(player->peer_id == PEER_ID_INEXISTENT){
4333 RespawnPlayer(player);
4337 SendPlayerHP(player);
4339 RemoteClient *client = getClient(player->peer_id);
4340 if(client->net_proto_version >= 3)
4342 SendDeathscreen(m_con, player->peer_id, false, v3f(0,0,0));
4343 srp->m_removed = true;
4344 srp->m_respawn_active = true;
4348 RespawnPlayer(player);
4352 void Server::RespawnPlayer(Player *player)
4355 ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
4356 bool repositioned = scriptapi_on_respawnplayer(m_lua, srp);
4358 v3f pos = findSpawnPos(m_env->getServerMap());
4359 player->setPosition(pos);
4360 srp->m_last_good_position = pos;
4361 srp->m_last_good_position_age = 0;
4363 SendMovePlayer(player);
4364 SendPlayerHP(player);
4367 void Server::UpdateCrafting(u16 peer_id)
4369 DSTACK(__FUNCTION_NAME);
4371 Player* player = m_env->getPlayer(peer_id);
4373 ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
4375 // No crafting in creative mode
4376 if(g_settings->getBool("creative_mode"))
4379 // Get the InventoryLists of the player in which we will operate
4380 InventoryList *clist = player->inventory.getList("craft");
4382 InventoryList *rlist = player->inventory.getList("craftresult");
4384 InventoryList *mlist = player->inventory.getList("main");
4387 // If the result list is not a preview and is not empty, try to
4388 // throw the item into main list
4389 if(!player->craftresult_is_preview && rlist->getUsedSlots() != 0)
4391 // Grab item out of craftresult
4392 InventoryItem *item = rlist->changeItem(0, NULL);
4393 // Try to put in main
4394 InventoryItem *leftover = mlist->addItem(item);
4395 // If there are leftovers, put them back to craftresult and
4397 delete rlist->addItem(leftover);
4398 // Inventory was modified
4399 srp->m_inventory_not_sent = true;
4402 // If result list is empty, we will make it preview what would be
4404 if(rlist->getUsedSlots() == 0)
4405 player->craftresult_is_preview = true;
4407 // If it is a preview, clear the possible old preview in it
4408 if(player->craftresult_is_preview)
4409 rlist->clearItems();
4411 // If it is a preview, find out what is the crafting result
4413 if(player->craftresult_is_preview)
4415 // Mangle crafting grid to an another format
4416 std::vector<InventoryItem*> items;
4417 for(u16 i=0; i<9; i++){
4418 if(clist->getItem(i) == NULL)
4419 items.push_back(NULL);
4421 items.push_back(clist->getItem(i)->clone());
4423 CraftPointerInput cpi(3, items);
4425 // Find out what is crafted and add it to result item slot
4426 InventoryItem *result = m_craftdef->getCraftResult(cpi, this);
4428 rlist->addItem(result);
4432 RemoteClient* Server::getClient(u16 peer_id)
4434 DSTACK(__FUNCTION_NAME);
4435 //JMutexAutoLock lock(m_con_mutex);
4436 core::map<u16, RemoteClient*>::Node *n;
4437 n = m_clients.find(peer_id);
4438 // A client should exist for all peers
4440 return n->getValue();
4443 std::wstring Server::getStatusString()
4445 std::wostringstream os(std::ios_base::binary);
4448 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4450 os<<L", uptime="<<m_uptime.get();
4451 // Information about clients
4453 for(core::map<u16, RemoteClient*>::Iterator
4454 i = m_clients.getIterator();
4455 i.atEnd() == false; i++)
4457 // Get client and check that it is valid
4458 RemoteClient *client = i.getNode()->getValue();
4459 assert(client->peer_id == i.getNode()->getKey());
4460 if(client->serialization_version == SER_FMT_VER_INVALID)
4463 Player *player = m_env->getPlayer(client->peer_id);
4464 // Get name of player
4465 std::wstring name = L"unknown";
4467 name = narrow_to_wide(player->getName());
4468 // Add name to information string
4472 if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
4473 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4474 if(g_settings->get("motd") != "")
4475 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4479 void Server::setPlayerPassword(const std::string &name, const std::wstring &password)
4481 // Add player to auth manager
4482 if(m_authmanager.exists(name) == false)
4484 infostream<<"Server: adding player "<<name
4485 <<" to auth manager"<<std::endl;
4486 m_authmanager.add(name);
4487 m_authmanager.setPrivs(name,
4488 stringToPrivs(g_settings->get("default_privs")));
4490 // Change password and save
4491 m_authmanager.setPassword(name, translatePassword(name, password));
4492 m_authmanager.save();
4495 // Saves g_settings to configpath given at initialization
4496 void Server::saveConfig()
4498 if(m_configpath != "")
4499 g_settings->updateConfigFile(m_configpath.c_str());
4502 void Server::notifyPlayer(const char *name, const std::wstring msg)
4504 Player *player = m_env->getPlayer(name);
4507 SendChatMessage(player->peer_id, std::wstring(L"Server: -!- ")+msg);
4510 void Server::notifyPlayers(const std::wstring msg)
4512 BroadcastChatMessage(msg);
4515 void Server::queueBlockEmerge(v3s16 blockpos, bool allow_generate)
4519 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
4520 m_emerge_queue.addBlock(PEER_ID_INEXISTENT, blockpos, flags);
4523 // IGameDef interface
4525 IToolDefManager* Server::getToolDefManager()
4529 INodeDefManager* Server::getNodeDefManager()
4533 ICraftDefManager* Server::getCraftDefManager()
4537 ICraftItemDefManager* Server::getCraftItemDefManager()
4539 return m_craftitemdef;
4541 ITextureSource* Server::getTextureSource()
4545 u16 Server::allocateUnknownNodeId(const std::string &name)
4547 return m_nodedef->allocateDummy(name);
4550 IWritableToolDefManager* Server::getWritableToolDefManager()
4554 IWritableNodeDefManager* Server::getWritableNodeDefManager()
4558 IWritableCraftDefManager* Server::getWritableCraftDefManager()
4562 IWritableCraftItemDefManager* Server::getWritableCraftItemDefManager()
4564 return m_craftitemdef;
4567 const ModSpec* Server::getModSpec(const std::string &modname)
4569 for(core::list<ModSpec>::Iterator i = m_mods.begin();
4570 i != m_mods.end(); i++){
4571 const ModSpec &mod = *i;
4572 if(mod.name == modname)
4578 v3f findSpawnPos(ServerMap &map)
4580 //return v3f(50,50,50)*BS;
4585 nodepos = v2s16(0,0);
4590 // Try to find a good place a few times
4591 for(s32 i=0; i<1000; i++)
4594 // We're going to try to throw the player to this position
4595 v2s16 nodepos2d = v2s16(-range + (myrand()%(range*2)),
4596 -range + (myrand()%(range*2)));
4597 //v2s16 sectorpos = getNodeSectorPos(nodepos2d);
4598 // Get ground height at point (fallbacks to heightmap function)
4599 s16 groundheight = map.findGroundLevel(nodepos2d);
4600 // Don't go underwater
4601 if(groundheight < WATER_LEVEL)
4603 //infostream<<"-> Underwater"<<std::endl;
4606 // Don't go to high places
4607 if(groundheight > WATER_LEVEL + 4)
4609 //infostream<<"-> Underwater"<<std::endl;
4613 nodepos = v3s16(nodepos2d.X, groundheight-2, nodepos2d.Y);
4614 bool is_good = false;
4616 for(s32 i=0; i<10; i++){
4617 v3s16 blockpos = getNodeBlockPos(nodepos);
4618 map.emergeBlock(blockpos, true);
4619 MapNode n = map.getNodeNoEx(nodepos);
4620 if(n.getContent() == CONTENT_AIR){
4631 // Found a good place
4632 //infostream<<"Searched through "<<i<<" places."<<std::endl;
4638 return intToFloat(nodepos, BS);
4641 ServerRemotePlayer *Server::emergePlayer(const char *name, u16 peer_id)
4644 Try to get an existing player
4646 ServerRemotePlayer *player =
4647 static_cast<ServerRemotePlayer*>(m_env->getPlayer(name));
4650 // If player is already connected, cancel
4651 if(player->peer_id != 0)
4653 infostream<<"emergePlayer(): Player already connected"<<std::endl;
4658 player->peer_id = peer_id;
4660 // Reset inventory to creative if in creative mode
4661 if(g_settings->getBool("creative_mode"))
4663 // Warning: double code below
4664 // Backup actual inventory
4665 player->inventory_backup = new Inventory();
4666 *(player->inventory_backup) = player->inventory;
4667 // Set creative inventory
4668 player->resetInventory();
4669 scriptapi_get_creative_inventory(m_lua, player);
4676 If player with the wanted peer_id already exists, cancel.
4678 if(m_env->getPlayer(peer_id) != NULL)
4680 infostream<<"emergePlayer(): Player with wrong name but same"
4681 " peer_id already exists"<<std::endl;
4689 /* Set player position */
4691 infostream<<"Server: Finding spawn place for player \""
4692 <<name<<"\""<<std::endl;
4694 v3f pos = findSpawnPos(m_env->getServerMap());
4696 player = new ServerRemotePlayer(m_env, pos, peer_id, name);
4698 /* Add player to environment */
4699 m_env->addPlayer(player);
4702 ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
4703 scriptapi_on_newplayer(m_lua, srp);
4705 /* Add stuff to inventory */
4706 if(g_settings->getBool("creative_mode"))
4708 // Warning: double code above
4709 // Backup actual inventory
4710 player->inventory_backup = new Inventory();
4711 *(player->inventory_backup) = player->inventory;
4712 // Set creative inventory
4713 player->resetInventory();
4714 scriptapi_get_creative_inventory(m_lua, player);
4719 } // create new player
4722 void Server::handlePeerChange(PeerChange &c)
4724 JMutexAutoLock envlock(m_env_mutex);
4725 JMutexAutoLock conlock(m_con_mutex);
4727 if(c.type == PEER_ADDED)
4734 core::map<u16, RemoteClient*>::Node *n;
4735 n = m_clients.find(c.peer_id);
4736 // The client shouldn't already exist
4740 RemoteClient *client = new RemoteClient();
4741 client->peer_id = c.peer_id;
4742 m_clients.insert(client->peer_id, client);
4745 else if(c.type == PEER_REMOVED)
4752 core::map<u16, RemoteClient*>::Node *n;
4753 n = m_clients.find(c.peer_id);
4754 // The client should exist
4758 Mark objects to be not known by the client
4760 RemoteClient *client = n->getValue();
4762 for(core::map<u16, bool>::Iterator
4763 i = client->m_known_objects.getIterator();
4764 i.atEnd()==false; i++)
4767 u16 id = i.getNode()->getKey();
4768 ServerActiveObject* obj = m_env->getActiveObject(id);
4770 if(obj && obj->m_known_by_count > 0)
4771 obj->m_known_by_count--;
4774 ServerRemotePlayer* player =
4775 static_cast<ServerRemotePlayer*>(m_env->getPlayer(c.peer_id));
4777 // Collect information about leaving in chat
4778 std::wstring message;
4782 std::wstring name = narrow_to_wide(player->getName());
4785 message += L" left game";
4787 message += L" (timed out)";
4791 // Remove from environment
4793 player->m_removed = true;
4795 // Set player client disconnected
4797 player->peer_id = 0;
4805 std::ostringstream os(std::ios_base::binary);
4806 for(core::map<u16, RemoteClient*>::Iterator
4807 i = m_clients.getIterator();
4808 i.atEnd() == false; i++)
4810 RemoteClient *client = i.getNode()->getValue();
4811 assert(client->peer_id == i.getNode()->getKey());
4812 if(client->serialization_version == SER_FMT_VER_INVALID)
4815 Player *player = m_env->getPlayer(client->peer_id);
4818 // Get name of player
4819 os<<player->getName()<<" ";
4822 actionstream<<player->getName()<<" "
4823 <<(c.timeout?"times out.":"leaves game.")
4824 <<" List of players: "
4825 <<os.str()<<std::endl;
4830 delete m_clients[c.peer_id];
4831 m_clients.remove(c.peer_id);
4833 // Send player info to all remaining clients
4834 //SendPlayerInfos();
4836 // Send leave chat message to all remaining clients
4837 if(message.length() != 0)
4838 BroadcastChatMessage(message);
4847 void Server::handlePeerChanges()
4849 while(m_peer_change_queue.size() > 0)
4851 PeerChange c = m_peer_change_queue.pop_front();
4853 infostream<<"Server: Handling peer change: "
4854 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4857 handlePeerChange(c);
4861 u64 Server::getPlayerPrivs(Player *player)
4865 std::string playername = player->getName();
4866 // Local player gets all privileges regardless of
4867 // what's set on their account.
4868 if(g_settings->get("name") == playername)
4874 return getPlayerAuthPrivs(playername);
4878 void dedicated_server_loop(Server &server, bool &kill)
4880 DSTACK(__FUNCTION_NAME);
4882 infostream<<DTIME<<std::endl;
4883 infostream<<"========================"<<std::endl;
4884 infostream<<"Running dedicated server"<<std::endl;
4885 infostream<<"========================"<<std::endl;
4886 infostream<<std::endl;
4888 IntervalLimiter m_profiler_interval;
4892 // This is kind of a hack but can be done like this
4893 // because server.step() is very light
4895 ScopeProfiler sp(g_profiler, "dedicated server sleep");
4900 if(server.getShutdownRequested() || kill)
4902 infostream<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4909 float profiler_print_interval =
4910 g_settings->getFloat("profiler_print_interval");
4911 if(profiler_print_interval != 0)
4913 if(m_profiler_interval.step(0.030, profiler_print_interval))
4915 infostream<<"Profiler:"<<std::endl;
4916 g_profiler->print(infostream);
4917 g_profiler->clear();
4924 static int counter = 0;
4930 core::list<PlayerInfo> list = server.getPlayerInfo();
4931 core::list<PlayerInfo>::Iterator i;
4932 static u32 sum_old = 0;
4933 u32 sum = PIChecksum(list);
4936 infostream<<DTIME<<"Player info:"<<std::endl;
4937 for(i=list.begin(); i!=list.end(); i++)
4939 i->PrintLine(&infostream);