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"
48 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
50 #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
52 class MapEditEventIgnorer
55 MapEditEventIgnorer(bool *flag):
64 ~MapEditEventIgnorer()
77 void * ServerThread::Thread()
81 log_register_thread("ServerThread");
83 DSTACK(__FUNCTION_NAME);
85 BEGIN_DEBUG_EXCEPTION_HANDLER
90 //TimeTaker timer("AsyncRunStep() + Receive()");
93 //TimeTaker timer("AsyncRunStep()");
94 m_server->AsyncRunStep();
97 //infostream<<"Running m_server->Receive()"<<std::endl;
100 catch(con::NoIncomingDataException &e)
103 catch(con::PeerNotFoundException &e)
105 infostream<<"Server: PeerNotFoundException"<<std::endl;
109 END_DEBUG_EXCEPTION_HANDLER(errorstream)
114 void * EmergeThread::Thread()
118 log_register_thread("EmergeThread");
120 DSTACK(__FUNCTION_NAME);
122 BEGIN_DEBUG_EXCEPTION_HANDLER
124 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
127 Get block info from queue, emerge them and send them
130 After queue is empty, exit.
134 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
138 SharedPtr<QueuedBlockEmerge> q(qptr);
144 Do not generate over-limit
146 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
147 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
148 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
149 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
150 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
151 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
154 //infostream<<"EmergeThread::Thread(): running"<<std::endl;
156 //TimeTaker timer("block emerge");
159 Try to emerge it from somewhere.
161 If it is only wanted as optional, only loading from disk
166 Check if any peer wants it as non-optional. In that case it
169 Also decrement the emerge queue count in clients.
172 bool only_from_disk = true;
175 core::map<u16, u8>::Iterator i;
176 for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
178 //u16 peer_id = i.getNode()->getKey();
181 u8 flags = i.getNode()->getValue();
182 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
183 only_from_disk = false;
188 if(enable_mapgen_debug_info)
189 infostream<<"EmergeThread: p="
190 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
191 <<"only_from_disk="<<only_from_disk<<std::endl;
193 ServerMap &map = ((ServerMap&)m_server->m_env->getMap());
195 //core::map<v3s16, MapBlock*> changed_blocks;
196 //core::map<v3s16, MapBlock*> lighting_invalidated_blocks;
198 MapBlock *block = NULL;
199 bool got_block = true;
200 core::map<v3s16, MapBlock*> modified_blocks;
203 Fetch block from map or generate a single block
206 JMutexAutoLock envlock(m_server->m_env_mutex);
208 // Load sector if it isn't loaded
209 if(map.getSectorNoGenerateNoEx(p2d) == NULL)
210 //map.loadSectorFull(p2d);
211 map.loadSectorMeta(p2d);
213 block = map.getBlockNoCreateNoEx(p);
214 if(!block || block->isDummy() || !block->isGenerated())
216 if(enable_mapgen_debug_info)
217 infostream<<"EmergeThread: not in memory, loading"<<std::endl;
219 // Get, load or create sector
220 /*ServerMapSector *sector =
221 (ServerMapSector*)map.createSector(p2d);*/
223 // Load/generate block
225 /*block = map.emergeBlock(p, sector, changed_blocks,
226 lighting_invalidated_blocks);*/
228 block = map.loadBlock(p);
230 if(only_from_disk == false)
232 if(block == NULL || block->isGenerated() == false)
234 if(enable_mapgen_debug_info)
235 infostream<<"EmergeThread: generating"<<std::endl;
236 block = map.generateBlock(p, modified_blocks);
238 v3s16 minp = block->getPos()*MAP_BLOCKSIZE;
239 v3s16 maxp = minp + v3s16(1,1,1)*(MAP_BLOCKSIZE-1);
240 scriptapi_environment_on_generated(m_server->m_lua,
245 if(enable_mapgen_debug_info)
246 infostream<<"EmergeThread: ended up with: "
247 <<analyze_block(block)<<std::endl;
256 Ignore map edit events, they will not need to be
257 sent to anybody because the block hasn't been sent
260 MapEditEventIgnorer ign(&m_server->m_ignore_map_edit_events);
262 // Activate objects and stuff
263 m_server->m_env->activateBlock(block, 3600);
268 /*if(block->getLightingExpired()){
269 lighting_invalidated_blocks[block->getPos()] = block;
273 // TODO: Some additional checking and lighting updating,
278 JMutexAutoLock envlock(m_server->m_env_mutex);
283 Collect a list of blocks that have been modified in
284 addition to the fetched one.
288 if(lighting_invalidated_blocks.size() > 0)
290 /*infostream<<"lighting "<<lighting_invalidated_blocks.size()
291 <<" blocks"<<std::endl;*/
293 // 50-100ms for single block generation
294 //TimeTaker timer("** EmergeThread updateLighting");
296 // Update lighting without locking the environment mutex,
297 // add modified blocks to changed blocks
298 map.updateLighting(lighting_invalidated_blocks, modified_blocks);
301 // Add all from changed_blocks to modified_blocks
302 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
303 i.atEnd() == false; i++)
305 MapBlock *block = i.getNode()->getValue();
306 modified_blocks.insert(block->getPos(), block);
310 // If we got no block, there should be no invalidated blocks
313 //assert(lighting_invalidated_blocks.size() == 0);
319 Set sent status of modified blocks on clients
322 // NOTE: Server's clients are also behind the connection mutex
323 JMutexAutoLock lock(m_server->m_con_mutex);
326 Add the originally fetched block to the modified list
330 modified_blocks.insert(p, block);
334 Set the modified blocks unsent for all the clients
337 for(core::map<u16, RemoteClient*>::Iterator
338 i = m_server->m_clients.getIterator();
339 i.atEnd() == false; i++)
341 RemoteClient *client = i.getNode()->getValue();
343 if(modified_blocks.size() > 0)
345 // Remove block from sent history
346 client->SetBlocksNotSent(modified_blocks);
352 END_DEBUG_EXCEPTION_HANDLER(errorstream)
357 void RemoteClient::GetNextBlocks(Server *server, float dtime,
358 core::array<PrioritySortedBlockTransfer> &dest)
360 DSTACK(__FUNCTION_NAME);
363 TimeTaker timer("RemoteClient::GetNextBlocks", &timer_result);*/
366 m_nothing_to_send_pause_timer -= dtime;
367 m_nearest_unsent_reset_timer += dtime;
369 if(m_nothing_to_send_pause_timer >= 0)
374 // Won't send anything if already sending
375 if(m_blocks_sending.size() >= g_settings->getU16
376 ("max_simultaneous_block_sends_per_client"))
378 //infostream<<"Not sending any blocks, Queue full."<<std::endl;
382 //TimeTaker timer("RemoteClient::GetNextBlocks");
384 Player *player = server->m_env->getPlayer(peer_id);
386 assert(player != NULL);
388 v3f playerpos = player->getPosition();
389 v3f playerspeed = player->getSpeed();
390 v3f playerspeeddir(0,0,0);
391 if(playerspeed.getLength() > 1.0*BS)
392 playerspeeddir = playerspeed / playerspeed.getLength();
393 // Predict to next block
394 v3f playerpos_predicted = playerpos + playerspeeddir*MAP_BLOCKSIZE*BS;
396 v3s16 center_nodepos = floatToInt(playerpos_predicted, BS);
398 v3s16 center = getNodeBlockPos(center_nodepos);
400 // Camera position and direction
401 v3f camera_pos = player->getEyePosition();
402 v3f camera_dir = v3f(0,0,1);
403 camera_dir.rotateYZBy(player->getPitch());
404 camera_dir.rotateXZBy(player->getYaw());
406 /*infostream<<"camera_dir=("<<camera_dir.X<<","<<camera_dir.Y<<","
407 <<camera_dir.Z<<")"<<std::endl;*/
410 Get the starting value of the block finder radius.
413 if(m_last_center != center)
415 m_nearest_unsent_d = 0;
416 m_last_center = center;
419 /*infostream<<"m_nearest_unsent_reset_timer="
420 <<m_nearest_unsent_reset_timer<<std::endl;*/
422 // Reset periodically to workaround for some bugs or stuff
423 if(m_nearest_unsent_reset_timer > 20.0)
425 m_nearest_unsent_reset_timer = 0;
426 m_nearest_unsent_d = 0;
427 //infostream<<"Resetting m_nearest_unsent_d for "
428 // <<server->getPlayerName(peer_id)<<std::endl;
431 //s16 last_nearest_unsent_d = m_nearest_unsent_d;
432 s16 d_start = m_nearest_unsent_d;
434 //infostream<<"d_start="<<d_start<<std::endl;
436 u16 max_simul_sends_setting = g_settings->getU16
437 ("max_simultaneous_block_sends_per_client");
438 u16 max_simul_sends_usually = max_simul_sends_setting;
441 Check the time from last addNode/removeNode.
443 Decrease send rate if player is building stuff.
445 m_time_from_building += dtime;
446 if(m_time_from_building < g_settings->getFloat(
447 "full_block_send_enable_min_time_from_building"))
449 max_simul_sends_usually
450 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
454 Number of blocks sending + number of blocks selected for sending
456 u32 num_blocks_selected = m_blocks_sending.size();
459 next time d will be continued from the d from which the nearest
460 unsent block was found this time.
462 This is because not necessarily any of the blocks found this
463 time are actually sent.
465 s32 new_nearest_unsent_d = -1;
467 s16 d_max = g_settings->getS16("max_block_send_distance");
468 s16 d_max_gen = g_settings->getS16("max_block_generate_distance");
470 // Don't loop very much at a time
471 s16 max_d_increment_at_time = 2;
472 if(d_max > d_start + max_d_increment_at_time)
473 d_max = d_start + max_d_increment_at_time;
474 /*if(d_max_gen > d_start+2)
475 d_max_gen = d_start+2;*/
477 //infostream<<"Starting from "<<d_start<<std::endl;
479 s32 nearest_emerged_d = -1;
480 s32 nearest_emergefull_d = -1;
481 s32 nearest_sent_d = -1;
482 bool queue_is_full = false;
485 for(d = d_start; d <= d_max; d++)
487 /*errorstream<<"checking d="<<d<<" for "
488 <<server->getPlayerName(peer_id)<<std::endl;*/
489 //infostream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
492 If m_nearest_unsent_d was changed by the EmergeThread
493 (it can change it to 0 through SetBlockNotSent),
495 Else update m_nearest_unsent_d
497 /*if(m_nearest_unsent_d != last_nearest_unsent_d)
499 d = m_nearest_unsent_d;
500 last_nearest_unsent_d = m_nearest_unsent_d;
504 Get the border/face dot coordinates of a "d-radiused"
507 core::list<v3s16> list;
508 getFacePositions(list, d);
510 core::list<v3s16>::Iterator li;
511 for(li=list.begin(); li!=list.end(); li++)
513 v3s16 p = *li + center;
517 - Don't allow too many simultaneous transfers
518 - EXCEPT when the blocks are very close
520 Also, don't send blocks that are already flying.
523 // Start with the usual maximum
524 u16 max_simul_dynamic = max_simul_sends_usually;
526 // If block is very close, allow full maximum
527 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
528 max_simul_dynamic = max_simul_sends_setting;
530 // Don't select too many blocks for sending
531 if(num_blocks_selected >= max_simul_dynamic)
533 queue_is_full = true;
534 goto queue_full_break;
537 // Don't send blocks that are currently being transferred
538 if(m_blocks_sending.find(p) != NULL)
544 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
545 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
546 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
547 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
548 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
549 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
552 // If this is true, inexistent block will be made from scratch
553 bool generate = d <= d_max_gen;
556 /*// Limit the generating area vertically to 2/3
557 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
560 // Limit the send area vertically to 1/2
561 if(abs(p.Y - center.Y) > d_max / 2)
567 If block is far away, don't generate it unless it is
573 // Block center y in nodes
574 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
575 // Don't generate if it's very high or very low
576 if(y < -64 || y > 64)
580 v2s16 p2d_nodes_center(
584 // Get ground height in nodes
585 s16 gh = server->m_env->getServerMap().findGroundLevel(
588 // If differs a lot, don't generate
589 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
591 // Actually, don't even send it
597 //infostream<<"d="<<d<<std::endl;
600 Don't generate or send if not in sight
601 FIXME This only works if the client uses a small enough
602 FOV setting. The default of 72 degrees is fine.
605 float camera_fov = (72.0*PI/180) * 4./3.;
606 if(isBlockInSight(p, camera_pos, camera_dir, camera_fov, 10000*BS) == false)
612 Don't send already sent blocks
615 if(m_blocks_sent.find(p) != NULL)
622 Check if map has this block
624 MapBlock *block = server->m_env->getMap().getBlockNoCreateNoEx(p);
626 bool surely_not_found_on_disk = false;
627 bool block_is_invalid = false;
630 // Reset usage timer, this block will be of use in the future.
631 block->resetUsageTimer();
633 // Block is dummy if data doesn't exist.
634 // It means it has been not found from disk and not generated
637 surely_not_found_on_disk = true;
640 // Block is valid if lighting is up-to-date and data exists
641 if(block->isValid() == false)
643 block_is_invalid = true;
646 /*if(block->isFullyGenerated() == false)
648 block_is_invalid = true;
653 ServerMap *map = (ServerMap*)(&server->m_env->getMap());
654 v2s16 chunkpos = map->sector_to_chunk(p2d);
655 if(map->chunkNonVolatile(chunkpos) == false)
656 block_is_invalid = true;
658 if(block->isGenerated() == false)
659 block_is_invalid = true;
662 If block is not close, don't send it unless it is near
665 Block is near ground level if night-time mesh
666 differs from day-time mesh.
670 if(block->dayNightDiffed() == false)
677 If block has been marked to not exist on disk (dummy)
678 and generating new ones is not wanted, skip block.
680 if(generate == false && surely_not_found_on_disk == true)
687 Add inexistent block to emerge queue.
689 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
691 //TODO: Get value from somewhere
692 // Allow only one block in emerge queue
693 //if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
694 // Allow two blocks in queue per client
695 //if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
697 // Make it more responsive when needing to generate stuff
698 if(surely_not_found_on_disk)
700 if(server->m_emerge_queue.peerItemCount(peer_id) < max_emerge)
702 //infostream<<"Adding block to emerge queue"<<std::endl;
704 // Add it to the emerge queue and trigger the thread
707 if(generate == false)
708 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
710 server->m_emerge_queue.addBlock(peer_id, p, flags);
711 server->m_emergethread.trigger();
713 if(nearest_emerged_d == -1)
714 nearest_emerged_d = d;
716 if(nearest_emergefull_d == -1)
717 nearest_emergefull_d = d;
724 if(nearest_sent_d == -1)
728 Add block to send queue
731 /*errorstream<<"sending from d="<<d<<" to "
732 <<server->getPlayerName(peer_id)<<std::endl;*/
734 PrioritySortedBlockTransfer q((float)d, p, peer_id);
738 num_blocks_selected += 1;
743 //infostream<<"Stopped at "<<d<<std::endl;
745 // If nothing was found for sending and nothing was queued for
746 // emerging, continue next time browsing from here
747 if(nearest_emerged_d != -1){
748 new_nearest_unsent_d = nearest_emerged_d;
749 } else if(nearest_emergefull_d != -1){
750 new_nearest_unsent_d = nearest_emergefull_d;
752 if(d > g_settings->getS16("max_block_send_distance")){
753 new_nearest_unsent_d = 0;
754 m_nothing_to_send_pause_timer = 2.0;
755 /*infostream<<"GetNextBlocks(): d wrapped around for "
756 <<server->getPlayerName(peer_id)
757 <<"; setting to 0 and pausing"<<std::endl;*/
759 if(nearest_sent_d != -1)
760 new_nearest_unsent_d = nearest_sent_d;
762 new_nearest_unsent_d = d;
766 if(new_nearest_unsent_d != -1)
767 m_nearest_unsent_d = new_nearest_unsent_d;
769 /*timer_result = timer.stop(true);
770 if(timer_result != 0)
771 infostream<<"GetNextBlocks duration: "<<timer_result<<" (!=0)"<<std::endl;*/
774 void RemoteClient::SendObjectData(
777 core::map<v3s16, bool> &stepped_blocks
780 DSTACK(__FUNCTION_NAME);
782 // Can't send anything without knowing version
783 if(serialization_version == SER_FMT_VER_INVALID)
785 infostream<<"RemoteClient::SendObjectData(): Not sending, no version."
791 Send a TOCLIENT_OBJECTDATA packet.
795 u16 number of player positions
807 std::ostringstream os(std::ios_base::binary);
811 writeU16(buf, TOCLIENT_OBJECTDATA);
812 os.write((char*)buf, 2);
815 Get and write player data
818 // Get connected players
819 core::list<Player*> players = server->m_env->getPlayers(true);
821 // Write player count
822 u16 playercount = players.size();
823 writeU16(buf, playercount);
824 os.write((char*)buf, 2);
826 core::list<Player*>::Iterator i;
827 for(i = players.begin();
828 i != players.end(); i++)
832 v3f pf = player->getPosition();
833 v3f sf = player->getSpeed();
835 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
836 v3s32 speed_i (sf.X*100, sf.Y*100, sf.Z*100);
837 s32 pitch_i (player->getPitch() * 100);
838 s32 yaw_i (player->getYaw() * 100);
840 writeU16(buf, player->peer_id);
841 os.write((char*)buf, 2);
842 writeV3S32(buf, position_i);
843 os.write((char*)buf, 12);
844 writeV3S32(buf, speed_i);
845 os.write((char*)buf, 12);
846 writeS32(buf, pitch_i);
847 os.write((char*)buf, 4);
848 writeS32(buf, yaw_i);
849 os.write((char*)buf, 4);
853 Get and write object data (dummy, for compatibility)
858 os.write((char*)buf, 2);
864 //infostream<<"Server: Sending object data to "<<peer_id<<std::endl;
867 std::string s = os.str();
868 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
869 // Send as unreliable
870 server->m_con.Send(peer_id, 0, data, false);
873 void RemoteClient::GotBlock(v3s16 p)
875 if(m_blocks_sending.find(p) != NULL)
876 m_blocks_sending.remove(p);
879 /*infostream<<"RemoteClient::GotBlock(): Didn't find in"
880 " m_blocks_sending"<<std::endl;*/
881 m_excess_gotblocks++;
883 m_blocks_sent.insert(p, true);
886 void RemoteClient::SentBlock(v3s16 p)
888 if(m_blocks_sending.find(p) == NULL)
889 m_blocks_sending.insert(p, 0.0);
891 infostream<<"RemoteClient::SentBlock(): Sent block"
892 " already in m_blocks_sending"<<std::endl;
895 void RemoteClient::SetBlockNotSent(v3s16 p)
897 m_nearest_unsent_d = 0;
899 if(m_blocks_sending.find(p) != NULL)
900 m_blocks_sending.remove(p);
901 if(m_blocks_sent.find(p) != NULL)
902 m_blocks_sent.remove(p);
905 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
907 m_nearest_unsent_d = 0;
909 for(core::map<v3s16, MapBlock*>::Iterator
910 i = blocks.getIterator();
911 i.atEnd()==false; i++)
913 v3s16 p = i.getNode()->getKey();
915 if(m_blocks_sending.find(p) != NULL)
916 m_blocks_sending.remove(p);
917 if(m_blocks_sent.find(p) != NULL)
918 m_blocks_sent.remove(p);
926 PlayerInfo::PlayerInfo()
932 void PlayerInfo::PrintLine(std::ostream *s)
935 (*s)<<"\""<<name<<"\" ("
936 <<(position.X/10)<<","<<(position.Y/10)
937 <<","<<(position.Z/10)<<") ";
939 (*s)<<" avg_rtt="<<avg_rtt;
943 u32 PIChecksum(core::list<PlayerInfo> &l)
945 core::list<PlayerInfo>::Iterator i;
948 for(i=l.begin(); i!=l.end(); i++)
950 checksum += a * (i->id+1);
951 checksum ^= 0x435aafcd;
962 ModSpec(const std::string &name_="", const std::string path_=""):
968 static core::list<ModSpec> getMods(core::list<std::string> &modspaths)
970 core::list<ModSpec> mods;
971 for(core::list<std::string>::Iterator i = modspaths.begin();
972 i != modspaths.end(); i++){
973 std::string modspath = *i;
974 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(modspath);
975 for(u32 j=0; j<dirlist.size(); j++){
978 std::string modname = dirlist[j].name;
979 std::string modpath = modspath + DIR_DELIM + modname;
980 mods.push_back(ModSpec(modname, modpath));
991 std::string mapsavedir,
992 std::string configpath
995 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
996 m_authmanager(mapsavedir+DIR_DELIM+"auth.txt"),
997 m_banmanager(mapsavedir+DIR_DELIM+"ipban.txt"),
999 m_toolmgr(createToolDefManager()),
1000 m_nodedef(createNodeDefManager()),
1001 m_craftdef(createCraftDefManager()),
1003 m_emergethread(this),
1005 m_time_of_day_send_timer(0),
1007 m_mapsavedir(mapsavedir),
1008 m_configpath(configpath),
1009 m_shutdown_requested(false),
1010 m_ignore_map_edit_events(false),
1011 m_ignore_map_edit_events_peer_id(0)
1013 m_liquid_transform_timer = 0.0;
1014 m_print_info_timer = 0.0;
1015 m_objectdata_timer = 0.0;
1016 m_emergethread_trigger_timer = 0.0;
1017 m_savemap_timer = 0.0;
1021 m_step_dtime_mutex.Init();
1024 JMutexAutoLock envlock(m_env_mutex);
1025 JMutexAutoLock conlock(m_con_mutex);
1027 infostream<<"m_nodedef="<<m_nodedef<<std::endl;
1029 // Path to builtin.lua
1030 std::string builtinpath = porting::path_data + DIR_DELIM + "builtin.lua";
1031 // Add default global mod path
1032 m_modspaths.push_back(porting::path_data + DIR_DELIM + "mods");
1034 // Initialize scripting
1036 infostream<<"Server: Initializing scripting"<<std::endl;
1037 m_lua = script_init();
1040 scriptapi_export(m_lua, this);
1041 // Load and run builtin.lua
1042 infostream<<"Server: Loading builtin Lua stuff from \""<<builtinpath
1044 bool success = script_load(m_lua, builtinpath.c_str());
1046 errorstream<<"Server: Failed to load and run "
1047 <<builtinpath<<std::endl;
1050 // Load and run "mod" scripts
1051 core::list<ModSpec> mods = getMods(m_modspaths);
1052 for(core::list<ModSpec>::Iterator i = mods.begin();
1053 i != mods.end(); i++){
1055 infostream<<"Server: Loading mod \""<<mod.name<<"\""<<std::endl;
1056 std::string scriptpath = mod.path + DIR_DELIM + "init.lua";
1057 bool success = script_load(m_lua, scriptpath.c_str());
1059 errorstream<<"Server: Failed to load and run "
1060 <<scriptpath<<std::endl;
1065 // Initialize Environment
1067 m_env = new ServerEnvironment(new ServerMap(mapsavedir, this), m_lua, this);
1069 // Give environment reference to scripting api
1070 scriptapi_add_environment(m_lua, m_env);
1072 // Register us to receive map edit events
1073 m_env->getMap().addEventReceiver(this);
1075 // If file exists, load environment metadata
1076 if(fs::PathExists(m_mapsavedir+DIR_DELIM+"env_meta.txt"))
1078 infostream<<"Server: Loading environment metadata"<<std::endl;
1079 m_env->loadMeta(m_mapsavedir);
1083 infostream<<"Server: Loading players"<<std::endl;
1084 m_env->deSerializePlayers(m_mapsavedir);
1089 infostream<<"Server::~Server()"<<std::endl;
1092 Send shutdown message
1095 JMutexAutoLock conlock(m_con_mutex);
1097 std::wstring line = L"*** Server shutting down";
1100 Send the message to clients
1102 for(core::map<u16, RemoteClient*>::Iterator
1103 i = m_clients.getIterator();
1104 i.atEnd() == false; i++)
1106 // Get client and check that it is valid
1107 RemoteClient *client = i.getNode()->getValue();
1108 assert(client->peer_id == i.getNode()->getKey());
1109 if(client->serialization_version == SER_FMT_VER_INVALID)
1113 SendChatMessage(client->peer_id, line);
1115 catch(con::PeerNotFoundException &e)
1121 JMutexAutoLock envlock(m_env_mutex);
1126 infostream<<"Server: Saving players"<<std::endl;
1127 m_env->serializePlayers(m_mapsavedir);
1130 Save environment metadata
1132 infostream<<"Server: Saving environment metadata"<<std::endl;
1133 m_env->saveMeta(m_mapsavedir);
1145 JMutexAutoLock clientslock(m_con_mutex);
1147 for(core::map<u16, RemoteClient*>::Iterator
1148 i = m_clients.getIterator();
1149 i.atEnd() == false; i++)
1152 // NOTE: These are removed by env destructor
1154 u16 peer_id = i.getNode()->getKey();
1155 JMutexAutoLock envlock(m_env_mutex);
1156 m_env->removePlayer(peer_id);
1160 delete i.getNode()->getValue();
1164 // Delete Environment
1170 // Deinitialize scripting
1171 infostream<<"Server: Deinitializing scripting"<<std::endl;
1172 script_deinit(m_lua);
1175 void Server::start(unsigned short port)
1177 DSTACK(__FUNCTION_NAME);
1178 // Stop thread if already running
1181 // Initialize connection
1182 m_con.SetTimeoutMs(30);
1186 m_thread.setRun(true);
1189 infostream<<"Server: Started on port "<<port<<std::endl;
1194 DSTACK(__FUNCTION_NAME);
1196 infostream<<"Server: Stopping and waiting threads"<<std::endl;
1198 // Stop threads (set run=false first so both start stopping)
1199 m_thread.setRun(false);
1200 m_emergethread.setRun(false);
1202 m_emergethread.stop();
1204 infostream<<"Server: Threads stopped"<<std::endl;
1207 void Server::step(float dtime)
1209 DSTACK(__FUNCTION_NAME);
1214 JMutexAutoLock lock(m_step_dtime_mutex);
1215 m_step_dtime += dtime;
1219 void Server::AsyncRunStep()
1221 DSTACK(__FUNCTION_NAME);
1223 g_profiler->add("Server::AsyncRunStep (num)", 1);
1227 JMutexAutoLock lock1(m_step_dtime_mutex);
1228 dtime = m_step_dtime;
1232 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
1233 // Send blocks to clients
1240 g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
1242 //infostream<<"Server steps "<<dtime<<std::endl;
1243 //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1246 JMutexAutoLock lock1(m_step_dtime_mutex);
1247 m_step_dtime -= dtime;
1254 m_uptime.set(m_uptime.get() + dtime);
1258 // Process connection's timeouts
1259 JMutexAutoLock lock2(m_con_mutex);
1260 ScopeProfiler sp(g_profiler, "Server: connection timeout processing");
1261 m_con.RunTimeouts(dtime);
1265 // This has to be called so that the client list gets synced
1266 // with the peer list of the connection
1267 handlePeerChanges();
1271 Update m_time_of_day and overall game time
1274 JMutexAutoLock envlock(m_env_mutex);
1276 m_time_counter += dtime;
1277 f32 speed = g_settings->getFloat("time_speed") * 24000./(24.*3600);
1278 u32 units = (u32)(m_time_counter*speed);
1279 m_time_counter -= (f32)units / speed;
1281 m_env->setTimeOfDay((m_env->getTimeOfDay() + units) % 24000);
1283 //infostream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1286 Send to clients at constant intervals
1289 m_time_of_day_send_timer -= dtime;
1290 if(m_time_of_day_send_timer < 0.0)
1292 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
1294 //JMutexAutoLock envlock(m_env_mutex);
1295 JMutexAutoLock conlock(m_con_mutex);
1297 for(core::map<u16, RemoteClient*>::Iterator
1298 i = m_clients.getIterator();
1299 i.atEnd() == false; i++)
1301 RemoteClient *client = i.getNode()->getValue();
1302 //Player *player = m_env->getPlayer(client->peer_id);
1304 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1305 m_env->getTimeOfDay());
1307 m_con.Send(client->peer_id, 0, data, true);
1313 JMutexAutoLock lock(m_env_mutex);
1315 ScopeProfiler sp(g_profiler, "SEnv step");
1316 ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
1320 const float map_timer_and_unload_dtime = 2.92;
1321 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
1323 JMutexAutoLock lock(m_env_mutex);
1324 // Run Map's timers and unload unused data
1325 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
1326 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
1327 g_settings->getFloat("server_unload_unused_data_timeout"));
1335 Check player movements
1337 NOTE: Actually the server should handle player physics like the
1338 client does and compare player's position to what is calculated
1339 on our side. This is required when eg. players fly due to an
1343 JMutexAutoLock lock(m_env_mutex);
1344 JMutexAutoLock lock2(m_con_mutex);
1346 //float player_max_speed = BS * 4.0; // Normal speed
1347 float player_max_speed = BS * 20; // Fast speed
1348 float player_max_speed_up = BS * 20;
1350 player_max_speed *= 1.7; // Tolerance
1351 player_max_speed_up *= 1.7;
1353 for(core::map<u16, RemoteClient*>::Iterator
1354 i = m_clients.getIterator();
1355 i.atEnd() == false; i++)
1357 RemoteClient *client = i.getNode()->getValue();
1358 ServerRemotePlayer *player =
1359 (ServerRemotePlayer*)m_env->getPlayer(client->peer_id);
1362 player->m_last_good_position_age += dtime;
1363 if(player->m_last_good_position_age >= 2.0){
1364 float age = player->m_last_good_position_age;
1365 v3f diff = (player->getPosition() - player->m_last_good_position);
1366 float d_vert = diff.Y;
1368 float d_horiz = diff.getLength();
1369 /*infostream<<player->getName()<<"'s horizontal speed is "
1370 <<(d_horiz/age)<<std::endl;*/
1371 if(d_horiz <= age * player_max_speed &&
1372 (d_vert < 0 || d_vert < age * player_max_speed_up)){
1373 player->m_last_good_position = player->getPosition();
1375 actionstream<<"Player "<<player->getName()
1376 <<" moved too fast; resetting position"
1378 player->setPosition(player->m_last_good_position);
1379 SendMovePlayer(player);
1381 player->m_last_good_position_age = 0;
1386 /* Transform liquids */
1387 m_liquid_transform_timer += dtime;
1388 if(m_liquid_transform_timer >= 1.00)
1390 m_liquid_transform_timer -= 1.00;
1392 JMutexAutoLock lock(m_env_mutex);
1394 ScopeProfiler sp(g_profiler, "Server: liquid transform");
1396 core::map<v3s16, MapBlock*> modified_blocks;
1397 m_env->getMap().transformLiquids(modified_blocks);
1402 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1403 ServerMap &map = ((ServerMap&)m_env->getMap());
1404 map.updateLighting(modified_blocks, lighting_modified_blocks);
1406 // Add blocks modified by lighting to modified_blocks
1407 for(core::map<v3s16, MapBlock*>::Iterator
1408 i = lighting_modified_blocks.getIterator();
1409 i.atEnd() == false; i++)
1411 MapBlock *block = i.getNode()->getValue();
1412 modified_blocks.insert(block->getPos(), block);
1416 Set the modified blocks unsent for all the clients
1419 JMutexAutoLock lock2(m_con_mutex);
1421 for(core::map<u16, RemoteClient*>::Iterator
1422 i = m_clients.getIterator();
1423 i.atEnd() == false; i++)
1425 RemoteClient *client = i.getNode()->getValue();
1427 if(modified_blocks.size() > 0)
1429 // Remove block from sent history
1430 client->SetBlocksNotSent(modified_blocks);
1435 // Periodically print some info
1437 float &counter = m_print_info_timer;
1443 JMutexAutoLock lock2(m_con_mutex);
1445 if(m_clients.size() != 0)
1446 infostream<<"Players:"<<std::endl;
1447 for(core::map<u16, RemoteClient*>::Iterator
1448 i = m_clients.getIterator();
1449 i.atEnd() == false; i++)
1451 //u16 peer_id = i.getNode()->getKey();
1452 RemoteClient *client = i.getNode()->getValue();
1453 Player *player = m_env->getPlayer(client->peer_id);
1456 infostream<<"* "<<player->getName()<<"\t";
1457 client->PrintInfo(infostream);
1462 //if(g_settings->getBool("enable_experimental"))
1466 Check added and deleted active objects
1469 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
1470 JMutexAutoLock envlock(m_env_mutex);
1471 JMutexAutoLock conlock(m_con_mutex);
1473 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
1475 // Radius inside which objects are active
1476 s16 radius = g_settings->getS16("active_object_send_range_blocks");
1477 radius *= MAP_BLOCKSIZE;
1479 for(core::map<u16, RemoteClient*>::Iterator
1480 i = m_clients.getIterator();
1481 i.atEnd() == false; i++)
1483 RemoteClient *client = i.getNode()->getValue();
1484 Player *player = m_env->getPlayer(client->peer_id);
1487 // This can happen if the client timeouts somehow
1488 /*infostream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
1490 <<" has no associated player"<<std::endl;*/
1493 v3s16 pos = floatToInt(player->getPosition(), BS);
1495 core::map<u16, bool> removed_objects;
1496 core::map<u16, bool> added_objects;
1497 m_env->getRemovedActiveObjects(pos, radius,
1498 client->m_known_objects, removed_objects);
1499 m_env->getAddedActiveObjects(pos, radius,
1500 client->m_known_objects, added_objects);
1502 // Ignore if nothing happened
1503 if(removed_objects.size() == 0 && added_objects.size() == 0)
1505 //infostream<<"active objects: none changed"<<std::endl;
1509 std::string data_buffer;
1513 // Handle removed objects
1514 writeU16((u8*)buf, removed_objects.size());
1515 data_buffer.append(buf, 2);
1516 for(core::map<u16, bool>::Iterator
1517 i = removed_objects.getIterator();
1518 i.atEnd()==false; i++)
1521 u16 id = i.getNode()->getKey();
1522 ServerActiveObject* obj = m_env->getActiveObject(id);
1524 // Add to data buffer for sending
1525 writeU16((u8*)buf, i.getNode()->getKey());
1526 data_buffer.append(buf, 2);
1528 // Remove from known objects
1529 client->m_known_objects.remove(i.getNode()->getKey());
1531 if(obj && obj->m_known_by_count > 0)
1532 obj->m_known_by_count--;
1535 // Handle added objects
1536 writeU16((u8*)buf, added_objects.size());
1537 data_buffer.append(buf, 2);
1538 for(core::map<u16, bool>::Iterator
1539 i = added_objects.getIterator();
1540 i.atEnd()==false; i++)
1543 u16 id = i.getNode()->getKey();
1544 ServerActiveObject* obj = m_env->getActiveObject(id);
1547 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1549 infostream<<"WARNING: "<<__FUNCTION_NAME
1550 <<": NULL object"<<std::endl;
1552 type = obj->getType();
1554 // Add to data buffer for sending
1555 writeU16((u8*)buf, id);
1556 data_buffer.append(buf, 2);
1557 writeU8((u8*)buf, type);
1558 data_buffer.append(buf, 1);
1561 data_buffer.append(serializeLongString(
1562 obj->getClientInitializationData()));
1564 data_buffer.append(serializeLongString(""));
1566 // Add to known objects
1567 client->m_known_objects.insert(i.getNode()->getKey(), false);
1570 obj->m_known_by_count++;
1574 SharedBuffer<u8> reply(2 + data_buffer.size());
1575 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1576 memcpy((char*)&reply[2], data_buffer.c_str(),
1577 data_buffer.size());
1579 m_con.Send(client->peer_id, 0, reply, true);
1581 infostream<<"Server: Sent object remove/add: "
1582 <<removed_objects.size()<<" removed, "
1583 <<added_objects.size()<<" added, "
1584 <<"packet size is "<<reply.getSize()<<std::endl;
1589 Collect a list of all the objects known by the clients
1590 and report it back to the environment.
1593 core::map<u16, bool> all_known_objects;
1595 for(core::map<u16, RemoteClient*>::Iterator
1596 i = m_clients.getIterator();
1597 i.atEnd() == false; i++)
1599 RemoteClient *client = i.getNode()->getValue();
1600 // Go through all known objects of client
1601 for(core::map<u16, bool>::Iterator
1602 i = client->m_known_objects.getIterator();
1603 i.atEnd()==false; i++)
1605 u16 id = i.getNode()->getKey();
1606 all_known_objects[id] = true;
1610 m_env->setKnownActiveObjects(whatever);
1616 Send object messages
1619 JMutexAutoLock envlock(m_env_mutex);
1620 JMutexAutoLock conlock(m_con_mutex);
1622 //ScopeProfiler sp(g_profiler, "Server: sending object messages");
1625 // Value = data sent by object
1626 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1628 // Get active object messages from environment
1631 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
1635 core::list<ActiveObjectMessage>* message_list = NULL;
1636 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1637 n = buffered_messages.find(aom.id);
1640 message_list = new core::list<ActiveObjectMessage>;
1641 buffered_messages.insert(aom.id, message_list);
1645 message_list = n->getValue();
1647 message_list->push_back(aom);
1650 // Route data to every client
1651 for(core::map<u16, RemoteClient*>::Iterator
1652 i = m_clients.getIterator();
1653 i.atEnd()==false; i++)
1655 RemoteClient *client = i.getNode()->getValue();
1656 std::string reliable_data;
1657 std::string unreliable_data;
1658 // Go through all objects in message buffer
1659 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1660 j = buffered_messages.getIterator();
1661 j.atEnd()==false; j++)
1663 // If object is not known by client, skip it
1664 u16 id = j.getNode()->getKey();
1665 if(client->m_known_objects.find(id) == NULL)
1667 // Get message list of object
1668 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1669 // Go through every message
1670 for(core::list<ActiveObjectMessage>::Iterator
1671 k = list->begin(); k != list->end(); k++)
1673 // Compose the full new data with header
1674 ActiveObjectMessage aom = *k;
1675 std::string new_data;
1678 writeU16((u8*)&buf[0], aom.id);
1679 new_data.append(buf, 2);
1681 new_data += serializeString(aom.datastring);
1682 // Add data to buffer
1684 reliable_data += new_data;
1686 unreliable_data += new_data;
1690 reliable_data and unreliable_data are now ready.
1693 if(reliable_data.size() > 0)
1695 SharedBuffer<u8> reply(2 + reliable_data.size());
1696 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1697 memcpy((char*)&reply[2], reliable_data.c_str(),
1698 reliable_data.size());
1700 m_con.Send(client->peer_id, 0, reply, true);
1702 if(unreliable_data.size() > 0)
1704 SharedBuffer<u8> reply(2 + unreliable_data.size());
1705 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1706 memcpy((char*)&reply[2], unreliable_data.c_str(),
1707 unreliable_data.size());
1708 // Send as unreliable
1709 m_con.Send(client->peer_id, 0, reply, false);
1712 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1714 infostream<<"Server: Size of object message data: "
1715 <<"reliable: "<<reliable_data.size()
1716 <<", unreliable: "<<unreliable_data.size()
1721 // Clear buffered_messages
1722 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1723 i = buffered_messages.getIterator();
1724 i.atEnd()==false; i++)
1726 delete i.getNode()->getValue();
1730 } // enable_experimental
1733 Send queued-for-sending map edit events.
1736 // Don't send too many at a time
1739 // Single change sending is disabled if queue size is not small
1740 bool disable_single_change_sending = false;
1741 if(m_unsent_map_edit_queue.size() >= 4)
1742 disable_single_change_sending = true;
1744 bool got_any_events = false;
1746 // We'll log the amount of each
1749 while(m_unsent_map_edit_queue.size() != 0)
1751 got_any_events = true;
1753 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1755 // Players far away from the change are stored here.
1756 // Instead of sending the changes, MapBlocks are set not sent
1758 core::list<u16> far_players;
1760 if(event->type == MEET_ADDNODE)
1762 //infostream<<"Server: MEET_ADDNODE"<<std::endl;
1763 prof.add("MEET_ADDNODE", 1);
1764 if(disable_single_change_sending)
1765 sendAddNode(event->p, event->n, event->already_known_by_peer,
1768 sendAddNode(event->p, event->n, event->already_known_by_peer,
1771 else if(event->type == MEET_REMOVENODE)
1773 //infostream<<"Server: MEET_REMOVENODE"<<std::endl;
1774 prof.add("MEET_REMOVENODE", 1);
1775 if(disable_single_change_sending)
1776 sendRemoveNode(event->p, event->already_known_by_peer,
1779 sendRemoveNode(event->p, event->already_known_by_peer,
1782 else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
1784 infostream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
1785 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
1786 setBlockNotSent(event->p);
1788 else if(event->type == MEET_OTHER)
1790 infostream<<"Server: MEET_OTHER"<<std::endl;
1791 prof.add("MEET_OTHER", 1);
1792 for(core::map<v3s16, bool>::Iterator
1793 i = event->modified_blocks.getIterator();
1794 i.atEnd()==false; i++)
1796 v3s16 p = i.getNode()->getKey();
1802 prof.add("unknown", 1);
1803 infostream<<"WARNING: Server: Unknown MapEditEvent "
1804 <<((u32)event->type)<<std::endl;
1808 Set blocks not sent to far players
1810 if(far_players.size() > 0)
1812 // Convert list format to that wanted by SetBlocksNotSent
1813 core::map<v3s16, MapBlock*> modified_blocks2;
1814 for(core::map<v3s16, bool>::Iterator
1815 i = event->modified_blocks.getIterator();
1816 i.atEnd()==false; i++)
1818 v3s16 p = i.getNode()->getKey();
1819 modified_blocks2.insert(p,
1820 m_env->getMap().getBlockNoCreateNoEx(p));
1822 // Set blocks not sent
1823 for(core::list<u16>::Iterator
1824 i = far_players.begin();
1825 i != far_players.end(); i++)
1828 RemoteClient *client = getClient(peer_id);
1831 client->SetBlocksNotSent(modified_blocks2);
1837 /*// Don't send too many at a time
1839 if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
1845 infostream<<"Server: MapEditEvents:"<<std::endl;
1846 prof.print(infostream);
1852 Send object positions
1855 float &counter = m_objectdata_timer;
1857 if(counter >= g_settings->getFloat("objectdata_interval"))
1859 JMutexAutoLock lock1(m_env_mutex);
1860 JMutexAutoLock lock2(m_con_mutex);
1862 //ScopeProfiler sp(g_profiler, "Server: sending player positions");
1864 SendObjectData(counter);
1871 Trigger emergethread (it somehow gets to a non-triggered but
1872 bysy state sometimes)
1875 float &counter = m_emergethread_trigger_timer;
1881 m_emergethread.trigger();
1885 // Save map, players and auth stuff
1887 float &counter = m_savemap_timer;
1889 if(counter >= g_settings->getFloat("server_map_save_interval"))
1893 ScopeProfiler sp(g_profiler, "Server: saving stuff");
1896 if(m_authmanager.isModified())
1897 m_authmanager.save();
1900 if(m_banmanager.isModified())
1901 m_banmanager.save();
1904 JMutexAutoLock lock(m_env_mutex);
1906 /*// Unload unused data (delete from memory)
1907 m_env->getMap().unloadUnusedData(
1908 g_settings->getFloat("server_unload_unused_sectors_timeout"));
1910 /*u32 deleted_count = m_env->getMap().unloadUnusedData(
1911 g_settings->getFloat("server_unload_unused_sectors_timeout"));
1914 // Save only changed parts
1915 m_env->getMap().save(true);
1917 /*if(deleted_count > 0)
1919 infostream<<"Server: Unloaded "<<deleted_count
1920 <<" blocks from memory"<<std::endl;
1924 m_env->serializePlayers(m_mapsavedir);
1926 // Save environment metadata
1927 m_env->saveMeta(m_mapsavedir);
1932 void Server::Receive()
1934 DSTACK(__FUNCTION_NAME);
1935 SharedBuffer<u8> data;
1940 JMutexAutoLock conlock(m_con_mutex);
1941 datasize = m_con.Receive(peer_id, data);
1944 // This has to be called so that the client list gets synced
1945 // with the peer list of the connection
1946 handlePeerChanges();
1948 ProcessData(*data, datasize, peer_id);
1950 catch(con::InvalidIncomingDataException &e)
1952 infostream<<"Server::Receive(): "
1953 "InvalidIncomingDataException: what()="
1954 <<e.what()<<std::endl;
1956 catch(con::PeerNotFoundException &e)
1958 //NOTE: This is not needed anymore
1960 // The peer has been disconnected.
1961 // Find the associated player and remove it.
1963 /*JMutexAutoLock envlock(m_env_mutex);
1965 infostream<<"ServerThread: peer_id="<<peer_id
1966 <<" has apparently closed connection. "
1967 <<"Removing player."<<std::endl;
1969 m_env->removePlayer(peer_id);*/
1973 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1975 DSTACK(__FUNCTION_NAME);
1976 // Environment is locked first.
1977 JMutexAutoLock envlock(m_env_mutex);
1978 JMutexAutoLock conlock(m_con_mutex);
1981 Address address = m_con.GetPeerAddress(peer_id);
1983 // drop player if is ip is banned
1984 if(m_banmanager.isIpBanned(address.serializeString())){
1985 SendAccessDenied(m_con, peer_id,
1986 L"Your ip is banned. Banned name was "
1987 +narrow_to_wide(m_banmanager.getBanName(
1988 address.serializeString())));
1989 m_con.DeletePeer(peer_id);
1993 catch(con::PeerNotFoundException &e)
1995 infostream<<"Server::ProcessData(): Cancelling: peer "
1996 <<peer_id<<" not found"<<std::endl;
2000 u8 peer_ser_ver = getClient(peer_id)->serialization_version;
2008 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
2010 if(command == TOSERVER_INIT)
2012 // [0] u16 TOSERVER_INIT
2013 // [2] u8 SER_FMT_VER_HIGHEST
2014 // [3] u8[20] player_name
2015 // [23] u8[28] password <--- can be sent without this, from old versions
2017 if(datasize < 2+1+PLAYERNAME_SIZE)
2020 infostream<<"Server: Got TOSERVER_INIT from "
2021 <<peer_id<<std::endl;
2023 // First byte after command is maximum supported
2024 // serialization version
2025 u8 client_max = data[2];
2026 u8 our_max = SER_FMT_VER_HIGHEST;
2027 // Use the highest version supported by both
2028 u8 deployed = core::min_(client_max, our_max);
2029 // If it's lower than the lowest supported, give up.
2030 if(deployed < SER_FMT_VER_LOWEST)
2031 deployed = SER_FMT_VER_INVALID;
2033 //peer->serialization_version = deployed;
2034 getClient(peer_id)->pending_serialization_version = deployed;
2036 if(deployed == SER_FMT_VER_INVALID)
2038 infostream<<"Server: Cannot negotiate "
2039 "serialization version with peer "
2040 <<peer_id<<std::endl;
2041 SendAccessDenied(m_con, peer_id,
2042 L"Your client is too old (map format)");
2047 Read and check network protocol version
2050 u16 net_proto_version = 0;
2051 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
2053 net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
2056 getClient(peer_id)->net_proto_version = net_proto_version;
2058 if(net_proto_version == 0)
2060 SendAccessDenied(m_con, peer_id,
2061 L"Your client is too old. Please upgrade.");
2065 /* Uhh... this should actually be a warning but let's do it like this */
2066 if(g_settings->getBool("strict_protocol_version_checking"))
2068 if(net_proto_version < PROTOCOL_VERSION)
2070 SendAccessDenied(m_con, peer_id,
2071 L"Your client is too old. Please upgrade.");
2081 char playername[PLAYERNAME_SIZE];
2082 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
2084 playername[i] = data[3+i];
2086 playername[PLAYERNAME_SIZE-1] = 0;
2088 if(playername[0]=='\0')
2090 infostream<<"Server: Player has empty name"<<std::endl;
2091 SendAccessDenied(m_con, peer_id,
2096 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
2098 infostream<<"Server: Player has invalid name"<<std::endl;
2099 SendAccessDenied(m_con, peer_id,
2100 L"Name contains unallowed characters");
2105 char password[PASSWORD_SIZE];
2106 if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
2108 // old version - assume blank password
2113 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2115 password[i] = data[23+i];
2117 password[PASSWORD_SIZE-1] = 0;
2120 std::string checkpwd;
2121 if(m_authmanager.exists(playername))
2123 checkpwd = m_authmanager.getPassword(playername);
2127 checkpwd = g_settings->get("default_password");
2130 /*infostream<<"Server: Client gave password '"<<password
2131 <<"', the correct one is '"<<checkpwd<<"'"<<std::endl;*/
2133 if(password != checkpwd && m_authmanager.exists(playername))
2135 infostream<<"Server: peer_id="<<peer_id
2136 <<": supplied invalid password for "
2137 <<playername<<std::endl;
2138 SendAccessDenied(m_con, peer_id, L"Invalid password");
2142 // Add player to auth manager
2143 if(m_authmanager.exists(playername) == false)
2145 infostream<<"Server: adding player "<<playername
2146 <<" to auth manager"<<std::endl;
2147 m_authmanager.add(playername);
2148 m_authmanager.setPassword(playername, checkpwd);
2149 m_authmanager.setPrivs(playername,
2150 stringToPrivs(g_settings->get("default_privs")));
2151 m_authmanager.save();
2154 // Enforce user limit.
2155 // Don't enforce for users that have some admin right
2156 if(m_clients.size() >= g_settings->getU16("max_users") &&
2157 (m_authmanager.getPrivs(playername)
2158 & (PRIV_SERVER|PRIV_BAN|PRIV_PRIVS)) == 0 &&
2159 playername != g_settings->get("name"))
2161 SendAccessDenied(m_con, peer_id, L"Too many users.");
2166 Player *player = emergePlayer(playername, password, peer_id);
2168 // If failed, cancel
2171 infostream<<"Server: peer_id="<<peer_id
2172 <<": failed to emerge player"<<std::endl;
2177 Answer with a TOCLIENT_INIT
2180 SharedBuffer<u8> reply(2+1+6+8);
2181 writeU16(&reply[0], TOCLIENT_INIT);
2182 writeU8(&reply[2], deployed);
2183 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
2184 writeU64(&reply[2+1+6], m_env->getServerMap().getSeed());
2187 m_con.Send(peer_id, 0, reply, true);
2191 Send complete position information
2193 SendMovePlayer(player);
2198 if(command == TOSERVER_INIT2)
2200 infostream<<"Server: Got TOSERVER_INIT2 from "
2201 <<peer_id<<std::endl;
2204 getClient(peer_id)->serialization_version
2205 = getClient(peer_id)->pending_serialization_version;
2208 Send some initialization data
2211 // Send tool definitions
2212 SendToolDef(m_con, peer_id, m_toolmgr);
2214 // Send node definitions
2215 SendNodeDef(m_con, peer_id, m_nodedef);
2218 SendTextures(peer_id);
2220 // Send player info to all players
2223 // Send inventory to player
2224 UpdateCrafting(peer_id);
2225 SendInventory(peer_id);
2227 // Send player items to all players
2230 Player *player = m_env->getPlayer(peer_id);
2233 SendPlayerHP(player);
2237 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2238 m_env->getTimeOfDay());
2239 m_con.Send(peer_id, 0, data, true);
2242 // Send information about server to player in chat
2243 SendChatMessage(peer_id, getStatusString());
2245 // Send information about joining in chat
2247 std::wstring name = L"unknown";
2248 Player *player = m_env->getPlayer(peer_id);
2250 name = narrow_to_wide(player->getName());
2252 std::wstring message;
2255 message += L" joined game";
2256 BroadcastChatMessage(message);
2259 // Warnings about protocol version can be issued here
2260 if(getClient(peer_id)->net_proto_version < PROTOCOL_VERSION)
2262 SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT IS OLD AND MAY WORK PROPERLY WITH THIS SERVER");
2266 Check HP, respawn if necessary
2268 HandlePlayerHP(player, 0);
2274 std::ostringstream os(std::ios_base::binary);
2275 for(core::map<u16, RemoteClient*>::Iterator
2276 i = m_clients.getIterator();
2277 i.atEnd() == false; i++)
2279 RemoteClient *client = i.getNode()->getValue();
2280 assert(client->peer_id == i.getNode()->getKey());
2281 if(client->serialization_version == SER_FMT_VER_INVALID)
2284 Player *player = m_env->getPlayer(client->peer_id);
2287 // Get name of player
2288 os<<player->getName()<<" ";
2291 actionstream<<player->getName()<<" joins game. List of players: "
2292 <<os.str()<<std::endl;
2298 if(peer_ser_ver == SER_FMT_VER_INVALID)
2300 infostream<<"Server::ProcessData(): Cancelling: Peer"
2301 " serialization format invalid or not initialized."
2302 " Skipping incoming command="<<command<<std::endl;
2306 Player *player = m_env->getPlayer(peer_id);
2309 infostream<<"Server::ProcessData(): Cancelling: "
2310 "No player for peer_id="<<peer_id
2314 if(command == TOSERVER_PLAYERPOS)
2316 if(datasize < 2+12+12+4+4)
2320 v3s32 ps = readV3S32(&data[start+2]);
2321 v3s32 ss = readV3S32(&data[start+2+12]);
2322 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2323 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2324 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2325 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2326 pitch = wrapDegrees(pitch);
2327 yaw = wrapDegrees(yaw);
2329 player->setPosition(position);
2330 player->setSpeed(speed);
2331 player->setPitch(pitch);
2332 player->setYaw(yaw);
2334 /*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2335 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2336 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2338 else if(command == TOSERVER_GOTBLOCKS)
2351 u16 count = data[2];
2352 for(u16 i=0; i<count; i++)
2354 if((s16)datasize < 2+1+(i+1)*6)
2355 throw con::InvalidIncomingDataException
2356 ("GOTBLOCKS length is too short");
2357 v3s16 p = readV3S16(&data[2+1+i*6]);
2358 /*infostream<<"Server: GOTBLOCKS ("
2359 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2360 RemoteClient *client = getClient(peer_id);
2361 client->GotBlock(p);
2364 else if(command == TOSERVER_DELETEDBLOCKS)
2377 u16 count = data[2];
2378 for(u16 i=0; i<count; i++)
2380 if((s16)datasize < 2+1+(i+1)*6)
2381 throw con::InvalidIncomingDataException
2382 ("DELETEDBLOCKS length is too short");
2383 v3s16 p = readV3S16(&data[2+1+i*6]);
2384 /*infostream<<"Server: DELETEDBLOCKS ("
2385 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2386 RemoteClient *client = getClient(peer_id);
2387 client->SetBlockNotSent(p);
2390 else if(command == TOSERVER_CLICK_OBJECT)
2392 infostream<<"Server: CLICK_OBJECT not supported anymore"<<std::endl;
2395 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2400 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2406 [2] u8 button (0=left, 1=right)
2410 u8 button = readU8(&data[2]);
2411 u16 id = readS16(&data[3]);
2412 u16 item_i = readU16(&data[5]);
2414 ServerActiveObject *obj = m_env->getActiveObject(id);
2418 infostream<<"Server: CLICK_ACTIVEOBJECT: object not found"
2423 // Skip if object has been removed
2427 //TODO: Check that object is reasonably close
2429 // Get ServerRemotePlayer
2430 ServerRemotePlayer *srp = (ServerRemotePlayer*)player;
2432 // Update wielded item
2433 srp->wieldItem(item_i);
2435 // Left click, pick/punch
2438 actionstream<<player->getName()<<" punches object "
2439 <<obj->getId()<<std::endl;
2446 Try creating inventory item
2448 InventoryItem *item = obj->createPickedUpItem();
2452 InventoryList *ilist = player->inventory.getList("main");
2455 actionstream<<player->getName()<<" picked up "
2456 <<item->getName()<<std::endl;
2457 if(g_settings->getBool("creative_mode") == false)
2459 // Skip if inventory has no free space
2460 if(ilist->roomForItem(item) == false)
2462 infostream<<"Player inventory has no free space"<<std::endl;
2466 // Add to inventory and send inventory
2467 ilist->addItem(item);
2468 UpdateCrafting(player->peer_id);
2469 SendInventory(player->peer_id);
2472 // Remove object from environment
2473 obj->m_removed = true;
2479 Item cannot be picked up. Punch it instead.
2482 actionstream<<player->getName()<<" punches object "
2483 <<obj->getId()<<std::endl;
2485 ToolItem *titem = NULL;
2486 std::string toolname = "";
2488 InventoryList *mlist = player->inventory.getList("main");
2491 InventoryItem *item = mlist->getItem(item_i);
2492 if(item && (std::string)item->getName() == "ToolItem")
2494 titem = (ToolItem*)item;
2495 toolname = titem->getToolName();
2499 v3f playerpos = player->getPosition();
2500 v3f objpos = obj->getBasePosition();
2501 v3f dir = (objpos - playerpos).normalize();
2503 u16 wear = obj->punch(toolname, dir, player->getName());
2507 bool weared_out = titem->addWear(wear);
2509 mlist->deleteItem(item_i);
2510 SendInventory(player->peer_id);
2515 // Right click, do something with object
2518 actionstream<<player->getName()<<" right clicks object "
2519 <<obj->getId()<<std::endl;
2522 obj->rightClick(srp);
2526 Update player state to client
2528 SendPlayerHP(player);
2529 UpdateCrafting(player->peer_id);
2530 SendInventory(player->peer_id);
2532 else if(command == TOSERVER_GROUND_ACTION)
2540 [3] v3s16 nodepos_undersurface
2541 [9] v3s16 nodepos_abovesurface
2546 2: stop digging (all parameters ignored)
2547 3: digging completed
2549 u8 action = readU8(&data[2]);
2551 p_under.X = readS16(&data[3]);
2552 p_under.Y = readS16(&data[5]);
2553 p_under.Z = readS16(&data[7]);
2555 p_over.X = readS16(&data[9]);
2556 p_over.Y = readS16(&data[11]);
2557 p_over.Z = readS16(&data[13]);
2558 u16 item_i = readU16(&data[15]);
2560 ServerRemotePlayer *srp = (ServerRemotePlayer*)player;
2563 Check that target is reasonably close
2566 v3f np_f = intToFloat(p_under, BS);
2567 float max_d = BS * 8; // Just some large enough value
2568 float d = srp->m_last_good_position.getDistanceFrom(np_f);
2570 actionstream<<"Player "<<player->getName()
2571 <<" tried to access node from too far: "
2572 <<"d="<<d<<", max_d="<<max_d
2573 <<". ignoring."<<std::endl;
2574 // Re-send block to revert change on client-side
2575 RemoteClient *client = getClient(peer_id);
2576 v3s16 blockpos = getNodeBlockPos(p_under);
2577 client->SetBlockNotSent(blockpos);
2589 NOTE: This can be used in the future to check if
2590 somebody is cheating, by checking the timing.
2592 bool cannot_punch_node = false;
2594 MapNode n(CONTENT_IGNORE);
2598 n = m_env->getMap().getNode(p_under);
2600 catch(InvalidPositionException &e)
2602 infostream<<"Server: Not punching: Node not found."
2603 <<" Adding block to emerge queue."
2605 m_emerge_queue.addBlock(peer_id,
2606 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2607 cannot_punch_node = true;
2610 if(cannot_punch_node)
2616 scriptapi_environment_on_punchnode(m_lua, p_under, n, srp);
2623 else if(action == 2)
2626 RemoteClient *client = getClient(peer_id);
2627 JMutexAutoLock digmutex(client->m_dig_mutex);
2628 client->m_dig_tool_item = -1;
2633 3: Digging completed
2635 else if(action == 3)
2637 // Mandatory parameter; actually used for nothing
2638 core::map<v3s16, MapBlock*> modified_blocks;
2640 content_t material = CONTENT_IGNORE;
2641 u8 mineral = MINERAL_NONE;
2643 bool cannot_remove_node = false;
2645 MapNode n(CONTENT_IGNORE);
2648 n = m_env->getMap().getNode(p_under);
2650 mineral = n.getMineral(m_nodedef);
2651 // Get material at position
2652 material = n.getContent();
2653 // If not yet cancelled
2654 if(cannot_remove_node == false)
2656 // If it's not diggable, do nothing
2657 if(m_nodedef->get(material).diggable == false)
2659 infostream<<"Server: Not finishing digging: "
2660 <<"Node not diggable"
2662 cannot_remove_node = true;
2665 // If not yet cancelled
2666 if(cannot_remove_node == false)
2668 // Get node metadata
2669 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p_under);
2670 if(meta && meta->nodeRemovalDisabled() == true)
2672 infostream<<"Server: Not finishing digging: "
2673 <<"Node metadata disables removal"
2675 cannot_remove_node = true;
2679 catch(InvalidPositionException &e)
2681 infostream<<"Server: Not finishing digging: Node not found."
2682 <<" Adding block to emerge queue."
2684 m_emerge_queue.addBlock(peer_id,
2685 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2686 cannot_remove_node = true;
2689 // Make sure the player is allowed to do it
2690 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2692 infostream<<"Player "<<player->getName()<<" cannot remove node"
2693 <<" because privileges are "<<getPlayerPrivs(player)
2695 cannot_remove_node = true;
2699 If node can't be removed, set block to be re-sent to
2702 if(cannot_remove_node)
2704 infostream<<"Server: Not finishing digging."<<std::endl;
2706 // Client probably has wrong data.
2707 // Set block not sent, so that client will get
2709 infostream<<"Client "<<peer_id<<" tried to dig "
2710 <<"node; but node cannot be removed."
2711 <<" setting MapBlock not sent."<<std::endl;
2712 RemoteClient *client = getClient(peer_id);
2713 v3s16 blockpos = getNodeBlockPos(p_under);
2714 client->SetBlockNotSent(blockpos);
2719 actionstream<<player->getName()<<" digs "<<PP(p_under)
2720 <<", gets material "<<(int)material<<", mineral "
2721 <<(int)mineral<<std::endl;
2724 Send the removal to all close-by players.
2725 - If other player is close, send REMOVENODE
2726 - Otherwise set blocks not sent
2728 core::list<u16> far_players;
2729 sendRemoveNode(p_under, peer_id, &far_players, 30);
2732 Update and send inventory
2735 if(g_settings->getBool("creative_mode") == false)
2740 InventoryList *mlist = player->inventory.getList("main");
2743 InventoryItem *item = mlist->getItem(item_i);
2744 if(item && (std::string)item->getName() == "ToolItem")
2746 ToolItem *titem = (ToolItem*)item;
2747 std::string toolname = titem->getToolName();
2749 // Get digging properties for material and tool
2750 ToolDiggingProperties tp =
2751 m_toolmgr->getDiggingProperties(toolname);
2752 DiggingProperties prop =
2753 getDiggingProperties(material, &tp, m_nodedef);
2755 if(prop.diggable == false)
2757 infostream<<"Server: WARNING: Player digged"
2758 <<" with impossible material + tool"
2759 <<" combination"<<std::endl;
2762 bool weared_out = titem->addWear(prop.wear);
2766 mlist->deleteItem(item_i);
2772 Add dug item to inventory
2775 InventoryItem *item = NULL;
2777 if(mineral != MINERAL_NONE)
2778 item = getDiggedMineralItem(mineral, this);
2783 const std::string &dug_s = m_nodedef->get(material).dug_item;
2786 std::istringstream is(dug_s, std::ios::binary);
2787 item = InventoryItem::deSerialize(is, this);
2793 // Add a item to inventory
2794 player->inventory.addItem("main", item);
2797 UpdateCrafting(player->peer_id);
2798 SendInventory(player->peer_id);
2803 if(mineral != MINERAL_NONE)
2804 item = getDiggedMineralItem(mineral, this);
2809 const std::string &extra_dug_s = m_nodedef->get(material).extra_dug_item;
2810 s32 extra_rarity = m_nodedef->get(material).extra_dug_item_rarity;
2811 if(extra_dug_s != "" && extra_rarity != 0
2812 && myrand() % extra_rarity == 0)
2814 std::istringstream is(extra_dug_s, std::ios::binary);
2815 item = InventoryItem::deSerialize(is, this);
2821 // Add a item to inventory
2822 player->inventory.addItem("main", item);
2825 UpdateCrafting(player->peer_id);
2826 SendInventory(player->peer_id);
2832 (this takes some time so it is done after the quick stuff)
2835 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
2837 m_env->getMap().removeNodeAndUpdate(p_under, modified_blocks);
2840 Set blocks not sent to far players
2842 for(core::list<u16>::Iterator
2843 i = far_players.begin();
2844 i != far_players.end(); i++)
2847 RemoteClient *client = getClient(peer_id);
2850 client->SetBlocksNotSent(modified_blocks);
2856 scriptapi_environment_on_dignode(m_lua, p_under, n, srp);
2862 else if(action == 1)
2865 InventoryList *ilist = player->inventory.getList("main");
2870 InventoryItem *item = ilist->getItem(item_i);
2872 // If there is no item, it is not possible to add it anywhere
2877 Handle material items
2879 if(std::string("MaterialItem") == item->getName())
2882 // Don't add a node if this is not a free space
2883 MapNode n2 = m_env->getMap().getNode(p_over);
2884 bool no_enough_privs =
2885 ((getPlayerPrivs(player) & PRIV_BUILD)==0);
2887 infostream<<"Player "<<player->getName()<<" cannot add node"
2888 <<" because privileges are "<<getPlayerPrivs(player)
2891 if(m_nodedef->get(n2).buildable_to == false
2894 // Client probably has wrong data.
2895 // Set block not sent, so that client will get
2897 infostream<<"Client "<<peer_id<<" tried to place"
2898 <<" node in invalid position; setting"
2899 <<" MapBlock not sent."<<std::endl;
2900 RemoteClient *client = getClient(peer_id);
2901 v3s16 blockpos = getNodeBlockPos(p_over);
2902 client->SetBlockNotSent(blockpos);
2906 catch(InvalidPositionException &e)
2908 infostream<<"Server: Ignoring ADDNODE: Node not found"
2909 <<" Adding block to emerge queue."
2911 m_emerge_queue.addBlock(peer_id,
2912 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2916 // Reset build time counter
2917 getClient(peer_id)->m_time_from_building = 0.0;
2920 MaterialItem *mitem = (MaterialItem*)item;
2922 n.setContent(mitem->getMaterial());
2924 actionstream<<player->getName()<<" places material "
2925 <<(int)mitem->getMaterial()
2926 <<" at "<<PP(p_under)<<std::endl;
2928 // Calculate direction for wall mounted stuff
2929 if(m_nodedef->get(n).wall_mounted)
2930 n.param2 = packDir(p_under - p_over);
2932 // Calculate the direction for furnaces and chests and stuff
2933 if(m_nodedef->get(n).param_type == CPT_FACEDIR_SIMPLE)
2935 v3f playerpos = player->getPosition();
2936 v3f blockpos = intToFloat(p_over, BS) - playerpos;
2937 blockpos = blockpos.normalize();
2939 if (fabs(blockpos.X) > fabs(blockpos.Z)) {
2953 Send to all close-by players
2955 core::list<u16> far_players;
2956 sendAddNode(p_over, n, 0, &far_players, 30);
2961 InventoryList *ilist = player->inventory.getList("main");
2962 if(g_settings->getBool("creative_mode") == false && ilist)
2964 // Remove from inventory and send inventory
2965 if(mitem->getCount() == 1)
2966 ilist->deleteItem(item_i);
2970 UpdateCrafting(peer_id);
2971 SendInventory(peer_id);
2977 This takes some time so it is done after the quick stuff
2979 core::map<v3s16, MapBlock*> modified_blocks;
2981 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
2983 std::string p_name = std::string(player->getName());
2984 m_env->getMap().addNodeAndUpdate(p_over, n, modified_blocks, p_name);
2987 Set blocks not sent to far players
2989 for(core::list<u16>::Iterator
2990 i = far_players.begin();
2991 i != far_players.end(); i++)
2994 RemoteClient *client = getClient(peer_id);
2997 client->SetBlocksNotSent(modified_blocks);
3003 scriptapi_environment_on_placenode(m_lua, p_over, n, srp);
3006 Calculate special events
3009 /*if(n.d == LEGN(m_nodedef, "CONTENT_MESE"))
3012 for(s16 z=-1; z<=1; z++)
3013 for(s16 y=-1; y<=1; y++)
3014 for(s16 x=-1; x<=1; x++)
3021 Place other item (not a block)
3025 v3s16 blockpos = getNodeBlockPos(p_over);
3028 Check that the block is loaded so that the item
3029 can properly be added to the static list too
3031 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3034 infostream<<"Error while placing object: "
3035 "block not found"<<std::endl;
3040 If in creative mode, item dropping is disabled unless
3041 player has build privileges
3043 if(g_settings->getBool("creative_mode") &&
3044 (getPlayerPrivs(player) & PRIV_BUILD) == 0)
3046 infostream<<"Not allowing player to drop item: "
3047 "creative mode and no build privs"<<std::endl;
3051 // Calculate a position for it
3052 v3f pos = intToFloat(p_over, BS);
3054 /*pos.Y -= BS*0.25; // let it drop a bit
3056 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
3057 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;*/
3062 ServerActiveObject *obj = item->createSAO(m_env, 0, pos);
3066 infostream<<"WARNING: item resulted in NULL object, "
3067 <<"not placing onto map"
3072 actionstream<<player->getName()<<" places "<<item->getName()
3073 <<" at "<<PP(p_over)<<std::endl;
3075 // Add the object to the environment
3076 m_env->addActiveObject(obj);
3078 infostream<<"Placed object"<<std::endl;
3080 if(g_settings->getBool("creative_mode") == false)
3082 // Delete the right amount of items from the slot
3083 u16 dropcount = item->getDropCount();
3085 // Delete item if all gone
3086 if(item->getCount() <= dropcount)
3088 if(item->getCount() < dropcount)
3089 infostream<<"WARNING: Server: dropped more items"
3090 <<" than the slot contains"<<std::endl;
3092 InventoryList *ilist = player->inventory.getList("main");
3094 // Remove from inventory and send inventory
3095 ilist->deleteItem(item_i);
3097 // Else decrement it
3099 item->remove(dropcount);
3102 UpdateCrafting(peer_id);
3103 SendInventory(peer_id);
3111 Catch invalid actions
3115 infostream<<"WARNING: Server: Invalid action "
3116 <<action<<std::endl;
3120 else if(command == TOSERVER_RELEASE)
3129 infostream<<"TOSERVER_RELEASE ignored"<<std::endl;
3132 else if(command == TOSERVER_SIGNTEXT)
3134 infostream<<"Server: TOSERVER_SIGNTEXT not supported anymore"
3138 else if(command == TOSERVER_SIGNNODETEXT)
3140 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
3148 std::string datastring((char*)&data[2], datasize-2);
3149 std::istringstream is(datastring, std::ios_base::binary);
3152 is.read((char*)buf, 6);
3153 v3s16 p = readV3S16(buf);
3154 is.read((char*)buf, 2);
3155 u16 textlen = readU16(buf);
3157 for(u16 i=0; i<textlen; i++)
3159 is.read((char*)buf, 1);
3160 text += (char)buf[0];
3163 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3166 if(meta->typeId() != LEGN(m_nodedef, "CONTENT_SIGN_WALL"))
3168 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
3169 signmeta->setText(text);
3171 actionstream<<player->getName()<<" writes \""<<text<<"\" to sign "
3172 <<" at "<<PP(p)<<std::endl;
3174 v3s16 blockpos = getNodeBlockPos(p);
3175 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3178 block->raiseModified(MOD_STATE_WRITE_NEEDED,
3182 setBlockNotSent(blockpos);
3184 else if(command == TOSERVER_INVENTORY_ACTION)
3186 /*// Ignore inventory changes if in creative mode
3187 if(g_settings->getBool("creative_mode") == true)
3189 infostream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
3193 // Strip command and create a stream
3194 std::string datastring((char*)&data[2], datasize-2);
3195 infostream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
3196 std::istringstream is(datastring, std::ios_base::binary);
3198 InventoryAction *a = InventoryAction::deSerialize(is);
3203 c.current_player = player;
3206 Handle craftresult specially if not in creative mode
3208 bool disable_action = false;
3209 if(a->getType() == IACTION_MOVE
3210 && g_settings->getBool("creative_mode") == false)
3212 IMoveAction *ma = (IMoveAction*)a;
3213 if(ma->to_inv == "current_player" &&
3214 ma->from_inv == "current_player")
3216 InventoryList *rlist = player->inventory.getList("craftresult");
3218 InventoryList *clist = player->inventory.getList("craft");
3220 InventoryList *mlist = player->inventory.getList("main");
3223 Craftresult is no longer preview if something
3226 if(ma->to_list == "craftresult"
3227 && ma->from_list != "craftresult")
3229 // If it currently is a preview, remove
3231 if(player->craftresult_is_preview)
3233 rlist->deleteItem(0);
3235 player->craftresult_is_preview = false;
3238 Crafting takes place if this condition is true.
3240 if(player->craftresult_is_preview &&
3241 ma->from_list == "craftresult")
3243 player->craftresult_is_preview = false;
3244 clist->decrementMaterials(1);
3246 /* Print out action */
3247 InventoryList *list =
3248 player->inventory.getList("craftresult");
3250 InventoryItem *item = list->getItem(0);
3251 std::string itemname = "NULL";
3253 itemname = item->getName();
3254 actionstream<<player->getName()<<" crafts "
3255 <<itemname<<std::endl;
3258 If the craftresult is placed on itself, move it to
3259 main inventory instead of doing the action
3261 if(ma->to_list == "craftresult"
3262 && ma->from_list == "craftresult")
3264 disable_action = true;
3266 InventoryItem *item1 = rlist->changeItem(0, NULL);
3267 mlist->addItem(item1);
3270 // Disallow moving items if not allowed to build
3271 else if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
3275 // if it's a locking chest, only allow the owner or server admins to move items
3276 else if (ma->from_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
3278 Strfnd fn(ma->from_inv);
3279 std::string id0 = fn.next(":");
3280 if(id0 == "nodemeta")
3283 p.X = stoi(fn.next(","));
3284 p.Y = stoi(fn.next(","));
3285 p.Z = stoi(fn.next(","));
3286 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3287 if(meta && meta->typeId() == LEGN(m_nodedef, "CONTENT_LOCKABLE_CHEST")) {
3288 LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
3289 if (lcm->getOwner() != player->getName())
3294 else if (ma->to_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
3296 Strfnd fn(ma->to_inv);
3297 std::string id0 = fn.next(":");
3298 if(id0 == "nodemeta")
3301 p.X = stoi(fn.next(","));
3302 p.Y = stoi(fn.next(","));
3303 p.Z = stoi(fn.next(","));
3304 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3305 if(meta && meta->typeId() == LEGN(m_nodedef, "CONTENT_LOCKABLE_CHEST")) {
3306 LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
3307 if (lcm->getOwner() != player->getName())
3314 if(disable_action == false)
3316 // Feed action to player inventory
3324 UpdateCrafting(player->peer_id);
3325 SendInventory(player->peer_id);
3330 infostream<<"TOSERVER_INVENTORY_ACTION: "
3331 <<"InventoryAction::deSerialize() returned NULL"
3335 else if(command == TOSERVER_CHAT_MESSAGE)
3343 std::string datastring((char*)&data[2], datasize-2);
3344 std::istringstream is(datastring, std::ios_base::binary);
3347 is.read((char*)buf, 2);
3348 u16 len = readU16(buf);
3350 std::wstring message;
3351 for(u16 i=0; i<len; i++)
3353 is.read((char*)buf, 2);
3354 message += (wchar_t)readU16(buf);
3357 // Get player name of this client
3358 std::wstring name = narrow_to_wide(player->getName());
3360 // Line to send to players
3362 // Whether to send to the player that sent the line
3363 bool send_to_sender = false;
3364 // Whether to send to other players
3365 bool send_to_others = false;
3367 // Local player gets all privileges regardless of
3368 // what's set on their account.
3369 u64 privs = getPlayerPrivs(player);
3372 if(message[0] == L'/')
3374 size_t strip_size = 1;
3375 if (message[1] == L'#') // support old-style commans
3377 message = message.substr(strip_size);
3379 WStrfnd f1(message);
3380 f1.next(L" "); // Skip over /#whatever
3381 std::wstring paramstring = f1.next(L"");
3383 ServerCommandContext *ctx = new ServerCommandContext(
3384 str_split(message, L' '),
3391 std::wstring reply(processServerCommand(ctx));
3392 send_to_sender = ctx->flags & SEND_TO_SENDER;
3393 send_to_others = ctx->flags & SEND_TO_OTHERS;
3395 if (ctx->flags & SEND_NO_PREFIX)
3398 line += L"Server: " + reply;
3405 if(privs & PRIV_SHOUT)
3411 send_to_others = true;
3415 line += L"Server: You are not allowed to shout";
3416 send_to_sender = true;
3423 actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
3426 Send the message to clients
3428 for(core::map<u16, RemoteClient*>::Iterator
3429 i = m_clients.getIterator();
3430 i.atEnd() == false; i++)
3432 // Get client and check that it is valid
3433 RemoteClient *client = i.getNode()->getValue();
3434 assert(client->peer_id == i.getNode()->getKey());
3435 if(client->serialization_version == SER_FMT_VER_INVALID)
3439 bool sender_selected = (peer_id == client->peer_id);
3440 if(sender_selected == true && send_to_sender == false)
3442 if(sender_selected == false && send_to_others == false)
3445 SendChatMessage(client->peer_id, line);
3449 else if(command == TOSERVER_DAMAGE)
3451 std::string datastring((char*)&data[2], datasize-2);
3452 std::istringstream is(datastring, std::ios_base::binary);
3453 u8 damage = readU8(is);
3455 if(g_settings->getBool("enable_damage"))
3457 actionstream<<player->getName()<<" damaged by "
3458 <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
3461 HandlePlayerHP(player, damage);
3465 SendPlayerHP(player);
3468 else if(command == TOSERVER_PASSWORD)
3471 [0] u16 TOSERVER_PASSWORD
3472 [2] u8[28] old password
3473 [30] u8[28] new password
3476 if(datasize != 2+PASSWORD_SIZE*2)
3478 /*char password[PASSWORD_SIZE];
3479 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3480 password[i] = data[2+i];
3481 password[PASSWORD_SIZE-1] = 0;*/
3483 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3491 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3493 char c = data[2+PASSWORD_SIZE+i];
3499 infostream<<"Server: Client requests a password change from "
3500 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
3502 std::string playername = player->getName();
3504 if(m_authmanager.exists(playername) == false)
3506 infostream<<"Server: playername not found in authmanager"<<std::endl;
3507 // Wrong old password supplied!!
3508 SendChatMessage(peer_id, L"playername not found in authmanager");
3512 std::string checkpwd = m_authmanager.getPassword(playername);
3514 if(oldpwd != checkpwd)
3516 infostream<<"Server: invalid old password"<<std::endl;
3517 // Wrong old password supplied!!
3518 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
3522 actionstream<<player->getName()<<" changes password"<<std::endl;
3524 m_authmanager.setPassword(playername, newpwd);
3526 infostream<<"Server: password change successful for "<<playername
3528 SendChatMessage(peer_id, L"Password change successful");
3530 else if(command == TOSERVER_PLAYERITEM)
3535 u16 item = readU16(&data[2]);
3536 player->wieldItem(item);
3537 SendWieldedItem(player);
3539 else if(command == TOSERVER_RESPAWN)
3544 RespawnPlayer(player);
3546 actionstream<<player->getName()<<" respawns at "
3547 <<PP(player->getPosition()/BS)<<std::endl;
3551 infostream<<"Server::ProcessData(): Ignoring "
3552 "unknown command "<<command<<std::endl;
3556 catch(SendFailedException &e)
3558 errorstream<<"Server::ProcessData(): SendFailedException: "
3564 void Server::onMapEditEvent(MapEditEvent *event)
3566 //infostream<<"Server::onMapEditEvent()"<<std::endl;
3567 if(m_ignore_map_edit_events)
3569 MapEditEvent *e = event->clone();
3570 m_unsent_map_edit_queue.push_back(e);
3573 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3575 if(id == "current_player")
3577 assert(c->current_player);
3578 return &(c->current_player->inventory);
3582 std::string id0 = fn.next(":");
3584 if(id0 == "nodemeta")
3587 p.X = stoi(fn.next(","));
3588 p.Y = stoi(fn.next(","));
3589 p.Z = stoi(fn.next(","));
3590 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3592 return meta->getInventory();
3593 infostream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3594 <<"no metadata found"<<std::endl;
3598 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3601 void Server::inventoryModified(InventoryContext *c, std::string id)
3603 if(id == "current_player")
3605 assert(c->current_player);
3607 UpdateCrafting(c->current_player->peer_id);
3608 SendInventory(c->current_player->peer_id);
3613 std::string id0 = fn.next(":");
3615 if(id0 == "nodemeta")
3618 p.X = stoi(fn.next(","));
3619 p.Y = stoi(fn.next(","));
3620 p.Z = stoi(fn.next(","));
3621 v3s16 blockpos = getNodeBlockPos(p);
3623 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3625 meta->inventoryModified();
3627 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3629 block->raiseModified(MOD_STATE_WRITE_NEEDED);
3631 setBlockNotSent(blockpos);
3636 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3639 core::list<PlayerInfo> Server::getPlayerInfo()
3641 DSTACK(__FUNCTION_NAME);
3642 JMutexAutoLock envlock(m_env_mutex);
3643 JMutexAutoLock conlock(m_con_mutex);
3645 core::list<PlayerInfo> list;
3647 core::list<Player*> players = m_env->getPlayers();
3649 core::list<Player*>::Iterator i;
3650 for(i = players.begin();
3651 i != players.end(); i++)
3655 Player *player = *i;
3658 // Copy info from connection to info struct
3659 info.id = player->peer_id;
3660 info.address = m_con.GetPeerAddress(player->peer_id);
3661 info.avg_rtt = m_con.GetPeerAvgRTT(player->peer_id);
3663 catch(con::PeerNotFoundException &e)
3665 // Set dummy peer info
3667 info.address = Address(0,0,0,0,0);
3671 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3672 info.position = player->getPosition();
3674 list.push_back(info);
3681 void Server::peerAdded(con::Peer *peer)
3683 DSTACK(__FUNCTION_NAME);
3684 infostream<<"Server::peerAdded(): peer->id="
3685 <<peer->id<<std::endl;
3688 c.type = PEER_ADDED;
3689 c.peer_id = peer->id;
3691 m_peer_change_queue.push_back(c);
3694 void Server::deletingPeer(con::Peer *peer, bool timeout)
3696 DSTACK(__FUNCTION_NAME);
3697 infostream<<"Server::deletingPeer(): peer->id="
3698 <<peer->id<<", timeout="<<timeout<<std::endl;
3701 c.type = PEER_REMOVED;
3702 c.peer_id = peer->id;
3703 c.timeout = timeout;
3704 m_peer_change_queue.push_back(c);
3711 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3713 DSTACK(__FUNCTION_NAME);
3714 std::ostringstream os(std::ios_base::binary);
3716 writeU16(os, TOCLIENT_HP);
3720 std::string s = os.str();
3721 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3723 con.Send(peer_id, 0, data, true);
3726 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3727 const std::wstring &reason)
3729 DSTACK(__FUNCTION_NAME);
3730 std::ostringstream os(std::ios_base::binary);
3732 writeU16(os, TOCLIENT_ACCESS_DENIED);
3733 os<<serializeWideString(reason);
3736 std::string s = os.str();
3737 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3739 con.Send(peer_id, 0, data, true);
3742 void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
3743 bool set_camera_point_target, v3f camera_point_target)
3745 DSTACK(__FUNCTION_NAME);
3746 std::ostringstream os(std::ios_base::binary);
3748 writeU16(os, TOCLIENT_DEATHSCREEN);
3749 writeU8(os, set_camera_point_target);
3750 writeV3F1000(os, camera_point_target);
3753 std::string s = os.str();
3754 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3756 con.Send(peer_id, 0, data, true);
3759 void Server::SendToolDef(con::Connection &con, u16 peer_id,
3760 IToolDefManager *tooldef)
3762 DSTACK(__FUNCTION_NAME);
3763 std::ostringstream os(std::ios_base::binary);
3767 u32 length of the next item
3768 serialized ToolDefManager
3770 writeU16(os, TOCLIENT_TOOLDEF);
3771 std::ostringstream tmp_os(std::ios::binary);
3772 tooldef->serialize(tmp_os);
3773 os<<serializeLongString(tmp_os.str());
3776 std::string s = os.str();
3777 infostream<<"Server::SendToolDef(): Sending tool definitions: size="
3778 <<s.size()<<std::endl;
3779 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3781 con.Send(peer_id, 0, data, true);
3784 void Server::SendNodeDef(con::Connection &con, u16 peer_id,
3785 INodeDefManager *nodedef)
3787 DSTACK(__FUNCTION_NAME);
3788 std::ostringstream os(std::ios_base::binary);
3792 u32 length of the next item
3793 serialized NodeDefManager
3795 writeU16(os, TOCLIENT_NODEDEF);
3796 std::ostringstream tmp_os(std::ios::binary);
3797 nodedef->serialize(tmp_os);
3798 os<<serializeLongString(tmp_os.str());
3801 std::string s = os.str();
3802 infostream<<"Server::SendNodeDef(): Sending node definitions: size="
3803 <<s.size()<<std::endl;
3804 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3806 con.Send(peer_id, 0, data, true);
3810 Non-static send methods
3813 void Server::SendObjectData(float dtime)
3815 DSTACK(__FUNCTION_NAME);
3817 core::map<v3s16, bool> stepped_blocks;
3819 for(core::map<u16, RemoteClient*>::Iterator
3820 i = m_clients.getIterator();
3821 i.atEnd() == false; i++)
3823 u16 peer_id = i.getNode()->getKey();
3824 RemoteClient *client = i.getNode()->getValue();
3825 assert(client->peer_id == peer_id);
3827 if(client->serialization_version == SER_FMT_VER_INVALID)
3830 client->SendObjectData(this, dtime, stepped_blocks);
3834 void Server::SendPlayerInfos()
3836 DSTACK(__FUNCTION_NAME);
3838 //JMutexAutoLock envlock(m_env_mutex);
3840 // Get connected players
3841 core::list<Player*> players = m_env->getPlayers(true);
3843 u32 player_count = players.getSize();
3844 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3846 SharedBuffer<u8> data(datasize);
3847 writeU16(&data[0], TOCLIENT_PLAYERINFO);
3850 core::list<Player*>::Iterator i;
3851 for(i = players.begin();
3852 i != players.end(); i++)
3854 Player *player = *i;
3856 /*infostream<<"Server sending player info for player with "
3857 "peer_id="<<player->peer_id<<std::endl;*/
3859 writeU16(&data[start], player->peer_id);
3860 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3861 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3862 start += 2+PLAYERNAME_SIZE;
3865 //JMutexAutoLock conlock(m_con_mutex);
3868 m_con.SendToAll(0, data, true);
3871 void Server::SendInventory(u16 peer_id)
3873 DSTACK(__FUNCTION_NAME);
3875 Player* player = m_env->getPlayer(peer_id);
3882 std::ostringstream os;
3883 //os.imbue(std::locale("C"));
3885 player->inventory.serialize(os);
3887 std::string s = os.str();
3889 SharedBuffer<u8> data(s.size()+2);
3890 writeU16(&data[0], TOCLIENT_INVENTORY);
3891 memcpy(&data[2], s.c_str(), s.size());
3894 m_con.Send(peer_id, 0, data, true);
3897 std::string getWieldedItemString(const Player *player)
3899 const InventoryItem *item = player->getWieldItem();
3901 return std::string("");
3902 std::ostringstream os(std::ios_base::binary);
3903 item->serialize(os);
3907 void Server::SendWieldedItem(const Player* player)
3909 DSTACK(__FUNCTION_NAME);
3913 std::ostringstream os(std::ios_base::binary);
3915 writeU16(os, TOCLIENT_PLAYERITEM);
3917 writeU16(os, player->peer_id);
3918 os<<serializeString(getWieldedItemString(player));
3921 std::string s = os.str();
3922 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3924 m_con.SendToAll(0, data, true);
3927 void Server::SendPlayerItems()
3929 DSTACK(__FUNCTION_NAME);
3931 std::ostringstream os(std::ios_base::binary);
3932 core::list<Player *> players = m_env->getPlayers(true);
3934 writeU16(os, TOCLIENT_PLAYERITEM);
3935 writeU16(os, players.size());
3936 core::list<Player *>::Iterator i;
3937 for(i = players.begin(); i != players.end(); ++i)
3940 writeU16(os, p->peer_id);
3941 os<<serializeString(getWieldedItemString(p));
3945 std::string s = os.str();
3946 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3948 m_con.SendToAll(0, data, true);
3951 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3953 DSTACK(__FUNCTION_NAME);
3955 std::ostringstream os(std::ios_base::binary);
3959 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3960 os.write((char*)buf, 2);
3963 writeU16(buf, message.size());
3964 os.write((char*)buf, 2);
3967 for(u32 i=0; i<message.size(); i++)
3971 os.write((char*)buf, 2);
3975 std::string s = os.str();
3976 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3978 m_con.Send(peer_id, 0, data, true);
3981 void Server::BroadcastChatMessage(const std::wstring &message)
3983 for(core::map<u16, RemoteClient*>::Iterator
3984 i = m_clients.getIterator();
3985 i.atEnd() == false; i++)
3987 // Get client and check that it is valid
3988 RemoteClient *client = i.getNode()->getValue();
3989 assert(client->peer_id == i.getNode()->getKey());
3990 if(client->serialization_version == SER_FMT_VER_INVALID)
3993 SendChatMessage(client->peer_id, message);
3997 void Server::SendPlayerHP(Player *player)
3999 SendHP(m_con, player->peer_id, player->hp);
4002 void Server::SendMovePlayer(Player *player)
4004 DSTACK(__FUNCTION_NAME);
4005 std::ostringstream os(std::ios_base::binary);
4007 writeU16(os, TOCLIENT_MOVE_PLAYER);
4008 writeV3F1000(os, player->getPosition());
4009 writeF1000(os, player->getPitch());
4010 writeF1000(os, player->getYaw());
4013 v3f pos = player->getPosition();
4014 f32 pitch = player->getPitch();
4015 f32 yaw = player->getYaw();
4016 infostream<<"Server sending TOCLIENT_MOVE_PLAYER"
4017 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
4024 std::string s = os.str();
4025 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4027 m_con.Send(player->peer_id, 0, data, true);
4030 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
4031 core::list<u16> *far_players, float far_d_nodes)
4033 float maxd = far_d_nodes*BS;
4034 v3f p_f = intToFloat(p, BS);
4038 SharedBuffer<u8> reply(replysize);
4039 writeU16(&reply[0], TOCLIENT_REMOVENODE);
4040 writeS16(&reply[2], p.X);
4041 writeS16(&reply[4], p.Y);
4042 writeS16(&reply[6], p.Z);
4044 for(core::map<u16, RemoteClient*>::Iterator
4045 i = m_clients.getIterator();
4046 i.atEnd() == false; i++)
4048 // Get client and check that it is valid
4049 RemoteClient *client = i.getNode()->getValue();
4050 assert(client->peer_id == i.getNode()->getKey());
4051 if(client->serialization_version == SER_FMT_VER_INVALID)
4054 // Don't send if it's the same one
4055 if(client->peer_id == ignore_id)
4061 Player *player = m_env->getPlayer(client->peer_id);
4064 // If player is far away, only set modified blocks not sent
4065 v3f player_pos = player->getPosition();
4066 if(player_pos.getDistanceFrom(p_f) > maxd)
4068 far_players->push_back(client->peer_id);
4075 m_con.Send(client->peer_id, 0, reply, true);
4079 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
4080 core::list<u16> *far_players, float far_d_nodes)
4082 float maxd = far_d_nodes*BS;
4083 v3f p_f = intToFloat(p, BS);
4085 for(core::map<u16, RemoteClient*>::Iterator
4086 i = m_clients.getIterator();
4087 i.atEnd() == false; i++)
4089 // Get client and check that it is valid
4090 RemoteClient *client = i.getNode()->getValue();
4091 assert(client->peer_id == i.getNode()->getKey());
4092 if(client->serialization_version == SER_FMT_VER_INVALID)
4095 // Don't send if it's the same one
4096 if(client->peer_id == ignore_id)
4102 Player *player = m_env->getPlayer(client->peer_id);
4105 // If player is far away, only set modified blocks not sent
4106 v3f player_pos = player->getPosition();
4107 if(player_pos.getDistanceFrom(p_f) > maxd)
4109 far_players->push_back(client->peer_id);
4116 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
4117 SharedBuffer<u8> reply(replysize);
4118 writeU16(&reply[0], TOCLIENT_ADDNODE);
4119 writeS16(&reply[2], p.X);
4120 writeS16(&reply[4], p.Y);
4121 writeS16(&reply[6], p.Z);
4122 n.serialize(&reply[8], client->serialization_version);
4125 m_con.Send(client->peer_id, 0, reply, true);
4129 void Server::setBlockNotSent(v3s16 p)
4131 for(core::map<u16, RemoteClient*>::Iterator
4132 i = m_clients.getIterator();
4133 i.atEnd()==false; i++)
4135 RemoteClient *client = i.getNode()->getValue();
4136 client->SetBlockNotSent(p);
4140 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
4142 DSTACK(__FUNCTION_NAME);
4144 v3s16 p = block->getPos();
4148 bool completely_air = true;
4149 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4150 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4151 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4153 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
4155 completely_air = false;
4156 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
4161 infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
4163 infostream<<"[completely air] ";
4164 infostream<<std::endl;
4168 Create a packet with the block in the right format
4171 std::ostringstream os(std::ios_base::binary);
4172 block->serialize(os, ver);
4173 std::string s = os.str();
4174 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
4176 u32 replysize = 8 + blockdata.getSize();
4177 SharedBuffer<u8> reply(replysize);
4178 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
4179 writeS16(&reply[2], p.X);
4180 writeS16(&reply[4], p.Y);
4181 writeS16(&reply[6], p.Z);
4182 memcpy(&reply[8], *blockdata, blockdata.getSize());
4184 /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4185 <<": \tpacket size: "<<replysize<<std::endl;*/
4190 m_con.Send(peer_id, 1, reply, true);
4193 void Server::SendBlocks(float dtime)
4195 DSTACK(__FUNCTION_NAME);
4197 JMutexAutoLock envlock(m_env_mutex);
4198 JMutexAutoLock conlock(m_con_mutex);
4200 //TimeTaker timer("Server::SendBlocks");
4202 core::array<PrioritySortedBlockTransfer> queue;
4204 s32 total_sending = 0;
4207 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
4209 for(core::map<u16, RemoteClient*>::Iterator
4210 i = m_clients.getIterator();
4211 i.atEnd() == false; i++)
4213 RemoteClient *client = i.getNode()->getValue();
4214 assert(client->peer_id == i.getNode()->getKey());
4216 total_sending += client->SendingCount();
4218 if(client->serialization_version == SER_FMT_VER_INVALID)
4221 client->GetNextBlocks(this, dtime, queue);
4226 // Lowest priority number comes first.
4227 // Lowest is most important.
4230 for(u32 i=0; i<queue.size(); i++)
4232 //TODO: Calculate limit dynamically
4233 if(total_sending >= g_settings->getS32
4234 ("max_simultaneous_block_sends_server_total"))
4237 PrioritySortedBlockTransfer q = queue[i];
4239 MapBlock *block = NULL;
4242 block = m_env->getMap().getBlockNoCreate(q.pos);
4244 catch(InvalidPositionException &e)
4249 RemoteClient *client = getClient(q.peer_id);
4251 SendBlockNoLock(q.peer_id, block, client->serialization_version);
4253 client->SentBlock(q.pos);
4259 struct SendableTexture
4265 SendableTexture(const std::string &name_="", const std::string path_="",
4266 const std::string &data_=""):
4273 void Server::SendTextures(u16 peer_id)
4275 DSTACK(__FUNCTION_NAME);
4277 infostream<<"Server::SendTextures(): Sending textures to client"<<std::endl;
4281 // Put 5kB in one bunch (this is not accurate)
4282 u32 bytes_per_bunch = 5000;
4284 core::array< core::list<SendableTexture> > texture_bunches;
4285 texture_bunches.push_back(core::list<SendableTexture>());
4287 u32 texture_size_bunch_total = 0;
4288 core::list<ModSpec> mods = getMods(m_modspaths);
4289 for(core::list<ModSpec>::Iterator i = mods.begin();
4290 i != mods.end(); i++){
4292 std::string texturepath = mod.path + DIR_DELIM + "textures";
4293 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(texturepath);
4294 for(u32 j=0; j<dirlist.size(); j++){
4295 if(dirlist[j].dir) // Ignode dirs
4297 std::string tname = dirlist[j].name;
4298 std::string tpath = texturepath + DIR_DELIM + tname;
4300 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
4301 if(fis.good() == false){
4302 errorstream<<"Server::SendTextures(): Could not open \""
4303 <<tname<<"\" for reading"<<std::endl;
4306 std::ostringstream tmp_os(std::ios_base::binary);
4310 fis.read(buf, 1024);
4311 std::streamsize len = fis.gcount();
4312 tmp_os.write(buf, len);
4313 texture_size_bunch_total += len;
4322 errorstream<<"Server::SendTextures(): Failed to read \""
4323 <<tname<<"\""<<std::endl;
4326 /*infostream<<"Server::SendTextures(): Loaded \""
4327 <<tname<<"\""<<std::endl;*/
4329 texture_bunches[texture_bunches.size()-1].push_back(
4330 SendableTexture(tname, tpath, tmp_os.str()));
4332 // Start next bunch if got enough data
4333 if(texture_size_bunch_total >= bytes_per_bunch){
4334 texture_bunches.push_back(core::list<SendableTexture>());
4335 texture_size_bunch_total = 0;
4340 /* Create and send packets */
4342 u32 num_bunches = texture_bunches.size();
4343 for(u32 i=0; i<num_bunches; i++)
4347 u16 total number of texture bunches
4348 u16 index of this bunch
4349 u32 number of textures in this bunch
4357 std::ostringstream os(std::ios_base::binary);
4359 writeU16(os, TOCLIENT_TEXTURES);
4360 writeU16(os, num_bunches);
4362 writeU32(os, texture_bunches[i].size());
4364 for(core::list<SendableTexture>::Iterator
4365 j = texture_bunches[i].begin();
4366 j != texture_bunches[i].end(); j++){
4367 os<<serializeString(j->name);
4368 os<<serializeLongString(j->data);
4372 std::string s = os.str();
4373 infostream<<"Server::SendTextures(): bunch "<<i<<"/"<<num_bunches
4374 <<" textures="<<texture_bunches[i].size()
4375 <<" size=" <<s.size()<<std::endl;
4376 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4378 m_con.Send(peer_id, 0, data, true);
4386 void Server::HandlePlayerHP(Player *player, s16 damage)
4388 if(player->hp > damage)
4390 player->hp -= damage;
4391 SendPlayerHP(player);
4395 infostream<<"Server::HandlePlayerHP(): Player "
4396 <<player->getName()<<" dies"<<std::endl;
4400 //TODO: Throw items around
4402 // Handle players that are not connected
4403 if(player->peer_id == PEER_ID_INEXISTENT){
4404 RespawnPlayer(player);
4408 SendPlayerHP(player);
4410 RemoteClient *client = getClient(player->peer_id);
4411 if(client->net_proto_version >= 3)
4413 SendDeathscreen(m_con, player->peer_id, false, v3f(0,0,0));
4417 RespawnPlayer(player);
4422 void Server::RespawnPlayer(Player *player)
4425 ServerRemotePlayer *srp = (ServerRemotePlayer*)player;
4426 bool repositioned = scriptapi_on_respawnplayer(m_lua, srp);
4428 v3f pos = findSpawnPos(m_env->getServerMap());
4429 player->setPosition(pos);
4430 srp->m_last_good_position = pos;
4431 srp->m_last_good_position_age = 0;
4433 SendMovePlayer(player);
4434 SendPlayerHP(player);
4437 void Server::UpdateCrafting(u16 peer_id)
4439 DSTACK(__FUNCTION_NAME);
4441 Player* player = m_env->getPlayer(peer_id);
4445 Calculate crafting stuff
4447 if(g_settings->getBool("creative_mode") == false)
4449 InventoryList *clist = player->inventory.getList("craft");
4450 InventoryList *rlist = player->inventory.getList("craftresult");
4452 if(rlist && rlist->getUsedSlots() == 0)
4453 player->craftresult_is_preview = true;
4455 if(rlist && player->craftresult_is_preview)
4457 rlist->clearItems();
4459 if(clist && rlist && player->craftresult_is_preview)
4461 // Get result of crafting grid
4463 std::vector<InventoryItem*> items;
4464 for(u16 i=0; i<9; i++){
4465 if(clist->getItem(i) == NULL)
4466 items.push_back(NULL);
4468 items.push_back(clist->getItem(i)->clone());
4470 CraftPointerInput cpi(3, items);
4472 InventoryItem *result = m_craftdef->getCraftResult(cpi, this);
4473 //InventoryItem *result = craft_get_result(items, this);
4475 rlist->addItem(result);
4478 } // if creative_mode == false
4481 RemoteClient* Server::getClient(u16 peer_id)
4483 DSTACK(__FUNCTION_NAME);
4484 //JMutexAutoLock lock(m_con_mutex);
4485 core::map<u16, RemoteClient*>::Node *n;
4486 n = m_clients.find(peer_id);
4487 // A client should exist for all peers
4489 return n->getValue();
4492 std::wstring Server::getStatusString()
4494 std::wostringstream os(std::ios_base::binary);
4497 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4499 os<<L", uptime="<<m_uptime.get();
4500 // Information about clients
4502 for(core::map<u16, RemoteClient*>::Iterator
4503 i = m_clients.getIterator();
4504 i.atEnd() == false; i++)
4506 // Get client and check that it is valid
4507 RemoteClient *client = i.getNode()->getValue();
4508 assert(client->peer_id == i.getNode()->getKey());
4509 if(client->serialization_version == SER_FMT_VER_INVALID)
4512 Player *player = m_env->getPlayer(client->peer_id);
4513 // Get name of player
4514 std::wstring name = L"unknown";
4516 name = narrow_to_wide(player->getName());
4517 // Add name to information string
4521 if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
4522 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4523 if(g_settings->get("motd") != "")
4524 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4528 // Saves g_settings to configpath given at initialization
4529 void Server::saveConfig()
4531 if(m_configpath != "")
4532 g_settings->updateConfigFile(m_configpath.c_str());
4535 void Server::notifyPlayer(const char *name, const std::wstring msg)
4537 Player *player = m_env->getPlayer(name);
4540 SendChatMessage(player->peer_id, std::wstring(L"Server: -!- ")+msg);
4543 void Server::notifyPlayers(const std::wstring msg)
4545 BroadcastChatMessage(msg);
4548 // IGameDef interface
4550 IToolDefManager* Server::getToolDefManager()
4554 INodeDefManager* Server::getNodeDefManager()
4558 ICraftDefManager* Server::getCraftDefManager()
4562 ITextureSource* Server::getTextureSource()
4566 u16 Server::allocateUnknownNodeId(const std::string &name)
4568 return m_nodedef->allocateDummy(name);
4571 IWritableToolDefManager* Server::getWritableToolDefManager()
4575 IWritableNodeDefManager* Server::getWritableNodeDefManager()
4579 IWritableCraftDefManager* Server::getWritableCraftDefManager()
4584 v3f findSpawnPos(ServerMap &map)
4586 //return v3f(50,50,50)*BS;
4591 nodepos = v2s16(0,0);
4596 // Try to find a good place a few times
4597 for(s32 i=0; i<1000; i++)
4600 // We're going to try to throw the player to this position
4601 v2s16 nodepos2d = v2s16(-range + (myrand()%(range*2)),
4602 -range + (myrand()%(range*2)));
4603 //v2s16 sectorpos = getNodeSectorPos(nodepos2d);
4604 // Get ground height at point (fallbacks to heightmap function)
4605 s16 groundheight = map.findGroundLevel(nodepos2d);
4606 // Don't go underwater
4607 if(groundheight < WATER_LEVEL)
4609 //infostream<<"-> Underwater"<<std::endl;
4612 // Don't go to high places
4613 if(groundheight > WATER_LEVEL + 4)
4615 //infostream<<"-> Underwater"<<std::endl;
4619 nodepos = v3s16(nodepos2d.X, groundheight-2, nodepos2d.Y);
4620 bool is_good = false;
4622 for(s32 i=0; i<10; i++){
4623 v3s16 blockpos = getNodeBlockPos(nodepos);
4624 map.emergeBlock(blockpos, true);
4625 MapNode n = map.getNodeNoEx(nodepos);
4626 if(n.getContent() == CONTENT_AIR){
4637 // Found a good place
4638 //infostream<<"Searched through "<<i<<" places."<<std::endl;
4644 return intToFloat(nodepos, BS);
4647 Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id)
4650 Try to get an existing player
4652 Player *player = m_env->getPlayer(name);
4655 // If player is already connected, cancel
4656 if(player->peer_id != 0)
4658 infostream<<"emergePlayer(): Player already connected"<<std::endl;
4663 player->peer_id = peer_id;
4665 // Reset inventory to creative if in creative mode
4666 if(g_settings->getBool("creative_mode"))
4668 // Warning: double code below
4669 // Backup actual inventory
4670 player->inventory_backup = new Inventory();
4671 *(player->inventory_backup) = player->inventory;
4672 // Set creative inventory
4673 craft_set_creative_inventory(player, this);
4680 If player with the wanted peer_id already exists, cancel.
4682 if(m_env->getPlayer(peer_id) != NULL)
4684 infostream<<"emergePlayer(): Player with wrong name but same"
4685 " peer_id already exists"<<std::endl;
4693 // Add authentication stuff
4694 m_authmanager.add(name);
4695 m_authmanager.setPassword(name, password);
4696 m_authmanager.setPrivs(name,
4697 stringToPrivs(g_settings->get("default_privs")));
4699 /* Set player position */
4701 infostream<<"Server: Finding spawn place for player \""
4702 <<name<<"\""<<std::endl;
4704 v3f pos = findSpawnPos(m_env->getServerMap());
4706 player = new ServerRemotePlayer(m_env, pos, peer_id, name);
4708 /* Add player to environment */
4709 m_env->addPlayer(player);
4712 ServerRemotePlayer *srp = (ServerRemotePlayer*)player;
4713 scriptapi_on_newplayer(m_lua, srp);
4715 /* Add stuff to inventory */
4716 if(g_settings->getBool("creative_mode"))
4718 // Warning: double code above
4719 // Backup actual inventory
4720 player->inventory_backup = new Inventory();
4721 *(player->inventory_backup) = player->inventory;
4722 // Set creative inventory
4723 craft_set_creative_inventory(player, this);
4728 } // create new player
4731 void Server::handlePeerChange(PeerChange &c)
4733 JMutexAutoLock envlock(m_env_mutex);
4734 JMutexAutoLock conlock(m_con_mutex);
4736 if(c.type == PEER_ADDED)
4743 core::map<u16, RemoteClient*>::Node *n;
4744 n = m_clients.find(c.peer_id);
4745 // The client shouldn't already exist
4749 RemoteClient *client = new RemoteClient();
4750 client->peer_id = c.peer_id;
4751 m_clients.insert(client->peer_id, client);
4754 else if(c.type == PEER_REMOVED)
4761 core::map<u16, RemoteClient*>::Node *n;
4762 n = m_clients.find(c.peer_id);
4763 // The client should exist
4767 Mark objects to be not known by the client
4769 RemoteClient *client = n->getValue();
4771 for(core::map<u16, bool>::Iterator
4772 i = client->m_known_objects.getIterator();
4773 i.atEnd()==false; i++)
4776 u16 id = i.getNode()->getKey();
4777 ServerActiveObject* obj = m_env->getActiveObject(id);
4779 if(obj && obj->m_known_by_count > 0)
4780 obj->m_known_by_count--;
4783 // Collect information about leaving in chat
4784 std::wstring message;
4786 Player *player = m_env->getPlayer(c.peer_id);
4789 std::wstring name = narrow_to_wide(player->getName());
4792 message += L" left game";
4794 message += L" (timed out)";
4800 m_env->removePlayer(c.peer_id);
4803 // Set player client disconnected
4805 Player *player = m_env->getPlayer(c.peer_id);
4807 player->peer_id = 0;
4814 std::ostringstream os(std::ios_base::binary);
4815 for(core::map<u16, RemoteClient*>::Iterator
4816 i = m_clients.getIterator();
4817 i.atEnd() == false; i++)
4819 RemoteClient *client = i.getNode()->getValue();
4820 assert(client->peer_id == i.getNode()->getKey());
4821 if(client->serialization_version == SER_FMT_VER_INVALID)
4824 Player *player = m_env->getPlayer(client->peer_id);
4827 // Get name of player
4828 os<<player->getName()<<" ";
4831 actionstream<<player->getName()<<" "
4832 <<(c.timeout?"times out.":"leaves game.")
4833 <<" List of players: "
4834 <<os.str()<<std::endl;
4839 delete m_clients[c.peer_id];
4840 m_clients.remove(c.peer_id);
4842 // Send player info to all remaining clients
4845 // Send leave chat message to all remaining clients
4846 BroadcastChatMessage(message);
4855 void Server::handlePeerChanges()
4857 while(m_peer_change_queue.size() > 0)
4859 PeerChange c = m_peer_change_queue.pop_front();
4861 infostream<<"Server: Handling peer change: "
4862 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4865 handlePeerChange(c);
4869 u64 Server::getPlayerPrivs(Player *player)
4873 std::string playername = player->getName();
4874 // Local player gets all privileges regardless of
4875 // what's set on their account.
4876 if(g_settings->get("name") == playername)
4882 return getPlayerAuthPrivs(playername);
4886 void dedicated_server_loop(Server &server, bool &kill)
4888 DSTACK(__FUNCTION_NAME);
4890 infostream<<DTIME<<std::endl;
4891 infostream<<"========================"<<std::endl;
4892 infostream<<"Running dedicated server"<<std::endl;
4893 infostream<<"========================"<<std::endl;
4894 infostream<<std::endl;
4896 IntervalLimiter m_profiler_interval;
4900 // This is kind of a hack but can be done like this
4901 // because server.step() is very light
4903 ScopeProfiler sp(g_profiler, "dedicated server sleep");
4908 if(server.getShutdownRequested() || kill)
4910 infostream<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4917 float profiler_print_interval =
4918 g_settings->getFloat("profiler_print_interval");
4919 if(profiler_print_interval != 0)
4921 if(m_profiler_interval.step(0.030, profiler_print_interval))
4923 infostream<<"Profiler:"<<std::endl;
4924 g_profiler->print(infostream);
4925 g_profiler->clear();
4932 static int counter = 0;
4938 core::list<PlayerInfo> list = server.getPlayerInfo();
4939 core::list<PlayerInfo>::Iterator i;
4940 static u32 sum_old = 0;
4941 u32 sum = PIChecksum(list);
4944 infostream<<DTIME<<"Player info:"<<std::endl;
4945 for(i=list.begin(); i!=list.end(); i++)
4947 i->PrintLine(&infostream);