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.
23 #include "clientserver.h"
25 #include "jmutexautolock.h"
27 #include "constants.h"
29 #include "materials.h"
32 #include "servercommand.h"
34 #include "content_mapnode.h"
35 #include "content_craft.h"
36 #include "content_nodemeta.h"
38 #include "serverobject.h"
43 #include "scriptapi.h"
49 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
51 #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
53 class MapEditEventIgnorer
56 MapEditEventIgnorer(bool *flag):
65 ~MapEditEventIgnorer()
78 void * ServerThread::Thread()
82 log_register_thread("ServerThread");
84 DSTACK(__FUNCTION_NAME);
86 BEGIN_DEBUG_EXCEPTION_HANDLER
91 //TimeTaker timer("AsyncRunStep() + Receive()");
94 //TimeTaker timer("AsyncRunStep()");
95 m_server->AsyncRunStep();
98 //infostream<<"Running m_server->Receive()"<<std::endl;
101 catch(con::NoIncomingDataException &e)
104 catch(con::PeerNotFoundException &e)
106 infostream<<"Server: PeerNotFoundException"<<std::endl;
110 END_DEBUG_EXCEPTION_HANDLER(errorstream)
115 void * EmergeThread::Thread()
119 log_register_thread("EmergeThread");
121 DSTACK(__FUNCTION_NAME);
123 BEGIN_DEBUG_EXCEPTION_HANDLER
125 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
128 Get block info from queue, emerge them and send them
131 After queue is empty, exit.
135 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
139 SharedPtr<QueuedBlockEmerge> q(qptr);
145 Do not generate over-limit
147 if(blockpos_over_limit(p))
150 //infostream<<"EmergeThread::Thread(): running"<<std::endl;
152 //TimeTaker timer("block emerge");
155 Try to emerge it from somewhere.
157 If it is only wanted as optional, only loading from disk
162 Check if any peer wants it as non-optional. In that case it
165 Also decrement the emerge queue count in clients.
168 bool only_from_disk = true;
171 core::map<u16, u8>::Iterator i;
172 for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
174 //u16 peer_id = i.getNode()->getKey();
177 u8 flags = i.getNode()->getValue();
178 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
179 only_from_disk = false;
184 if(enable_mapgen_debug_info)
185 infostream<<"EmergeThread: p="
186 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
187 <<"only_from_disk="<<only_from_disk<<std::endl;
189 ServerMap &map = ((ServerMap&)m_server->m_env->getMap());
191 MapBlock *block = NULL;
192 bool got_block = true;
193 core::map<v3s16, MapBlock*> modified_blocks;
196 Try to fetch block from memory or disk.
197 If not found and asked to generate, initialize generator.
200 bool started_generate = false;
201 mapgen::BlockMakeData data;
204 JMutexAutoLock envlock(m_server->m_env_mutex);
206 // Load sector if it isn't loaded
207 if(map.getSectorNoGenerateNoEx(p2d) == NULL)
208 map.loadSectorMeta(p2d);
210 // Attempt to load block
211 block = map.getBlockNoCreateNoEx(p);
212 if(!block || block->isDummy() || !block->isGenerated())
214 if(enable_mapgen_debug_info)
215 infostream<<"EmergeThread: not in memory, "
216 <<"attempting to load from disk"<<std::endl;
218 block = map.loadBlock(p);
221 // If could not load and allowed to generate, start generation
222 // inside this same envlock
223 if(only_from_disk == false &&
224 (block == NULL || block->isGenerated() == false)){
225 if(enable_mapgen_debug_info)
226 infostream<<"EmergeThread: generating"<<std::endl;
227 started_generate = true;
229 map.initBlockMake(&data, p);
234 If generator was initialized, generate now when envlock is free.
239 ScopeProfiler sp(g_profiler, "EmergeThread: mapgen::make_block",
241 TimeTaker t("mapgen::make_block()");
243 mapgen::make_block(&data);
245 if(enable_mapgen_debug_info == false)
246 t.stop(true); // Hide output
250 // Lock environment again to access the map
251 JMutexAutoLock envlock(m_server->m_env_mutex);
253 ScopeProfiler sp(g_profiler, "EmergeThread: after "
254 "mapgen::make_block (envlock)", SPT_AVG);
256 // Blit data back on map, update lighting, add mobs and
257 // whatever this does
258 map.finishBlockMake(&data, modified_blocks);
261 block = map.getBlockNoCreateNoEx(p);
264 Do some post-generate stuff
267 v3s16 minp = block->getPos()*MAP_BLOCKSIZE;
268 v3s16 maxp = minp + v3s16(1,1,1)*(MAP_BLOCKSIZE-1);
269 scriptapi_environment_on_generated(m_server->m_lua,
272 if(enable_mapgen_debug_info)
273 infostream<<"EmergeThread: ended up with: "
274 <<analyze_block(block)<<std::endl;
277 Ignore map edit events, they will not need to be
278 sent to anybody because the block hasn't been sent
281 MapEditEventIgnorer ign(&m_server->m_ignore_map_edit_events);
283 // Activate objects and stuff
284 m_server->m_env->activateBlock(block, 3600);
292 Set sent status of modified blocks on clients
295 // NOTE: Server's clients are also behind the connection mutex
296 JMutexAutoLock lock(m_server->m_con_mutex);
299 Add the originally fetched block to the modified list
303 modified_blocks.insert(p, block);
307 Set the modified blocks unsent for all the clients
310 for(core::map<u16, RemoteClient*>::Iterator
311 i = m_server->m_clients.getIterator();
312 i.atEnd() == false; i++)
314 RemoteClient *client = i.getNode()->getValue();
316 if(modified_blocks.size() > 0)
318 // Remove block from sent history
319 client->SetBlocksNotSent(modified_blocks);
325 END_DEBUG_EXCEPTION_HANDLER(errorstream)
330 void RemoteClient::GetNextBlocks(Server *server, float dtime,
331 core::array<PrioritySortedBlockTransfer> &dest)
333 DSTACK(__FUNCTION_NAME);
336 TimeTaker timer("RemoteClient::GetNextBlocks", &timer_result);*/
339 m_nothing_to_send_pause_timer -= dtime;
340 m_nearest_unsent_reset_timer += dtime;
342 if(m_nothing_to_send_pause_timer >= 0)
347 // Won't send anything if already sending
348 if(m_blocks_sending.size() >= g_settings->getU16
349 ("max_simultaneous_block_sends_per_client"))
351 //infostream<<"Not sending any blocks, Queue full."<<std::endl;
355 //TimeTaker timer("RemoteClient::GetNextBlocks");
357 Player *player = server->m_env->getPlayer(peer_id);
359 assert(player != NULL);
361 v3f playerpos = player->getPosition();
362 v3f playerspeed = player->getSpeed();
363 v3f playerspeeddir(0,0,0);
364 if(playerspeed.getLength() > 1.0*BS)
365 playerspeeddir = playerspeed / playerspeed.getLength();
366 // Predict to next block
367 v3f playerpos_predicted = playerpos + playerspeeddir*MAP_BLOCKSIZE*BS;
369 v3s16 center_nodepos = floatToInt(playerpos_predicted, BS);
371 v3s16 center = getNodeBlockPos(center_nodepos);
373 // Camera position and direction
374 v3f camera_pos = player->getEyePosition();
375 v3f camera_dir = v3f(0,0,1);
376 camera_dir.rotateYZBy(player->getPitch());
377 camera_dir.rotateXZBy(player->getYaw());
379 /*infostream<<"camera_dir=("<<camera_dir.X<<","<<camera_dir.Y<<","
380 <<camera_dir.Z<<")"<<std::endl;*/
383 Get the starting value of the block finder radius.
386 if(m_last_center != center)
388 m_nearest_unsent_d = 0;
389 m_last_center = center;
392 /*infostream<<"m_nearest_unsent_reset_timer="
393 <<m_nearest_unsent_reset_timer<<std::endl;*/
395 // Reset periodically to workaround for some bugs or stuff
396 if(m_nearest_unsent_reset_timer > 20.0)
398 m_nearest_unsent_reset_timer = 0;
399 m_nearest_unsent_d = 0;
400 //infostream<<"Resetting m_nearest_unsent_d for "
401 // <<server->getPlayerName(peer_id)<<std::endl;
404 //s16 last_nearest_unsent_d = m_nearest_unsent_d;
405 s16 d_start = m_nearest_unsent_d;
407 //infostream<<"d_start="<<d_start<<std::endl;
409 u16 max_simul_sends_setting = g_settings->getU16
410 ("max_simultaneous_block_sends_per_client");
411 u16 max_simul_sends_usually = max_simul_sends_setting;
414 Check the time from last addNode/removeNode.
416 Decrease send rate if player is building stuff.
418 m_time_from_building += dtime;
419 if(m_time_from_building < g_settings->getFloat(
420 "full_block_send_enable_min_time_from_building"))
422 max_simul_sends_usually
423 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
427 Number of blocks sending + number of blocks selected for sending
429 u32 num_blocks_selected = m_blocks_sending.size();
432 next time d will be continued from the d from which the nearest
433 unsent block was found this time.
435 This is because not necessarily any of the blocks found this
436 time are actually sent.
438 s32 new_nearest_unsent_d = -1;
440 s16 d_max = g_settings->getS16("max_block_send_distance");
441 s16 d_max_gen = g_settings->getS16("max_block_generate_distance");
443 // Don't loop very much at a time
444 s16 max_d_increment_at_time = 2;
445 if(d_max > d_start + max_d_increment_at_time)
446 d_max = d_start + max_d_increment_at_time;
447 /*if(d_max_gen > d_start+2)
448 d_max_gen = d_start+2;*/
450 //infostream<<"Starting from "<<d_start<<std::endl;
452 s32 nearest_emerged_d = -1;
453 s32 nearest_emergefull_d = -1;
454 s32 nearest_sent_d = -1;
455 bool queue_is_full = false;
458 for(d = d_start; d <= d_max; d++)
460 /*errorstream<<"checking d="<<d<<" for "
461 <<server->getPlayerName(peer_id)<<std::endl;*/
462 //infostream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
465 If m_nearest_unsent_d was changed by the EmergeThread
466 (it can change it to 0 through SetBlockNotSent),
468 Else update m_nearest_unsent_d
470 /*if(m_nearest_unsent_d != last_nearest_unsent_d)
472 d = m_nearest_unsent_d;
473 last_nearest_unsent_d = m_nearest_unsent_d;
477 Get the border/face dot coordinates of a "d-radiused"
480 core::list<v3s16> list;
481 getFacePositions(list, d);
483 core::list<v3s16>::Iterator li;
484 for(li=list.begin(); li!=list.end(); li++)
486 v3s16 p = *li + center;
490 - Don't allow too many simultaneous transfers
491 - EXCEPT when the blocks are very close
493 Also, don't send blocks that are already flying.
496 // Start with the usual maximum
497 u16 max_simul_dynamic = max_simul_sends_usually;
499 // If block is very close, allow full maximum
500 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
501 max_simul_dynamic = max_simul_sends_setting;
503 // Don't select too many blocks for sending
504 if(num_blocks_selected >= max_simul_dynamic)
506 queue_is_full = true;
507 goto queue_full_break;
510 // Don't send blocks that are currently being transferred
511 if(m_blocks_sending.find(p) != NULL)
517 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
518 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
519 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
520 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
521 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
522 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
525 // If this is true, inexistent block will be made from scratch
526 bool generate = d <= d_max_gen;
529 /*// Limit the generating area vertically to 2/3
530 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
533 // Limit the send area vertically to 1/2
534 if(abs(p.Y - center.Y) > d_max / 2)
540 If block is far away, don't generate it unless it is
546 // Block center y in nodes
547 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
548 // Don't generate if it's very high or very low
549 if(y < -64 || y > 64)
553 v2s16 p2d_nodes_center(
557 // Get ground height in nodes
558 s16 gh = server->m_env->getServerMap().findGroundLevel(
561 // If differs a lot, don't generate
562 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
564 // Actually, don't even send it
570 //infostream<<"d="<<d<<std::endl;
573 Don't generate or send if not in sight
574 FIXME This only works if the client uses a small enough
575 FOV setting. The default of 72 degrees is fine.
578 float camera_fov = (72.0*PI/180) * 4./3.;
579 if(isBlockInSight(p, camera_pos, camera_dir, camera_fov, 10000*BS) == false)
585 Don't send already sent blocks
588 if(m_blocks_sent.find(p) != NULL)
595 Check if map has this block
597 MapBlock *block = server->m_env->getMap().getBlockNoCreateNoEx(p);
599 bool surely_not_found_on_disk = false;
600 bool block_is_invalid = false;
603 // Reset usage timer, this block will be of use in the future.
604 block->resetUsageTimer();
606 // Block is dummy if data doesn't exist.
607 // It means it has been not found from disk and not generated
610 surely_not_found_on_disk = true;
613 // Block is valid if lighting is up-to-date and data exists
614 if(block->isValid() == false)
616 block_is_invalid = true;
619 /*if(block->isFullyGenerated() == false)
621 block_is_invalid = true;
626 ServerMap *map = (ServerMap*)(&server->m_env->getMap());
627 v2s16 chunkpos = map->sector_to_chunk(p2d);
628 if(map->chunkNonVolatile(chunkpos) == false)
629 block_is_invalid = true;
631 if(block->isGenerated() == false)
632 block_is_invalid = true;
635 If block is not close, don't send it unless it is near
638 Block is near ground level if night-time mesh
639 differs from day-time mesh.
643 if(block->dayNightDiffed() == false)
650 If block has been marked to not exist on disk (dummy)
651 and generating new ones is not wanted, skip block.
653 if(generate == false && surely_not_found_on_disk == true)
660 Add inexistent block to emerge queue.
662 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
664 //TODO: Get value from somewhere
665 // Allow only one block in emerge queue
666 //if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
667 // Allow two blocks in queue per client
668 //if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
670 // Make it more responsive when needing to generate stuff
671 if(surely_not_found_on_disk)
673 if(server->m_emerge_queue.peerItemCount(peer_id) < max_emerge)
675 //infostream<<"Adding block to emerge queue"<<std::endl;
677 // Add it to the emerge queue and trigger the thread
680 if(generate == false)
681 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
683 server->m_emerge_queue.addBlock(peer_id, p, flags);
684 server->m_emergethread.trigger();
686 if(nearest_emerged_d == -1)
687 nearest_emerged_d = d;
689 if(nearest_emergefull_d == -1)
690 nearest_emergefull_d = d;
697 if(nearest_sent_d == -1)
701 Add block to send queue
704 /*errorstream<<"sending from d="<<d<<" to "
705 <<server->getPlayerName(peer_id)<<std::endl;*/
707 PrioritySortedBlockTransfer q((float)d, p, peer_id);
711 num_blocks_selected += 1;
716 //infostream<<"Stopped at "<<d<<std::endl;
718 // If nothing was found for sending and nothing was queued for
719 // emerging, continue next time browsing from here
720 if(nearest_emerged_d != -1){
721 new_nearest_unsent_d = nearest_emerged_d;
722 } else if(nearest_emergefull_d != -1){
723 new_nearest_unsent_d = nearest_emergefull_d;
725 if(d > g_settings->getS16("max_block_send_distance")){
726 new_nearest_unsent_d = 0;
727 m_nothing_to_send_pause_timer = 2.0;
728 /*infostream<<"GetNextBlocks(): d wrapped around for "
729 <<server->getPlayerName(peer_id)
730 <<"; setting to 0 and pausing"<<std::endl;*/
732 if(nearest_sent_d != -1)
733 new_nearest_unsent_d = nearest_sent_d;
735 new_nearest_unsent_d = d;
739 if(new_nearest_unsent_d != -1)
740 m_nearest_unsent_d = new_nearest_unsent_d;
742 /*timer_result = timer.stop(true);
743 if(timer_result != 0)
744 infostream<<"GetNextBlocks duration: "<<timer_result<<" (!=0)"<<std::endl;*/
747 void RemoteClient::SendObjectData(
750 core::map<v3s16, bool> &stepped_blocks
753 DSTACK(__FUNCTION_NAME);
755 // Can't send anything without knowing version
756 if(serialization_version == SER_FMT_VER_INVALID)
758 infostream<<"RemoteClient::SendObjectData(): Not sending, no version."
764 Send a TOCLIENT_OBJECTDATA packet.
768 u16 number of player positions
780 std::ostringstream os(std::ios_base::binary);
784 writeU16(buf, TOCLIENT_OBJECTDATA);
785 os.write((char*)buf, 2);
788 Get and write player data
791 // Get connected players
792 core::list<Player*> players = server->m_env->getPlayers(true);
794 // Write player count
795 u16 playercount = players.size();
796 writeU16(buf, playercount);
797 os.write((char*)buf, 2);
799 core::list<Player*>::Iterator i;
800 for(i = players.begin();
801 i != players.end(); i++)
805 v3f pf = player->getPosition();
806 v3f sf = player->getSpeed();
808 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
809 v3s32 speed_i (sf.X*100, sf.Y*100, sf.Z*100);
810 s32 pitch_i (player->getPitch() * 100);
811 s32 yaw_i (player->getYaw() * 100);
813 writeU16(buf, player->peer_id);
814 os.write((char*)buf, 2);
815 writeV3S32(buf, position_i);
816 os.write((char*)buf, 12);
817 writeV3S32(buf, speed_i);
818 os.write((char*)buf, 12);
819 writeS32(buf, pitch_i);
820 os.write((char*)buf, 4);
821 writeS32(buf, yaw_i);
822 os.write((char*)buf, 4);
826 Get and write object data (dummy, for compatibility)
831 os.write((char*)buf, 2);
837 //infostream<<"Server: Sending object data to "<<peer_id<<std::endl;
840 std::string s = os.str();
841 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
842 // Send as unreliable
843 server->m_con.Send(peer_id, 0, data, false);
846 void RemoteClient::GotBlock(v3s16 p)
848 if(m_blocks_sending.find(p) != NULL)
849 m_blocks_sending.remove(p);
852 /*infostream<<"RemoteClient::GotBlock(): Didn't find in"
853 " m_blocks_sending"<<std::endl;*/
854 m_excess_gotblocks++;
856 m_blocks_sent.insert(p, true);
859 void RemoteClient::SentBlock(v3s16 p)
861 if(m_blocks_sending.find(p) == NULL)
862 m_blocks_sending.insert(p, 0.0);
864 infostream<<"RemoteClient::SentBlock(): Sent block"
865 " already in m_blocks_sending"<<std::endl;
868 void RemoteClient::SetBlockNotSent(v3s16 p)
870 m_nearest_unsent_d = 0;
872 if(m_blocks_sending.find(p) != NULL)
873 m_blocks_sending.remove(p);
874 if(m_blocks_sent.find(p) != NULL)
875 m_blocks_sent.remove(p);
878 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
880 m_nearest_unsent_d = 0;
882 for(core::map<v3s16, MapBlock*>::Iterator
883 i = blocks.getIterator();
884 i.atEnd()==false; i++)
886 v3s16 p = i.getNode()->getKey();
888 if(m_blocks_sending.find(p) != NULL)
889 m_blocks_sending.remove(p);
890 if(m_blocks_sent.find(p) != NULL)
891 m_blocks_sent.remove(p);
899 PlayerInfo::PlayerInfo()
905 void PlayerInfo::PrintLine(std::ostream *s)
908 (*s)<<"\""<<name<<"\" ("
909 <<(position.X/10)<<","<<(position.Y/10)
910 <<","<<(position.Z/10)<<") ";
912 (*s)<<" avg_rtt="<<avg_rtt;
916 u32 PIChecksum(core::list<PlayerInfo> &l)
918 core::list<PlayerInfo>::Iterator i;
921 for(i=l.begin(); i!=l.end(); i++)
923 checksum += a * (i->id+1);
924 checksum ^= 0x435aafcd;
935 ModSpec(const std::string &name_="", const std::string path_=""):
941 static core::list<ModSpec> getMods(core::list<std::string> &modspaths)
943 core::list<ModSpec> mods;
944 for(core::list<std::string>::Iterator i = modspaths.begin();
945 i != modspaths.end(); i++){
946 std::string modspath = *i;
947 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(modspath);
948 for(u32 j=0; j<dirlist.size(); j++){
951 std::string modname = dirlist[j].name;
952 std::string modpath = modspath + DIR_DELIM + modname;
953 mods.push_back(ModSpec(modname, modpath));
964 std::string mapsavedir,
965 std::string configpath
968 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
969 m_authmanager(mapsavedir+DIR_DELIM+"auth.txt"),
970 m_banmanager(mapsavedir+DIR_DELIM+"ipban.txt"),
972 m_toolmgr(createToolDefManager()),
973 m_nodedef(createNodeDefManager()),
974 m_craftdef(createCraftDefManager()),
976 m_emergethread(this),
978 m_time_of_day_send_timer(0),
980 m_mapsavedir(mapsavedir),
981 m_configpath(configpath),
982 m_shutdown_requested(false),
983 m_ignore_map_edit_events(false),
984 m_ignore_map_edit_events_peer_id(0)
986 m_liquid_transform_timer = 0.0;
987 m_print_info_timer = 0.0;
988 m_objectdata_timer = 0.0;
989 m_emergethread_trigger_timer = 0.0;
990 m_savemap_timer = 0.0;
994 m_step_dtime_mutex.Init();
997 JMutexAutoLock envlock(m_env_mutex);
998 JMutexAutoLock conlock(m_con_mutex);
1000 infostream<<"m_nodedef="<<m_nodedef<<std::endl;
1002 // Path to builtin.lua
1003 std::string builtinpath = porting::path_data + DIR_DELIM + "builtin.lua";
1004 // Add default global mod path
1005 m_modspaths.push_back(porting::path_data + DIR_DELIM + "mods");
1007 // Initialize scripting
1009 infostream<<"Server: Initializing scripting"<<std::endl;
1010 m_lua = script_init();
1013 scriptapi_export(m_lua, this);
1014 // Load and run builtin.lua
1015 infostream<<"Server: Loading builtin Lua stuff from \""<<builtinpath
1017 bool success = script_load(m_lua, builtinpath.c_str());
1019 errorstream<<"Server: Failed to load and run "
1020 <<builtinpath<<std::endl;
1023 // Load and run "mod" scripts
1024 core::list<ModSpec> mods = getMods(m_modspaths);
1025 for(core::list<ModSpec>::Iterator i = mods.begin();
1026 i != mods.end(); i++){
1028 infostream<<"Server: Loading mod \""<<mod.name<<"\""<<std::endl;
1029 std::string scriptpath = mod.path + DIR_DELIM + "init.lua";
1030 bool success = script_load(m_lua, scriptpath.c_str());
1032 errorstream<<"Server: Failed to load and run "
1033 <<scriptpath<<std::endl;
1038 // Initialize Environment
1040 m_env = new ServerEnvironment(new ServerMap(mapsavedir, this), m_lua, this);
1042 // Give environment reference to scripting api
1043 scriptapi_add_environment(m_lua, m_env);
1045 // Register us to receive map edit events
1046 m_env->getMap().addEventReceiver(this);
1048 // If file exists, load environment metadata
1049 if(fs::PathExists(m_mapsavedir+DIR_DELIM+"env_meta.txt"))
1051 infostream<<"Server: Loading environment metadata"<<std::endl;
1052 m_env->loadMeta(m_mapsavedir);
1056 infostream<<"Server: Loading players"<<std::endl;
1057 m_env->deSerializePlayers(m_mapsavedir);
1062 infostream<<"Server::~Server()"<<std::endl;
1065 Send shutdown message
1068 JMutexAutoLock conlock(m_con_mutex);
1070 std::wstring line = L"*** Server shutting down";
1073 Send the message to clients
1075 for(core::map<u16, RemoteClient*>::Iterator
1076 i = m_clients.getIterator();
1077 i.atEnd() == false; i++)
1079 // Get client and check that it is valid
1080 RemoteClient *client = i.getNode()->getValue();
1081 assert(client->peer_id == i.getNode()->getKey());
1082 if(client->serialization_version == SER_FMT_VER_INVALID)
1086 SendChatMessage(client->peer_id, line);
1088 catch(con::PeerNotFoundException &e)
1094 JMutexAutoLock envlock(m_env_mutex);
1099 infostream<<"Server: Saving players"<<std::endl;
1100 m_env->serializePlayers(m_mapsavedir);
1103 Save environment metadata
1105 infostream<<"Server: Saving environment metadata"<<std::endl;
1106 m_env->saveMeta(m_mapsavedir);
1118 JMutexAutoLock clientslock(m_con_mutex);
1120 for(core::map<u16, RemoteClient*>::Iterator
1121 i = m_clients.getIterator();
1122 i.atEnd() == false; i++)
1125 // NOTE: These are removed by env destructor
1127 u16 peer_id = i.getNode()->getKey();
1128 JMutexAutoLock envlock(m_env_mutex);
1129 m_env->removePlayer(peer_id);
1133 delete i.getNode()->getValue();
1137 // Delete Environment
1143 // Deinitialize scripting
1144 infostream<<"Server: Deinitializing scripting"<<std::endl;
1145 script_deinit(m_lua);
1148 void Server::start(unsigned short port)
1150 DSTACK(__FUNCTION_NAME);
1151 // Stop thread if already running
1154 // Initialize connection
1155 m_con.SetTimeoutMs(30);
1159 m_thread.setRun(true);
1162 infostream<<"Server: Started on port "<<port<<std::endl;
1167 DSTACK(__FUNCTION_NAME);
1169 infostream<<"Server: Stopping and waiting threads"<<std::endl;
1171 // Stop threads (set run=false first so both start stopping)
1172 m_thread.setRun(false);
1173 m_emergethread.setRun(false);
1175 m_emergethread.stop();
1177 infostream<<"Server: Threads stopped"<<std::endl;
1180 void Server::step(float dtime)
1182 DSTACK(__FUNCTION_NAME);
1187 JMutexAutoLock lock(m_step_dtime_mutex);
1188 m_step_dtime += dtime;
1192 void Server::AsyncRunStep()
1194 DSTACK(__FUNCTION_NAME);
1196 g_profiler->add("Server::AsyncRunStep (num)", 1);
1200 JMutexAutoLock lock1(m_step_dtime_mutex);
1201 dtime = m_step_dtime;
1205 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
1206 // Send blocks to clients
1213 g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
1215 //infostream<<"Server steps "<<dtime<<std::endl;
1216 //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1219 JMutexAutoLock lock1(m_step_dtime_mutex);
1220 m_step_dtime -= dtime;
1227 m_uptime.set(m_uptime.get() + dtime);
1231 // Process connection's timeouts
1232 JMutexAutoLock lock2(m_con_mutex);
1233 ScopeProfiler sp(g_profiler, "Server: connection timeout processing");
1234 m_con.RunTimeouts(dtime);
1238 // This has to be called so that the client list gets synced
1239 // with the peer list of the connection
1240 handlePeerChanges();
1244 Update m_time_of_day and overall game time
1247 JMutexAutoLock envlock(m_env_mutex);
1249 m_time_counter += dtime;
1250 f32 speed = g_settings->getFloat("time_speed") * 24000./(24.*3600);
1251 u32 units = (u32)(m_time_counter*speed);
1252 m_time_counter -= (f32)units / speed;
1254 m_env->setTimeOfDay((m_env->getTimeOfDay() + units) % 24000);
1256 //infostream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1259 Send to clients at constant intervals
1262 m_time_of_day_send_timer -= dtime;
1263 if(m_time_of_day_send_timer < 0.0)
1265 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
1267 //JMutexAutoLock envlock(m_env_mutex);
1268 JMutexAutoLock conlock(m_con_mutex);
1270 for(core::map<u16, RemoteClient*>::Iterator
1271 i = m_clients.getIterator();
1272 i.atEnd() == false; i++)
1274 RemoteClient *client = i.getNode()->getValue();
1275 //Player *player = m_env->getPlayer(client->peer_id);
1277 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1278 m_env->getTimeOfDay());
1280 m_con.Send(client->peer_id, 0, data, true);
1286 JMutexAutoLock lock(m_env_mutex);
1288 ScopeProfiler sp(g_profiler, "SEnv step");
1289 ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
1293 const float map_timer_and_unload_dtime = 2.92;
1294 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
1296 JMutexAutoLock lock(m_env_mutex);
1297 // Run Map's timers and unload unused data
1298 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
1299 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
1300 g_settings->getFloat("server_unload_unused_data_timeout"));
1308 Check player movements
1310 NOTE: Actually the server should handle player physics like the
1311 client does and compare player's position to what is calculated
1312 on our side. This is required when eg. players fly due to an
1316 JMutexAutoLock lock(m_env_mutex);
1317 JMutexAutoLock lock2(m_con_mutex);
1319 //float player_max_speed = BS * 4.0; // Normal speed
1320 float player_max_speed = BS * 20; // Fast speed
1321 float player_max_speed_up = BS * 20;
1323 player_max_speed *= 2.5; // Tolerance
1324 player_max_speed_up *= 2.5;
1326 for(core::map<u16, RemoteClient*>::Iterator
1327 i = m_clients.getIterator();
1328 i.atEnd() == false; i++)
1330 RemoteClient *client = i.getNode()->getValue();
1331 ServerRemotePlayer *player =
1332 (ServerRemotePlayer*)m_env->getPlayer(client->peer_id);
1335 player->m_last_good_position_age += dtime;
1336 if(player->m_last_good_position_age >= 2.0){
1337 float age = player->m_last_good_position_age;
1338 v3f diff = (player->getPosition() - player->m_last_good_position);
1339 float d_vert = diff.Y;
1341 float d_horiz = diff.getLength();
1342 /*infostream<<player->getName()<<"'s horizontal speed is "
1343 <<(d_horiz/age)<<std::endl;*/
1344 if(d_horiz <= age * player_max_speed &&
1345 (d_vert < 0 || d_vert < age * player_max_speed_up)){
1346 player->m_last_good_position = player->getPosition();
1348 actionstream<<"Player "<<player->getName()
1349 <<" moved too fast; resetting position"
1351 player->setPosition(player->m_last_good_position);
1352 SendMovePlayer(player);
1354 player->m_last_good_position_age = 0;
1359 /* Transform liquids */
1360 m_liquid_transform_timer += dtime;
1361 if(m_liquid_transform_timer >= 1.00)
1363 m_liquid_transform_timer -= 1.00;
1365 JMutexAutoLock lock(m_env_mutex);
1367 ScopeProfiler sp(g_profiler, "Server: liquid transform");
1369 core::map<v3s16, MapBlock*> modified_blocks;
1370 m_env->getMap().transformLiquids(modified_blocks);
1375 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1376 ServerMap &map = ((ServerMap&)m_env->getMap());
1377 map.updateLighting(modified_blocks, lighting_modified_blocks);
1379 // Add blocks modified by lighting to modified_blocks
1380 for(core::map<v3s16, MapBlock*>::Iterator
1381 i = lighting_modified_blocks.getIterator();
1382 i.atEnd() == false; i++)
1384 MapBlock *block = i.getNode()->getValue();
1385 modified_blocks.insert(block->getPos(), block);
1389 Set the modified blocks unsent for all the clients
1392 JMutexAutoLock lock2(m_con_mutex);
1394 for(core::map<u16, RemoteClient*>::Iterator
1395 i = m_clients.getIterator();
1396 i.atEnd() == false; i++)
1398 RemoteClient *client = i.getNode()->getValue();
1400 if(modified_blocks.size() > 0)
1402 // Remove block from sent history
1403 client->SetBlocksNotSent(modified_blocks);
1408 // Periodically print some info
1410 float &counter = m_print_info_timer;
1416 JMutexAutoLock lock2(m_con_mutex);
1418 if(m_clients.size() != 0)
1419 infostream<<"Players:"<<std::endl;
1420 for(core::map<u16, RemoteClient*>::Iterator
1421 i = m_clients.getIterator();
1422 i.atEnd() == false; i++)
1424 //u16 peer_id = i.getNode()->getKey();
1425 RemoteClient *client = i.getNode()->getValue();
1426 Player *player = m_env->getPlayer(client->peer_id);
1429 infostream<<"* "<<player->getName()<<"\t";
1430 client->PrintInfo(infostream);
1435 //if(g_settings->getBool("enable_experimental"))
1439 Check added and deleted active objects
1442 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
1443 JMutexAutoLock envlock(m_env_mutex);
1444 JMutexAutoLock conlock(m_con_mutex);
1446 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
1448 // Radius inside which objects are active
1449 s16 radius = g_settings->getS16("active_object_send_range_blocks");
1450 radius *= MAP_BLOCKSIZE;
1452 for(core::map<u16, RemoteClient*>::Iterator
1453 i = m_clients.getIterator();
1454 i.atEnd() == false; i++)
1456 RemoteClient *client = i.getNode()->getValue();
1457 Player *player = m_env->getPlayer(client->peer_id);
1460 // This can happen if the client timeouts somehow
1461 /*infostream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
1463 <<" has no associated player"<<std::endl;*/
1466 v3s16 pos = floatToInt(player->getPosition(), BS);
1468 core::map<u16, bool> removed_objects;
1469 core::map<u16, bool> added_objects;
1470 m_env->getRemovedActiveObjects(pos, radius,
1471 client->m_known_objects, removed_objects);
1472 m_env->getAddedActiveObjects(pos, radius,
1473 client->m_known_objects, added_objects);
1475 // Ignore if nothing happened
1476 if(removed_objects.size() == 0 && added_objects.size() == 0)
1478 //infostream<<"active objects: none changed"<<std::endl;
1482 std::string data_buffer;
1486 // Handle removed objects
1487 writeU16((u8*)buf, removed_objects.size());
1488 data_buffer.append(buf, 2);
1489 for(core::map<u16, bool>::Iterator
1490 i = removed_objects.getIterator();
1491 i.atEnd()==false; i++)
1494 u16 id = i.getNode()->getKey();
1495 ServerActiveObject* obj = m_env->getActiveObject(id);
1497 // Add to data buffer for sending
1498 writeU16((u8*)buf, i.getNode()->getKey());
1499 data_buffer.append(buf, 2);
1501 // Remove from known objects
1502 client->m_known_objects.remove(i.getNode()->getKey());
1504 if(obj && obj->m_known_by_count > 0)
1505 obj->m_known_by_count--;
1508 // Handle added objects
1509 writeU16((u8*)buf, added_objects.size());
1510 data_buffer.append(buf, 2);
1511 for(core::map<u16, bool>::Iterator
1512 i = added_objects.getIterator();
1513 i.atEnd()==false; i++)
1516 u16 id = i.getNode()->getKey();
1517 ServerActiveObject* obj = m_env->getActiveObject(id);
1520 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1522 infostream<<"WARNING: "<<__FUNCTION_NAME
1523 <<": NULL object"<<std::endl;
1525 type = obj->getType();
1527 // Add to data buffer for sending
1528 writeU16((u8*)buf, id);
1529 data_buffer.append(buf, 2);
1530 writeU8((u8*)buf, type);
1531 data_buffer.append(buf, 1);
1534 data_buffer.append(serializeLongString(
1535 obj->getClientInitializationData()));
1537 data_buffer.append(serializeLongString(""));
1539 // Add to known objects
1540 client->m_known_objects.insert(i.getNode()->getKey(), false);
1543 obj->m_known_by_count++;
1547 SharedBuffer<u8> reply(2 + data_buffer.size());
1548 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1549 memcpy((char*)&reply[2], data_buffer.c_str(),
1550 data_buffer.size());
1552 m_con.Send(client->peer_id, 0, reply, true);
1554 infostream<<"Server: Sent object remove/add: "
1555 <<removed_objects.size()<<" removed, "
1556 <<added_objects.size()<<" added, "
1557 <<"packet size is "<<reply.getSize()<<std::endl;
1562 Collect a list of all the objects known by the clients
1563 and report it back to the environment.
1566 core::map<u16, bool> all_known_objects;
1568 for(core::map<u16, RemoteClient*>::Iterator
1569 i = m_clients.getIterator();
1570 i.atEnd() == false; i++)
1572 RemoteClient *client = i.getNode()->getValue();
1573 // Go through all known objects of client
1574 for(core::map<u16, bool>::Iterator
1575 i = client->m_known_objects.getIterator();
1576 i.atEnd()==false; i++)
1578 u16 id = i.getNode()->getKey();
1579 all_known_objects[id] = true;
1583 m_env->setKnownActiveObjects(whatever);
1589 Send object messages
1592 JMutexAutoLock envlock(m_env_mutex);
1593 JMutexAutoLock conlock(m_con_mutex);
1595 //ScopeProfiler sp(g_profiler, "Server: sending object messages");
1598 // Value = data sent by object
1599 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1601 // Get active object messages from environment
1604 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
1608 core::list<ActiveObjectMessage>* message_list = NULL;
1609 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1610 n = buffered_messages.find(aom.id);
1613 message_list = new core::list<ActiveObjectMessage>;
1614 buffered_messages.insert(aom.id, message_list);
1618 message_list = n->getValue();
1620 message_list->push_back(aom);
1623 // Route data to every client
1624 for(core::map<u16, RemoteClient*>::Iterator
1625 i = m_clients.getIterator();
1626 i.atEnd()==false; i++)
1628 RemoteClient *client = i.getNode()->getValue();
1629 std::string reliable_data;
1630 std::string unreliable_data;
1631 // Go through all objects in message buffer
1632 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1633 j = buffered_messages.getIterator();
1634 j.atEnd()==false; j++)
1636 // If object is not known by client, skip it
1637 u16 id = j.getNode()->getKey();
1638 if(client->m_known_objects.find(id) == NULL)
1640 // Get message list of object
1641 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1642 // Go through every message
1643 for(core::list<ActiveObjectMessage>::Iterator
1644 k = list->begin(); k != list->end(); k++)
1646 // Compose the full new data with header
1647 ActiveObjectMessage aom = *k;
1648 std::string new_data;
1651 writeU16((u8*)&buf[0], aom.id);
1652 new_data.append(buf, 2);
1654 new_data += serializeString(aom.datastring);
1655 // Add data to buffer
1657 reliable_data += new_data;
1659 unreliable_data += new_data;
1663 reliable_data and unreliable_data are now ready.
1666 if(reliable_data.size() > 0)
1668 SharedBuffer<u8> reply(2 + reliable_data.size());
1669 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1670 memcpy((char*)&reply[2], reliable_data.c_str(),
1671 reliable_data.size());
1673 m_con.Send(client->peer_id, 0, reply, true);
1675 if(unreliable_data.size() > 0)
1677 SharedBuffer<u8> reply(2 + unreliable_data.size());
1678 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1679 memcpy((char*)&reply[2], unreliable_data.c_str(),
1680 unreliable_data.size());
1681 // Send as unreliable
1682 m_con.Send(client->peer_id, 0, reply, false);
1685 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1687 infostream<<"Server: Size of object message data: "
1688 <<"reliable: "<<reliable_data.size()
1689 <<", unreliable: "<<unreliable_data.size()
1694 // Clear buffered_messages
1695 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1696 i = buffered_messages.getIterator();
1697 i.atEnd()==false; i++)
1699 delete i.getNode()->getValue();
1703 } // enable_experimental
1706 Send queued-for-sending map edit events.
1709 // Don't send too many at a time
1712 // Single change sending is disabled if queue size is not small
1713 bool disable_single_change_sending = false;
1714 if(m_unsent_map_edit_queue.size() >= 4)
1715 disable_single_change_sending = true;
1717 bool got_any_events = false;
1719 // We'll log the amount of each
1722 while(m_unsent_map_edit_queue.size() != 0)
1724 got_any_events = true;
1726 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1728 // Players far away from the change are stored here.
1729 // Instead of sending the changes, MapBlocks are set not sent
1731 core::list<u16> far_players;
1733 if(event->type == MEET_ADDNODE)
1735 //infostream<<"Server: MEET_ADDNODE"<<std::endl;
1736 prof.add("MEET_ADDNODE", 1);
1737 if(disable_single_change_sending)
1738 sendAddNode(event->p, event->n, event->already_known_by_peer,
1741 sendAddNode(event->p, event->n, event->already_known_by_peer,
1744 else if(event->type == MEET_REMOVENODE)
1746 //infostream<<"Server: MEET_REMOVENODE"<<std::endl;
1747 prof.add("MEET_REMOVENODE", 1);
1748 if(disable_single_change_sending)
1749 sendRemoveNode(event->p, event->already_known_by_peer,
1752 sendRemoveNode(event->p, event->already_known_by_peer,
1755 else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
1757 infostream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
1758 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
1759 setBlockNotSent(event->p);
1761 else if(event->type == MEET_OTHER)
1763 infostream<<"Server: MEET_OTHER"<<std::endl;
1764 prof.add("MEET_OTHER", 1);
1765 for(core::map<v3s16, bool>::Iterator
1766 i = event->modified_blocks.getIterator();
1767 i.atEnd()==false; i++)
1769 v3s16 p = i.getNode()->getKey();
1775 prof.add("unknown", 1);
1776 infostream<<"WARNING: Server: Unknown MapEditEvent "
1777 <<((u32)event->type)<<std::endl;
1781 Set blocks not sent to far players
1783 if(far_players.size() > 0)
1785 // Convert list format to that wanted by SetBlocksNotSent
1786 core::map<v3s16, MapBlock*> modified_blocks2;
1787 for(core::map<v3s16, bool>::Iterator
1788 i = event->modified_blocks.getIterator();
1789 i.atEnd()==false; i++)
1791 v3s16 p = i.getNode()->getKey();
1792 modified_blocks2.insert(p,
1793 m_env->getMap().getBlockNoCreateNoEx(p));
1795 // Set blocks not sent
1796 for(core::list<u16>::Iterator
1797 i = far_players.begin();
1798 i != far_players.end(); i++)
1801 RemoteClient *client = getClient(peer_id);
1804 client->SetBlocksNotSent(modified_blocks2);
1810 /*// Don't send too many at a time
1812 if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
1818 infostream<<"Server: MapEditEvents:"<<std::endl;
1819 prof.print(infostream);
1825 Send object positions
1828 float &counter = m_objectdata_timer;
1830 if(counter >= g_settings->getFloat("objectdata_interval"))
1832 JMutexAutoLock lock1(m_env_mutex);
1833 JMutexAutoLock lock2(m_con_mutex);
1835 //ScopeProfiler sp(g_profiler, "Server: sending player positions");
1837 SendObjectData(counter);
1844 Trigger emergethread (it somehow gets to a non-triggered but
1845 bysy state sometimes)
1848 float &counter = m_emergethread_trigger_timer;
1854 m_emergethread.trigger();
1858 // Save map, players and auth stuff
1860 float &counter = m_savemap_timer;
1862 if(counter >= g_settings->getFloat("server_map_save_interval"))
1866 ScopeProfiler sp(g_profiler, "Server: saving stuff");
1869 if(m_authmanager.isModified())
1870 m_authmanager.save();
1873 if(m_banmanager.isModified())
1874 m_banmanager.save();
1877 JMutexAutoLock lock(m_env_mutex);
1879 /*// Unload unused data (delete from memory)
1880 m_env->getMap().unloadUnusedData(
1881 g_settings->getFloat("server_unload_unused_sectors_timeout"));
1883 /*u32 deleted_count = m_env->getMap().unloadUnusedData(
1884 g_settings->getFloat("server_unload_unused_sectors_timeout"));
1887 // Save only changed parts
1888 m_env->getMap().save(true);
1890 /*if(deleted_count > 0)
1892 infostream<<"Server: Unloaded "<<deleted_count
1893 <<" blocks from memory"<<std::endl;
1897 m_env->serializePlayers(m_mapsavedir);
1899 // Save environment metadata
1900 m_env->saveMeta(m_mapsavedir);
1905 void Server::Receive()
1907 DSTACK(__FUNCTION_NAME);
1908 SharedBuffer<u8> data;
1913 JMutexAutoLock conlock(m_con_mutex);
1914 datasize = m_con.Receive(peer_id, data);
1917 // This has to be called so that the client list gets synced
1918 // with the peer list of the connection
1919 handlePeerChanges();
1921 ProcessData(*data, datasize, peer_id);
1923 catch(con::InvalidIncomingDataException &e)
1925 infostream<<"Server::Receive(): "
1926 "InvalidIncomingDataException: what()="
1927 <<e.what()<<std::endl;
1929 catch(con::PeerNotFoundException &e)
1931 //NOTE: This is not needed anymore
1933 // The peer has been disconnected.
1934 // Find the associated player and remove it.
1936 /*JMutexAutoLock envlock(m_env_mutex);
1938 infostream<<"ServerThread: peer_id="<<peer_id
1939 <<" has apparently closed connection. "
1940 <<"Removing player."<<std::endl;
1942 m_env->removePlayer(peer_id);*/
1946 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1948 DSTACK(__FUNCTION_NAME);
1949 // Environment is locked first.
1950 JMutexAutoLock envlock(m_env_mutex);
1951 JMutexAutoLock conlock(m_con_mutex);
1954 Address address = m_con.GetPeerAddress(peer_id);
1956 // drop player if is ip is banned
1957 if(m_banmanager.isIpBanned(address.serializeString())){
1958 SendAccessDenied(m_con, peer_id,
1959 L"Your ip is banned. Banned name was "
1960 +narrow_to_wide(m_banmanager.getBanName(
1961 address.serializeString())));
1962 m_con.DeletePeer(peer_id);
1966 catch(con::PeerNotFoundException &e)
1968 infostream<<"Server::ProcessData(): Cancelling: peer "
1969 <<peer_id<<" not found"<<std::endl;
1973 u8 peer_ser_ver = getClient(peer_id)->serialization_version;
1981 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1983 if(command == TOSERVER_INIT)
1985 // [0] u16 TOSERVER_INIT
1986 // [2] u8 SER_FMT_VER_HIGHEST
1987 // [3] u8[20] player_name
1988 // [23] u8[28] password <--- can be sent without this, from old versions
1990 if(datasize < 2+1+PLAYERNAME_SIZE)
1993 infostream<<"Server: Got TOSERVER_INIT from "
1994 <<peer_id<<std::endl;
1996 // First byte after command is maximum supported
1997 // serialization version
1998 u8 client_max = data[2];
1999 u8 our_max = SER_FMT_VER_HIGHEST;
2000 // Use the highest version supported by both
2001 u8 deployed = core::min_(client_max, our_max);
2002 // If it's lower than the lowest supported, give up.
2003 if(deployed < SER_FMT_VER_LOWEST)
2004 deployed = SER_FMT_VER_INVALID;
2006 //peer->serialization_version = deployed;
2007 getClient(peer_id)->pending_serialization_version = deployed;
2009 if(deployed == SER_FMT_VER_INVALID)
2011 infostream<<"Server: Cannot negotiate "
2012 "serialization version with peer "
2013 <<peer_id<<std::endl;
2014 SendAccessDenied(m_con, peer_id,
2015 L"Your client is too old (map format)");
2020 Read and check network protocol version
2023 u16 net_proto_version = 0;
2024 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
2026 net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
2029 getClient(peer_id)->net_proto_version = net_proto_version;
2031 if(net_proto_version == 0)
2033 SendAccessDenied(m_con, peer_id,
2034 L"Your client is too old. Please upgrade.");
2038 /* Uhh... this should actually be a warning but let's do it like this */
2039 if(g_settings->getBool("strict_protocol_version_checking"))
2041 if(net_proto_version < PROTOCOL_VERSION)
2043 SendAccessDenied(m_con, peer_id,
2044 L"Your client is too old. Please upgrade.");
2054 char playername[PLAYERNAME_SIZE];
2055 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
2057 playername[i] = data[3+i];
2059 playername[PLAYERNAME_SIZE-1] = 0;
2061 if(playername[0]=='\0')
2063 infostream<<"Server: Player has empty name"<<std::endl;
2064 SendAccessDenied(m_con, peer_id,
2069 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
2071 infostream<<"Server: Player has invalid name"<<std::endl;
2072 SendAccessDenied(m_con, peer_id,
2073 L"Name contains unallowed characters");
2078 char password[PASSWORD_SIZE];
2079 if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
2081 // old version - assume blank password
2086 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2088 password[i] = data[23+i];
2090 password[PASSWORD_SIZE-1] = 0;
2093 std::string checkpwd;
2094 if(m_authmanager.exists(playername))
2096 checkpwd = m_authmanager.getPassword(playername);
2100 checkpwd = g_settings->get("default_password");
2103 /*infostream<<"Server: Client gave password '"<<password
2104 <<"', the correct one is '"<<checkpwd<<"'"<<std::endl;*/
2106 if(password != checkpwd && m_authmanager.exists(playername))
2108 infostream<<"Server: peer_id="<<peer_id
2109 <<": supplied invalid password for "
2110 <<playername<<std::endl;
2111 SendAccessDenied(m_con, peer_id, L"Invalid password");
2115 // Add player to auth manager
2116 if(m_authmanager.exists(playername) == false)
2118 infostream<<"Server: adding player "<<playername
2119 <<" to auth manager"<<std::endl;
2120 m_authmanager.add(playername);
2121 m_authmanager.setPassword(playername, checkpwd);
2122 m_authmanager.setPrivs(playername,
2123 stringToPrivs(g_settings->get("default_privs")));
2124 m_authmanager.save();
2127 // Enforce user limit.
2128 // Don't enforce for users that have some admin right
2129 if(m_clients.size() >= g_settings->getU16("max_users") &&
2130 (m_authmanager.getPrivs(playername)
2131 & (PRIV_SERVER|PRIV_BAN|PRIV_PRIVS)) == 0 &&
2132 playername != g_settings->get("name"))
2134 SendAccessDenied(m_con, peer_id, L"Too many users.");
2139 Player *player = emergePlayer(playername, password, peer_id);
2141 // If failed, cancel
2144 infostream<<"Server: peer_id="<<peer_id
2145 <<": failed to emerge player"<<std::endl;
2150 Answer with a TOCLIENT_INIT
2153 SharedBuffer<u8> reply(2+1+6+8);
2154 writeU16(&reply[0], TOCLIENT_INIT);
2155 writeU8(&reply[2], deployed);
2156 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
2157 writeU64(&reply[2+1+6], m_env->getServerMap().getSeed());
2160 m_con.Send(peer_id, 0, reply, true);
2164 Send complete position information
2166 SendMovePlayer(player);
2171 if(command == TOSERVER_INIT2)
2173 infostream<<"Server: Got TOSERVER_INIT2 from "
2174 <<peer_id<<std::endl;
2177 getClient(peer_id)->serialization_version
2178 = getClient(peer_id)->pending_serialization_version;
2181 Send some initialization data
2184 // Send tool definitions
2185 SendToolDef(m_con, peer_id, m_toolmgr);
2187 // Send node definitions
2188 SendNodeDef(m_con, peer_id, m_nodedef);
2191 SendTextures(peer_id);
2193 // Send player info to all players
2196 // Send inventory to player
2197 UpdateCrafting(peer_id);
2198 SendInventory(peer_id);
2200 // Send player items to all players
2203 Player *player = m_env->getPlayer(peer_id);
2206 SendPlayerHP(player);
2210 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2211 m_env->getTimeOfDay());
2212 m_con.Send(peer_id, 0, data, true);
2215 // Send information about server to player in chat
2216 SendChatMessage(peer_id, getStatusString());
2218 // Send information about joining in chat
2220 std::wstring name = L"unknown";
2221 Player *player = m_env->getPlayer(peer_id);
2223 name = narrow_to_wide(player->getName());
2225 std::wstring message;
2228 message += L" joined game";
2229 BroadcastChatMessage(message);
2232 // Warnings about protocol version can be issued here
2233 if(getClient(peer_id)->net_proto_version < PROTOCOL_VERSION)
2235 SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT IS OLD AND MAY WORK PROPERLY WITH THIS SERVER");
2239 Check HP, respawn if necessary
2241 HandlePlayerHP(player, 0);
2247 std::ostringstream os(std::ios_base::binary);
2248 for(core::map<u16, RemoteClient*>::Iterator
2249 i = m_clients.getIterator();
2250 i.atEnd() == false; i++)
2252 RemoteClient *client = i.getNode()->getValue();
2253 assert(client->peer_id == i.getNode()->getKey());
2254 if(client->serialization_version == SER_FMT_VER_INVALID)
2257 Player *player = m_env->getPlayer(client->peer_id);
2260 // Get name of player
2261 os<<player->getName()<<" ";
2264 actionstream<<player->getName()<<" joins game. List of players: "
2265 <<os.str()<<std::endl;
2271 if(peer_ser_ver == SER_FMT_VER_INVALID)
2273 infostream<<"Server::ProcessData(): Cancelling: Peer"
2274 " serialization format invalid or not initialized."
2275 " Skipping incoming command="<<command<<std::endl;
2279 Player *player = m_env->getPlayer(peer_id);
2282 infostream<<"Server::ProcessData(): Cancelling: "
2283 "No player for peer_id="<<peer_id
2287 if(command == TOSERVER_PLAYERPOS)
2289 if(datasize < 2+12+12+4+4)
2293 v3s32 ps = readV3S32(&data[start+2]);
2294 v3s32 ss = readV3S32(&data[start+2+12]);
2295 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2296 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2297 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2298 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2299 pitch = wrapDegrees(pitch);
2300 yaw = wrapDegrees(yaw);
2302 player->setPosition(position);
2303 player->setSpeed(speed);
2304 player->setPitch(pitch);
2305 player->setYaw(yaw);
2307 /*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2308 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2309 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2311 else if(command == TOSERVER_GOTBLOCKS)
2324 u16 count = data[2];
2325 for(u16 i=0; i<count; i++)
2327 if((s16)datasize < 2+1+(i+1)*6)
2328 throw con::InvalidIncomingDataException
2329 ("GOTBLOCKS length is too short");
2330 v3s16 p = readV3S16(&data[2+1+i*6]);
2331 /*infostream<<"Server: GOTBLOCKS ("
2332 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2333 RemoteClient *client = getClient(peer_id);
2334 client->GotBlock(p);
2337 else if(command == TOSERVER_DELETEDBLOCKS)
2350 u16 count = data[2];
2351 for(u16 i=0; i<count; i++)
2353 if((s16)datasize < 2+1+(i+1)*6)
2354 throw con::InvalidIncomingDataException
2355 ("DELETEDBLOCKS length is too short");
2356 v3s16 p = readV3S16(&data[2+1+i*6]);
2357 /*infostream<<"Server: DELETEDBLOCKS ("
2358 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2359 RemoteClient *client = getClient(peer_id);
2360 client->SetBlockNotSent(p);
2363 else if(command == TOSERVER_CLICK_OBJECT)
2365 infostream<<"Server: CLICK_OBJECT not supported anymore"<<std::endl;
2368 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2373 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2379 [2] u8 button (0=left, 1=right)
2383 u8 button = readU8(&data[2]);
2384 u16 id = readS16(&data[3]);
2385 u16 item_i = readU16(&data[5]);
2387 ServerActiveObject *obj = m_env->getActiveObject(id);
2391 infostream<<"Server: CLICK_ACTIVEOBJECT: object not found"
2396 // Skip if object has been removed
2400 //TODO: Check that object is reasonably close
2402 // Get ServerRemotePlayer
2403 ServerRemotePlayer *srp = (ServerRemotePlayer*)player;
2405 // Update wielded item
2406 srp->wieldItem(item_i);
2408 // Left click, pick/punch
2411 actionstream<<player->getName()<<" punches object "
2412 <<obj->getId()<<std::endl;
2419 Try creating inventory item
2421 InventoryItem *item = obj->createPickedUpItem();
2425 InventoryList *ilist = player->inventory.getList("main");
2428 actionstream<<player->getName()<<" picked up "
2429 <<item->getName()<<std::endl;
2430 if(g_settings->getBool("creative_mode") == false)
2432 // Skip if inventory has no free space
2433 if(ilist->roomForItem(item) == false)
2435 infostream<<"Player inventory has no free space"<<std::endl;
2439 // Add to inventory and send inventory
2440 ilist->addItem(item);
2441 UpdateCrafting(player->peer_id);
2442 SendInventory(player->peer_id);
2445 // Remove object from environment
2446 obj->m_removed = true;
2452 Item cannot be picked up. Punch it instead.
2455 actionstream<<player->getName()<<" punches object "
2456 <<obj->getId()<<std::endl;
2458 ToolItem *titem = NULL;
2459 std::string toolname = "";
2461 InventoryList *mlist = player->inventory.getList("main");
2464 InventoryItem *item = mlist->getItem(item_i);
2465 if(item && (std::string)item->getName() == "ToolItem")
2467 titem = (ToolItem*)item;
2468 toolname = titem->getToolName();
2472 v3f playerpos = player->getPosition();
2473 v3f objpos = obj->getBasePosition();
2474 v3f dir = (objpos - playerpos).normalize();
2476 u16 wear = obj->punch(toolname, dir, player->getName());
2480 bool weared_out = titem->addWear(wear);
2482 mlist->deleteItem(item_i);
2483 SendInventory(player->peer_id);
2488 // Right click, do something with object
2491 actionstream<<player->getName()<<" right clicks object "
2492 <<obj->getId()<<std::endl;
2495 obj->rightClick(srp);
2499 Update player state to client
2501 SendPlayerHP(player);
2502 UpdateCrafting(player->peer_id);
2503 SendInventory(player->peer_id);
2505 else if(command == TOSERVER_GROUND_ACTION)
2513 [3] v3s16 nodepos_undersurface
2514 [9] v3s16 nodepos_abovesurface
2519 2: stop digging (all parameters ignored)
2520 3: digging completed
2522 u8 action = readU8(&data[2]);
2524 p_under.X = readS16(&data[3]);
2525 p_under.Y = readS16(&data[5]);
2526 p_under.Z = readS16(&data[7]);
2528 p_over.X = readS16(&data[9]);
2529 p_over.Y = readS16(&data[11]);
2530 p_over.Z = readS16(&data[13]);
2531 u16 item_i = readU16(&data[15]);
2533 ServerRemotePlayer *srp = (ServerRemotePlayer*)player;
2536 Check that target is reasonably close
2538 if(action != 2) // action 2 has always position (0,0,0)
2540 v3f np_f = intToFloat(p_under, BS);
2541 float max_d = BS * 10; // Just some large enough value
2542 float d = srp->m_last_good_position.getDistanceFrom(np_f);
2544 actionstream<<"Player "<<player->getName()
2545 <<" tried to access node from too far: "
2546 <<"d="<<d<<", max_d="<<max_d
2547 <<". ignoring."<<std::endl;
2548 // Re-send block to revert change on client-side
2549 RemoteClient *client = getClient(peer_id);
2550 v3s16 blockpos = getNodeBlockPos(p_under);
2551 client->SetBlockNotSent(blockpos);
2563 NOTE: This can be used in the future to check if
2564 somebody is cheating, by checking the timing.
2566 bool cannot_punch_node = false;
2568 MapNode n(CONTENT_IGNORE);
2572 n = m_env->getMap().getNode(p_under);
2574 catch(InvalidPositionException &e)
2576 infostream<<"Server: Not punching: Node not found."
2577 <<" Adding block to emerge queue."
2579 m_emerge_queue.addBlock(peer_id,
2580 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2581 cannot_punch_node = true;
2584 if(cannot_punch_node)
2590 scriptapi_environment_on_punchnode(m_lua, p_under, n, srp);
2597 else if(action == 2)
2600 RemoteClient *client = getClient(peer_id);
2601 JMutexAutoLock digmutex(client->m_dig_mutex);
2602 client->m_dig_tool_item = -1;
2607 3: Digging completed
2609 else if(action == 3)
2611 // Mandatory parameter; actually used for nothing
2612 core::map<v3s16, MapBlock*> modified_blocks;
2614 content_t material = CONTENT_IGNORE;
2615 u8 mineral = MINERAL_NONE;
2617 bool cannot_remove_node = false;
2619 MapNode n(CONTENT_IGNORE);
2622 n = m_env->getMap().getNode(p_under);
2624 mineral = n.getMineral(m_nodedef);
2625 // Get material at position
2626 material = n.getContent();
2627 // If not yet cancelled
2628 if(cannot_remove_node == false)
2630 // If it's not diggable, do nothing
2631 if(m_nodedef->get(material).diggable == false)
2633 infostream<<"Server: Not finishing digging: "
2634 <<"Node not diggable"
2636 cannot_remove_node = true;
2639 // If not yet cancelled
2640 if(cannot_remove_node == false)
2642 // Get node metadata
2643 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p_under);
2644 if(meta && meta->nodeRemovalDisabled() == true)
2646 infostream<<"Server: Not finishing digging: "
2647 <<"Node metadata disables removal"
2649 cannot_remove_node = true;
2653 catch(InvalidPositionException &e)
2655 infostream<<"Server: Not finishing digging: Node not found."
2656 <<" Adding block to emerge queue."
2658 m_emerge_queue.addBlock(peer_id,
2659 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2660 cannot_remove_node = true;
2663 // Make sure the player is allowed to do it
2664 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2666 infostream<<"Player "<<player->getName()<<" cannot remove node"
2667 <<" because privileges are "<<getPlayerPrivs(player)
2669 cannot_remove_node = true;
2673 If node can't be removed, set block to be re-sent to
2676 if(cannot_remove_node)
2678 infostream<<"Server: Not finishing digging."<<std::endl;
2680 // Client probably has wrong data.
2681 // Set block not sent, so that client will get
2683 infostream<<"Client "<<peer_id<<" tried to dig "
2684 <<"node; but node cannot be removed."
2685 <<" setting MapBlock not sent."<<std::endl;
2686 RemoteClient *client = getClient(peer_id);
2687 v3s16 blockpos = getNodeBlockPos(p_under);
2688 client->SetBlockNotSent(blockpos);
2693 actionstream<<player->getName()<<" digs "<<PP(p_under)
2694 <<", gets material "<<(int)material<<", mineral "
2695 <<(int)mineral<<std::endl;
2698 Send the removal to all close-by players.
2699 - If other player is close, send REMOVENODE
2700 - Otherwise set blocks not sent
2702 core::list<u16> far_players;
2703 sendRemoveNode(p_under, peer_id, &far_players, 30);
2706 Update and send inventory
2709 if(g_settings->getBool("creative_mode") == false)
2714 InventoryList *mlist = player->inventory.getList("main");
2717 InventoryItem *item = mlist->getItem(item_i);
2718 if(item && (std::string)item->getName() == "ToolItem")
2720 ToolItem *titem = (ToolItem*)item;
2721 std::string toolname = titem->getToolName();
2723 // Get digging properties for material and tool
2724 ToolDiggingProperties tp =
2725 m_toolmgr->getDiggingProperties(toolname);
2726 DiggingProperties prop =
2727 getDiggingProperties(material, &tp, m_nodedef);
2729 if(prop.diggable == false)
2731 infostream<<"Server: WARNING: Player digged"
2732 <<" with impossible material + tool"
2733 <<" combination"<<std::endl;
2736 bool weared_out = titem->addWear(prop.wear);
2740 mlist->deleteItem(item_i);
2746 Add dug item to inventory
2749 InventoryItem *item = NULL;
2751 if(mineral != MINERAL_NONE)
2752 item = getDiggedMineralItem(mineral, this);
2757 const std::string &dug_s = m_nodedef->get(material).dug_item;
2760 std::istringstream is(dug_s, std::ios::binary);
2761 item = InventoryItem::deSerialize(is, this);
2767 // Add a item to inventory
2768 player->inventory.addItem("main", item);
2771 UpdateCrafting(player->peer_id);
2772 SendInventory(player->peer_id);
2777 if(mineral != MINERAL_NONE)
2778 item = getDiggedMineralItem(mineral, this);
2783 const std::string &extra_dug_s = m_nodedef->get(material).extra_dug_item;
2784 s32 extra_rarity = m_nodedef->get(material).extra_dug_item_rarity;
2785 if(extra_dug_s != "" && extra_rarity != 0
2786 && myrand() % extra_rarity == 0)
2788 std::istringstream is(extra_dug_s, std::ios::binary);
2789 item = InventoryItem::deSerialize(is, this);
2795 // Add a item to inventory
2796 player->inventory.addItem("main", item);
2799 UpdateCrafting(player->peer_id);
2800 SendInventory(player->peer_id);
2806 (this takes some time so it is done after the quick stuff)
2809 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
2811 m_env->getMap().removeNodeAndUpdate(p_under, modified_blocks);
2814 Set blocks not sent to far players
2816 for(core::list<u16>::Iterator
2817 i = far_players.begin();
2818 i != far_players.end(); i++)
2821 RemoteClient *client = getClient(peer_id);
2824 client->SetBlocksNotSent(modified_blocks);
2830 scriptapi_environment_on_dignode(m_lua, p_under, n, srp);
2836 else if(action == 1)
2839 InventoryList *ilist = player->inventory.getList("main");
2844 InventoryItem *item = ilist->getItem(item_i);
2846 // If there is no item, it is not possible to add it anywhere
2851 Handle material items
2853 if(std::string("MaterialItem") == item->getName())
2856 // Don't add a node if this is not a free space
2857 MapNode n2 = m_env->getMap().getNode(p_over);
2858 bool no_enough_privs =
2859 ((getPlayerPrivs(player) & PRIV_BUILD)==0);
2861 infostream<<"Player "<<player->getName()<<" cannot add node"
2862 <<" because privileges are "<<getPlayerPrivs(player)
2865 if(m_nodedef->get(n2).buildable_to == false
2868 // Client probably has wrong data.
2869 // Set block not sent, so that client will get
2871 infostream<<"Client "<<peer_id<<" tried to place"
2872 <<" node in invalid position; setting"
2873 <<" MapBlock not sent."<<std::endl;
2874 RemoteClient *client = getClient(peer_id);
2875 v3s16 blockpos = getNodeBlockPos(p_over);
2876 client->SetBlockNotSent(blockpos);
2880 catch(InvalidPositionException &e)
2882 infostream<<"Server: Ignoring ADDNODE: Node not found"
2883 <<" Adding block to emerge queue."
2885 m_emerge_queue.addBlock(peer_id,
2886 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2890 // Reset build time counter
2891 getClient(peer_id)->m_time_from_building = 0.0;
2894 MaterialItem *mitem = (MaterialItem*)item;
2896 n.setContent(mitem->getMaterial());
2898 actionstream<<player->getName()<<" places material "
2899 <<(int)mitem->getMaterial()
2900 <<" at "<<PP(p_under)<<std::endl;
2902 // Calculate direction for wall mounted stuff
2903 if(m_nodedef->get(n).wall_mounted)
2904 n.param2 = packDir(p_under - p_over);
2906 // Calculate the direction for furnaces and chests and stuff
2907 if(m_nodedef->get(n).param_type == CPT_FACEDIR_SIMPLE)
2909 v3f playerpos = player->getPosition();
2910 v3f blockpos = intToFloat(p_over, BS) - playerpos;
2911 blockpos = blockpos.normalize();
2913 if (fabs(blockpos.X) > fabs(blockpos.Z)) {
2927 Send to all close-by players
2929 core::list<u16> far_players;
2930 sendAddNode(p_over, n, 0, &far_players, 30);
2935 InventoryList *ilist = player->inventory.getList("main");
2936 if(g_settings->getBool("creative_mode") == false && ilist)
2938 // Remove from inventory and send inventory
2939 if(mitem->getCount() == 1)
2940 ilist->deleteItem(item_i);
2944 UpdateCrafting(peer_id);
2945 SendInventory(peer_id);
2951 This takes some time so it is done after the quick stuff
2953 core::map<v3s16, MapBlock*> modified_blocks;
2955 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
2957 std::string p_name = std::string(player->getName());
2958 m_env->getMap().addNodeAndUpdate(p_over, n, modified_blocks, p_name);
2961 Set blocks not sent to far players
2963 for(core::list<u16>::Iterator
2964 i = far_players.begin();
2965 i != far_players.end(); i++)
2968 RemoteClient *client = getClient(peer_id);
2971 client->SetBlocksNotSent(modified_blocks);
2977 scriptapi_environment_on_placenode(m_lua, p_over, n, srp);
2980 Calculate special events
2983 /*if(n.d == LEGN(m_nodedef, "CONTENT_MESE"))
2986 for(s16 z=-1; z<=1; z++)
2987 for(s16 y=-1; y<=1; y++)
2988 for(s16 x=-1; x<=1; x++)
2995 Place other item (not a block)
2999 v3s16 blockpos = getNodeBlockPos(p_over);
3002 Check that the block is loaded so that the item
3003 can properly be added to the static list too
3005 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3008 infostream<<"Error while placing object: "
3009 "block not found"<<std::endl;
3014 If in creative mode, item dropping is disabled unless
3015 player has build privileges
3017 if(g_settings->getBool("creative_mode") &&
3018 (getPlayerPrivs(player) & PRIV_BUILD) == 0)
3020 infostream<<"Not allowing player to drop item: "
3021 "creative mode and no build privs"<<std::endl;
3025 // Calculate a position for it
3026 v3f pos = intToFloat(p_over, BS);
3028 /*pos.Y -= BS*0.25; // let it drop a bit
3030 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
3031 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;*/
3036 ServerActiveObject *obj = item->createSAO(m_env, pos);
3040 infostream<<"WARNING: item resulted in NULL object, "
3041 <<"not placing onto map"
3046 actionstream<<player->getName()<<" places "<<item->getName()
3047 <<" at "<<PP(p_over)<<std::endl;
3049 // Add the object to the environment
3050 m_env->addActiveObject(obj);
3052 infostream<<"Placed object"<<std::endl;
3054 if(g_settings->getBool("creative_mode") == false)
3056 // Delete the right amount of items from the slot
3057 u16 dropcount = item->getDropCount();
3059 // Delete item if all gone
3060 if(item->getCount() <= dropcount)
3062 if(item->getCount() < dropcount)
3063 infostream<<"WARNING: Server: dropped more items"
3064 <<" than the slot contains"<<std::endl;
3066 InventoryList *ilist = player->inventory.getList("main");
3068 // Remove from inventory and send inventory
3069 ilist->deleteItem(item_i);
3071 // Else decrement it
3073 item->remove(dropcount);
3076 UpdateCrafting(peer_id);
3077 SendInventory(peer_id);
3085 Catch invalid actions
3089 infostream<<"WARNING: Server: Invalid action "
3090 <<action<<std::endl;
3094 else if(command == TOSERVER_RELEASE)
3103 infostream<<"TOSERVER_RELEASE ignored"<<std::endl;
3106 else if(command == TOSERVER_SIGNTEXT)
3108 infostream<<"Server: TOSERVER_SIGNTEXT not supported anymore"
3112 else if(command == TOSERVER_SIGNNODETEXT)
3114 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
3122 std::string datastring((char*)&data[2], datasize-2);
3123 std::istringstream is(datastring, std::ios_base::binary);
3126 is.read((char*)buf, 6);
3127 v3s16 p = readV3S16(buf);
3128 is.read((char*)buf, 2);
3129 u16 textlen = readU16(buf);
3131 for(u16 i=0; i<textlen; i++)
3133 is.read((char*)buf, 1);
3134 text += (char)buf[0];
3137 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3140 if(meta->typeId() != LEGN(m_nodedef, "CONTENT_SIGN_WALL"))
3142 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
3143 signmeta->setText(text);
3145 actionstream<<player->getName()<<" writes \""<<text<<"\" to sign "
3146 <<" at "<<PP(p)<<std::endl;
3148 v3s16 blockpos = getNodeBlockPos(p);
3149 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3152 block->raiseModified(MOD_STATE_WRITE_NEEDED,
3156 setBlockNotSent(blockpos);
3158 else if(command == TOSERVER_INVENTORY_ACTION)
3160 /*// Ignore inventory changes if in creative mode
3161 if(g_settings->getBool("creative_mode") == true)
3163 infostream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
3167 // Strip command and create a stream
3168 std::string datastring((char*)&data[2], datasize-2);
3169 infostream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
3170 std::istringstream is(datastring, std::ios_base::binary);
3172 InventoryAction *a = InventoryAction::deSerialize(is);
3177 c.current_player = player;
3180 Handle craftresult specially if not in creative mode
3182 bool disable_action = false;
3183 if(a->getType() == IACTION_MOVE
3184 && g_settings->getBool("creative_mode") == false)
3186 IMoveAction *ma = (IMoveAction*)a;
3187 if(ma->to_inv == "current_player" &&
3188 ma->from_inv == "current_player")
3190 InventoryList *rlist = player->inventory.getList("craftresult");
3192 InventoryList *clist = player->inventory.getList("craft");
3194 InventoryList *mlist = player->inventory.getList("main");
3197 Craftresult is no longer preview if something
3200 if(ma->to_list == "craftresult"
3201 && ma->from_list != "craftresult")
3203 // If it currently is a preview, remove
3205 if(player->craftresult_is_preview)
3207 rlist->deleteItem(0);
3209 player->craftresult_is_preview = false;
3212 Crafting takes place if this condition is true.
3214 if(player->craftresult_is_preview &&
3215 ma->from_list == "craftresult")
3217 player->craftresult_is_preview = false;
3218 clist->decrementMaterials(1);
3220 /* Print out action */
3221 InventoryList *list =
3222 player->inventory.getList("craftresult");
3224 InventoryItem *item = list->getItem(0);
3225 std::string itemname = "NULL";
3227 itemname = item->getName();
3228 actionstream<<player->getName()<<" crafts "
3229 <<itemname<<std::endl;
3232 If the craftresult is placed on itself, move it to
3233 main inventory instead of doing the action
3235 if(ma->to_list == "craftresult"
3236 && ma->from_list == "craftresult")
3238 disable_action = true;
3240 InventoryItem *item1 = rlist->changeItem(0, NULL);
3241 mlist->addItem(item1);
3244 // Disallow moving items if not allowed to build
3245 else if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
3247 disable_action = true;
3249 // if it's a locking chest, only allow the owner or server admins to move items
3250 else if (ma->from_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
3252 Strfnd fn(ma->from_inv);
3253 std::string id0 = fn.next(":");
3254 if(id0 == "nodemeta")
3257 p.X = stoi(fn.next(","));
3258 p.Y = stoi(fn.next(","));
3259 p.Z = stoi(fn.next(","));
3260 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3261 if(meta && meta->typeId() == LEGN(m_nodedef, "CONTENT_LOCKABLE_CHEST")) {
3262 LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
3263 if (lcm->getOwner() != player->getName())
3264 disable_action = true;
3268 else if (ma->to_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
3270 Strfnd fn(ma->to_inv);
3271 std::string id0 = fn.next(":");
3272 if(id0 == "nodemeta")
3275 p.X = stoi(fn.next(","));
3276 p.Y = stoi(fn.next(","));
3277 p.Z = stoi(fn.next(","));
3278 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3279 if(meta && meta->typeId() == LEGN(m_nodedef, "CONTENT_LOCKABLE_CHEST")) {
3280 LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
3281 if (lcm->getOwner() != player->getName())
3282 disable_action = true;
3288 if(a->getType() == IACTION_DROP)
3290 IDropAction *da = (IDropAction*)a;
3291 // Disallow dropping items if not allowed to build
3292 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
3294 disable_action = true;
3296 // if it's a locking chest, only allow the owner or server admins to drop items
3297 else if (da->from_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
3299 Strfnd fn(da->from_inv);
3300 std::string id0 = fn.next(":");
3301 if(id0 == "nodemeta")
3304 p.X = stoi(fn.next(","));
3305 p.Y = stoi(fn.next(","));
3306 p.Z = stoi(fn.next(","));
3307 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3308 if(meta && meta->typeId() == LEGN(m_nodedef, "CONTENT_LOCKABLE_CHEST")) {
3309 LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
3310 if (lcm->getOwner() != player->getName())
3311 disable_action = true;
3317 if(disable_action == false)
3319 // Feed action to player inventory
3320 a->apply(&c, this, m_env);
3325 UpdateCrafting(player->peer_id);
3326 SendInventory(player->peer_id);
3334 infostream<<"TOSERVER_INVENTORY_ACTION: "
3335 <<"InventoryAction::deSerialize() returned NULL"
3339 else if(command == TOSERVER_CHAT_MESSAGE)
3347 std::string datastring((char*)&data[2], datasize-2);
3348 std::istringstream is(datastring, std::ios_base::binary);
3351 is.read((char*)buf, 2);
3352 u16 len = readU16(buf);
3354 std::wstring message;
3355 for(u16 i=0; i<len; i++)
3357 is.read((char*)buf, 2);
3358 message += (wchar_t)readU16(buf);
3361 // Get player name of this client
3362 std::wstring name = narrow_to_wide(player->getName());
3364 // Line to send to players
3366 // Whether to send to the player that sent the line
3367 bool send_to_sender = false;
3368 // Whether to send to other players
3369 bool send_to_others = false;
3371 // Local player gets all privileges regardless of
3372 // what's set on their account.
3373 u64 privs = getPlayerPrivs(player);
3376 if(message[0] == L'/')
3378 size_t strip_size = 1;
3379 if (message[1] == L'#') // support old-style commans
3381 message = message.substr(strip_size);
3383 WStrfnd f1(message);
3384 f1.next(L" "); // Skip over /#whatever
3385 std::wstring paramstring = f1.next(L"");
3387 ServerCommandContext *ctx = new ServerCommandContext(
3388 str_split(message, L' '),
3395 std::wstring reply(processServerCommand(ctx));
3396 send_to_sender = ctx->flags & SEND_TO_SENDER;
3397 send_to_others = ctx->flags & SEND_TO_OTHERS;
3399 if (ctx->flags & SEND_NO_PREFIX)
3402 line += L"Server: " + reply;
3409 if(privs & PRIV_SHOUT)
3415 send_to_others = true;
3419 line += L"Server: You are not allowed to shout";
3420 send_to_sender = true;
3427 actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
3430 Send the message to clients
3432 for(core::map<u16, RemoteClient*>::Iterator
3433 i = m_clients.getIterator();
3434 i.atEnd() == false; i++)
3436 // Get client and check that it is valid
3437 RemoteClient *client = i.getNode()->getValue();
3438 assert(client->peer_id == i.getNode()->getKey());
3439 if(client->serialization_version == SER_FMT_VER_INVALID)
3443 bool sender_selected = (peer_id == client->peer_id);
3444 if(sender_selected == true && send_to_sender == false)
3446 if(sender_selected == false && send_to_others == false)
3449 SendChatMessage(client->peer_id, line);
3453 else if(command == TOSERVER_DAMAGE)
3455 std::string datastring((char*)&data[2], datasize-2);
3456 std::istringstream is(datastring, std::ios_base::binary);
3457 u8 damage = readU8(is);
3459 if(g_settings->getBool("enable_damage"))
3461 actionstream<<player->getName()<<" damaged by "
3462 <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
3465 HandlePlayerHP(player, damage);
3469 SendPlayerHP(player);
3472 else if(command == TOSERVER_PASSWORD)
3475 [0] u16 TOSERVER_PASSWORD
3476 [2] u8[28] old password
3477 [30] u8[28] new password
3480 if(datasize != 2+PASSWORD_SIZE*2)
3482 /*char password[PASSWORD_SIZE];
3483 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3484 password[i] = data[2+i];
3485 password[PASSWORD_SIZE-1] = 0;*/
3487 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3495 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3497 char c = data[2+PASSWORD_SIZE+i];
3503 infostream<<"Server: Client requests a password change from "
3504 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
3506 std::string playername = player->getName();
3508 if(m_authmanager.exists(playername) == false)
3510 infostream<<"Server: playername not found in authmanager"<<std::endl;
3511 // Wrong old password supplied!!
3512 SendChatMessage(peer_id, L"playername not found in authmanager");
3516 std::string checkpwd = m_authmanager.getPassword(playername);
3518 if(oldpwd != checkpwd)
3520 infostream<<"Server: invalid old password"<<std::endl;
3521 // Wrong old password supplied!!
3522 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
3526 actionstream<<player->getName()<<" changes password"<<std::endl;
3528 m_authmanager.setPassword(playername, newpwd);
3530 infostream<<"Server: password change successful for "<<playername
3532 SendChatMessage(peer_id, L"Password change successful");
3534 else if(command == TOSERVER_PLAYERITEM)
3539 u16 item = readU16(&data[2]);
3540 player->wieldItem(item);
3541 SendWieldedItem(player);
3543 else if(command == TOSERVER_RESPAWN)
3548 RespawnPlayer(player);
3550 actionstream<<player->getName()<<" respawns at "
3551 <<PP(player->getPosition()/BS)<<std::endl;
3555 infostream<<"Server::ProcessData(): Ignoring "
3556 "unknown command "<<command<<std::endl;
3560 catch(SendFailedException &e)
3562 errorstream<<"Server::ProcessData(): SendFailedException: "
3568 void Server::onMapEditEvent(MapEditEvent *event)
3570 //infostream<<"Server::onMapEditEvent()"<<std::endl;
3571 if(m_ignore_map_edit_events)
3573 MapEditEvent *e = event->clone();
3574 m_unsent_map_edit_queue.push_back(e);
3577 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3579 if(id == "current_player")
3581 assert(c->current_player);
3582 return &(c->current_player->inventory);
3586 std::string id0 = fn.next(":");
3588 if(id0 == "nodemeta")
3591 p.X = stoi(fn.next(","));
3592 p.Y = stoi(fn.next(","));
3593 p.Z = stoi(fn.next(","));
3594 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3596 return meta->getInventory();
3597 infostream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3598 <<"no metadata found"<<std::endl;
3602 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3605 void Server::inventoryModified(InventoryContext *c, std::string id)
3607 if(id == "current_player")
3609 assert(c->current_player);
3611 UpdateCrafting(c->current_player->peer_id);
3612 SendInventory(c->current_player->peer_id);
3617 std::string id0 = fn.next(":");
3619 if(id0 == "nodemeta")
3622 p.X = stoi(fn.next(","));
3623 p.Y = stoi(fn.next(","));
3624 p.Z = stoi(fn.next(","));
3625 v3s16 blockpos = getNodeBlockPos(p);
3627 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3629 meta->inventoryModified();
3631 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3633 block->raiseModified(MOD_STATE_WRITE_NEEDED);
3635 setBlockNotSent(blockpos);
3640 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3643 core::list<PlayerInfo> Server::getPlayerInfo()
3645 DSTACK(__FUNCTION_NAME);
3646 JMutexAutoLock envlock(m_env_mutex);
3647 JMutexAutoLock conlock(m_con_mutex);
3649 core::list<PlayerInfo> list;
3651 core::list<Player*> players = m_env->getPlayers();
3653 core::list<Player*>::Iterator i;
3654 for(i = players.begin();
3655 i != players.end(); i++)
3659 Player *player = *i;
3662 // Copy info from connection to info struct
3663 info.id = player->peer_id;
3664 info.address = m_con.GetPeerAddress(player->peer_id);
3665 info.avg_rtt = m_con.GetPeerAvgRTT(player->peer_id);
3667 catch(con::PeerNotFoundException &e)
3669 // Set dummy peer info
3671 info.address = Address(0,0,0,0,0);
3675 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3676 info.position = player->getPosition();
3678 list.push_back(info);
3685 void Server::peerAdded(con::Peer *peer)
3687 DSTACK(__FUNCTION_NAME);
3688 infostream<<"Server::peerAdded(): peer->id="
3689 <<peer->id<<std::endl;
3692 c.type = PEER_ADDED;
3693 c.peer_id = peer->id;
3695 m_peer_change_queue.push_back(c);
3698 void Server::deletingPeer(con::Peer *peer, bool timeout)
3700 DSTACK(__FUNCTION_NAME);
3701 infostream<<"Server::deletingPeer(): peer->id="
3702 <<peer->id<<", timeout="<<timeout<<std::endl;
3705 c.type = PEER_REMOVED;
3706 c.peer_id = peer->id;
3707 c.timeout = timeout;
3708 m_peer_change_queue.push_back(c);
3715 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3717 DSTACK(__FUNCTION_NAME);
3718 std::ostringstream os(std::ios_base::binary);
3720 writeU16(os, TOCLIENT_HP);
3724 std::string s = os.str();
3725 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3727 con.Send(peer_id, 0, data, true);
3730 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3731 const std::wstring &reason)
3733 DSTACK(__FUNCTION_NAME);
3734 std::ostringstream os(std::ios_base::binary);
3736 writeU16(os, TOCLIENT_ACCESS_DENIED);
3737 os<<serializeWideString(reason);
3740 std::string s = os.str();
3741 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3743 con.Send(peer_id, 0, data, true);
3746 void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
3747 bool set_camera_point_target, v3f camera_point_target)
3749 DSTACK(__FUNCTION_NAME);
3750 std::ostringstream os(std::ios_base::binary);
3752 writeU16(os, TOCLIENT_DEATHSCREEN);
3753 writeU8(os, set_camera_point_target);
3754 writeV3F1000(os, camera_point_target);
3757 std::string s = os.str();
3758 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3760 con.Send(peer_id, 0, data, true);
3763 void Server::SendToolDef(con::Connection &con, u16 peer_id,
3764 IToolDefManager *tooldef)
3766 DSTACK(__FUNCTION_NAME);
3767 std::ostringstream os(std::ios_base::binary);
3771 u32 length of the next item
3772 serialized ToolDefManager
3774 writeU16(os, TOCLIENT_TOOLDEF);
3775 std::ostringstream tmp_os(std::ios::binary);
3776 tooldef->serialize(tmp_os);
3777 os<<serializeLongString(tmp_os.str());
3780 std::string s = os.str();
3781 infostream<<"Server::SendToolDef(): Sending tool definitions: size="
3782 <<s.size()<<std::endl;
3783 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3785 con.Send(peer_id, 0, data, true);
3788 void Server::SendNodeDef(con::Connection &con, u16 peer_id,
3789 INodeDefManager *nodedef)
3791 DSTACK(__FUNCTION_NAME);
3792 std::ostringstream os(std::ios_base::binary);
3796 u32 length of the next item
3797 serialized NodeDefManager
3799 writeU16(os, TOCLIENT_NODEDEF);
3800 std::ostringstream tmp_os(std::ios::binary);
3801 nodedef->serialize(tmp_os);
3802 os<<serializeLongString(tmp_os.str());
3805 std::string s = os.str();
3806 infostream<<"Server::SendNodeDef(): Sending node definitions: size="
3807 <<s.size()<<std::endl;
3808 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3810 con.Send(peer_id, 0, data, true);
3814 Non-static send methods
3817 void Server::SendObjectData(float dtime)
3819 DSTACK(__FUNCTION_NAME);
3821 core::map<v3s16, bool> stepped_blocks;
3823 for(core::map<u16, RemoteClient*>::Iterator
3824 i = m_clients.getIterator();
3825 i.atEnd() == false; i++)
3827 u16 peer_id = i.getNode()->getKey();
3828 RemoteClient *client = i.getNode()->getValue();
3829 assert(client->peer_id == peer_id);
3831 if(client->serialization_version == SER_FMT_VER_INVALID)
3834 client->SendObjectData(this, dtime, stepped_blocks);
3838 void Server::SendPlayerInfos()
3840 DSTACK(__FUNCTION_NAME);
3842 //JMutexAutoLock envlock(m_env_mutex);
3844 // Get connected players
3845 core::list<Player*> players = m_env->getPlayers(true);
3847 u32 player_count = players.getSize();
3848 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3850 SharedBuffer<u8> data(datasize);
3851 writeU16(&data[0], TOCLIENT_PLAYERINFO);
3854 core::list<Player*>::Iterator i;
3855 for(i = players.begin();
3856 i != players.end(); i++)
3858 Player *player = *i;
3860 /*infostream<<"Server sending player info for player with "
3861 "peer_id="<<player->peer_id<<std::endl;*/
3863 writeU16(&data[start], player->peer_id);
3864 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3865 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3866 start += 2+PLAYERNAME_SIZE;
3869 //JMutexAutoLock conlock(m_con_mutex);
3872 m_con.SendToAll(0, data, true);
3875 void Server::SendInventory(u16 peer_id)
3877 DSTACK(__FUNCTION_NAME);
3879 Player* player = m_env->getPlayer(peer_id);
3886 std::ostringstream os;
3887 //os.imbue(std::locale("C"));
3889 player->inventory.serialize(os);
3891 std::string s = os.str();
3893 SharedBuffer<u8> data(s.size()+2);
3894 writeU16(&data[0], TOCLIENT_INVENTORY);
3895 memcpy(&data[2], s.c_str(), s.size());
3898 m_con.Send(peer_id, 0, data, true);
3901 std::string getWieldedItemString(const Player *player)
3903 const InventoryItem *item = player->getWieldItem();
3905 return std::string("");
3906 std::ostringstream os(std::ios_base::binary);
3907 item->serialize(os);
3911 void Server::SendWieldedItem(const Player* player)
3913 DSTACK(__FUNCTION_NAME);
3917 std::ostringstream os(std::ios_base::binary);
3919 writeU16(os, TOCLIENT_PLAYERITEM);
3921 writeU16(os, player->peer_id);
3922 os<<serializeString(getWieldedItemString(player));
3925 std::string s = os.str();
3926 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3928 m_con.SendToAll(0, data, true);
3931 void Server::SendPlayerItems()
3933 DSTACK(__FUNCTION_NAME);
3935 std::ostringstream os(std::ios_base::binary);
3936 core::list<Player *> players = m_env->getPlayers(true);
3938 writeU16(os, TOCLIENT_PLAYERITEM);
3939 writeU16(os, players.size());
3940 core::list<Player *>::Iterator i;
3941 for(i = players.begin(); i != players.end(); ++i)
3944 writeU16(os, p->peer_id);
3945 os<<serializeString(getWieldedItemString(p));
3949 std::string s = os.str();
3950 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3952 m_con.SendToAll(0, data, true);
3955 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3957 DSTACK(__FUNCTION_NAME);
3959 std::ostringstream os(std::ios_base::binary);
3963 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3964 os.write((char*)buf, 2);
3967 writeU16(buf, message.size());
3968 os.write((char*)buf, 2);
3971 for(u32 i=0; i<message.size(); i++)
3975 os.write((char*)buf, 2);
3979 std::string s = os.str();
3980 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3982 m_con.Send(peer_id, 0, data, true);
3985 void Server::BroadcastChatMessage(const std::wstring &message)
3987 for(core::map<u16, RemoteClient*>::Iterator
3988 i = m_clients.getIterator();
3989 i.atEnd() == false; i++)
3991 // Get client and check that it is valid
3992 RemoteClient *client = i.getNode()->getValue();
3993 assert(client->peer_id == i.getNode()->getKey());
3994 if(client->serialization_version == SER_FMT_VER_INVALID)
3997 SendChatMessage(client->peer_id, message);
4001 void Server::SendPlayerHP(Player *player)
4003 SendHP(m_con, player->peer_id, player->hp);
4006 void Server::SendMovePlayer(Player *player)
4008 DSTACK(__FUNCTION_NAME);
4009 std::ostringstream os(std::ios_base::binary);
4011 writeU16(os, TOCLIENT_MOVE_PLAYER);
4012 writeV3F1000(os, player->getPosition());
4013 writeF1000(os, player->getPitch());
4014 writeF1000(os, player->getYaw());
4017 v3f pos = player->getPosition();
4018 f32 pitch = player->getPitch();
4019 f32 yaw = player->getYaw();
4020 infostream<<"Server sending TOCLIENT_MOVE_PLAYER"
4021 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
4028 std::string s = os.str();
4029 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4031 m_con.Send(player->peer_id, 0, data, true);
4034 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
4035 core::list<u16> *far_players, float far_d_nodes)
4037 float maxd = far_d_nodes*BS;
4038 v3f p_f = intToFloat(p, BS);
4042 SharedBuffer<u8> reply(replysize);
4043 writeU16(&reply[0], TOCLIENT_REMOVENODE);
4044 writeS16(&reply[2], p.X);
4045 writeS16(&reply[4], p.Y);
4046 writeS16(&reply[6], p.Z);
4048 for(core::map<u16, RemoteClient*>::Iterator
4049 i = m_clients.getIterator();
4050 i.atEnd() == false; i++)
4052 // Get client and check that it is valid
4053 RemoteClient *client = i.getNode()->getValue();
4054 assert(client->peer_id == i.getNode()->getKey());
4055 if(client->serialization_version == SER_FMT_VER_INVALID)
4058 // Don't send if it's the same one
4059 if(client->peer_id == ignore_id)
4065 Player *player = m_env->getPlayer(client->peer_id);
4068 // If player is far away, only set modified blocks not sent
4069 v3f player_pos = player->getPosition();
4070 if(player_pos.getDistanceFrom(p_f) > maxd)
4072 far_players->push_back(client->peer_id);
4079 m_con.Send(client->peer_id, 0, reply, true);
4083 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
4084 core::list<u16> *far_players, float far_d_nodes)
4086 float maxd = far_d_nodes*BS;
4087 v3f p_f = intToFloat(p, BS);
4089 for(core::map<u16, RemoteClient*>::Iterator
4090 i = m_clients.getIterator();
4091 i.atEnd() == false; i++)
4093 // Get client and check that it is valid
4094 RemoteClient *client = i.getNode()->getValue();
4095 assert(client->peer_id == i.getNode()->getKey());
4096 if(client->serialization_version == SER_FMT_VER_INVALID)
4099 // Don't send if it's the same one
4100 if(client->peer_id == ignore_id)
4106 Player *player = m_env->getPlayer(client->peer_id);
4109 // If player is far away, only set modified blocks not sent
4110 v3f player_pos = player->getPosition();
4111 if(player_pos.getDistanceFrom(p_f) > maxd)
4113 far_players->push_back(client->peer_id);
4120 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
4121 SharedBuffer<u8> reply(replysize);
4122 writeU16(&reply[0], TOCLIENT_ADDNODE);
4123 writeS16(&reply[2], p.X);
4124 writeS16(&reply[4], p.Y);
4125 writeS16(&reply[6], p.Z);
4126 n.serialize(&reply[8], client->serialization_version);
4129 m_con.Send(client->peer_id, 0, reply, true);
4133 void Server::setBlockNotSent(v3s16 p)
4135 for(core::map<u16, RemoteClient*>::Iterator
4136 i = m_clients.getIterator();
4137 i.atEnd()==false; i++)
4139 RemoteClient *client = i.getNode()->getValue();
4140 client->SetBlockNotSent(p);
4144 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
4146 DSTACK(__FUNCTION_NAME);
4148 v3s16 p = block->getPos();
4152 bool completely_air = true;
4153 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4154 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4155 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4157 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
4159 completely_air = false;
4160 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
4165 infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
4167 infostream<<"[completely air] ";
4168 infostream<<std::endl;
4172 Create a packet with the block in the right format
4175 std::ostringstream os(std::ios_base::binary);
4176 block->serialize(os, ver);
4177 std::string s = os.str();
4178 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
4180 u32 replysize = 8 + blockdata.getSize();
4181 SharedBuffer<u8> reply(replysize);
4182 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
4183 writeS16(&reply[2], p.X);
4184 writeS16(&reply[4], p.Y);
4185 writeS16(&reply[6], p.Z);
4186 memcpy(&reply[8], *blockdata, blockdata.getSize());
4188 /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4189 <<": \tpacket size: "<<replysize<<std::endl;*/
4194 m_con.Send(peer_id, 1, reply, true);
4197 void Server::SendBlocks(float dtime)
4199 DSTACK(__FUNCTION_NAME);
4201 JMutexAutoLock envlock(m_env_mutex);
4202 JMutexAutoLock conlock(m_con_mutex);
4204 //TimeTaker timer("Server::SendBlocks");
4206 core::array<PrioritySortedBlockTransfer> queue;
4208 s32 total_sending = 0;
4211 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
4213 for(core::map<u16, RemoteClient*>::Iterator
4214 i = m_clients.getIterator();
4215 i.atEnd() == false; i++)
4217 RemoteClient *client = i.getNode()->getValue();
4218 assert(client->peer_id == i.getNode()->getKey());
4220 total_sending += client->SendingCount();
4222 if(client->serialization_version == SER_FMT_VER_INVALID)
4225 client->GetNextBlocks(this, dtime, queue);
4230 // Lowest priority number comes first.
4231 // Lowest is most important.
4234 for(u32 i=0; i<queue.size(); i++)
4236 //TODO: Calculate limit dynamically
4237 if(total_sending >= g_settings->getS32
4238 ("max_simultaneous_block_sends_server_total"))
4241 PrioritySortedBlockTransfer q = queue[i];
4243 MapBlock *block = NULL;
4246 block = m_env->getMap().getBlockNoCreate(q.pos);
4248 catch(InvalidPositionException &e)
4253 RemoteClient *client = getClient(q.peer_id);
4255 SendBlockNoLock(q.peer_id, block, client->serialization_version);
4257 client->SentBlock(q.pos);
4263 struct SendableTexture
4269 SendableTexture(const std::string &name_="", const std::string path_="",
4270 const std::string &data_=""):
4277 void Server::SendTextures(u16 peer_id)
4279 DSTACK(__FUNCTION_NAME);
4281 infostream<<"Server::SendTextures(): Sending textures to client"<<std::endl;
4285 // Put 5kB in one bunch (this is not accurate)
4286 u32 bytes_per_bunch = 5000;
4288 core::array< core::list<SendableTexture> > texture_bunches;
4289 texture_bunches.push_back(core::list<SendableTexture>());
4291 u32 texture_size_bunch_total = 0;
4292 core::list<ModSpec> mods = getMods(m_modspaths);
4293 for(core::list<ModSpec>::Iterator i = mods.begin();
4294 i != mods.end(); i++){
4296 std::string texturepath = mod.path + DIR_DELIM + "textures";
4297 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(texturepath);
4298 for(u32 j=0; j<dirlist.size(); j++){
4299 if(dirlist[j].dir) // Ignode dirs
4301 std::string tname = dirlist[j].name;
4302 std::string tpath = texturepath + DIR_DELIM + tname;
4304 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
4305 if(fis.good() == false){
4306 errorstream<<"Server::SendTextures(): Could not open \""
4307 <<tname<<"\" for reading"<<std::endl;
4310 std::ostringstream tmp_os(std::ios_base::binary);
4314 fis.read(buf, 1024);
4315 std::streamsize len = fis.gcount();
4316 tmp_os.write(buf, len);
4317 texture_size_bunch_total += len;
4326 errorstream<<"Server::SendTextures(): Failed to read \""
4327 <<tname<<"\""<<std::endl;
4330 /*infostream<<"Server::SendTextures(): Loaded \""
4331 <<tname<<"\""<<std::endl;*/
4333 texture_bunches[texture_bunches.size()-1].push_back(
4334 SendableTexture(tname, tpath, tmp_os.str()));
4336 // Start next bunch if got enough data
4337 if(texture_size_bunch_total >= bytes_per_bunch){
4338 texture_bunches.push_back(core::list<SendableTexture>());
4339 texture_size_bunch_total = 0;
4344 /* Create and send packets */
4346 u32 num_bunches = texture_bunches.size();
4347 for(u32 i=0; i<num_bunches; i++)
4351 u16 total number of texture bunches
4352 u16 index of this bunch
4353 u32 number of textures in this bunch
4361 std::ostringstream os(std::ios_base::binary);
4363 writeU16(os, TOCLIENT_TEXTURES);
4364 writeU16(os, num_bunches);
4366 writeU32(os, texture_bunches[i].size());
4368 for(core::list<SendableTexture>::Iterator
4369 j = texture_bunches[i].begin();
4370 j != texture_bunches[i].end(); j++){
4371 os<<serializeString(j->name);
4372 os<<serializeLongString(j->data);
4376 std::string s = os.str();
4377 infostream<<"Server::SendTextures(): bunch "<<i<<"/"<<num_bunches
4378 <<" textures="<<texture_bunches[i].size()
4379 <<" size=" <<s.size()<<std::endl;
4380 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4382 m_con.Send(peer_id, 0, data, true);
4390 void Server::HandlePlayerHP(Player *player, s16 damage)
4392 if(player->hp > damage)
4394 player->hp -= damage;
4395 SendPlayerHP(player);
4399 infostream<<"Server::HandlePlayerHP(): Player "
4400 <<player->getName()<<" dies"<<std::endl;
4404 //TODO: Throw items around
4406 // Handle players that are not connected
4407 if(player->peer_id == PEER_ID_INEXISTENT){
4408 RespawnPlayer(player);
4412 SendPlayerHP(player);
4414 RemoteClient *client = getClient(player->peer_id);
4415 if(client->net_proto_version >= 3)
4417 SendDeathscreen(m_con, player->peer_id, false, v3f(0,0,0));
4421 RespawnPlayer(player);
4426 void Server::RespawnPlayer(Player *player)
4429 ServerRemotePlayer *srp = (ServerRemotePlayer*)player;
4430 bool repositioned = scriptapi_on_respawnplayer(m_lua, srp);
4432 v3f pos = findSpawnPos(m_env->getServerMap());
4433 player->setPosition(pos);
4434 srp->m_last_good_position = pos;
4435 srp->m_last_good_position_age = 0;
4437 SendMovePlayer(player);
4438 SendPlayerHP(player);
4441 void Server::UpdateCrafting(u16 peer_id)
4443 DSTACK(__FUNCTION_NAME);
4445 Player* player = m_env->getPlayer(peer_id);
4449 Calculate crafting stuff
4451 if(g_settings->getBool("creative_mode") == false)
4453 InventoryList *clist = player->inventory.getList("craft");
4454 InventoryList *rlist = player->inventory.getList("craftresult");
4456 if(rlist && rlist->getUsedSlots() == 0)
4457 player->craftresult_is_preview = true;
4459 if(rlist && player->craftresult_is_preview)
4461 rlist->clearItems();
4463 if(clist && rlist && player->craftresult_is_preview)
4465 // Get result of crafting grid
4467 std::vector<InventoryItem*> items;
4468 for(u16 i=0; i<9; i++){
4469 if(clist->getItem(i) == NULL)
4470 items.push_back(NULL);
4472 items.push_back(clist->getItem(i)->clone());
4474 CraftPointerInput cpi(3, items);
4476 InventoryItem *result = m_craftdef->getCraftResult(cpi, this);
4477 //InventoryItem *result = craft_get_result(items, this);
4479 rlist->addItem(result);
4482 } // if creative_mode == false
4485 RemoteClient* Server::getClient(u16 peer_id)
4487 DSTACK(__FUNCTION_NAME);
4488 //JMutexAutoLock lock(m_con_mutex);
4489 core::map<u16, RemoteClient*>::Node *n;
4490 n = m_clients.find(peer_id);
4491 // A client should exist for all peers
4493 return n->getValue();
4496 std::wstring Server::getStatusString()
4498 std::wostringstream os(std::ios_base::binary);
4501 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4503 os<<L", uptime="<<m_uptime.get();
4504 // Information about clients
4506 for(core::map<u16, RemoteClient*>::Iterator
4507 i = m_clients.getIterator();
4508 i.atEnd() == false; i++)
4510 // Get client and check that it is valid
4511 RemoteClient *client = i.getNode()->getValue();
4512 assert(client->peer_id == i.getNode()->getKey());
4513 if(client->serialization_version == SER_FMT_VER_INVALID)
4516 Player *player = m_env->getPlayer(client->peer_id);
4517 // Get name of player
4518 std::wstring name = L"unknown";
4520 name = narrow_to_wide(player->getName());
4521 // Add name to information string
4525 if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
4526 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4527 if(g_settings->get("motd") != "")
4528 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4532 // Saves g_settings to configpath given at initialization
4533 void Server::saveConfig()
4535 if(m_configpath != "")
4536 g_settings->updateConfigFile(m_configpath.c_str());
4539 void Server::notifyPlayer(const char *name, const std::wstring msg)
4541 Player *player = m_env->getPlayer(name);
4544 SendChatMessage(player->peer_id, std::wstring(L"Server: -!- ")+msg);
4547 void Server::notifyPlayers(const std::wstring msg)
4549 BroadcastChatMessage(msg);
4552 // IGameDef interface
4554 IToolDefManager* Server::getToolDefManager()
4558 INodeDefManager* Server::getNodeDefManager()
4562 ICraftDefManager* Server::getCraftDefManager()
4566 ITextureSource* Server::getTextureSource()
4570 u16 Server::allocateUnknownNodeId(const std::string &name)
4572 return m_nodedef->allocateDummy(name);
4575 IWritableToolDefManager* Server::getWritableToolDefManager()
4579 IWritableNodeDefManager* Server::getWritableNodeDefManager()
4583 IWritableCraftDefManager* Server::getWritableCraftDefManager()
4588 v3f findSpawnPos(ServerMap &map)
4590 //return v3f(50,50,50)*BS;
4595 nodepos = v2s16(0,0);
4600 // Try to find a good place a few times
4601 for(s32 i=0; i<1000; i++)
4604 // We're going to try to throw the player to this position
4605 v2s16 nodepos2d = v2s16(-range + (myrand()%(range*2)),
4606 -range + (myrand()%(range*2)));
4607 //v2s16 sectorpos = getNodeSectorPos(nodepos2d);
4608 // Get ground height at point (fallbacks to heightmap function)
4609 s16 groundheight = map.findGroundLevel(nodepos2d);
4610 // Don't go underwater
4611 if(groundheight < WATER_LEVEL)
4613 //infostream<<"-> Underwater"<<std::endl;
4616 // Don't go to high places
4617 if(groundheight > WATER_LEVEL + 4)
4619 //infostream<<"-> Underwater"<<std::endl;
4623 nodepos = v3s16(nodepos2d.X, groundheight-2, nodepos2d.Y);
4624 bool is_good = false;
4626 for(s32 i=0; i<10; i++){
4627 v3s16 blockpos = getNodeBlockPos(nodepos);
4628 map.emergeBlock(blockpos, true);
4629 MapNode n = map.getNodeNoEx(nodepos);
4630 if(n.getContent() == CONTENT_AIR){
4641 // Found a good place
4642 //infostream<<"Searched through "<<i<<" places."<<std::endl;
4648 return intToFloat(nodepos, BS);
4651 Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id)
4654 Try to get an existing player
4656 Player *player = m_env->getPlayer(name);
4659 // If player is already connected, cancel
4660 if(player->peer_id != 0)
4662 infostream<<"emergePlayer(): Player already connected"<<std::endl;
4667 player->peer_id = peer_id;
4669 // Reset inventory to creative if in creative mode
4670 if(g_settings->getBool("creative_mode"))
4672 // Warning: double code below
4673 // Backup actual inventory
4674 player->inventory_backup = new Inventory();
4675 *(player->inventory_backup) = player->inventory;
4676 // Set creative inventory
4677 craft_set_creative_inventory(player, this);
4684 If player with the wanted peer_id already exists, cancel.
4686 if(m_env->getPlayer(peer_id) != NULL)
4688 infostream<<"emergePlayer(): Player with wrong name but same"
4689 " peer_id already exists"<<std::endl;
4697 // Add authentication stuff
4698 m_authmanager.add(name);
4699 m_authmanager.setPassword(name, password);
4700 m_authmanager.setPrivs(name,
4701 stringToPrivs(g_settings->get("default_privs")));
4703 /* Set player position */
4705 infostream<<"Server: Finding spawn place for player \""
4706 <<name<<"\""<<std::endl;
4708 v3f pos = findSpawnPos(m_env->getServerMap());
4710 player = new ServerRemotePlayer(m_env, pos, peer_id, name);
4712 /* Add player to environment */
4713 m_env->addPlayer(player);
4716 ServerRemotePlayer *srp = (ServerRemotePlayer*)player;
4717 scriptapi_on_newplayer(m_lua, srp);
4719 /* Add stuff to inventory */
4720 if(g_settings->getBool("creative_mode"))
4722 // Warning: double code above
4723 // Backup actual inventory
4724 player->inventory_backup = new Inventory();
4725 *(player->inventory_backup) = player->inventory;
4726 // Set creative inventory
4727 craft_set_creative_inventory(player, this);
4732 } // create new player
4735 void Server::handlePeerChange(PeerChange &c)
4737 JMutexAutoLock envlock(m_env_mutex);
4738 JMutexAutoLock conlock(m_con_mutex);
4740 if(c.type == PEER_ADDED)
4747 core::map<u16, RemoteClient*>::Node *n;
4748 n = m_clients.find(c.peer_id);
4749 // The client shouldn't already exist
4753 RemoteClient *client = new RemoteClient();
4754 client->peer_id = c.peer_id;
4755 m_clients.insert(client->peer_id, client);
4758 else if(c.type == PEER_REMOVED)
4765 core::map<u16, RemoteClient*>::Node *n;
4766 n = m_clients.find(c.peer_id);
4767 // The client should exist
4771 Mark objects to be not known by the client
4773 RemoteClient *client = n->getValue();
4775 for(core::map<u16, bool>::Iterator
4776 i = client->m_known_objects.getIterator();
4777 i.atEnd()==false; i++)
4780 u16 id = i.getNode()->getKey();
4781 ServerActiveObject* obj = m_env->getActiveObject(id);
4783 if(obj && obj->m_known_by_count > 0)
4784 obj->m_known_by_count--;
4787 // Collect information about leaving in chat
4788 std::wstring message;
4790 Player *player = m_env->getPlayer(c.peer_id);
4793 std::wstring name = narrow_to_wide(player->getName());
4796 message += L" left game";
4798 message += L" (timed out)";
4804 m_env->removePlayer(c.peer_id);
4807 // Set player client disconnected
4809 Player *player = m_env->getPlayer(c.peer_id);
4811 player->peer_id = 0;
4818 std::ostringstream os(std::ios_base::binary);
4819 for(core::map<u16, RemoteClient*>::Iterator
4820 i = m_clients.getIterator();
4821 i.atEnd() == false; i++)
4823 RemoteClient *client = i.getNode()->getValue();
4824 assert(client->peer_id == i.getNode()->getKey());
4825 if(client->serialization_version == SER_FMT_VER_INVALID)
4828 Player *player = m_env->getPlayer(client->peer_id);
4831 // Get name of player
4832 os<<player->getName()<<" ";
4835 actionstream<<player->getName()<<" "
4836 <<(c.timeout?"times out.":"leaves game.")
4837 <<" List of players: "
4838 <<os.str()<<std::endl;
4843 delete m_clients[c.peer_id];
4844 m_clients.remove(c.peer_id);
4846 // Send player info to all remaining clients
4849 // Send leave chat message to all remaining clients
4850 BroadcastChatMessage(message);
4859 void Server::handlePeerChanges()
4861 while(m_peer_change_queue.size() > 0)
4863 PeerChange c = m_peer_change_queue.pop_front();
4865 infostream<<"Server: Handling peer change: "
4866 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4869 handlePeerChange(c);
4873 u64 Server::getPlayerPrivs(Player *player)
4877 std::string playername = player->getName();
4878 // Local player gets all privileges regardless of
4879 // what's set on their account.
4880 if(g_settings->get("name") == playername)
4886 return getPlayerAuthPrivs(playername);
4890 void dedicated_server_loop(Server &server, bool &kill)
4892 DSTACK(__FUNCTION_NAME);
4894 infostream<<DTIME<<std::endl;
4895 infostream<<"========================"<<std::endl;
4896 infostream<<"Running dedicated server"<<std::endl;
4897 infostream<<"========================"<<std::endl;
4898 infostream<<std::endl;
4900 IntervalLimiter m_profiler_interval;
4904 // This is kind of a hack but can be done like this
4905 // because server.step() is very light
4907 ScopeProfiler sp(g_profiler, "dedicated server sleep");
4912 if(server.getShutdownRequested() || kill)
4914 infostream<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4921 float profiler_print_interval =
4922 g_settings->getFloat("profiler_print_interval");
4923 if(profiler_print_interval != 0)
4925 if(m_profiler_interval.step(0.030, profiler_print_interval))
4927 infostream<<"Profiler:"<<std::endl;
4928 g_profiler->print(infostream);
4929 g_profiler->clear();
4936 static int counter = 0;
4942 core::list<PlayerInfo> list = server.getPlayerInfo();
4943 core::list<PlayerInfo>::Iterator i;
4944 static u32 sum_old = 0;
4945 u32 sum = PIChecksum(list);
4948 infostream<<DTIME<<"Player info:"<<std::endl;
4949 for(i=list.begin(); i!=list.end(); i++)
4951 i->PrintLine(&infostream);