3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 #include "clientserver.h"
26 #include "jmutexautolock.h"
28 #include "constants.h"
30 #include "materials.h"
33 #include "servercommand.h"
35 #include "content_mapnode.h"
36 #include "content_craft.h"
37 #include "content_nodemeta.h"
39 #include "serverobject.h"
44 #include "scriptapi.h"
48 #include "craftitemdef.h"
50 #include "content_abm.h"
52 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
54 #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
56 class MapEditEventIgnorer
59 MapEditEventIgnorer(bool *flag):
68 ~MapEditEventIgnorer()
81 void * ServerThread::Thread()
85 log_register_thread("ServerThread");
87 DSTACK(__FUNCTION_NAME);
89 BEGIN_DEBUG_EXCEPTION_HANDLER
94 //TimeTaker timer("AsyncRunStep() + Receive()");
97 //TimeTaker timer("AsyncRunStep()");
98 m_server->AsyncRunStep();
101 //infostream<<"Running m_server->Receive()"<<std::endl;
104 catch(con::NoIncomingDataException &e)
107 catch(con::PeerNotFoundException &e)
109 infostream<<"Server: PeerNotFoundException"<<std::endl;
113 END_DEBUG_EXCEPTION_HANDLER(errorstream)
118 void * EmergeThread::Thread()
122 log_register_thread("EmergeThread");
124 DSTACK(__FUNCTION_NAME);
126 BEGIN_DEBUG_EXCEPTION_HANDLER
128 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
131 Get block info from queue, emerge them and send them
134 After queue is empty, exit.
138 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
142 SharedPtr<QueuedBlockEmerge> q(qptr);
148 Do not generate over-limit
150 if(blockpos_over_limit(p))
153 //infostream<<"EmergeThread::Thread(): running"<<std::endl;
155 //TimeTaker timer("block emerge");
158 Try to emerge it from somewhere.
160 If it is only wanted as optional, only loading from disk
165 Check if any peer wants it as non-optional. In that case it
168 Also decrement the emerge queue count in clients.
171 bool only_from_disk = true;
174 core::map<u16, u8>::Iterator i;
175 for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
177 //u16 peer_id = i.getNode()->getKey();
180 u8 flags = i.getNode()->getValue();
181 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
182 only_from_disk = false;
187 if(enable_mapgen_debug_info)
188 infostream<<"EmergeThread: p="
189 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
190 <<"only_from_disk="<<only_from_disk<<std::endl;
192 ServerMap &map = ((ServerMap&)m_server->m_env->getMap());
194 MapBlock *block = NULL;
195 bool got_block = true;
196 core::map<v3s16, MapBlock*> modified_blocks;
199 Try to fetch block from memory or disk.
200 If not found and asked to generate, initialize generator.
203 bool started_generate = false;
204 mapgen::BlockMakeData data;
207 JMutexAutoLock envlock(m_server->m_env_mutex);
209 // Load sector if it isn't loaded
210 if(map.getSectorNoGenerateNoEx(p2d) == NULL)
211 map.loadSectorMeta(p2d);
213 // Attempt to load block
214 block = map.getBlockNoCreateNoEx(p);
215 if(!block || block->isDummy() || !block->isGenerated())
217 if(enable_mapgen_debug_info)
218 infostream<<"EmergeThread: not in memory, "
219 <<"attempting to load from disk"<<std::endl;
221 block = map.loadBlock(p);
224 // If could not load and allowed to generate, start generation
225 // inside this same envlock
226 if(only_from_disk == false &&
227 (block == NULL || block->isGenerated() == false)){
228 if(enable_mapgen_debug_info)
229 infostream<<"EmergeThread: generating"<<std::endl;
230 started_generate = true;
232 map.initBlockMake(&data, p);
237 If generator was initialized, generate now when envlock is free.
242 ScopeProfiler sp(g_profiler, "EmergeThread: mapgen::make_block",
244 TimeTaker t("mapgen::make_block()");
246 mapgen::make_block(&data);
248 if(enable_mapgen_debug_info == false)
249 t.stop(true); // Hide output
253 // Lock environment again to access the map
254 JMutexAutoLock envlock(m_server->m_env_mutex);
256 ScopeProfiler sp(g_profiler, "EmergeThread: after "
257 "mapgen::make_block (envlock)", SPT_AVG);
259 // Blit data back on map, update lighting, add mobs and
260 // whatever this does
261 map.finishBlockMake(&data, modified_blocks);
264 block = map.getBlockNoCreateNoEx(p);
267 Do some post-generate stuff
270 v3s16 minp = block->getPos()*MAP_BLOCKSIZE;
271 v3s16 maxp = minp + v3s16(1,1,1)*(MAP_BLOCKSIZE-1);
272 scriptapi_environment_on_generated(m_server->m_lua,
275 if(enable_mapgen_debug_info)
276 infostream<<"EmergeThread: ended up with: "
277 <<analyze_block(block)<<std::endl;
280 Ignore map edit events, they will not need to be
281 sent to anybody because the block hasn't been sent
284 MapEditEventIgnorer ign(&m_server->m_ignore_map_edit_events);
286 // Activate objects and stuff
287 m_server->m_env->activateBlock(block, 0);
295 Set sent status of modified blocks on clients
298 // NOTE: Server's clients are also behind the connection mutex
299 JMutexAutoLock lock(m_server->m_con_mutex);
302 Add the originally fetched block to the modified list
306 modified_blocks.insert(p, block);
310 Set the modified blocks unsent for all the clients
313 for(core::map<u16, RemoteClient*>::Iterator
314 i = m_server->m_clients.getIterator();
315 i.atEnd() == false; i++)
317 RemoteClient *client = i.getNode()->getValue();
319 if(modified_blocks.size() > 0)
321 // Remove block from sent history
322 client->SetBlocksNotSent(modified_blocks);
328 END_DEBUG_EXCEPTION_HANDLER(errorstream)
330 log_deregister_thread();
335 void RemoteClient::GetNextBlocks(Server *server, float dtime,
336 core::array<PrioritySortedBlockTransfer> &dest)
338 DSTACK(__FUNCTION_NAME);
341 TimeTaker timer("RemoteClient::GetNextBlocks", &timer_result);*/
344 m_nothing_to_send_pause_timer -= dtime;
345 m_nearest_unsent_reset_timer += dtime;
347 if(m_nothing_to_send_pause_timer >= 0)
352 // Won't send anything if already sending
353 if(m_blocks_sending.size() >= g_settings->getU16
354 ("max_simultaneous_block_sends_per_client"))
356 //infostream<<"Not sending any blocks, Queue full."<<std::endl;
360 //TimeTaker timer("RemoteClient::GetNextBlocks");
362 Player *player = server->m_env->getPlayer(peer_id);
364 assert(player != NULL);
366 v3f playerpos = player->getPosition();
367 v3f playerspeed = player->getSpeed();
368 v3f playerspeeddir(0,0,0);
369 if(playerspeed.getLength() > 1.0*BS)
370 playerspeeddir = playerspeed / playerspeed.getLength();
371 // Predict to next block
372 v3f playerpos_predicted = playerpos + playerspeeddir*MAP_BLOCKSIZE*BS;
374 v3s16 center_nodepos = floatToInt(playerpos_predicted, BS);
376 v3s16 center = getNodeBlockPos(center_nodepos);
378 // Camera position and direction
379 v3f camera_pos = player->getEyePosition();
380 v3f camera_dir = v3f(0,0,1);
381 camera_dir.rotateYZBy(player->getPitch());
382 camera_dir.rotateXZBy(player->getYaw());
384 /*infostream<<"camera_dir=("<<camera_dir.X<<","<<camera_dir.Y<<","
385 <<camera_dir.Z<<")"<<std::endl;*/
388 Get the starting value of the block finder radius.
391 if(m_last_center != center)
393 m_nearest_unsent_d = 0;
394 m_last_center = center;
397 /*infostream<<"m_nearest_unsent_reset_timer="
398 <<m_nearest_unsent_reset_timer<<std::endl;*/
400 // Reset periodically to workaround for some bugs or stuff
401 if(m_nearest_unsent_reset_timer > 20.0)
403 m_nearest_unsent_reset_timer = 0;
404 m_nearest_unsent_d = 0;
405 //infostream<<"Resetting m_nearest_unsent_d for "
406 // <<server->getPlayerName(peer_id)<<std::endl;
409 //s16 last_nearest_unsent_d = m_nearest_unsent_d;
410 s16 d_start = m_nearest_unsent_d;
412 //infostream<<"d_start="<<d_start<<std::endl;
414 u16 max_simul_sends_setting = g_settings->getU16
415 ("max_simultaneous_block_sends_per_client");
416 u16 max_simul_sends_usually = max_simul_sends_setting;
419 Check the time from last addNode/removeNode.
421 Decrease send rate if player is building stuff.
423 m_time_from_building += dtime;
424 if(m_time_from_building < g_settings->getFloat(
425 "full_block_send_enable_min_time_from_building"))
427 max_simul_sends_usually
428 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
432 Number of blocks sending + number of blocks selected for sending
434 u32 num_blocks_selected = m_blocks_sending.size();
437 next time d will be continued from the d from which the nearest
438 unsent block was found this time.
440 This is because not necessarily any of the blocks found this
441 time are actually sent.
443 s32 new_nearest_unsent_d = -1;
445 s16 d_max = g_settings->getS16("max_block_send_distance");
446 s16 d_max_gen = g_settings->getS16("max_block_generate_distance");
448 // Don't loop very much at a time
449 s16 max_d_increment_at_time = 2;
450 if(d_max > d_start + max_d_increment_at_time)
451 d_max = d_start + max_d_increment_at_time;
452 /*if(d_max_gen > d_start+2)
453 d_max_gen = d_start+2;*/
455 //infostream<<"Starting from "<<d_start<<std::endl;
457 s32 nearest_emerged_d = -1;
458 s32 nearest_emergefull_d = -1;
459 s32 nearest_sent_d = -1;
460 bool queue_is_full = false;
463 for(d = d_start; d <= d_max; d++)
465 /*errorstream<<"checking d="<<d<<" for "
466 <<server->getPlayerName(peer_id)<<std::endl;*/
467 //infostream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
470 If m_nearest_unsent_d was changed by the EmergeThread
471 (it can change it to 0 through SetBlockNotSent),
473 Else update m_nearest_unsent_d
475 /*if(m_nearest_unsent_d != last_nearest_unsent_d)
477 d = m_nearest_unsent_d;
478 last_nearest_unsent_d = m_nearest_unsent_d;
482 Get the border/face dot coordinates of a "d-radiused"
485 core::list<v3s16> list;
486 getFacePositions(list, d);
488 core::list<v3s16>::Iterator li;
489 for(li=list.begin(); li!=list.end(); li++)
491 v3s16 p = *li + center;
495 - Don't allow too many simultaneous transfers
496 - EXCEPT when the blocks are very close
498 Also, don't send blocks that are already flying.
501 // Start with the usual maximum
502 u16 max_simul_dynamic = max_simul_sends_usually;
504 // If block is very close, allow full maximum
505 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
506 max_simul_dynamic = max_simul_sends_setting;
508 // Don't select too many blocks for sending
509 if(num_blocks_selected >= max_simul_dynamic)
511 queue_is_full = true;
512 goto queue_full_break;
515 // Don't send blocks that are currently being transferred
516 if(m_blocks_sending.find(p) != NULL)
522 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
523 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
524 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
525 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
526 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
527 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
530 // If this is true, inexistent block will be made from scratch
531 bool generate = d <= d_max_gen;
534 /*// Limit the generating area vertically to 2/3
535 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
538 // Limit the send area vertically to 1/2
539 if(abs(p.Y - center.Y) > d_max / 2)
545 If block is far away, don't generate it unless it is
551 // Block center y in nodes
552 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
553 // Don't generate if it's very high or very low
554 if(y < -64 || y > 64)
558 v2s16 p2d_nodes_center(
562 // Get ground height in nodes
563 s16 gh = server->m_env->getServerMap().findGroundLevel(
566 // If differs a lot, don't generate
567 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
569 // Actually, don't even send it
575 //infostream<<"d="<<d<<std::endl;
578 Don't generate or send if not in sight
579 FIXME This only works if the client uses a small enough
580 FOV setting. The default of 72 degrees is fine.
583 float camera_fov = (72.0*PI/180) * 4./3.;
584 if(isBlockInSight(p, camera_pos, camera_dir, camera_fov, 10000*BS) == false)
590 Don't send already sent blocks
593 if(m_blocks_sent.find(p) != NULL)
600 Check if map has this block
602 MapBlock *block = server->m_env->getMap().getBlockNoCreateNoEx(p);
604 bool surely_not_found_on_disk = false;
605 bool block_is_invalid = false;
608 // Reset usage timer, this block will be of use in the future.
609 block->resetUsageTimer();
611 // Block is dummy if data doesn't exist.
612 // It means it has been not found from disk and not generated
615 surely_not_found_on_disk = true;
618 // Block is valid if lighting is up-to-date and data exists
619 if(block->isValid() == false)
621 block_is_invalid = true;
624 /*if(block->isFullyGenerated() == false)
626 block_is_invalid = true;
631 ServerMap *map = (ServerMap*)(&server->m_env->getMap());
632 v2s16 chunkpos = map->sector_to_chunk(p2d);
633 if(map->chunkNonVolatile(chunkpos) == false)
634 block_is_invalid = true;
636 if(block->isGenerated() == false)
637 block_is_invalid = true;
640 If block is not close, don't send it unless it is near
643 Block is near ground level if night-time mesh
644 differs from day-time mesh.
648 if(block->dayNightDiffed() == false)
655 If block has been marked to not exist on disk (dummy)
656 and generating new ones is not wanted, skip block.
658 if(generate == false && surely_not_found_on_disk == true)
665 Add inexistent block to emerge queue.
667 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
669 //TODO: Get value from somewhere
670 // Allow only one block in emerge queue
671 //if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
672 // Allow two blocks in queue per client
673 //if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
675 // Make it more responsive when needing to generate stuff
676 if(surely_not_found_on_disk)
678 if(server->m_emerge_queue.peerItemCount(peer_id) < max_emerge)
680 //infostream<<"Adding block to emerge queue"<<std::endl;
682 // Add it to the emerge queue and trigger the thread
685 if(generate == false)
686 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
688 server->m_emerge_queue.addBlock(peer_id, p, flags);
689 server->m_emergethread.trigger();
691 if(nearest_emerged_d == -1)
692 nearest_emerged_d = d;
694 if(nearest_emergefull_d == -1)
695 nearest_emergefull_d = d;
702 if(nearest_sent_d == -1)
706 Add block to send queue
709 /*errorstream<<"sending from d="<<d<<" to "
710 <<server->getPlayerName(peer_id)<<std::endl;*/
712 PrioritySortedBlockTransfer q((float)d, p, peer_id);
716 num_blocks_selected += 1;
721 //infostream<<"Stopped at "<<d<<std::endl;
723 // If nothing was found for sending and nothing was queued for
724 // emerging, continue next time browsing from here
725 if(nearest_emerged_d != -1){
726 new_nearest_unsent_d = nearest_emerged_d;
727 } else if(nearest_emergefull_d != -1){
728 new_nearest_unsent_d = nearest_emergefull_d;
730 if(d > g_settings->getS16("max_block_send_distance")){
731 new_nearest_unsent_d = 0;
732 m_nothing_to_send_pause_timer = 2.0;
733 /*infostream<<"GetNextBlocks(): d wrapped around for "
734 <<server->getPlayerName(peer_id)
735 <<"; setting to 0 and pausing"<<std::endl;*/
737 if(nearest_sent_d != -1)
738 new_nearest_unsent_d = nearest_sent_d;
740 new_nearest_unsent_d = d;
744 if(new_nearest_unsent_d != -1)
745 m_nearest_unsent_d = new_nearest_unsent_d;
747 /*timer_result = timer.stop(true);
748 if(timer_result != 0)
749 infostream<<"GetNextBlocks duration: "<<timer_result<<" (!=0)"<<std::endl;*/
752 void RemoteClient::SendObjectData(
755 core::map<v3s16, bool> &stepped_blocks
758 DSTACK(__FUNCTION_NAME);
760 // Can't send anything without knowing version
761 if(serialization_version == SER_FMT_VER_INVALID)
763 infostream<<"RemoteClient::SendObjectData(): Not sending, no version."
769 Send a TOCLIENT_OBJECTDATA packet.
773 u16 number of player positions
785 std::ostringstream os(std::ios_base::binary);
789 writeU16(buf, TOCLIENT_OBJECTDATA);
790 os.write((char*)buf, 2);
793 Get and write player data
796 // Get connected players
797 core::list<Player*> players = server->m_env->getPlayers(true);
799 // Write player count
800 u16 playercount = players.size();
801 writeU16(buf, playercount);
802 os.write((char*)buf, 2);
804 core::list<Player*>::Iterator i;
805 for(i = players.begin();
806 i != players.end(); i++)
810 v3f pf = player->getPosition();
811 v3f sf = player->getSpeed();
813 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
814 v3s32 speed_i (sf.X*100, sf.Y*100, sf.Z*100);
815 s32 pitch_i (player->getPitch() * 100);
816 s32 yaw_i (player->getYaw() * 100);
818 writeU16(buf, player->peer_id);
819 os.write((char*)buf, 2);
820 writeV3S32(buf, position_i);
821 os.write((char*)buf, 12);
822 writeV3S32(buf, speed_i);
823 os.write((char*)buf, 12);
824 writeS32(buf, pitch_i);
825 os.write((char*)buf, 4);
826 writeS32(buf, yaw_i);
827 os.write((char*)buf, 4);
831 Get and write object data (dummy, for compatibility)
836 os.write((char*)buf, 2);
842 //infostream<<"Server: Sending object data to "<<peer_id<<std::endl;
845 std::string s = os.str();
846 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
847 // Send as unreliable
848 server->m_con.Send(peer_id, 0, data, false);
851 void RemoteClient::GotBlock(v3s16 p)
853 if(m_blocks_sending.find(p) != NULL)
854 m_blocks_sending.remove(p);
857 /*infostream<<"RemoteClient::GotBlock(): Didn't find in"
858 " m_blocks_sending"<<std::endl;*/
859 m_excess_gotblocks++;
861 m_blocks_sent.insert(p, true);
864 void RemoteClient::SentBlock(v3s16 p)
866 if(m_blocks_sending.find(p) == NULL)
867 m_blocks_sending.insert(p, 0.0);
869 infostream<<"RemoteClient::SentBlock(): Sent block"
870 " already in m_blocks_sending"<<std::endl;
873 void RemoteClient::SetBlockNotSent(v3s16 p)
875 m_nearest_unsent_d = 0;
877 if(m_blocks_sending.find(p) != NULL)
878 m_blocks_sending.remove(p);
879 if(m_blocks_sent.find(p) != NULL)
880 m_blocks_sent.remove(p);
883 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
885 m_nearest_unsent_d = 0;
887 for(core::map<v3s16, MapBlock*>::Iterator
888 i = blocks.getIterator();
889 i.atEnd()==false; i++)
891 v3s16 p = i.getNode()->getKey();
893 if(m_blocks_sending.find(p) != NULL)
894 m_blocks_sending.remove(p);
895 if(m_blocks_sent.find(p) != NULL)
896 m_blocks_sent.remove(p);
904 PlayerInfo::PlayerInfo()
910 void PlayerInfo::PrintLine(std::ostream *s)
913 (*s)<<"\""<<name<<"\" ("
914 <<(position.X/10)<<","<<(position.Y/10)
915 <<","<<(position.Z/10)<<") ";
917 (*s)<<" avg_rtt="<<avg_rtt;
921 u32 PIChecksum(core::list<PlayerInfo> &l)
923 core::list<PlayerInfo>::Iterator i;
926 for(i=l.begin(); i!=l.end(); i++)
928 checksum += a * (i->id+1);
929 checksum ^= 0x435aafcd;
943 std::set<std::string> depends;
944 std::set<std::string> unsatisfied_depends;
946 ModSpec(const std::string &name_="", const std::string path_="",
947 const std::set<std::string> &depends_=std::set<std::string>()):
951 unsatisfied_depends(depends_)
955 // Get a dependency-sorted list of ModSpecs
956 static core::list<ModSpec> getMods(core::list<std::string> &modspaths)
958 std::queue<ModSpec> mods_satisfied;
959 core::list<ModSpec> mods_unsorted;
960 core::list<ModSpec> mods_sorted;
961 for(core::list<std::string>::Iterator i = modspaths.begin();
962 i != modspaths.end(); i++){
963 std::string modspath = *i;
964 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(modspath);
965 for(u32 j=0; j<dirlist.size(); j++){
968 std::string modname = dirlist[j].name;
969 std::string modpath = modspath + DIR_DELIM + modname;
970 std::set<std::string> depends;
971 std::ifstream is((modpath+DIR_DELIM+"depends.txt").c_str(),
972 std::ios_base::binary);
975 std::getline(is, dep);
980 ModSpec spec(modname, modpath, depends);
981 mods_unsorted.push_back(spec);
983 mods_satisfied.push(spec);
986 // Sort by depencencies
987 while(!mods_satisfied.empty()){
988 ModSpec mod = mods_satisfied.front();
989 mods_satisfied.pop();
990 mods_sorted.push_back(mod);
991 for(core::list<ModSpec>::Iterator i = mods_unsorted.begin();
992 i != mods_unsorted.end(); i++){
994 if(mod2.unsatisfied_depends.empty())
996 mod2.unsatisfied_depends.erase(mod.name);
997 if(!mod2.unsatisfied_depends.empty())
999 mods_satisfied.push(mod2);
1002 // Check unsatisfied dependencies
1003 for(core::list<ModSpec>::Iterator i = mods_unsorted.begin();
1004 i != mods_unsorted.end(); i++){
1006 if(mod.unsatisfied_depends.empty())
1008 errorstream<<"mod \""<<mod.name
1009 <<"\" has unsatisfied dependencies:";
1010 for(std::set<std::string>::iterator
1011 i = mod.unsatisfied_depends.begin();
1012 i != mod.unsatisfied_depends.end(); i++){
1013 errorstream<<" \""<<(*i)<<"\"";
1015 errorstream<<". Loading nevertheless."<<std::endl;
1016 mods_sorted.push_back(mod);
1026 std::string mapsavedir,
1027 std::string configpath
1030 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
1031 m_authmanager(mapsavedir+DIR_DELIM+"auth.txt"),
1032 m_banmanager(mapsavedir+DIR_DELIM+"ipban.txt"),
1034 m_toolmgr(createToolDefManager()),
1035 m_nodedef(createNodeDefManager()),
1036 m_craftdef(createCraftDefManager()),
1037 m_craftitemdef(createCraftItemDefManager()),
1039 m_emergethread(this),
1041 m_time_of_day_send_timer(0),
1043 m_mapsavedir(mapsavedir),
1044 m_configpath(configpath),
1045 m_shutdown_requested(false),
1046 m_ignore_map_edit_events(false),
1047 m_ignore_map_edit_events_peer_id(0)
1049 m_liquid_transform_timer = 0.0;
1050 m_print_info_timer = 0.0;
1051 m_objectdata_timer = 0.0;
1052 m_emergethread_trigger_timer = 0.0;
1053 m_savemap_timer = 0.0;
1057 m_step_dtime_mutex.Init();
1060 JMutexAutoLock envlock(m_env_mutex);
1061 JMutexAutoLock conlock(m_con_mutex);
1063 infostream<<"m_nodedef="<<m_nodedef<<std::endl;
1065 // Path to builtin.lua
1066 std::string builtinpath = porting::path_data + DIR_DELIM + "builtin.lua";
1067 // Add default global mod path
1068 m_modspaths.push_back(porting::path_data + DIR_DELIM + "mods");
1070 // Initialize scripting
1072 infostream<<"Server: Initializing scripting"<<std::endl;
1073 m_lua = script_init();
1076 scriptapi_export(m_lua, this);
1077 // Load and run builtin.lua
1078 infostream<<"Server: Loading builtin Lua stuff from \""<<builtinpath
1080 bool success = script_load(m_lua, builtinpath.c_str());
1082 errorstream<<"Server: Failed to load and run "
1083 <<builtinpath<<std::endl;
1086 // Load and run "mod" scripts
1087 core::list<ModSpec> mods = getMods(m_modspaths);
1088 for(core::list<ModSpec>::Iterator i = mods.begin();
1089 i != mods.end(); i++){
1091 infostream<<"Server: Loading mod \""<<mod.name<<"\""<<std::endl;
1092 std::string scriptpath = mod.path + DIR_DELIM + "init.lua";
1093 bool success = script_load(m_lua, scriptpath.c_str());
1095 errorstream<<"Server: Failed to load and run "
1096 <<scriptpath<<std::endl;
1101 // Initialize Environment
1103 m_env = new ServerEnvironment(new ServerMap(mapsavedir, this), m_lua,
1106 // Give environment reference to scripting api
1107 scriptapi_add_environment(m_lua, m_env);
1109 // Register us to receive map edit events
1110 m_env->getMap().addEventReceiver(this);
1112 // If file exists, load environment metadata
1113 if(fs::PathExists(m_mapsavedir+DIR_DELIM+"env_meta.txt"))
1115 infostream<<"Server: Loading environment metadata"<<std::endl;
1116 m_env->loadMeta(m_mapsavedir);
1120 infostream<<"Server: Loading players"<<std::endl;
1121 m_env->deSerializePlayers(m_mapsavedir);
1124 Add some test ActiveBlockModifiers to environment
1126 add_legacy_abms(m_env, m_nodedef);
1131 infostream<<"Server::~Server()"<<std::endl;
1134 Send shutdown message
1137 JMutexAutoLock conlock(m_con_mutex);
1139 std::wstring line = L"*** Server shutting down";
1142 Send the message to clients
1144 for(core::map<u16, RemoteClient*>::Iterator
1145 i = m_clients.getIterator();
1146 i.atEnd() == false; i++)
1148 // Get client and check that it is valid
1149 RemoteClient *client = i.getNode()->getValue();
1150 assert(client->peer_id == i.getNode()->getKey());
1151 if(client->serialization_version == SER_FMT_VER_INVALID)
1155 SendChatMessage(client->peer_id, line);
1157 catch(con::PeerNotFoundException &e)
1163 JMutexAutoLock envlock(m_env_mutex);
1168 infostream<<"Server: Saving players"<<std::endl;
1169 m_env->serializePlayers(m_mapsavedir);
1172 Save environment metadata
1174 infostream<<"Server: Saving environment metadata"<<std::endl;
1175 m_env->saveMeta(m_mapsavedir);
1187 JMutexAutoLock clientslock(m_con_mutex);
1189 for(core::map<u16, RemoteClient*>::Iterator
1190 i = m_clients.getIterator();
1191 i.atEnd() == false; i++)
1194 // NOTE: These are removed by env destructor
1196 u16 peer_id = i.getNode()->getKey();
1197 JMutexAutoLock envlock(m_env_mutex);
1198 m_env->removePlayer(peer_id);
1202 delete i.getNode()->getValue();
1206 // Delete Environment
1212 delete m_craftitemdef;
1214 // Deinitialize scripting
1215 infostream<<"Server: Deinitializing scripting"<<std::endl;
1216 script_deinit(m_lua);
1219 void Server::start(unsigned short port)
1221 DSTACK(__FUNCTION_NAME);
1222 // Stop thread if already running
1225 // Initialize connection
1226 m_con.SetTimeoutMs(30);
1230 m_thread.setRun(true);
1233 infostream<<"Server: Started on port "<<port<<std::endl;
1238 DSTACK(__FUNCTION_NAME);
1240 infostream<<"Server: Stopping and waiting threads"<<std::endl;
1242 // Stop threads (set run=false first so both start stopping)
1243 m_thread.setRun(false);
1244 m_emergethread.setRun(false);
1246 m_emergethread.stop();
1248 infostream<<"Server: Threads stopped"<<std::endl;
1251 void Server::step(float dtime)
1253 DSTACK(__FUNCTION_NAME);
1258 JMutexAutoLock lock(m_step_dtime_mutex);
1259 m_step_dtime += dtime;
1263 void Server::AsyncRunStep()
1265 DSTACK(__FUNCTION_NAME);
1267 g_profiler->add("Server::AsyncRunStep (num)", 1);
1271 JMutexAutoLock lock1(m_step_dtime_mutex);
1272 dtime = m_step_dtime;
1276 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
1277 // Send blocks to clients
1284 g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
1286 //infostream<<"Server steps "<<dtime<<std::endl;
1287 //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1290 JMutexAutoLock lock1(m_step_dtime_mutex);
1291 m_step_dtime -= dtime;
1298 m_uptime.set(m_uptime.get() + dtime);
1302 // Process connection's timeouts
1303 JMutexAutoLock lock2(m_con_mutex);
1304 ScopeProfiler sp(g_profiler, "Server: connection timeout processing");
1305 m_con.RunTimeouts(dtime);
1309 // This has to be called so that the client list gets synced
1310 // with the peer list of the connection
1311 handlePeerChanges();
1315 Update m_time_of_day and overall game time
1318 JMutexAutoLock envlock(m_env_mutex);
1320 m_time_counter += dtime;
1321 f32 speed = g_settings->getFloat("time_speed") * 24000./(24.*3600);
1322 u32 units = (u32)(m_time_counter*speed);
1323 m_time_counter -= (f32)units / speed;
1325 m_env->setTimeOfDay((m_env->getTimeOfDay() + units) % 24000);
1327 //infostream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1330 Send to clients at constant intervals
1333 m_time_of_day_send_timer -= dtime;
1334 if(m_time_of_day_send_timer < 0.0)
1336 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
1338 //JMutexAutoLock envlock(m_env_mutex);
1339 JMutexAutoLock conlock(m_con_mutex);
1341 for(core::map<u16, RemoteClient*>::Iterator
1342 i = m_clients.getIterator();
1343 i.atEnd() == false; i++)
1345 RemoteClient *client = i.getNode()->getValue();
1346 //Player *player = m_env->getPlayer(client->peer_id);
1348 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1349 m_env->getTimeOfDay());
1351 m_con.Send(client->peer_id, 0, data, true);
1357 JMutexAutoLock lock(m_env_mutex);
1359 ScopeProfiler sp(g_profiler, "SEnv step");
1360 ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
1364 const float map_timer_and_unload_dtime = 2.92;
1365 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
1367 JMutexAutoLock lock(m_env_mutex);
1368 // Run Map's timers and unload unused data
1369 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
1370 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
1371 g_settings->getFloat("server_unload_unused_data_timeout"));
1382 JMutexAutoLock lock(m_env_mutex);
1383 JMutexAutoLock lock2(m_con_mutex);
1385 //float player_max_speed = BS * 4.0; // Normal speed
1386 float player_max_speed = BS * 20; // Fast speed
1387 float player_max_speed_up = BS * 20;
1389 player_max_speed *= 2.5; // Tolerance
1390 player_max_speed_up *= 2.5;
1392 for(core::map<u16, RemoteClient*>::Iterator
1393 i = m_clients.getIterator();
1394 i.atEnd() == false; i++)
1396 RemoteClient *client = i.getNode()->getValue();
1397 ServerRemotePlayer *player =
1398 static_cast<ServerRemotePlayer*>
1399 (m_env->getPlayer(client->peer_id));
1404 Check player movements
1406 NOTE: Actually the server should handle player physics like the
1407 client does and compare player's position to what is calculated
1408 on our side. This is required when eg. players fly due to an
1411 player->m_last_good_position_age += dtime;
1412 if(player->m_last_good_position_age >= 2.0){
1413 float age = player->m_last_good_position_age;
1414 v3f diff = (player->getPosition() - player->m_last_good_position);
1415 float d_vert = diff.Y;
1417 float d_horiz = diff.getLength();
1418 /*infostream<<player->getName()<<"'s horizontal speed is "
1419 <<(d_horiz/age)<<std::endl;*/
1420 if(d_horiz <= age * player_max_speed &&
1421 (d_vert < 0 || d_vert < age * player_max_speed_up)){
1422 player->m_last_good_position = player->getPosition();
1424 actionstream<<"Player "<<player->getName()
1425 <<" moved too fast; resetting position"
1427 player->setPosition(player->m_last_good_position);
1428 SendMovePlayer(player);
1430 player->m_last_good_position_age = 0;
1434 Send player inventories and HPs if necessary
1436 if(player->m_inventory_not_sent){
1437 UpdateCrafting(player->peer_id);
1438 SendInventory(player->peer_id);
1440 if(player->m_hp_not_sent){
1441 SendPlayerHP(player);
1446 /* Transform liquids */
1447 m_liquid_transform_timer += dtime;
1448 if(m_liquid_transform_timer >= 1.00)
1450 m_liquid_transform_timer -= 1.00;
1452 JMutexAutoLock lock(m_env_mutex);
1454 ScopeProfiler sp(g_profiler, "Server: liquid transform");
1456 core::map<v3s16, MapBlock*> modified_blocks;
1457 m_env->getMap().transformLiquids(modified_blocks);
1462 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1463 ServerMap &map = ((ServerMap&)m_env->getMap());
1464 map.updateLighting(modified_blocks, lighting_modified_blocks);
1466 // Add blocks modified by lighting to modified_blocks
1467 for(core::map<v3s16, MapBlock*>::Iterator
1468 i = lighting_modified_blocks.getIterator();
1469 i.atEnd() == false; i++)
1471 MapBlock *block = i.getNode()->getValue();
1472 modified_blocks.insert(block->getPos(), block);
1476 Set the modified blocks unsent for all the clients
1479 JMutexAutoLock lock2(m_con_mutex);
1481 for(core::map<u16, RemoteClient*>::Iterator
1482 i = m_clients.getIterator();
1483 i.atEnd() == false; i++)
1485 RemoteClient *client = i.getNode()->getValue();
1487 if(modified_blocks.size() > 0)
1489 // Remove block from sent history
1490 client->SetBlocksNotSent(modified_blocks);
1495 // Periodically print some info
1497 float &counter = m_print_info_timer;
1503 JMutexAutoLock lock2(m_con_mutex);
1505 if(m_clients.size() != 0)
1506 infostream<<"Players:"<<std::endl;
1507 for(core::map<u16, RemoteClient*>::Iterator
1508 i = m_clients.getIterator();
1509 i.atEnd() == false; i++)
1511 //u16 peer_id = i.getNode()->getKey();
1512 RemoteClient *client = i.getNode()->getValue();
1513 Player *player = m_env->getPlayer(client->peer_id);
1516 infostream<<"* "<<player->getName()<<"\t";
1517 client->PrintInfo(infostream);
1522 //if(g_settings->getBool("enable_experimental"))
1526 Check added and deleted active objects
1529 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
1530 JMutexAutoLock envlock(m_env_mutex);
1531 JMutexAutoLock conlock(m_con_mutex);
1533 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
1535 // Radius inside which objects are active
1536 s16 radius = g_settings->getS16("active_object_send_range_blocks");
1537 radius *= MAP_BLOCKSIZE;
1539 for(core::map<u16, RemoteClient*>::Iterator
1540 i = m_clients.getIterator();
1541 i.atEnd() == false; i++)
1543 RemoteClient *client = i.getNode()->getValue();
1544 Player *player = m_env->getPlayer(client->peer_id);
1547 // This can happen if the client timeouts somehow
1548 /*infostream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
1550 <<" has no associated player"<<std::endl;*/
1553 v3s16 pos = floatToInt(player->getPosition(), BS);
1555 core::map<u16, bool> removed_objects;
1556 core::map<u16, bool> added_objects;
1557 m_env->getRemovedActiveObjects(pos, radius,
1558 client->m_known_objects, removed_objects);
1559 m_env->getAddedActiveObjects(pos, radius,
1560 client->m_known_objects, added_objects);
1562 // Ignore if nothing happened
1563 if(removed_objects.size() == 0 && added_objects.size() == 0)
1565 //infostream<<"active objects: none changed"<<std::endl;
1569 std::string data_buffer;
1573 // Handle removed objects
1574 writeU16((u8*)buf, removed_objects.size());
1575 data_buffer.append(buf, 2);
1576 for(core::map<u16, bool>::Iterator
1577 i = removed_objects.getIterator();
1578 i.atEnd()==false; i++)
1581 u16 id = i.getNode()->getKey();
1582 ServerActiveObject* obj = m_env->getActiveObject(id);
1584 // Add to data buffer for sending
1585 writeU16((u8*)buf, i.getNode()->getKey());
1586 data_buffer.append(buf, 2);
1588 // Remove from known objects
1589 client->m_known_objects.remove(i.getNode()->getKey());
1591 if(obj && obj->m_known_by_count > 0)
1592 obj->m_known_by_count--;
1595 // Handle added objects
1596 writeU16((u8*)buf, added_objects.size());
1597 data_buffer.append(buf, 2);
1598 for(core::map<u16, bool>::Iterator
1599 i = added_objects.getIterator();
1600 i.atEnd()==false; i++)
1603 u16 id = i.getNode()->getKey();
1604 ServerActiveObject* obj = m_env->getActiveObject(id);
1607 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1609 infostream<<"WARNING: "<<__FUNCTION_NAME
1610 <<": NULL object"<<std::endl;
1612 type = obj->getType();
1614 // Add to data buffer for sending
1615 writeU16((u8*)buf, id);
1616 data_buffer.append(buf, 2);
1617 writeU8((u8*)buf, type);
1618 data_buffer.append(buf, 1);
1621 data_buffer.append(serializeLongString(
1622 obj->getClientInitializationData()));
1624 data_buffer.append(serializeLongString(""));
1626 // Add to known objects
1627 client->m_known_objects.insert(i.getNode()->getKey(), false);
1630 obj->m_known_by_count++;
1634 SharedBuffer<u8> reply(2 + data_buffer.size());
1635 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1636 memcpy((char*)&reply[2], data_buffer.c_str(),
1637 data_buffer.size());
1639 m_con.Send(client->peer_id, 0, reply, true);
1641 infostream<<"Server: Sent object remove/add: "
1642 <<removed_objects.size()<<" removed, "
1643 <<added_objects.size()<<" added, "
1644 <<"packet size is "<<reply.getSize()<<std::endl;
1649 Collect a list of all the objects known by the clients
1650 and report it back to the environment.
1653 core::map<u16, bool> all_known_objects;
1655 for(core::map<u16, RemoteClient*>::Iterator
1656 i = m_clients.getIterator();
1657 i.atEnd() == false; i++)
1659 RemoteClient *client = i.getNode()->getValue();
1660 // Go through all known objects of client
1661 for(core::map<u16, bool>::Iterator
1662 i = client->m_known_objects.getIterator();
1663 i.atEnd()==false; i++)
1665 u16 id = i.getNode()->getKey();
1666 all_known_objects[id] = true;
1670 m_env->setKnownActiveObjects(whatever);
1676 Send object messages
1679 JMutexAutoLock envlock(m_env_mutex);
1680 JMutexAutoLock conlock(m_con_mutex);
1682 //ScopeProfiler sp(g_profiler, "Server: sending object messages");
1685 // Value = data sent by object
1686 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1688 // Get active object messages from environment
1691 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
1695 core::list<ActiveObjectMessage>* message_list = NULL;
1696 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1697 n = buffered_messages.find(aom.id);
1700 message_list = new core::list<ActiveObjectMessage>;
1701 buffered_messages.insert(aom.id, message_list);
1705 message_list = n->getValue();
1707 message_list->push_back(aom);
1710 // Route data to every client
1711 for(core::map<u16, RemoteClient*>::Iterator
1712 i = m_clients.getIterator();
1713 i.atEnd()==false; i++)
1715 RemoteClient *client = i.getNode()->getValue();
1716 std::string reliable_data;
1717 std::string unreliable_data;
1718 // Go through all objects in message buffer
1719 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1720 j = buffered_messages.getIterator();
1721 j.atEnd()==false; j++)
1723 // If object is not known by client, skip it
1724 u16 id = j.getNode()->getKey();
1725 if(client->m_known_objects.find(id) == NULL)
1727 // Get message list of object
1728 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1729 // Go through every message
1730 for(core::list<ActiveObjectMessage>::Iterator
1731 k = list->begin(); k != list->end(); k++)
1733 // Compose the full new data with header
1734 ActiveObjectMessage aom = *k;
1735 std::string new_data;
1738 writeU16((u8*)&buf[0], aom.id);
1739 new_data.append(buf, 2);
1741 new_data += serializeString(aom.datastring);
1742 // Add data to buffer
1744 reliable_data += new_data;
1746 unreliable_data += new_data;
1750 reliable_data and unreliable_data are now ready.
1753 if(reliable_data.size() > 0)
1755 SharedBuffer<u8> reply(2 + reliable_data.size());
1756 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1757 memcpy((char*)&reply[2], reliable_data.c_str(),
1758 reliable_data.size());
1760 m_con.Send(client->peer_id, 0, reply, true);
1762 if(unreliable_data.size() > 0)
1764 SharedBuffer<u8> reply(2 + unreliable_data.size());
1765 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1766 memcpy((char*)&reply[2], unreliable_data.c_str(),
1767 unreliable_data.size());
1768 // Send as unreliable
1769 m_con.Send(client->peer_id, 0, reply, false);
1772 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1774 infostream<<"Server: Size of object message data: "
1775 <<"reliable: "<<reliable_data.size()
1776 <<", unreliable: "<<unreliable_data.size()
1781 // Clear buffered_messages
1782 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1783 i = buffered_messages.getIterator();
1784 i.atEnd()==false; i++)
1786 delete i.getNode()->getValue();
1790 } // enable_experimental
1793 Send queued-for-sending map edit events.
1796 // Don't send too many at a time
1799 // Single change sending is disabled if queue size is not small
1800 bool disable_single_change_sending = false;
1801 if(m_unsent_map_edit_queue.size() >= 4)
1802 disable_single_change_sending = true;
1804 bool got_any_events = false;
1806 // We'll log the amount of each
1809 while(m_unsent_map_edit_queue.size() != 0)
1811 got_any_events = true;
1813 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1815 // Players far away from the change are stored here.
1816 // Instead of sending the changes, MapBlocks are set not sent
1818 core::list<u16> far_players;
1820 if(event->type == MEET_ADDNODE)
1822 //infostream<<"Server: MEET_ADDNODE"<<std::endl;
1823 prof.add("MEET_ADDNODE", 1);
1824 if(disable_single_change_sending)
1825 sendAddNode(event->p, event->n, event->already_known_by_peer,
1828 sendAddNode(event->p, event->n, event->already_known_by_peer,
1831 else if(event->type == MEET_REMOVENODE)
1833 //infostream<<"Server: MEET_REMOVENODE"<<std::endl;
1834 prof.add("MEET_REMOVENODE", 1);
1835 if(disable_single_change_sending)
1836 sendRemoveNode(event->p, event->already_known_by_peer,
1839 sendRemoveNode(event->p, event->already_known_by_peer,
1842 else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
1844 infostream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
1845 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
1846 setBlockNotSent(event->p);
1848 else if(event->type == MEET_OTHER)
1850 infostream<<"Server: MEET_OTHER"<<std::endl;
1851 prof.add("MEET_OTHER", 1);
1852 for(core::map<v3s16, bool>::Iterator
1853 i = event->modified_blocks.getIterator();
1854 i.atEnd()==false; i++)
1856 v3s16 p = i.getNode()->getKey();
1862 prof.add("unknown", 1);
1863 infostream<<"WARNING: Server: Unknown MapEditEvent "
1864 <<((u32)event->type)<<std::endl;
1868 Set blocks not sent to far players
1870 if(far_players.size() > 0)
1872 // Convert list format to that wanted by SetBlocksNotSent
1873 core::map<v3s16, MapBlock*> modified_blocks2;
1874 for(core::map<v3s16, bool>::Iterator
1875 i = event->modified_blocks.getIterator();
1876 i.atEnd()==false; i++)
1878 v3s16 p = i.getNode()->getKey();
1879 modified_blocks2.insert(p,
1880 m_env->getMap().getBlockNoCreateNoEx(p));
1882 // Set blocks not sent
1883 for(core::list<u16>::Iterator
1884 i = far_players.begin();
1885 i != far_players.end(); i++)
1888 RemoteClient *client = getClient(peer_id);
1891 client->SetBlocksNotSent(modified_blocks2);
1897 /*// Don't send too many at a time
1899 if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
1905 infostream<<"Server: MapEditEvents:"<<std::endl;
1906 prof.print(infostream);
1912 Send object positions
1915 float &counter = m_objectdata_timer;
1917 if(counter >= g_settings->getFloat("objectdata_interval"))
1919 JMutexAutoLock lock1(m_env_mutex);
1920 JMutexAutoLock lock2(m_con_mutex);
1922 //ScopeProfiler sp(g_profiler, "Server: sending player positions");
1924 SendObjectData(counter);
1931 Trigger emergethread (it somehow gets to a non-triggered but
1932 bysy state sometimes)
1935 float &counter = m_emergethread_trigger_timer;
1941 m_emergethread.trigger();
1945 // Save map, players and auth stuff
1947 float &counter = m_savemap_timer;
1949 if(counter >= g_settings->getFloat("server_map_save_interval"))
1953 ScopeProfiler sp(g_profiler, "Server: saving stuff");
1956 if(m_authmanager.isModified())
1957 m_authmanager.save();
1960 if(m_banmanager.isModified())
1961 m_banmanager.save();
1964 JMutexAutoLock lock(m_env_mutex);
1966 // Save changed parts of map
1967 m_env->getMap().save(MOD_STATE_WRITE_NEEDED);
1970 m_env->serializePlayers(m_mapsavedir);
1972 // Save environment metadata
1973 m_env->saveMeta(m_mapsavedir);
1978 void Server::Receive()
1980 DSTACK(__FUNCTION_NAME);
1981 SharedBuffer<u8> data;
1986 JMutexAutoLock conlock(m_con_mutex);
1987 datasize = m_con.Receive(peer_id, data);
1990 // This has to be called so that the client list gets synced
1991 // with the peer list of the connection
1992 handlePeerChanges();
1994 ProcessData(*data, datasize, peer_id);
1996 catch(con::InvalidIncomingDataException &e)
1998 infostream<<"Server::Receive(): "
1999 "InvalidIncomingDataException: what()="
2000 <<e.what()<<std::endl;
2002 catch(con::PeerNotFoundException &e)
2004 //NOTE: This is not needed anymore
2006 // The peer has been disconnected.
2007 // Find the associated player and remove it.
2009 /*JMutexAutoLock envlock(m_env_mutex);
2011 infostream<<"ServerThread: peer_id="<<peer_id
2012 <<" has apparently closed connection. "
2013 <<"Removing player."<<std::endl;
2015 m_env->removePlayer(peer_id);*/
2019 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
2021 DSTACK(__FUNCTION_NAME);
2022 // Environment is locked first.
2023 JMutexAutoLock envlock(m_env_mutex);
2024 JMutexAutoLock conlock(m_con_mutex);
2027 Address address = m_con.GetPeerAddress(peer_id);
2029 // drop player if is ip is banned
2030 if(m_banmanager.isIpBanned(address.serializeString())){
2031 SendAccessDenied(m_con, peer_id,
2032 L"Your ip is banned. Banned name was "
2033 +narrow_to_wide(m_banmanager.getBanName(
2034 address.serializeString())));
2035 m_con.DeletePeer(peer_id);
2039 catch(con::PeerNotFoundException &e)
2041 infostream<<"Server::ProcessData(): Cancelling: peer "
2042 <<peer_id<<" not found"<<std::endl;
2046 u8 peer_ser_ver = getClient(peer_id)->serialization_version;
2054 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
2056 if(command == TOSERVER_INIT)
2058 // [0] u16 TOSERVER_INIT
2059 // [2] u8 SER_FMT_VER_HIGHEST
2060 // [3] u8[20] player_name
2061 // [23] u8[28] password <--- can be sent without this, from old versions
2063 if(datasize < 2+1+PLAYERNAME_SIZE)
2066 infostream<<"Server: Got TOSERVER_INIT from "
2067 <<peer_id<<std::endl;
2069 // First byte after command is maximum supported
2070 // serialization version
2071 u8 client_max = data[2];
2072 u8 our_max = SER_FMT_VER_HIGHEST;
2073 // Use the highest version supported by both
2074 u8 deployed = core::min_(client_max, our_max);
2075 // If it's lower than the lowest supported, give up.
2076 if(deployed < SER_FMT_VER_LOWEST)
2077 deployed = SER_FMT_VER_INVALID;
2079 //peer->serialization_version = deployed;
2080 getClient(peer_id)->pending_serialization_version = deployed;
2082 if(deployed == SER_FMT_VER_INVALID)
2084 infostream<<"Server: Cannot negotiate "
2085 "serialization version with peer "
2086 <<peer_id<<std::endl;
2087 SendAccessDenied(m_con, peer_id, std::wstring(
2088 L"Your client's version is not supported.\n"
2089 L"Server version is ")
2090 + narrow_to_wide(VERSION_STRING) + L"."
2096 Read and check network protocol version
2099 u16 net_proto_version = 0;
2100 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
2102 net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
2105 getClient(peer_id)->net_proto_version = net_proto_version;
2107 if(net_proto_version == 0)
2109 SendAccessDenied(m_con, peer_id, std::wstring(
2110 L"Your client's version is not supported.\n"
2111 L"Server version is ")
2112 + narrow_to_wide(VERSION_STRING) + L"."
2117 if(g_settings->getBool("strict_protocol_version_checking"))
2119 if(net_proto_version != PROTOCOL_VERSION)
2121 SendAccessDenied(m_con, peer_id, std::wstring(
2122 L"Your client's version is not supported.\n"
2123 L"Server version is ")
2124 + narrow_to_wide(VERSION_STRING) + L"."
2135 char playername[PLAYERNAME_SIZE];
2136 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
2138 playername[i] = data[3+i];
2140 playername[PLAYERNAME_SIZE-1] = 0;
2142 if(playername[0]=='\0')
2144 infostream<<"Server: Player has empty name"<<std::endl;
2145 SendAccessDenied(m_con, peer_id,
2150 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
2152 infostream<<"Server: Player has invalid name"<<std::endl;
2153 SendAccessDenied(m_con, peer_id,
2154 L"Name contains unallowed characters");
2159 char password[PASSWORD_SIZE];
2160 if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
2162 // old version - assume blank password
2167 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2169 password[i] = data[23+i];
2171 password[PASSWORD_SIZE-1] = 0;
2174 // Add player to auth manager
2175 if(m_authmanager.exists(playername) == false)
2177 std::wstring default_password =
2178 narrow_to_wide(g_settings->get("default_password"));
2179 std::string translated_default_password =
2180 translatePassword(playername, default_password);
2182 // If default_password is empty, allow any initial password
2183 if (default_password.length() == 0)
2184 translated_default_password = password;
2186 infostream<<"Server: adding player "<<playername
2187 <<" to auth manager"<<std::endl;
2188 m_authmanager.add(playername);
2189 m_authmanager.setPassword(playername, translated_default_password);
2190 m_authmanager.setPrivs(playername,
2191 stringToPrivs(g_settings->get("default_privs")));
2192 m_authmanager.save();
2195 std::string checkpwd = m_authmanager.getPassword(playername);
2197 /*infostream<<"Server: Client gave password '"<<password
2198 <<"', the correct one is '"<<checkpwd<<"'"<<std::endl;*/
2200 if(password != checkpwd)
2202 infostream<<"Server: peer_id="<<peer_id
2203 <<": supplied invalid password for "
2204 <<playername<<std::endl;
2205 SendAccessDenied(m_con, peer_id, L"Invalid password");
2209 // Enforce user limit.
2210 // Don't enforce for users that have some admin right
2211 if(m_clients.size() >= g_settings->getU16("max_users") &&
2212 (m_authmanager.getPrivs(playername)
2213 & (PRIV_SERVER|PRIV_BAN|PRIV_PRIVS|PRIV_PASSWORD)) == 0 &&
2214 playername != g_settings->get("name"))
2216 SendAccessDenied(m_con, peer_id, L"Too many users.");
2221 Player *player = emergePlayer(playername, peer_id);
2223 // If failed, cancel
2226 infostream<<"Server: peer_id="<<peer_id
2227 <<": failed to emerge player"<<std::endl;
2232 Answer with a TOCLIENT_INIT
2235 SharedBuffer<u8> reply(2+1+6+8);
2236 writeU16(&reply[0], TOCLIENT_INIT);
2237 writeU8(&reply[2], deployed);
2238 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
2239 writeU64(&reply[2+1+6], m_env->getServerMap().getSeed());
2242 m_con.Send(peer_id, 0, reply, true);
2246 Send complete position information
2248 SendMovePlayer(player);
2253 if(command == TOSERVER_INIT2)
2255 infostream<<"Server: Got TOSERVER_INIT2 from "
2256 <<peer_id<<std::endl;
2259 getClient(peer_id)->serialization_version
2260 = getClient(peer_id)->pending_serialization_version;
2263 Send some initialization data
2266 // Send tool definitions
2267 SendToolDef(m_con, peer_id, m_toolmgr);
2269 // Send node definitions
2270 SendNodeDef(m_con, peer_id, m_nodedef);
2272 // Send CraftItem definitions
2273 SendCraftItemDef(m_con, peer_id, m_craftitemdef);
2276 SendTextures(peer_id);
2278 // Send player info to all players
2281 // Send inventory to player
2282 UpdateCrafting(peer_id);
2283 SendInventory(peer_id);
2285 // Send player items to all players
2288 Player *player = m_env->getPlayer(peer_id);
2291 SendPlayerHP(player);
2295 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2296 m_env->getTimeOfDay());
2297 m_con.Send(peer_id, 0, data, true);
2300 // Send information about server to player in chat
2301 SendChatMessage(peer_id, getStatusString());
2303 // Send information about joining in chat
2305 std::wstring name = L"unknown";
2306 Player *player = m_env->getPlayer(peer_id);
2308 name = narrow_to_wide(player->getName());
2310 std::wstring message;
2313 message += L" joined game";
2314 BroadcastChatMessage(message);
2317 // Warnings about protocol version can be issued here
2318 if(getClient(peer_id)->net_proto_version < PROTOCOL_VERSION)
2320 SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT IS OLD AND MAY WORK PROPERLY WITH THIS SERVER");
2324 Check HP, respawn if necessary
2326 HandlePlayerHP(player, 0);
2332 std::ostringstream os(std::ios_base::binary);
2333 for(core::map<u16, RemoteClient*>::Iterator
2334 i = m_clients.getIterator();
2335 i.atEnd() == false; i++)
2337 RemoteClient *client = i.getNode()->getValue();
2338 assert(client->peer_id == i.getNode()->getKey());
2339 if(client->serialization_version == SER_FMT_VER_INVALID)
2342 Player *player = m_env->getPlayer(client->peer_id);
2345 // Get name of player
2346 os<<player->getName()<<" ";
2349 actionstream<<player->getName()<<" joins game. List of players: "
2350 <<os.str()<<std::endl;
2356 if(peer_ser_ver == SER_FMT_VER_INVALID)
2358 infostream<<"Server::ProcessData(): Cancelling: Peer"
2359 " serialization format invalid or not initialized."
2360 " Skipping incoming command="<<command<<std::endl;
2364 Player *player = m_env->getPlayer(peer_id);
2365 ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
2368 infostream<<"Server::ProcessData(): Cancelling: "
2369 "No player for peer_id="<<peer_id
2373 if(command == TOSERVER_PLAYERPOS)
2375 if(datasize < 2+12+12+4+4)
2379 v3s32 ps = readV3S32(&data[start+2]);
2380 v3s32 ss = readV3S32(&data[start+2+12]);
2381 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2382 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2383 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2384 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2385 pitch = wrapDegrees(pitch);
2386 yaw = wrapDegrees(yaw);
2388 player->setPosition(position);
2389 player->setSpeed(speed);
2390 player->setPitch(pitch);
2391 player->setYaw(yaw);
2393 /*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2394 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2395 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2397 else if(command == TOSERVER_GOTBLOCKS)
2410 u16 count = data[2];
2411 for(u16 i=0; i<count; i++)
2413 if((s16)datasize < 2+1+(i+1)*6)
2414 throw con::InvalidIncomingDataException
2415 ("GOTBLOCKS length is too short");
2416 v3s16 p = readV3S16(&data[2+1+i*6]);
2417 /*infostream<<"Server: GOTBLOCKS ("
2418 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2419 RemoteClient *client = getClient(peer_id);
2420 client->GotBlock(p);
2423 else if(command == TOSERVER_DELETEDBLOCKS)
2436 u16 count = data[2];
2437 for(u16 i=0; i<count; i++)
2439 if((s16)datasize < 2+1+(i+1)*6)
2440 throw con::InvalidIncomingDataException
2441 ("DELETEDBLOCKS length is too short");
2442 v3s16 p = readV3S16(&data[2+1+i*6]);
2443 /*infostream<<"Server: DELETEDBLOCKS ("
2444 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2445 RemoteClient *client = getClient(peer_id);
2446 client->SetBlockNotSent(p);
2449 else if(command == TOSERVER_CLICK_OBJECT)
2451 infostream<<"Server: CLICK_OBJECT not supported anymore"<<std::endl;
2454 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2456 infostream<<"Server: CLICK_ACTIVEOBJECT not supported anymore"<<std::endl;
2459 else if(command == TOSERVER_GROUND_ACTION)
2461 infostream<<"Server: GROUND_ACTION not supported anymore"<<std::endl;
2465 else if(command == TOSERVER_RELEASE)
2467 infostream<<"Server: RELEASE not supported anymore"<<std::endl;
2470 else if(command == TOSERVER_SIGNTEXT)
2472 infostream<<"Server: SIGNTEXT not supported anymore"
2476 else if(command == TOSERVER_SIGNNODETEXT)
2478 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2486 std::string datastring((char*)&data[2], datasize-2);
2487 std::istringstream is(datastring, std::ios_base::binary);
2490 is.read((char*)buf, 6);
2491 v3s16 p = readV3S16(buf);
2492 is.read((char*)buf, 2);
2493 u16 textlen = readU16(buf);
2495 for(u16 i=0; i<textlen; i++)
2497 is.read((char*)buf, 1);
2498 text += (char)buf[0];
2501 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
2505 meta->setText(text);
2507 actionstream<<player->getName()<<" writes \""<<text<<"\" to sign"
2508 <<" at "<<PP(p)<<std::endl;
2510 v3s16 blockpos = getNodeBlockPos(p);
2511 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
2514 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2518 setBlockNotSent(blockpos);
2520 else if(command == TOSERVER_INVENTORY_ACTION)
2522 /*// Ignore inventory changes if in creative mode
2523 if(g_settings->getBool("creative_mode") == true)
2525 infostream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2529 // Strip command and create a stream
2530 std::string datastring((char*)&data[2], datasize-2);
2531 infostream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2532 std::istringstream is(datastring, std::ios_base::binary);
2534 InventoryAction *a = InventoryAction::deSerialize(is);
2537 infostream<<"TOSERVER_INVENTORY_ACTION: "
2538 <<"InventoryAction::deSerialize() returned NULL"
2544 c.current_player = player;
2547 Handle restrictions and special cases of the move action
2549 if(a->getType() == IACTION_MOVE
2550 && g_settings->getBool("creative_mode") == false)
2552 InventoryList *rlist = player->inventory.getList("craftresult");
2554 InventoryList *clist = player->inventory.getList("craft");
2556 InventoryList *mlist = player->inventory.getList("main");
2559 IMoveAction *ma = (IMoveAction*)a;
2562 Disable moving items into craftresult from elsewhere
2564 if(ma->to_inv == "current_player"
2565 && ma->to_list == "craftresult"
2566 && (ma->from_inv != "current_player"
2567 || ma->from_list != "craftresult"))
2569 infostream<<"Ignoring IMoveAction from "
2570 <<ma->from_inv<<":"<<ma->from_list
2571 <<" to "<<ma->to_inv<<":"<<ma->to_list
2572 <<" because dst is craftresult"
2573 <<" and src isn't craftresult"<<std::endl;
2579 Handle crafting (source is craftresult, which is preview)
2581 if(ma->from_inv == "current_player"
2582 && ma->from_list == "craftresult"
2583 && player->craftresult_is_preview)
2586 If the craftresult is placed on itself, crafting takes
2587 place and result is moved into main list
2589 if(ma->to_inv == "current_player"
2590 && ma->to_list == "craftresult")
2592 // Except if main list doesn't have free slots
2593 if(mlist->getFreeSlots() == 0){
2594 infostream<<"Cannot craft: Main list doesn't have"
2595 <<" free slots"<<std::endl;
2600 player->craftresult_is_preview = false;
2601 clist->decrementMaterials(1);
2603 InventoryItem *item1 = rlist->changeItem(0, NULL);
2604 mlist->addItem(item1);
2606 srp->m_inventory_not_sent = true;
2612 Disable action if there are no free slots in
2615 If the item is placed on an item that is not of the
2616 same kind, the existing item will be first moved to
2617 craftresult and immediately moved to the free slot.
2620 Inventory *inv_to = getInventory(&c, ma->to_inv);
2622 InventoryList *list_to = inv_to->getList(ma->to_list);
2624 if(list_to->getFreeSlots() == 0){
2625 infostream<<"Cannot craft: Destination doesn't have"
2626 <<" free slots"<<std::endl;
2630 }while(0); // Allow break
2635 player->craftresult_is_preview = false;
2636 clist->decrementMaterials(1);
2638 /* Print out action */
2639 InventoryItem *item = rlist->getItem(0);
2640 std::string itemstring = "NULL";
2642 itemstring = item->getItemString();
2643 actionstream<<player->getName()<<" crafts "
2644 <<itemstring<<std::endl;
2647 a->apply(&c, this, m_env);
2657 // Disallow moving items in elsewhere than player's inventory
2658 // if not allowed to build
2659 if((getPlayerPrivs(player) & PRIV_BUILD) == 0
2660 && (ma->from_inv != "current_player"
2661 || ma->to_inv != "current_player"))
2663 infostream<<"Cannot move outside of player's inventory: "
2664 <<"No build privilege"<<std::endl;
2669 // If player is not an admin, check for ownership of src
2670 if(ma->from_inv != "current_player"
2671 && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
2673 Strfnd fn(ma->from_inv);
2674 std::string id0 = fn.next(":");
2675 if(id0 == "nodemeta")
2678 p.X = stoi(fn.next(","));
2679 p.Y = stoi(fn.next(","));
2680 p.Z = stoi(fn.next(","));
2681 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
2682 if(meta->getOwner() != "" &&
2683 meta->getOwner() != player->getName())
2685 infostream<<"Cannot move item: "
2686 "not owner of metadata"
2693 // If player is not an admin, check for ownership of dst
2694 if(ma->to_inv != "current_player"
2695 && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
2697 Strfnd fn(ma->to_inv);
2698 std::string id0 = fn.next(":");
2699 if(id0 == "nodemeta")
2702 p.X = stoi(fn.next(","));
2703 p.Y = stoi(fn.next(","));
2704 p.Z = stoi(fn.next(","));
2705 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
2706 if(meta->getOwner() != "" &&
2707 meta->getOwner() != player->getName())
2709 infostream<<"Cannot move item: "
2710 "not owner of metadata"
2719 Handle restrictions and special cases of the drop action
2721 else if(a->getType() == IACTION_DROP)
2723 IDropAction *da = (IDropAction*)a;
2724 // Disallow dropping items if not allowed to build
2725 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2730 // If player is not an admin, check for ownership
2731 else if (da->from_inv != "current_player"
2732 && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
2734 Strfnd fn(da->from_inv);
2735 std::string id0 = fn.next(":");
2736 if(id0 == "nodemeta")
2739 p.X = stoi(fn.next(","));
2740 p.Y = stoi(fn.next(","));
2741 p.Z = stoi(fn.next(","));
2742 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
2743 if(meta->getOwner() != "" &&
2744 meta->getOwner() != player->getName())
2746 infostream<<"Cannot move item: "
2747 "not owner of metadata"
2757 a->apply(&c, this, m_env);
2761 else if(command == TOSERVER_CHAT_MESSAGE)
2769 std::string datastring((char*)&data[2], datasize-2);
2770 std::istringstream is(datastring, std::ios_base::binary);
2773 is.read((char*)buf, 2);
2774 u16 len = readU16(buf);
2776 std::wstring message;
2777 for(u16 i=0; i<len; i++)
2779 is.read((char*)buf, 2);
2780 message += (wchar_t)readU16(buf);
2783 // Get player name of this client
2784 std::wstring name = narrow_to_wide(player->getName());
2787 bool ate = scriptapi_on_chat_message(m_lua, player->getName(),
2788 wide_to_narrow(message));
2789 // If script ate the message, don't proceed
2793 // Line to send to players
2795 // Whether to send to the player that sent the line
2796 bool send_to_sender = false;
2797 // Whether to send to other players
2798 bool send_to_others = false;
2800 // Local player gets all privileges regardless of
2801 // what's set on their account.
2802 u64 privs = getPlayerPrivs(player);
2805 if(message[0] == L'/')
2807 size_t strip_size = 1;
2808 if (message[1] == L'#') // support old-style commans
2810 message = message.substr(strip_size);
2812 WStrfnd f1(message);
2813 f1.next(L" "); // Skip over /#whatever
2814 std::wstring paramstring = f1.next(L"");
2816 ServerCommandContext *ctx = new ServerCommandContext(
2817 str_split(message, L' '),
2824 std::wstring reply(processServerCommand(ctx));
2825 send_to_sender = ctx->flags & SEND_TO_SENDER;
2826 send_to_others = ctx->flags & SEND_TO_OTHERS;
2828 if (ctx->flags & SEND_NO_PREFIX)
2831 line += L"Server: " + reply;
2838 if(privs & PRIV_SHOUT)
2844 send_to_others = true;
2848 line += L"Server: You are not allowed to shout";
2849 send_to_sender = true;
2856 actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2859 Send the message to clients
2861 for(core::map<u16, RemoteClient*>::Iterator
2862 i = m_clients.getIterator();
2863 i.atEnd() == false; i++)
2865 // Get client and check that it is valid
2866 RemoteClient *client = i.getNode()->getValue();
2867 assert(client->peer_id == i.getNode()->getKey());
2868 if(client->serialization_version == SER_FMT_VER_INVALID)
2872 bool sender_selected = (peer_id == client->peer_id);
2873 if(sender_selected == true && send_to_sender == false)
2875 if(sender_selected == false && send_to_others == false)
2878 SendChatMessage(client->peer_id, line);
2882 else if(command == TOSERVER_DAMAGE)
2884 std::string datastring((char*)&data[2], datasize-2);
2885 std::istringstream is(datastring, std::ios_base::binary);
2886 u8 damage = readU8(is);
2888 if(g_settings->getBool("enable_damage"))
2890 actionstream<<player->getName()<<" damaged by "
2891 <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
2894 HandlePlayerHP(player, damage);
2898 SendPlayerHP(player);
2901 else if(command == TOSERVER_PASSWORD)
2904 [0] u16 TOSERVER_PASSWORD
2905 [2] u8[28] old password
2906 [30] u8[28] new password
2909 if(datasize != 2+PASSWORD_SIZE*2)
2911 /*char password[PASSWORD_SIZE];
2912 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2913 password[i] = data[2+i];
2914 password[PASSWORD_SIZE-1] = 0;*/
2916 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2924 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2926 char c = data[2+PASSWORD_SIZE+i];
2932 infostream<<"Server: Client requests a password change from "
2933 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
2935 std::string playername = player->getName();
2937 if(m_authmanager.exists(playername) == false)
2939 infostream<<"Server: playername not found in authmanager"<<std::endl;
2940 // Wrong old password supplied!!
2941 SendChatMessage(peer_id, L"playername not found in authmanager");
2945 std::string checkpwd = m_authmanager.getPassword(playername);
2947 if(oldpwd != checkpwd)
2949 infostream<<"Server: invalid old password"<<std::endl;
2950 // Wrong old password supplied!!
2951 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
2955 actionstream<<player->getName()<<" changes password"<<std::endl;
2957 m_authmanager.setPassword(playername, newpwd);
2959 infostream<<"Server: password change successful for "<<playername
2961 SendChatMessage(peer_id, L"Password change successful");
2963 else if(command == TOSERVER_PLAYERITEM)
2968 u16 item = readU16(&data[2]);
2969 player->wieldItem(item);
2970 SendWieldedItem(player);
2972 else if(command == TOSERVER_RESPAWN)
2977 RespawnPlayer(player);
2979 actionstream<<player->getName()<<" respawns at "
2980 <<PP(player->getPosition()/BS)<<std::endl;
2982 else if(command == TOSERVER_INTERACT)
2984 std::string datastring((char*)&data[2], datasize-2);
2985 std::istringstream is(datastring, std::ios_base::binary);
2991 [5] u32 length of the next item
2992 [9] serialized PointedThing
2994 0: start digging (from undersurface) or use
2995 1: stop digging (all parameters ignored)
2996 2: digging completed
2997 3: place block or item (to abovesurface)
3000 u8 action = readU8(is);
3001 u16 item_i = readU16(is);
3002 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
3003 PointedThing pointed;
3004 pointed.deSerialize(tmp_is);
3006 infostream<<"TOSERVER_INTERACT: action="<<(int)action<<", item="<<item_i<<", pointed="<<pointed.dump()<<std::endl;
3008 v3f player_pos = srp->m_last_good_position;
3010 // Update wielded item
3011 srp->wieldItem(item_i);
3013 // Get pointed to node (undefined if not POINTEDTYPE_NODE)
3014 v3s16 p_under = pointed.node_undersurface;
3015 v3s16 p_above = pointed.node_abovesurface;
3017 // Get pointed to object (NULL if not POINTEDTYPE_OBJECT)
3018 ServerActiveObject *pointed_object = NULL;
3019 if(pointed.type == POINTEDTHING_OBJECT)
3021 pointed_object = m_env->getActiveObject(pointed.object_id);
3022 if(pointed_object == NULL)
3024 infostream<<"TOSERVER_INTERACT: "
3025 "pointed object is NULL"<<std::endl;
3032 Check that target is reasonably close
3033 (only when digging or placing things)
3035 if(action == 0 || action == 2 || action == 3)
3037 v3f pointed_pos = player_pos;
3038 if(pointed.type == POINTEDTHING_NODE)
3040 pointed_pos = intToFloat(p_under, BS);
3042 else if(pointed.type == POINTEDTHING_OBJECT)
3044 pointed_pos = pointed_object->getBasePosition();
3047 float d = player_pos.getDistanceFrom(pointed_pos);
3048 float max_d = BS * 10; // Just some large enough value
3050 actionstream<<"Player "<<player->getName()
3051 <<" tried to access "<<pointed.dump()
3053 <<"d="<<d<<", max_d="<<max_d
3054 <<". ignoring."<<std::endl;
3055 // Re-send block to revert change on client-side
3056 RemoteClient *client = getClient(peer_id);
3057 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos, BS));
3058 client->SetBlockNotSent(blockpos);
3065 Make sure the player is allowed to do it
3067 bool build_priv = (getPlayerPrivs(player) & PRIV_BUILD) != 0;
3070 infostream<<"Ignoring interaction from player "<<player->getName()
3071 <<" because privileges are "<<getPlayerPrivs(player)
3073 // NOTE: no return; here, fall through
3077 0: start digging or punch object
3081 if(pointed.type == POINTEDTHING_NODE)
3084 NOTE: This can be used in the future to check if
3085 somebody is cheating, by checking the timing.
3087 bool cannot_punch_node = !build_priv;
3089 MapNode n(CONTENT_IGNORE);
3093 n = m_env->getMap().getNode(p_under);
3095 catch(InvalidPositionException &e)
3097 infostream<<"Server: Not punching: Node not found."
3098 <<" Adding block to emerge queue."
3100 m_emerge_queue.addBlock(peer_id,
3101 getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
3102 cannot_punch_node = true;
3105 if(cannot_punch_node)
3111 scriptapi_environment_on_punchnode(m_lua, p_under, n, srp);
3113 else if(pointed.type == POINTEDTHING_OBJECT)
3118 // Skip if object has been removed
3119 if(pointed_object->m_removed)
3122 actionstream<<player->getName()<<" punches object "
3123 <<pointed.object_id<<std::endl;
3126 pointed_object->punch(srp);
3134 else if(action == 1)
3139 2: Digging completed
3141 else if(action == 2)
3143 // Only complete digging of nodes
3144 if(pointed.type != POINTEDTHING_NODE)
3147 // Mandatory parameter; actually used for nothing
3148 core::map<v3s16, MapBlock*> modified_blocks;
3150 content_t material = CONTENT_IGNORE;
3151 u8 mineral = MINERAL_NONE;
3153 bool cannot_remove_node = !build_priv;
3155 MapNode n(CONTENT_IGNORE);
3158 n = m_env->getMap().getNode(p_under);
3160 mineral = n.getMineral(m_nodedef);
3161 // Get material at position
3162 material = n.getContent();
3163 // If not yet cancelled
3164 if(cannot_remove_node == false)
3166 // If it's not diggable, do nothing
3167 if(m_nodedef->get(material).diggable == false)
3169 infostream<<"Server: Not finishing digging: "
3170 <<"Node not diggable"
3172 cannot_remove_node = true;
3175 // If not yet cancelled
3176 if(cannot_remove_node == false)
3178 // Get node metadata
3179 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p_under);
3180 if(meta && meta->nodeRemovalDisabled() == true)
3182 infostream<<"Server: Not finishing digging: "
3183 <<"Node metadata disables removal"
3185 cannot_remove_node = true;
3189 catch(InvalidPositionException &e)
3191 infostream<<"Server: Not finishing digging: Node not found."
3192 <<" Adding block to emerge queue."
3194 m_emerge_queue.addBlock(peer_id,
3195 getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
3196 cannot_remove_node = true;
3200 If node can't be removed, set block to be re-sent to
3203 if(cannot_remove_node)
3205 infostream<<"Server: Not finishing digging."<<std::endl;
3207 // Client probably has wrong data.
3208 // Set block not sent, so that client will get
3210 infostream<<"Client "<<peer_id<<" tried to dig "
3211 <<"node; but node cannot be removed."
3212 <<" setting MapBlock not sent."<<std::endl;
3213 RemoteClient *client = getClient(peer_id);
3214 v3s16 blockpos = getNodeBlockPos(p_under);
3215 client->SetBlockNotSent(blockpos);
3220 actionstream<<player->getName()<<" digs "<<PP(p_under)
3221 <<", gets material "<<(int)material<<", mineral "
3222 <<(int)mineral<<std::endl;
3225 Send the removal to all close-by players.
3226 - If other player is close, send REMOVENODE
3227 - Otherwise set blocks not sent
3229 core::list<u16> far_players;
3230 sendRemoveNode(p_under, peer_id, &far_players, 30);
3233 Update and send inventory
3236 if(g_settings->getBool("creative_mode") == false)
3241 InventoryList *mlist = player->inventory.getList("main");
3244 InventoryItem *item = mlist->getItem(item_i);
3245 if(item && (std::string)item->getName() == "ToolItem")
3247 ToolItem *titem = (ToolItem*)item;
3248 std::string toolname = titem->getToolName();
3250 // Get digging properties for material and tool
3251 ToolDiggingProperties tp =
3252 m_toolmgr->getDiggingProperties(toolname);
3253 DiggingProperties prop =
3254 getDiggingProperties(material, &tp, m_nodedef);
3256 if(prop.diggable == false)
3258 infostream<<"Server: WARNING: Player digged"
3259 <<" with impossible material + tool"
3260 <<" combination"<<std::endl;
3263 bool weared_out = titem->addWear(prop.wear);
3267 mlist->deleteItem(item_i);
3270 srp->m_inventory_not_sent = true;
3275 Add dug item to inventory
3278 InventoryItem *item = NULL;
3280 if(mineral != MINERAL_NONE)
3281 item = getDiggedMineralItem(mineral, this);
3286 const std::string &dug_s = m_nodedef->get(material).dug_item;
3289 std::istringstream is(dug_s, std::ios::binary);
3290 item = InventoryItem::deSerialize(is, this);
3296 // Add a item to inventory
3297 player->inventory.addItem("main", item);
3298 srp->m_inventory_not_sent = true;
3303 if(mineral != MINERAL_NONE)
3304 item = getDiggedMineralItem(mineral, this);
3309 const std::string &extra_dug_s = m_nodedef->get(material).extra_dug_item;
3310 s32 extra_rarity = m_nodedef->get(material).extra_dug_item_rarity;
3311 if(extra_dug_s != "" && extra_rarity != 0
3312 && myrand() % extra_rarity == 0)
3314 std::istringstream is(extra_dug_s, std::ios::binary);
3315 item = InventoryItem::deSerialize(is, this);
3321 // Add a item to inventory
3322 player->inventory.addItem("main", item);
3323 srp->m_inventory_not_sent = true;
3329 (this takes some time so it is done after the quick stuff)
3332 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
3334 m_env->getMap().removeNodeAndUpdate(p_under, modified_blocks);
3337 Set blocks not sent to far players
3339 for(core::list<u16>::Iterator
3340 i = far_players.begin();
3341 i != far_players.end(); i++)
3344 RemoteClient *client = getClient(peer_id);
3347 client->SetBlocksNotSent(modified_blocks);
3353 scriptapi_environment_on_dignode(m_lua, p_under, n, srp);
3357 3: place block or right-click object
3359 else if(action == 3)
3361 if(pointed.type == POINTEDTHING_NODE)
3363 InventoryList *ilist = player->inventory.getList("main");
3368 InventoryItem *item = ilist->getItem(item_i);
3370 // If there is no item, it is not possible to add it anywhere
3375 Handle material items
3377 if(std::string("MaterialItem") == item->getName())
3379 bool cannot_place_node = !build_priv;
3382 // Don't add a node if this is not a free space
3383 MapNode n2 = m_env->getMap().getNode(p_above);
3384 if(m_nodedef->get(n2).buildable_to == false)
3386 infostream<<"Client "<<peer_id<<" tried to place"
3387 <<" node in invalid position."<<std::endl;
3388 cannot_place_node = true;
3391 catch(InvalidPositionException &e)
3393 infostream<<"Server: Ignoring ADDNODE: Node not found"
3394 <<" Adding block to emerge queue."
3396 m_emerge_queue.addBlock(peer_id,
3397 getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
3398 cannot_place_node = true;
3401 if(cannot_place_node)
3403 // Client probably has wrong data.
3404 // Set block not sent, so that client will get
3406 RemoteClient *client = getClient(peer_id);
3407 v3s16 blockpos = getNodeBlockPos(p_above);
3408 client->SetBlockNotSent(blockpos);
3412 // Reset build time counter
3413 getClient(peer_id)->m_time_from_building = 0.0;
3416 MaterialItem *mitem = (MaterialItem*)item;
3418 n.setContent(mitem->getMaterial());
3420 actionstream<<player->getName()<<" places material "
3421 <<(int)mitem->getMaterial()
3422 <<" at "<<PP(p_under)<<std::endl;
3424 // Calculate direction for wall mounted stuff
3425 if(m_nodedef->get(n).wall_mounted)
3426 n.param2 = packDir(p_under - p_above);
3428 // Calculate the direction for furnaces and chests and stuff
3429 if(m_nodedef->get(n).param_type == CPT_FACEDIR_SIMPLE)
3431 v3f playerpos = player->getPosition();
3432 v3f blockpos = intToFloat(p_above, BS) - playerpos;
3433 blockpos = blockpos.normalize();
3435 if (fabs(blockpos.X) > fabs(blockpos.Z)) {
3449 Send to all close-by players
3451 core::list<u16> far_players;
3452 sendAddNode(p_above, n, 0, &far_players, 30);
3457 InventoryList *ilist = player->inventory.getList("main");
3458 if(g_settings->getBool("creative_mode") == false && ilist)
3460 // Remove from inventory and send inventory
3461 if(mitem->getCount() <= 1)
3462 ilist->deleteItem(item_i);
3465 srp->m_inventory_not_sent = true;
3471 This takes some time so it is done after the quick stuff
3473 core::map<v3s16, MapBlock*> modified_blocks;
3475 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
3477 std::string p_name = std::string(player->getName());
3478 m_env->getMap().addNodeAndUpdate(p_above, n, modified_blocks, p_name);
3481 Set blocks not sent to far players
3483 for(core::list<u16>::Iterator
3484 i = far_players.begin();
3485 i != far_players.end(); i++)
3488 RemoteClient *client = getClient(peer_id);
3491 client->SetBlocksNotSent(modified_blocks);
3497 scriptapi_environment_on_placenode(m_lua, p_above, n, srp);
3500 Calculate special events
3503 /*if(n.d == LEGN(m_nodedef, "CONTENT_MESE"))
3506 for(s16 z=-1; z<=1; z++)
3507 for(s16 y=-1; y<=1; y++)
3508 for(s16 x=-1; x<=1; x++)
3515 Place other item (not a block)
3521 infostream<<"Not allowing player to place item: "
3522 "no build privileges"<<std::endl;
3526 // Calculate a position for it
3527 v3f pos = player_pos;
3528 if(pointed.type == POINTEDTHING_NOTHING)
3530 infostream<<"Not allowing player to place item: "
3531 "pointing to nothing"<<std::endl;
3534 else if(pointed.type == POINTEDTHING_NODE)
3536 pos = intToFloat(p_above, BS);
3538 else if(pointed.type == POINTEDTHING_OBJECT)
3540 pos = pointed_object->getBasePosition();
3543 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
3544 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
3548 //pos.Y -= BS*0.25; // let it drop a bit
3551 Check that the block is loaded so that the item
3552 can properly be added to the static list too
3554 v3s16 blockpos = getNodeBlockPos(floatToInt(pos, BS));
3555 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3558 infostream<<"Error while placing item: "
3559 "block not found"<<std::endl;
3563 actionstream<<player->getName()<<" places "<<item->getName()
3564 <<" at "<<PP(pos)<<std::endl;
3569 bool remove = item->dropOrPlace(m_env, srp, pos, true, -1);
3570 if(remove && g_settings->getBool("creative_mode") == false)
3572 InventoryList *ilist = player->inventory.getList("main");
3574 // Remove from inventory and send inventory
3575 ilist->deleteItem(item_i);
3576 srp->m_inventory_not_sent = true;
3581 else if(pointed.type == POINTEDTHING_OBJECT)
3583 // Right click object
3588 // Skip if object has been removed
3589 if(pointed_object->m_removed)
3592 actionstream<<player->getName()<<" right-clicks object "
3593 <<pointed.object_id<<std::endl;
3596 pointed_object->rightClick(srp);
3604 else if(action == 4)
3606 InventoryList *ilist = player->inventory.getList("main");
3611 InventoryItem *item = ilist->getItem(item_i);
3613 // If there is no item, it is not possible to add it anywhere
3617 // Requires build privs
3620 infostream<<"Not allowing player to use item: "
3621 "no build privileges"<<std::endl;
3625 actionstream<<player->getName()<<" uses "<<item->getName()
3626 <<", pointing at "<<pointed.dump()<<std::endl;
3628 bool remove = item->use(m_env, srp, pointed);
3630 if(remove && g_settings->getBool("creative_mode") == false)
3632 InventoryList *ilist = player->inventory.getList("main");
3634 // Remove from inventory and send inventory
3635 ilist->deleteItem(item_i);
3636 srp->m_inventory_not_sent = true;
3643 Catch invalid actions
3647 infostream<<"WARNING: Server: Invalid action "
3648 <<action<<std::endl;
3651 // Complete add_to_inventory_later
3652 srp->completeAddToInventoryLater(item_i);
3656 infostream<<"Server::ProcessData(): Ignoring "
3657 "unknown command "<<command<<std::endl;
3661 catch(SendFailedException &e)
3663 errorstream<<"Server::ProcessData(): SendFailedException: "
3669 void Server::onMapEditEvent(MapEditEvent *event)
3671 //infostream<<"Server::onMapEditEvent()"<<std::endl;
3672 if(m_ignore_map_edit_events)
3674 MapEditEvent *e = event->clone();
3675 m_unsent_map_edit_queue.push_back(e);
3678 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3680 if(id == "current_player")
3682 assert(c->current_player);
3683 return &(c->current_player->inventory);
3687 std::string id0 = fn.next(":");
3689 if(id0 == "nodemeta")
3692 p.X = stoi(fn.next(","));
3693 p.Y = stoi(fn.next(","));
3694 p.Z = stoi(fn.next(","));
3695 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3697 return meta->getInventory();
3698 infostream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3699 <<"no metadata found"<<std::endl;
3703 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3706 void Server::inventoryModified(InventoryContext *c, std::string id)
3708 if(id == "current_player")
3710 assert(c->current_player);
3711 ServerRemotePlayer *srp =
3712 static_cast<ServerRemotePlayer*>(c->current_player);
3713 srp->m_inventory_not_sent = true;
3718 std::string id0 = fn.next(":");
3720 if(id0 == "nodemeta")
3723 p.X = stoi(fn.next(","));
3724 p.Y = stoi(fn.next(","));
3725 p.Z = stoi(fn.next(","));
3726 v3s16 blockpos = getNodeBlockPos(p);
3728 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3730 meta->inventoryModified();
3732 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3734 block->raiseModified(MOD_STATE_WRITE_NEEDED);
3736 setBlockNotSent(blockpos);
3741 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3744 core::list<PlayerInfo> Server::getPlayerInfo()
3746 DSTACK(__FUNCTION_NAME);
3747 JMutexAutoLock envlock(m_env_mutex);
3748 JMutexAutoLock conlock(m_con_mutex);
3750 core::list<PlayerInfo> list;
3752 core::list<Player*> players = m_env->getPlayers();
3754 core::list<Player*>::Iterator i;
3755 for(i = players.begin();
3756 i != players.end(); i++)
3760 Player *player = *i;
3763 // Copy info from connection to info struct
3764 info.id = player->peer_id;
3765 info.address = m_con.GetPeerAddress(player->peer_id);
3766 info.avg_rtt = m_con.GetPeerAvgRTT(player->peer_id);
3768 catch(con::PeerNotFoundException &e)
3770 // Set dummy peer info
3772 info.address = Address(0,0,0,0,0);
3776 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3777 info.position = player->getPosition();
3779 list.push_back(info);
3786 void Server::peerAdded(con::Peer *peer)
3788 DSTACK(__FUNCTION_NAME);
3789 infostream<<"Server::peerAdded(): peer->id="
3790 <<peer->id<<std::endl;
3793 c.type = PEER_ADDED;
3794 c.peer_id = peer->id;
3796 m_peer_change_queue.push_back(c);
3799 void Server::deletingPeer(con::Peer *peer, bool timeout)
3801 DSTACK(__FUNCTION_NAME);
3802 infostream<<"Server::deletingPeer(): peer->id="
3803 <<peer->id<<", timeout="<<timeout<<std::endl;
3806 c.type = PEER_REMOVED;
3807 c.peer_id = peer->id;
3808 c.timeout = timeout;
3809 m_peer_change_queue.push_back(c);
3816 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3818 DSTACK(__FUNCTION_NAME);
3819 std::ostringstream os(std::ios_base::binary);
3821 writeU16(os, TOCLIENT_HP);
3825 std::string s = os.str();
3826 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3828 con.Send(peer_id, 0, data, true);
3831 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3832 const std::wstring &reason)
3834 DSTACK(__FUNCTION_NAME);
3835 std::ostringstream os(std::ios_base::binary);
3837 writeU16(os, TOCLIENT_ACCESS_DENIED);
3838 os<<serializeWideString(reason);
3841 std::string s = os.str();
3842 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3844 con.Send(peer_id, 0, data, true);
3847 void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
3848 bool set_camera_point_target, v3f camera_point_target)
3850 DSTACK(__FUNCTION_NAME);
3851 std::ostringstream os(std::ios_base::binary);
3853 writeU16(os, TOCLIENT_DEATHSCREEN);
3854 writeU8(os, set_camera_point_target);
3855 writeV3F1000(os, camera_point_target);
3858 std::string s = os.str();
3859 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3861 con.Send(peer_id, 0, data, true);
3864 void Server::SendToolDef(con::Connection &con, u16 peer_id,
3865 IToolDefManager *tooldef)
3867 DSTACK(__FUNCTION_NAME);
3868 std::ostringstream os(std::ios_base::binary);
3872 u32 length of the next item
3873 serialized ToolDefManager
3875 writeU16(os, TOCLIENT_TOOLDEF);
3876 std::ostringstream tmp_os(std::ios::binary);
3877 tooldef->serialize(tmp_os);
3878 os<<serializeLongString(tmp_os.str());
3881 std::string s = os.str();
3882 infostream<<"Server::SendToolDef(): Sending tool definitions: size="
3883 <<s.size()<<std::endl;
3884 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3886 con.Send(peer_id, 0, data, true);
3889 void Server::SendNodeDef(con::Connection &con, u16 peer_id,
3890 INodeDefManager *nodedef)
3892 DSTACK(__FUNCTION_NAME);
3893 std::ostringstream os(std::ios_base::binary);
3897 u32 length of the next item
3898 serialized NodeDefManager
3900 writeU16(os, TOCLIENT_NODEDEF);
3901 std::ostringstream tmp_os(std::ios::binary);
3902 nodedef->serialize(tmp_os);
3903 os<<serializeLongString(tmp_os.str());
3906 std::string s = os.str();
3907 infostream<<"Server::SendNodeDef(): Sending node definitions: size="
3908 <<s.size()<<std::endl;
3909 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3911 con.Send(peer_id, 0, data, true);
3914 void Server::SendCraftItemDef(con::Connection &con, u16 peer_id,
3915 ICraftItemDefManager *craftitemdef)
3917 DSTACK(__FUNCTION_NAME);
3918 std::ostringstream os(std::ios_base::binary);
3922 u32 length of the next item
3923 serialized CraftItemDefManager
3925 writeU16(os, TOCLIENT_CRAFTITEMDEF);
3926 std::ostringstream tmp_os(std::ios::binary);
3927 craftitemdef->serialize(tmp_os);
3928 os<<serializeLongString(tmp_os.str());
3931 std::string s = os.str();
3932 infostream<<"Server::SendCraftItemDef(): Sending craft item definitions: size="
3933 <<s.size()<<std::endl;
3934 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3936 con.Send(peer_id, 0, data, true);
3940 Non-static send methods
3943 void Server::SendObjectData(float dtime)
3945 DSTACK(__FUNCTION_NAME);
3947 core::map<v3s16, bool> stepped_blocks;
3949 for(core::map<u16, RemoteClient*>::Iterator
3950 i = m_clients.getIterator();
3951 i.atEnd() == false; i++)
3953 u16 peer_id = i.getNode()->getKey();
3954 RemoteClient *client = i.getNode()->getValue();
3955 assert(client->peer_id == peer_id);
3957 if(client->serialization_version == SER_FMT_VER_INVALID)
3960 client->SendObjectData(this, dtime, stepped_blocks);
3964 void Server::SendPlayerInfos()
3966 DSTACK(__FUNCTION_NAME);
3968 //JMutexAutoLock envlock(m_env_mutex);
3970 // Get connected players
3971 core::list<Player*> players = m_env->getPlayers(true);
3973 u32 player_count = players.getSize();
3974 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3976 SharedBuffer<u8> data(datasize);
3977 writeU16(&data[0], TOCLIENT_PLAYERINFO);
3980 core::list<Player*>::Iterator i;
3981 for(i = players.begin();
3982 i != players.end(); i++)
3984 Player *player = *i;
3986 /*infostream<<"Server sending player info for player with "
3987 "peer_id="<<player->peer_id<<std::endl;*/
3989 writeU16(&data[start], player->peer_id);
3990 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3991 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3992 start += 2+PLAYERNAME_SIZE;
3995 //JMutexAutoLock conlock(m_con_mutex);
3998 m_con.SendToAll(0, data, true);
4001 void Server::SendInventory(u16 peer_id)
4003 DSTACK(__FUNCTION_NAME);
4005 ServerRemotePlayer* player =
4006 static_cast<ServerRemotePlayer*>(m_env->getPlayer(peer_id));
4009 player->m_inventory_not_sent = false;
4015 std::ostringstream os;
4016 //os.imbue(std::locale("C"));
4018 player->inventory.serialize(os);
4020 std::string s = os.str();
4022 SharedBuffer<u8> data(s.size()+2);
4023 writeU16(&data[0], TOCLIENT_INVENTORY);
4024 memcpy(&data[2], s.c_str(), s.size());
4027 m_con.Send(peer_id, 0, data, true);
4030 std::string getWieldedItemString(const Player *player)
4032 const InventoryItem *item = player->getWieldItem();
4034 return std::string("");
4035 std::ostringstream os(std::ios_base::binary);
4036 item->serialize(os);
4040 void Server::SendWieldedItem(const Player* player)
4042 DSTACK(__FUNCTION_NAME);
4046 std::ostringstream os(std::ios_base::binary);
4048 writeU16(os, TOCLIENT_PLAYERITEM);
4050 writeU16(os, player->peer_id);
4051 os<<serializeString(getWieldedItemString(player));
4054 std::string s = os.str();
4055 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4057 m_con.SendToAll(0, data, true);
4060 void Server::SendPlayerItems()
4062 DSTACK(__FUNCTION_NAME);
4064 std::ostringstream os(std::ios_base::binary);
4065 core::list<Player *> players = m_env->getPlayers(true);
4067 writeU16(os, TOCLIENT_PLAYERITEM);
4068 writeU16(os, players.size());
4069 core::list<Player *>::Iterator i;
4070 for(i = players.begin(); i != players.end(); ++i)
4073 writeU16(os, p->peer_id);
4074 os<<serializeString(getWieldedItemString(p));
4078 std::string s = os.str();
4079 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4081 m_con.SendToAll(0, data, true);
4084 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
4086 DSTACK(__FUNCTION_NAME);
4088 std::ostringstream os(std::ios_base::binary);
4092 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
4093 os.write((char*)buf, 2);
4096 writeU16(buf, message.size());
4097 os.write((char*)buf, 2);
4100 for(u32 i=0; i<message.size(); i++)
4104 os.write((char*)buf, 2);
4108 std::string s = os.str();
4109 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4111 m_con.Send(peer_id, 0, data, true);
4114 void Server::BroadcastChatMessage(const std::wstring &message)
4116 for(core::map<u16, RemoteClient*>::Iterator
4117 i = m_clients.getIterator();
4118 i.atEnd() == false; i++)
4120 // Get client and check that it is valid
4121 RemoteClient *client = i.getNode()->getValue();
4122 assert(client->peer_id == i.getNode()->getKey());
4123 if(client->serialization_version == SER_FMT_VER_INVALID)
4126 SendChatMessage(client->peer_id, message);
4130 void Server::SendPlayerHP(Player *player)
4132 SendHP(m_con, player->peer_id, player->hp);
4135 void Server::SendMovePlayer(Player *player)
4137 DSTACK(__FUNCTION_NAME);
4138 std::ostringstream os(std::ios_base::binary);
4140 writeU16(os, TOCLIENT_MOVE_PLAYER);
4141 writeV3F1000(os, player->getPosition());
4142 writeF1000(os, player->getPitch());
4143 writeF1000(os, player->getYaw());
4146 v3f pos = player->getPosition();
4147 f32 pitch = player->getPitch();
4148 f32 yaw = player->getYaw();
4149 infostream<<"Server sending TOCLIENT_MOVE_PLAYER"
4150 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
4157 std::string s = os.str();
4158 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4160 m_con.Send(player->peer_id, 0, data, true);
4163 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
4164 core::list<u16> *far_players, float far_d_nodes)
4166 float maxd = far_d_nodes*BS;
4167 v3f p_f = intToFloat(p, BS);
4171 SharedBuffer<u8> reply(replysize);
4172 writeU16(&reply[0], TOCLIENT_REMOVENODE);
4173 writeS16(&reply[2], p.X);
4174 writeS16(&reply[4], p.Y);
4175 writeS16(&reply[6], p.Z);
4177 for(core::map<u16, RemoteClient*>::Iterator
4178 i = m_clients.getIterator();
4179 i.atEnd() == false; i++)
4181 // Get client and check that it is valid
4182 RemoteClient *client = i.getNode()->getValue();
4183 assert(client->peer_id == i.getNode()->getKey());
4184 if(client->serialization_version == SER_FMT_VER_INVALID)
4187 // Don't send if it's the same one
4188 if(client->peer_id == ignore_id)
4194 Player *player = m_env->getPlayer(client->peer_id);
4197 // If player is far away, only set modified blocks not sent
4198 v3f player_pos = player->getPosition();
4199 if(player_pos.getDistanceFrom(p_f) > maxd)
4201 far_players->push_back(client->peer_id);
4208 m_con.Send(client->peer_id, 0, reply, true);
4212 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
4213 core::list<u16> *far_players, float far_d_nodes)
4215 float maxd = far_d_nodes*BS;
4216 v3f p_f = intToFloat(p, BS);
4218 for(core::map<u16, RemoteClient*>::Iterator
4219 i = m_clients.getIterator();
4220 i.atEnd() == false; i++)
4222 // Get client and check that it is valid
4223 RemoteClient *client = i.getNode()->getValue();
4224 assert(client->peer_id == i.getNode()->getKey());
4225 if(client->serialization_version == SER_FMT_VER_INVALID)
4228 // Don't send if it's the same one
4229 if(client->peer_id == ignore_id)
4235 Player *player = m_env->getPlayer(client->peer_id);
4238 // If player is far away, only set modified blocks not sent
4239 v3f player_pos = player->getPosition();
4240 if(player_pos.getDistanceFrom(p_f) > maxd)
4242 far_players->push_back(client->peer_id);
4249 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
4250 SharedBuffer<u8> reply(replysize);
4251 writeU16(&reply[0], TOCLIENT_ADDNODE);
4252 writeS16(&reply[2], p.X);
4253 writeS16(&reply[4], p.Y);
4254 writeS16(&reply[6], p.Z);
4255 n.serialize(&reply[8], client->serialization_version);
4258 m_con.Send(client->peer_id, 0, reply, true);
4262 void Server::setBlockNotSent(v3s16 p)
4264 for(core::map<u16, RemoteClient*>::Iterator
4265 i = m_clients.getIterator();
4266 i.atEnd()==false; i++)
4268 RemoteClient *client = i.getNode()->getValue();
4269 client->SetBlockNotSent(p);
4273 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
4275 DSTACK(__FUNCTION_NAME);
4277 v3s16 p = block->getPos();
4281 bool completely_air = true;
4282 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4283 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4284 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4286 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
4288 completely_air = false;
4289 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
4294 infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
4296 infostream<<"[completely air] ";
4297 infostream<<std::endl;
4301 Create a packet with the block in the right format
4304 std::ostringstream os(std::ios_base::binary);
4305 block->serialize(os, ver);
4306 std::string s = os.str();
4307 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
4309 u32 replysize = 8 + blockdata.getSize();
4310 SharedBuffer<u8> reply(replysize);
4311 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
4312 writeS16(&reply[2], p.X);
4313 writeS16(&reply[4], p.Y);
4314 writeS16(&reply[6], p.Z);
4315 memcpy(&reply[8], *blockdata, blockdata.getSize());
4317 /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4318 <<": \tpacket size: "<<replysize<<std::endl;*/
4323 m_con.Send(peer_id, 1, reply, true);
4326 void Server::SendBlocks(float dtime)
4328 DSTACK(__FUNCTION_NAME);
4330 JMutexAutoLock envlock(m_env_mutex);
4331 JMutexAutoLock conlock(m_con_mutex);
4333 //TimeTaker timer("Server::SendBlocks");
4335 core::array<PrioritySortedBlockTransfer> queue;
4337 s32 total_sending = 0;
4340 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
4342 for(core::map<u16, RemoteClient*>::Iterator
4343 i = m_clients.getIterator();
4344 i.atEnd() == false; i++)
4346 RemoteClient *client = i.getNode()->getValue();
4347 assert(client->peer_id == i.getNode()->getKey());
4349 total_sending += client->SendingCount();
4351 if(client->serialization_version == SER_FMT_VER_INVALID)
4354 client->GetNextBlocks(this, dtime, queue);
4359 // Lowest priority number comes first.
4360 // Lowest is most important.
4363 for(u32 i=0; i<queue.size(); i++)
4365 //TODO: Calculate limit dynamically
4366 if(total_sending >= g_settings->getS32
4367 ("max_simultaneous_block_sends_server_total"))
4370 PrioritySortedBlockTransfer q = queue[i];
4372 MapBlock *block = NULL;
4375 block = m_env->getMap().getBlockNoCreate(q.pos);
4377 catch(InvalidPositionException &e)
4382 RemoteClient *client = getClient(q.peer_id);
4384 SendBlockNoLock(q.peer_id, block, client->serialization_version);
4386 client->SentBlock(q.pos);
4392 struct SendableTexture
4398 SendableTexture(const std::string &name_="", const std::string path_="",
4399 const std::string &data_=""):
4406 void Server::SendTextures(u16 peer_id)
4408 DSTACK(__FUNCTION_NAME);
4410 infostream<<"Server::SendTextures(): Sending textures to client"<<std::endl;
4414 // Put 5kB in one bunch (this is not accurate)
4415 u32 bytes_per_bunch = 5000;
4417 core::array< core::list<SendableTexture> > texture_bunches;
4418 texture_bunches.push_back(core::list<SendableTexture>());
4420 u32 texture_size_bunch_total = 0;
4421 core::list<ModSpec> mods = getMods(m_modspaths);
4422 for(core::list<ModSpec>::Iterator i = mods.begin();
4423 i != mods.end(); i++){
4425 std::string texturepath = mod.path + DIR_DELIM + "textures";
4426 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(texturepath);
4427 for(u32 j=0; j<dirlist.size(); j++){
4428 if(dirlist[j].dir) // Ignode dirs
4430 std::string tname = dirlist[j].name;
4431 std::string tpath = texturepath + DIR_DELIM + tname;
4433 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
4434 if(fis.good() == false){
4435 errorstream<<"Server::SendTextures(): Could not open \""
4436 <<tname<<"\" for reading"<<std::endl;
4439 std::ostringstream tmp_os(std::ios_base::binary);
4443 fis.read(buf, 1024);
4444 std::streamsize len = fis.gcount();
4445 tmp_os.write(buf, len);
4446 texture_size_bunch_total += len;
4455 errorstream<<"Server::SendTextures(): Failed to read \""
4456 <<tname<<"\""<<std::endl;
4459 /*infostream<<"Server::SendTextures(): Loaded \""
4460 <<tname<<"\""<<std::endl;*/
4462 texture_bunches[texture_bunches.size()-1].push_back(
4463 SendableTexture(tname, tpath, tmp_os.str()));
4465 // Start next bunch if got enough data
4466 if(texture_size_bunch_total >= bytes_per_bunch){
4467 texture_bunches.push_back(core::list<SendableTexture>());
4468 texture_size_bunch_total = 0;
4473 /* Create and send packets */
4475 u32 num_bunches = texture_bunches.size();
4476 for(u32 i=0; i<num_bunches; i++)
4480 u16 total number of texture bunches
4481 u16 index of this bunch
4482 u32 number of textures in this bunch
4490 std::ostringstream os(std::ios_base::binary);
4492 writeU16(os, TOCLIENT_TEXTURES);
4493 writeU16(os, num_bunches);
4495 writeU32(os, texture_bunches[i].size());
4497 for(core::list<SendableTexture>::Iterator
4498 j = texture_bunches[i].begin();
4499 j != texture_bunches[i].end(); j++){
4500 os<<serializeString(j->name);
4501 os<<serializeLongString(j->data);
4505 std::string s = os.str();
4506 infostream<<"Server::SendTextures(): bunch "<<i<<"/"<<num_bunches
4507 <<" textures="<<texture_bunches[i].size()
4508 <<" size=" <<s.size()<<std::endl;
4509 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4511 m_con.Send(peer_id, 0, data, true);
4519 void Server::HandlePlayerHP(Player *player, s16 damage)
4521 if(player->hp > damage)
4523 player->hp -= damage;
4524 SendPlayerHP(player);
4528 infostream<<"Server::HandlePlayerHP(): Player "
4529 <<player->getName()<<" dies"<<std::endl;
4533 //TODO: Throw items around
4535 // Handle players that are not connected
4536 if(player->peer_id == PEER_ID_INEXISTENT){
4537 RespawnPlayer(player);
4541 SendPlayerHP(player);
4543 RemoteClient *client = getClient(player->peer_id);
4544 if(client->net_proto_version >= 3)
4546 SendDeathscreen(m_con, player->peer_id, false, v3f(0,0,0));
4550 RespawnPlayer(player);
4555 void Server::RespawnPlayer(Player *player)
4558 ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
4559 bool repositioned = scriptapi_on_respawnplayer(m_lua, srp);
4561 v3f pos = findSpawnPos(m_env->getServerMap());
4562 player->setPosition(pos);
4563 srp->m_last_good_position = pos;
4564 srp->m_last_good_position_age = 0;
4566 SendMovePlayer(player);
4567 SendPlayerHP(player);
4570 void Server::UpdateCrafting(u16 peer_id)
4572 DSTACK(__FUNCTION_NAME);
4574 Player* player = m_env->getPlayer(peer_id);
4576 ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
4578 // No crafting in creative mode
4579 if(g_settings->getBool("creative_mode"))
4582 // Get the InventoryLists of the player in which we will operate
4583 InventoryList *clist = player->inventory.getList("craft");
4585 InventoryList *rlist = player->inventory.getList("craftresult");
4587 InventoryList *mlist = player->inventory.getList("main");
4590 // If the result list is not a preview and is not empty, try to
4591 // throw the item into main list
4592 if(!player->craftresult_is_preview && rlist->getUsedSlots() != 0)
4594 // Grab item out of craftresult
4595 InventoryItem *item = rlist->changeItem(0, NULL);
4596 // Try to put in main
4597 InventoryItem *leftover = mlist->addItem(item);
4598 // If there are leftovers, put them back to craftresult and
4600 delete rlist->addItem(leftover);
4601 // Inventory was modified
4602 srp->m_inventory_not_sent = true;
4605 // If result list is empty, we will make it preview what would be
4607 if(rlist->getUsedSlots() == 0)
4608 player->craftresult_is_preview = true;
4610 // If it is a preview, clear the possible old preview in it
4611 if(player->craftresult_is_preview)
4612 rlist->clearItems();
4614 // If it is a preview, find out what is the crafting result
4616 if(player->craftresult_is_preview)
4618 // Mangle crafting grid to an another format
4619 std::vector<InventoryItem*> items;
4620 for(u16 i=0; i<9; i++){
4621 if(clist->getItem(i) == NULL)
4622 items.push_back(NULL);
4624 items.push_back(clist->getItem(i)->clone());
4626 CraftPointerInput cpi(3, items);
4628 // Find out what is crafted and add it to result item slot
4629 InventoryItem *result = m_craftdef->getCraftResult(cpi, this);
4631 rlist->addItem(result);
4635 RemoteClient* Server::getClient(u16 peer_id)
4637 DSTACK(__FUNCTION_NAME);
4638 //JMutexAutoLock lock(m_con_mutex);
4639 core::map<u16, RemoteClient*>::Node *n;
4640 n = m_clients.find(peer_id);
4641 // A client should exist for all peers
4643 return n->getValue();
4646 std::wstring Server::getStatusString()
4648 std::wostringstream os(std::ios_base::binary);
4651 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4653 os<<L", uptime="<<m_uptime.get();
4654 // Information about clients
4656 for(core::map<u16, RemoteClient*>::Iterator
4657 i = m_clients.getIterator();
4658 i.atEnd() == false; i++)
4660 // Get client and check that it is valid
4661 RemoteClient *client = i.getNode()->getValue();
4662 assert(client->peer_id == i.getNode()->getKey());
4663 if(client->serialization_version == SER_FMT_VER_INVALID)
4666 Player *player = m_env->getPlayer(client->peer_id);
4667 // Get name of player
4668 std::wstring name = L"unknown";
4670 name = narrow_to_wide(player->getName());
4671 // Add name to information string
4675 if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
4676 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4677 if(g_settings->get("motd") != "")
4678 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4682 void Server::setPlayerPassword(const std::string &name, const std::wstring &password)
4684 // Add player to auth manager
4685 if(m_authmanager.exists(name) == false)
4687 infostream<<"Server: adding player "<<name
4688 <<" to auth manager"<<std::endl;
4689 m_authmanager.add(name);
4690 m_authmanager.setPrivs(name,
4691 stringToPrivs(g_settings->get("default_privs")));
4693 // Change password and save
4694 m_authmanager.setPassword(name, translatePassword(name, password));
4695 m_authmanager.save();
4698 // Saves g_settings to configpath given at initialization
4699 void Server::saveConfig()
4701 if(m_configpath != "")
4702 g_settings->updateConfigFile(m_configpath.c_str());
4705 void Server::notifyPlayer(const char *name, const std::wstring msg)
4707 Player *player = m_env->getPlayer(name);
4710 SendChatMessage(player->peer_id, std::wstring(L"Server: -!- ")+msg);
4713 void Server::notifyPlayers(const std::wstring msg)
4715 BroadcastChatMessage(msg);
4718 void Server::queueBlockEmerge(v3s16 blockpos, bool allow_generate)
4722 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
4723 m_emerge_queue.addBlock(PEER_ID_INEXISTENT, blockpos, flags);
4726 // IGameDef interface
4728 IToolDefManager* Server::getToolDefManager()
4732 INodeDefManager* Server::getNodeDefManager()
4736 ICraftDefManager* Server::getCraftDefManager()
4740 ICraftItemDefManager* Server::getCraftItemDefManager()
4742 return m_craftitemdef;
4744 ITextureSource* Server::getTextureSource()
4748 u16 Server::allocateUnknownNodeId(const std::string &name)
4750 return m_nodedef->allocateDummy(name);
4753 IWritableToolDefManager* Server::getWritableToolDefManager()
4757 IWritableNodeDefManager* Server::getWritableNodeDefManager()
4761 IWritableCraftDefManager* Server::getWritableCraftDefManager()
4765 IWritableCraftItemDefManager* Server::getWritableCraftItemDefManager()
4767 return m_craftitemdef;
4770 v3f findSpawnPos(ServerMap &map)
4772 //return v3f(50,50,50)*BS;
4777 nodepos = v2s16(0,0);
4782 // Try to find a good place a few times
4783 for(s32 i=0; i<1000; i++)
4786 // We're going to try to throw the player to this position
4787 v2s16 nodepos2d = v2s16(-range + (myrand()%(range*2)),
4788 -range + (myrand()%(range*2)));
4789 //v2s16 sectorpos = getNodeSectorPos(nodepos2d);
4790 // Get ground height at point (fallbacks to heightmap function)
4791 s16 groundheight = map.findGroundLevel(nodepos2d);
4792 // Don't go underwater
4793 if(groundheight < WATER_LEVEL)
4795 //infostream<<"-> Underwater"<<std::endl;
4798 // Don't go to high places
4799 if(groundheight > WATER_LEVEL + 4)
4801 //infostream<<"-> Underwater"<<std::endl;
4805 nodepos = v3s16(nodepos2d.X, groundheight-2, nodepos2d.Y);
4806 bool is_good = false;
4808 for(s32 i=0; i<10; i++){
4809 v3s16 blockpos = getNodeBlockPos(nodepos);
4810 map.emergeBlock(blockpos, true);
4811 MapNode n = map.getNodeNoEx(nodepos);
4812 if(n.getContent() == CONTENT_AIR){
4823 // Found a good place
4824 //infostream<<"Searched through "<<i<<" places."<<std::endl;
4830 return intToFloat(nodepos, BS);
4833 Player *Server::emergePlayer(const char *name, u16 peer_id)
4836 Try to get an existing player
4838 Player *player = m_env->getPlayer(name);
4841 // If player is already connected, cancel
4842 if(player->peer_id != 0)
4844 infostream<<"emergePlayer(): Player already connected"<<std::endl;
4849 player->peer_id = peer_id;
4851 // Reset inventory to creative if in creative mode
4852 if(g_settings->getBool("creative_mode"))
4854 // Warning: double code below
4855 // Backup actual inventory
4856 player->inventory_backup = new Inventory();
4857 *(player->inventory_backup) = player->inventory;
4858 // Set creative inventory
4859 craft_set_creative_inventory(player, this);
4866 If player with the wanted peer_id already exists, cancel.
4868 if(m_env->getPlayer(peer_id) != NULL)
4870 infostream<<"emergePlayer(): Player with wrong name but same"
4871 " peer_id already exists"<<std::endl;
4879 /* Set player position */
4881 infostream<<"Server: Finding spawn place for player \""
4882 <<name<<"\""<<std::endl;
4884 v3f pos = findSpawnPos(m_env->getServerMap());
4886 player = new ServerRemotePlayer(m_env, pos, peer_id, name);
4888 /* Add player to environment */
4889 m_env->addPlayer(player);
4892 ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
4893 scriptapi_on_newplayer(m_lua, srp);
4895 /* Add stuff to inventory */
4896 if(g_settings->getBool("creative_mode"))
4898 // Warning: double code above
4899 // Backup actual inventory
4900 player->inventory_backup = new Inventory();
4901 *(player->inventory_backup) = player->inventory;
4902 // Set creative inventory
4903 craft_set_creative_inventory(player, this);
4908 } // create new player
4911 void Server::handlePeerChange(PeerChange &c)
4913 JMutexAutoLock envlock(m_env_mutex);
4914 JMutexAutoLock conlock(m_con_mutex);
4916 if(c.type == PEER_ADDED)
4923 core::map<u16, RemoteClient*>::Node *n;
4924 n = m_clients.find(c.peer_id);
4925 // The client shouldn't already exist
4929 RemoteClient *client = new RemoteClient();
4930 client->peer_id = c.peer_id;
4931 m_clients.insert(client->peer_id, client);
4934 else if(c.type == PEER_REMOVED)
4941 core::map<u16, RemoteClient*>::Node *n;
4942 n = m_clients.find(c.peer_id);
4943 // The client should exist
4947 Mark objects to be not known by the client
4949 RemoteClient *client = n->getValue();
4951 for(core::map<u16, bool>::Iterator
4952 i = client->m_known_objects.getIterator();
4953 i.atEnd()==false; i++)
4956 u16 id = i.getNode()->getKey();
4957 ServerActiveObject* obj = m_env->getActiveObject(id);
4959 if(obj && obj->m_known_by_count > 0)
4960 obj->m_known_by_count--;
4963 // Collect information about leaving in chat
4964 std::wstring message;
4966 Player *player = m_env->getPlayer(c.peer_id);
4969 std::wstring name = narrow_to_wide(player->getName());
4972 message += L" left game";
4974 message += L" (timed out)";
4980 m_env->removePlayer(c.peer_id);
4983 // Set player client disconnected
4985 Player *player = m_env->getPlayer(c.peer_id);
4987 player->peer_id = 0;
4994 std::ostringstream os(std::ios_base::binary);
4995 for(core::map<u16, RemoteClient*>::Iterator
4996 i = m_clients.getIterator();
4997 i.atEnd() == false; i++)
4999 RemoteClient *client = i.getNode()->getValue();
5000 assert(client->peer_id == i.getNode()->getKey());
5001 if(client->serialization_version == SER_FMT_VER_INVALID)
5004 Player *player = m_env->getPlayer(client->peer_id);
5007 // Get name of player
5008 os<<player->getName()<<" ";
5011 actionstream<<player->getName()<<" "
5012 <<(c.timeout?"times out.":"leaves game.")
5013 <<" List of players: "
5014 <<os.str()<<std::endl;
5019 delete m_clients[c.peer_id];
5020 m_clients.remove(c.peer_id);
5022 // Send player info to all remaining clients
5025 // Send leave chat message to all remaining clients
5026 BroadcastChatMessage(message);
5035 void Server::handlePeerChanges()
5037 while(m_peer_change_queue.size() > 0)
5039 PeerChange c = m_peer_change_queue.pop_front();
5041 infostream<<"Server: Handling peer change: "
5042 <<"id="<<c.peer_id<<", timeout="<<c.timeout
5045 handlePeerChange(c);
5049 u64 Server::getPlayerPrivs(Player *player)
5053 std::string playername = player->getName();
5054 // Local player gets all privileges regardless of
5055 // what's set on their account.
5056 if(g_settings->get("name") == playername)
5062 return getPlayerAuthPrivs(playername);
5066 void dedicated_server_loop(Server &server, bool &kill)
5068 DSTACK(__FUNCTION_NAME);
5070 infostream<<DTIME<<std::endl;
5071 infostream<<"========================"<<std::endl;
5072 infostream<<"Running dedicated server"<<std::endl;
5073 infostream<<"========================"<<std::endl;
5074 infostream<<std::endl;
5076 IntervalLimiter m_profiler_interval;
5080 // This is kind of a hack but can be done like this
5081 // because server.step() is very light
5083 ScopeProfiler sp(g_profiler, "dedicated server sleep");
5088 if(server.getShutdownRequested() || kill)
5090 infostream<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
5097 float profiler_print_interval =
5098 g_settings->getFloat("profiler_print_interval");
5099 if(profiler_print_interval != 0)
5101 if(m_profiler_interval.step(0.030, profiler_print_interval))
5103 infostream<<"Profiler:"<<std::endl;
5104 g_profiler->print(infostream);
5105 g_profiler->clear();
5112 static int counter = 0;
5118 core::list<PlayerInfo> list = server.getPlayerInfo();
5119 core::list<PlayerInfo>::Iterator i;
5120 static u32 sum_old = 0;
5121 u32 sum = PIChecksum(list);
5124 infostream<<DTIME<<"Player info:"<<std::endl;
5125 for(i=list.begin(); i!=list.end(); i++)
5127 i->PrintLine(&infostream);