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 std::string checkpwd;
2175 if(m_authmanager.exists(playername))
2177 checkpwd = m_authmanager.getPassword(playername);
2181 checkpwd = g_settings->get("default_password");
2184 /*infostream<<"Server: Client gave password '"<<password
2185 <<"', the correct one is '"<<checkpwd<<"'"<<std::endl;*/
2187 if(password != checkpwd && m_authmanager.exists(playername))
2189 infostream<<"Server: peer_id="<<peer_id
2190 <<": supplied invalid password for "
2191 <<playername<<std::endl;
2192 SendAccessDenied(m_con, peer_id, L"Invalid password");
2196 // Add player to auth manager
2197 if(m_authmanager.exists(playername) == false)
2199 infostream<<"Server: adding player "<<playername
2200 <<" to auth manager"<<std::endl;
2201 m_authmanager.add(playername);
2202 m_authmanager.setPassword(playername, checkpwd);
2203 m_authmanager.setPrivs(playername,
2204 stringToPrivs(g_settings->get("default_privs")));
2205 m_authmanager.save();
2208 // Enforce user limit.
2209 // Don't enforce for users that have some admin right
2210 if(m_clients.size() >= g_settings->getU16("max_users") &&
2211 (m_authmanager.getPrivs(playername)
2212 & (PRIV_SERVER|PRIV_BAN|PRIV_PRIVS)) == 0 &&
2213 playername != g_settings->get("name"))
2215 SendAccessDenied(m_con, peer_id, L"Too many users.");
2220 Player *player = emergePlayer(playername, password, peer_id);
2222 // If failed, cancel
2225 infostream<<"Server: peer_id="<<peer_id
2226 <<": failed to emerge player"<<std::endl;
2231 Answer with a TOCLIENT_INIT
2234 SharedBuffer<u8> reply(2+1+6+8);
2235 writeU16(&reply[0], TOCLIENT_INIT);
2236 writeU8(&reply[2], deployed);
2237 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
2238 writeU64(&reply[2+1+6], m_env->getServerMap().getSeed());
2241 m_con.Send(peer_id, 0, reply, true);
2245 Send complete position information
2247 SendMovePlayer(player);
2252 if(command == TOSERVER_INIT2)
2254 infostream<<"Server: Got TOSERVER_INIT2 from "
2255 <<peer_id<<std::endl;
2258 getClient(peer_id)->serialization_version
2259 = getClient(peer_id)->pending_serialization_version;
2262 Send some initialization data
2265 // Send tool definitions
2266 SendToolDef(m_con, peer_id, m_toolmgr);
2268 // Send node definitions
2269 SendNodeDef(m_con, peer_id, m_nodedef);
2271 // Send CraftItem definitions
2272 SendCraftItemDef(m_con, peer_id, m_craftitemdef);
2275 SendTextures(peer_id);
2277 // Send player info to all players
2280 // Send inventory to player
2281 UpdateCrafting(peer_id);
2282 SendInventory(peer_id);
2284 // Send player items to all players
2287 Player *player = m_env->getPlayer(peer_id);
2290 SendPlayerHP(player);
2294 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2295 m_env->getTimeOfDay());
2296 m_con.Send(peer_id, 0, data, true);
2299 // Send information about server to player in chat
2300 SendChatMessage(peer_id, getStatusString());
2302 // Send information about joining in chat
2304 std::wstring name = L"unknown";
2305 Player *player = m_env->getPlayer(peer_id);
2307 name = narrow_to_wide(player->getName());
2309 std::wstring message;
2312 message += L" joined game";
2313 BroadcastChatMessage(message);
2316 // Warnings about protocol version can be issued here
2317 if(getClient(peer_id)->net_proto_version < PROTOCOL_VERSION)
2319 SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT IS OLD AND MAY WORK PROPERLY WITH THIS SERVER");
2323 Check HP, respawn if necessary
2325 HandlePlayerHP(player, 0);
2331 std::ostringstream os(std::ios_base::binary);
2332 for(core::map<u16, RemoteClient*>::Iterator
2333 i = m_clients.getIterator();
2334 i.atEnd() == false; i++)
2336 RemoteClient *client = i.getNode()->getValue();
2337 assert(client->peer_id == i.getNode()->getKey());
2338 if(client->serialization_version == SER_FMT_VER_INVALID)
2341 Player *player = m_env->getPlayer(client->peer_id);
2344 // Get name of player
2345 os<<player->getName()<<" ";
2348 actionstream<<player->getName()<<" joins game. List of players: "
2349 <<os.str()<<std::endl;
2355 if(peer_ser_ver == SER_FMT_VER_INVALID)
2357 infostream<<"Server::ProcessData(): Cancelling: Peer"
2358 " serialization format invalid or not initialized."
2359 " Skipping incoming command="<<command<<std::endl;
2363 Player *player = m_env->getPlayer(peer_id);
2364 ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
2367 infostream<<"Server::ProcessData(): Cancelling: "
2368 "No player for peer_id="<<peer_id
2372 if(command == TOSERVER_PLAYERPOS)
2374 if(datasize < 2+12+12+4+4)
2378 v3s32 ps = readV3S32(&data[start+2]);
2379 v3s32 ss = readV3S32(&data[start+2+12]);
2380 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2381 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2382 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2383 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2384 pitch = wrapDegrees(pitch);
2385 yaw = wrapDegrees(yaw);
2387 player->setPosition(position);
2388 player->setSpeed(speed);
2389 player->setPitch(pitch);
2390 player->setYaw(yaw);
2392 /*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2393 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2394 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2396 else if(command == TOSERVER_GOTBLOCKS)
2409 u16 count = data[2];
2410 for(u16 i=0; i<count; i++)
2412 if((s16)datasize < 2+1+(i+1)*6)
2413 throw con::InvalidIncomingDataException
2414 ("GOTBLOCKS length is too short");
2415 v3s16 p = readV3S16(&data[2+1+i*6]);
2416 /*infostream<<"Server: GOTBLOCKS ("
2417 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2418 RemoteClient *client = getClient(peer_id);
2419 client->GotBlock(p);
2422 else if(command == TOSERVER_DELETEDBLOCKS)
2435 u16 count = data[2];
2436 for(u16 i=0; i<count; i++)
2438 if((s16)datasize < 2+1+(i+1)*6)
2439 throw con::InvalidIncomingDataException
2440 ("DELETEDBLOCKS length is too short");
2441 v3s16 p = readV3S16(&data[2+1+i*6]);
2442 /*infostream<<"Server: DELETEDBLOCKS ("
2443 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2444 RemoteClient *client = getClient(peer_id);
2445 client->SetBlockNotSent(p);
2448 else if(command == TOSERVER_CLICK_OBJECT)
2450 infostream<<"Server: CLICK_OBJECT not supported anymore"<<std::endl;
2453 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2455 infostream<<"Server: CLICK_ACTIVEOBJECT not supported anymore"<<std::endl;
2458 else if(command == TOSERVER_GROUND_ACTION)
2460 infostream<<"Server: GROUND_ACTION not supported anymore"<<std::endl;
2464 else if(command == TOSERVER_RELEASE)
2466 infostream<<"Server: RELEASE not supported anymore"<<std::endl;
2469 else if(command == TOSERVER_SIGNTEXT)
2471 infostream<<"Server: SIGNTEXT not supported anymore"
2475 else if(command == TOSERVER_SIGNNODETEXT)
2477 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2485 std::string datastring((char*)&data[2], datasize-2);
2486 std::istringstream is(datastring, std::ios_base::binary);
2489 is.read((char*)buf, 6);
2490 v3s16 p = readV3S16(buf);
2491 is.read((char*)buf, 2);
2492 u16 textlen = readU16(buf);
2494 for(u16 i=0; i<textlen; i++)
2496 is.read((char*)buf, 1);
2497 text += (char)buf[0];
2500 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
2504 meta->setText(text);
2506 actionstream<<player->getName()<<" writes \""<<text<<"\" to sign"
2507 <<" at "<<PP(p)<<std::endl;
2509 v3s16 blockpos = getNodeBlockPos(p);
2510 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
2513 block->raiseModified(MOD_STATE_WRITE_NEEDED,
2517 setBlockNotSent(blockpos);
2519 else if(command == TOSERVER_INVENTORY_ACTION)
2521 /*// Ignore inventory changes if in creative mode
2522 if(g_settings->getBool("creative_mode") == true)
2524 infostream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2528 // Strip command and create a stream
2529 std::string datastring((char*)&data[2], datasize-2);
2530 infostream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2531 std::istringstream is(datastring, std::ios_base::binary);
2533 InventoryAction *a = InventoryAction::deSerialize(is);
2536 infostream<<"TOSERVER_INVENTORY_ACTION: "
2537 <<"InventoryAction::deSerialize() returned NULL"
2543 c.current_player = player;
2546 Handle restrictions and special cases of the move action
2548 if(a->getType() == IACTION_MOVE
2549 && g_settings->getBool("creative_mode") == false)
2551 InventoryList *rlist = player->inventory.getList("craftresult");
2553 InventoryList *clist = player->inventory.getList("craft");
2555 InventoryList *mlist = player->inventory.getList("main");
2558 IMoveAction *ma = (IMoveAction*)a;
2561 Disable moving items into craftresult from elsewhere
2563 if(ma->to_inv == "current_player"
2564 && ma->to_list == "craftresult"
2565 && (ma->from_inv != "current_player"
2566 || ma->from_list != "craftresult"))
2568 infostream<<"Ignoring IMoveAction from "
2569 <<ma->from_inv<<":"<<ma->from_list
2570 <<" to "<<ma->to_inv<<":"<<ma->to_list
2571 <<" because dst is craftresult"
2572 <<" and src isn't craftresult"<<std::endl;
2578 Handle crafting (source is craftresult, which is preview)
2580 if(ma->from_inv == "current_player"
2581 && ma->from_list == "craftresult"
2582 && player->craftresult_is_preview)
2585 If the craftresult is placed on itself, crafting takes
2586 place and result is moved into main list
2588 if(ma->to_inv == "current_player"
2589 && ma->to_list == "craftresult")
2591 // Except if main list doesn't have free slots
2592 if(mlist->getFreeSlots() == 0){
2593 infostream<<"Cannot craft: Main list doesn't have"
2594 <<" free slots"<<std::endl;
2599 player->craftresult_is_preview = false;
2600 clist->decrementMaterials(1);
2602 InventoryItem *item1 = rlist->changeItem(0, NULL);
2603 mlist->addItem(item1);
2605 srp->m_inventory_not_sent = true;
2611 Disable action if there are no free slots in
2614 If the item is placed on an item that is not of the
2615 same kind, the existing item will be first moved to
2616 craftresult and immediately moved to the free slot.
2619 Inventory *inv_to = getInventory(&c, ma->to_inv);
2621 InventoryList *list_to = inv_to->getList(ma->to_list);
2623 if(list_to->getFreeSlots() == 0){
2624 infostream<<"Cannot craft: Destination doesn't have"
2625 <<" free slots"<<std::endl;
2629 }while(0); // Allow break
2634 player->craftresult_is_preview = false;
2635 clist->decrementMaterials(1);
2637 /* Print out action */
2638 InventoryItem *item = rlist->getItem(0);
2639 std::string itemstring = "NULL";
2641 itemstring = item->getItemString();
2642 actionstream<<player->getName()<<" crafts "
2643 <<itemstring<<std::endl;
2646 a->apply(&c, this, m_env);
2656 // Disallow moving items in elsewhere than player's inventory
2657 // if not allowed to build
2658 if((getPlayerPrivs(player) & PRIV_BUILD) == 0
2659 && (ma->from_inv != "current_player"
2660 || ma->to_inv != "current_player"))
2662 infostream<<"Cannot move outside of player's inventory: "
2663 <<"No build privilege"<<std::endl;
2668 // If player is not an admin, check for ownership of src
2669 if(ma->from_inv != "current_player"
2670 && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
2672 Strfnd fn(ma->from_inv);
2673 std::string id0 = fn.next(":");
2674 if(id0 == "nodemeta")
2677 p.X = stoi(fn.next(","));
2678 p.Y = stoi(fn.next(","));
2679 p.Z = stoi(fn.next(","));
2680 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
2681 if(meta->getOwner() != "" &&
2682 meta->getOwner() != player->getName())
2684 infostream<<"Cannot move item: "
2685 "not owner of metadata"
2692 // If player is not an admin, check for ownership of dst
2693 if(ma->to_inv != "current_player"
2694 && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
2696 Strfnd fn(ma->to_inv);
2697 std::string id0 = fn.next(":");
2698 if(id0 == "nodemeta")
2701 p.X = stoi(fn.next(","));
2702 p.Y = stoi(fn.next(","));
2703 p.Z = stoi(fn.next(","));
2704 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
2705 if(meta->getOwner() != "" &&
2706 meta->getOwner() != player->getName())
2708 infostream<<"Cannot move item: "
2709 "not owner of metadata"
2718 Handle restrictions and special cases of the drop action
2720 else if(a->getType() == IACTION_DROP)
2722 IDropAction *da = (IDropAction*)a;
2723 // Disallow dropping items if not allowed to build
2724 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2729 // If player is not an admin, check for ownership
2730 else if (da->from_inv != "current_player"
2731 && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
2733 Strfnd fn(da->from_inv);
2734 std::string id0 = fn.next(":");
2735 if(id0 == "nodemeta")
2738 p.X = stoi(fn.next(","));
2739 p.Y = stoi(fn.next(","));
2740 p.Z = stoi(fn.next(","));
2741 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
2742 if(meta->getOwner() != "" &&
2743 meta->getOwner() != player->getName())
2745 infostream<<"Cannot move item: "
2746 "not owner of metadata"
2756 a->apply(&c, this, m_env);
2760 else if(command == TOSERVER_CHAT_MESSAGE)
2768 std::string datastring((char*)&data[2], datasize-2);
2769 std::istringstream is(datastring, std::ios_base::binary);
2772 is.read((char*)buf, 2);
2773 u16 len = readU16(buf);
2775 std::wstring message;
2776 for(u16 i=0; i<len; i++)
2778 is.read((char*)buf, 2);
2779 message += (wchar_t)readU16(buf);
2782 // Get player name of this client
2783 std::wstring name = narrow_to_wide(player->getName());
2786 bool ate = scriptapi_on_chat_message(m_lua, player->getName(),
2787 wide_to_narrow(message));
2788 // If script ate the message, don't proceed
2792 // Line to send to players
2794 // Whether to send to the player that sent the line
2795 bool send_to_sender = false;
2796 // Whether to send to other players
2797 bool send_to_others = false;
2799 // Local player gets all privileges regardless of
2800 // what's set on their account.
2801 u64 privs = getPlayerPrivs(player);
2804 if(message[0] == L'/')
2806 size_t strip_size = 1;
2807 if (message[1] == L'#') // support old-style commans
2809 message = message.substr(strip_size);
2811 WStrfnd f1(message);
2812 f1.next(L" "); // Skip over /#whatever
2813 std::wstring paramstring = f1.next(L"");
2815 ServerCommandContext *ctx = new ServerCommandContext(
2816 str_split(message, L' '),
2823 std::wstring reply(processServerCommand(ctx));
2824 send_to_sender = ctx->flags & SEND_TO_SENDER;
2825 send_to_others = ctx->flags & SEND_TO_OTHERS;
2827 if (ctx->flags & SEND_NO_PREFIX)
2830 line += L"Server: " + reply;
2837 if(privs & PRIV_SHOUT)
2843 send_to_others = true;
2847 line += L"Server: You are not allowed to shout";
2848 send_to_sender = true;
2855 actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
2858 Send the message to clients
2860 for(core::map<u16, RemoteClient*>::Iterator
2861 i = m_clients.getIterator();
2862 i.atEnd() == false; i++)
2864 // Get client and check that it is valid
2865 RemoteClient *client = i.getNode()->getValue();
2866 assert(client->peer_id == i.getNode()->getKey());
2867 if(client->serialization_version == SER_FMT_VER_INVALID)
2871 bool sender_selected = (peer_id == client->peer_id);
2872 if(sender_selected == true && send_to_sender == false)
2874 if(sender_selected == false && send_to_others == false)
2877 SendChatMessage(client->peer_id, line);
2881 else if(command == TOSERVER_DAMAGE)
2883 std::string datastring((char*)&data[2], datasize-2);
2884 std::istringstream is(datastring, std::ios_base::binary);
2885 u8 damage = readU8(is);
2887 if(g_settings->getBool("enable_damage"))
2889 actionstream<<player->getName()<<" damaged by "
2890 <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
2893 HandlePlayerHP(player, damage);
2897 SendPlayerHP(player);
2900 else if(command == TOSERVER_PASSWORD)
2903 [0] u16 TOSERVER_PASSWORD
2904 [2] u8[28] old password
2905 [30] u8[28] new password
2908 if(datasize != 2+PASSWORD_SIZE*2)
2910 /*char password[PASSWORD_SIZE];
2911 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2912 password[i] = data[2+i];
2913 password[PASSWORD_SIZE-1] = 0;*/
2915 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2923 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2925 char c = data[2+PASSWORD_SIZE+i];
2931 infostream<<"Server: Client requests a password change from "
2932 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
2934 std::string playername = player->getName();
2936 if(m_authmanager.exists(playername) == false)
2938 infostream<<"Server: playername not found in authmanager"<<std::endl;
2939 // Wrong old password supplied!!
2940 SendChatMessage(peer_id, L"playername not found in authmanager");
2944 std::string checkpwd = m_authmanager.getPassword(playername);
2946 if(oldpwd != checkpwd)
2948 infostream<<"Server: invalid old password"<<std::endl;
2949 // Wrong old password supplied!!
2950 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
2954 actionstream<<player->getName()<<" changes password"<<std::endl;
2956 m_authmanager.setPassword(playername, newpwd);
2958 infostream<<"Server: password change successful for "<<playername
2960 SendChatMessage(peer_id, L"Password change successful");
2962 else if(command == TOSERVER_PLAYERITEM)
2967 u16 item = readU16(&data[2]);
2968 player->wieldItem(item);
2969 SendWieldedItem(player);
2971 else if(command == TOSERVER_RESPAWN)
2976 RespawnPlayer(player);
2978 actionstream<<player->getName()<<" respawns at "
2979 <<PP(player->getPosition()/BS)<<std::endl;
2981 else if(command == TOSERVER_INTERACT)
2983 std::string datastring((char*)&data[2], datasize-2);
2984 std::istringstream is(datastring, std::ios_base::binary);
2990 [5] u32 length of the next item
2991 [9] serialized PointedThing
2993 0: start digging (from undersurface) or use
2994 1: stop digging (all parameters ignored)
2995 2: digging completed
2996 3: place block or item (to abovesurface)
2999 u8 action = readU8(is);
3000 u16 item_i = readU16(is);
3001 std::istringstream tmp_is(deSerializeLongString(is), std::ios::binary);
3002 PointedThing pointed;
3003 pointed.deSerialize(tmp_is);
3005 infostream<<"TOSERVER_INTERACT: action="<<(int)action<<", item="<<item_i<<", pointed="<<pointed.dump()<<std::endl;
3007 v3f player_pos = srp->m_last_good_position;
3009 // Update wielded item
3010 srp->wieldItem(item_i);
3012 // Get pointed to node (undefined if not POINTEDTYPE_NODE)
3013 v3s16 p_under = pointed.node_undersurface;
3014 v3s16 p_above = pointed.node_abovesurface;
3016 // Get pointed to object (NULL if not POINTEDTYPE_OBJECT)
3017 ServerActiveObject *pointed_object = NULL;
3018 if(pointed.type == POINTEDTHING_OBJECT)
3020 pointed_object = m_env->getActiveObject(pointed.object_id);
3021 if(pointed_object == NULL)
3023 infostream<<"TOSERVER_INTERACT: "
3024 "pointed object is NULL"<<std::endl;
3031 Check that target is reasonably close
3032 (only when digging or placing things)
3034 if(action == 0 || action == 2 || action == 3)
3036 v3f pointed_pos = player_pos;
3037 if(pointed.type == POINTEDTHING_NODE)
3039 pointed_pos = intToFloat(p_under, BS);
3041 else if(pointed.type == POINTEDTHING_OBJECT)
3043 pointed_pos = pointed_object->getBasePosition();
3046 float d = player_pos.getDistanceFrom(pointed_pos);
3047 float max_d = BS * 10; // Just some large enough value
3049 actionstream<<"Player "<<player->getName()
3050 <<" tried to access "<<pointed.dump()
3052 <<"d="<<d<<", max_d="<<max_d
3053 <<". ignoring."<<std::endl;
3054 // Re-send block to revert change on client-side
3055 RemoteClient *client = getClient(peer_id);
3056 v3s16 blockpos = getNodeBlockPos(floatToInt(pointed_pos, BS));
3057 client->SetBlockNotSent(blockpos);
3064 Make sure the player is allowed to do it
3066 bool build_priv = (getPlayerPrivs(player) & PRIV_BUILD) != 0;
3069 infostream<<"Ignoring interaction from player "<<player->getName()
3070 <<" because privileges are "<<getPlayerPrivs(player)
3072 // NOTE: no return; here, fall through
3076 0: start digging or punch object
3080 if(pointed.type == POINTEDTHING_NODE)
3083 NOTE: This can be used in the future to check if
3084 somebody is cheating, by checking the timing.
3086 bool cannot_punch_node = !build_priv;
3088 MapNode n(CONTENT_IGNORE);
3092 n = m_env->getMap().getNode(p_under);
3094 catch(InvalidPositionException &e)
3096 infostream<<"Server: Not punching: Node not found."
3097 <<" Adding block to emerge queue."
3099 m_emerge_queue.addBlock(peer_id,
3100 getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
3101 cannot_punch_node = true;
3104 if(cannot_punch_node)
3110 scriptapi_environment_on_punchnode(m_lua, p_under, n, srp);
3112 else if(pointed.type == POINTEDTHING_OBJECT)
3117 // Skip if object has been removed
3118 if(pointed_object->m_removed)
3121 actionstream<<player->getName()<<" punches object "
3122 <<pointed.object_id<<std::endl;
3125 pointed_object->punch(srp);
3133 else if(action == 1)
3138 2: Digging completed
3140 else if(action == 2)
3142 // Only complete digging of nodes
3143 if(pointed.type != POINTEDTHING_NODE)
3146 // Mandatory parameter; actually used for nothing
3147 core::map<v3s16, MapBlock*> modified_blocks;
3149 content_t material = CONTENT_IGNORE;
3150 u8 mineral = MINERAL_NONE;
3152 bool cannot_remove_node = !build_priv;
3154 MapNode n(CONTENT_IGNORE);
3157 n = m_env->getMap().getNode(p_under);
3159 mineral = n.getMineral(m_nodedef);
3160 // Get material at position
3161 material = n.getContent();
3162 // If not yet cancelled
3163 if(cannot_remove_node == false)
3165 // If it's not diggable, do nothing
3166 if(m_nodedef->get(material).diggable == false)
3168 infostream<<"Server: Not finishing digging: "
3169 <<"Node not diggable"
3171 cannot_remove_node = true;
3174 // If not yet cancelled
3175 if(cannot_remove_node == false)
3177 // Get node metadata
3178 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p_under);
3179 if(meta && meta->nodeRemovalDisabled() == true)
3181 infostream<<"Server: Not finishing digging: "
3182 <<"Node metadata disables removal"
3184 cannot_remove_node = true;
3188 catch(InvalidPositionException &e)
3190 infostream<<"Server: Not finishing digging: Node not found."
3191 <<" Adding block to emerge queue."
3193 m_emerge_queue.addBlock(peer_id,
3194 getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
3195 cannot_remove_node = true;
3199 If node can't be removed, set block to be re-sent to
3202 if(cannot_remove_node)
3204 infostream<<"Server: Not finishing digging."<<std::endl;
3206 // Client probably has wrong data.
3207 // Set block not sent, so that client will get
3209 infostream<<"Client "<<peer_id<<" tried to dig "
3210 <<"node; but node cannot be removed."
3211 <<" setting MapBlock not sent."<<std::endl;
3212 RemoteClient *client = getClient(peer_id);
3213 v3s16 blockpos = getNodeBlockPos(p_under);
3214 client->SetBlockNotSent(blockpos);
3219 actionstream<<player->getName()<<" digs "<<PP(p_under)
3220 <<", gets material "<<(int)material<<", mineral "
3221 <<(int)mineral<<std::endl;
3224 Send the removal to all close-by players.
3225 - If other player is close, send REMOVENODE
3226 - Otherwise set blocks not sent
3228 core::list<u16> far_players;
3229 sendRemoveNode(p_under, peer_id, &far_players, 30);
3232 Update and send inventory
3235 if(g_settings->getBool("creative_mode") == false)
3240 InventoryList *mlist = player->inventory.getList("main");
3243 InventoryItem *item = mlist->getItem(item_i);
3244 if(item && (std::string)item->getName() == "ToolItem")
3246 ToolItem *titem = (ToolItem*)item;
3247 std::string toolname = titem->getToolName();
3249 // Get digging properties for material and tool
3250 ToolDiggingProperties tp =
3251 m_toolmgr->getDiggingProperties(toolname);
3252 DiggingProperties prop =
3253 getDiggingProperties(material, &tp, m_nodedef);
3255 if(prop.diggable == false)
3257 infostream<<"Server: WARNING: Player digged"
3258 <<" with impossible material + tool"
3259 <<" combination"<<std::endl;
3262 bool weared_out = titem->addWear(prop.wear);
3266 mlist->deleteItem(item_i);
3269 srp->m_inventory_not_sent = true;
3274 Add dug item to inventory
3277 InventoryItem *item = NULL;
3279 if(mineral != MINERAL_NONE)
3280 item = getDiggedMineralItem(mineral, this);
3285 const std::string &dug_s = m_nodedef->get(material).dug_item;
3288 std::istringstream is(dug_s, std::ios::binary);
3289 item = InventoryItem::deSerialize(is, this);
3295 // Add a item to inventory
3296 player->inventory.addItem("main", item);
3297 srp->m_inventory_not_sent = true;
3302 if(mineral != MINERAL_NONE)
3303 item = getDiggedMineralItem(mineral, this);
3308 const std::string &extra_dug_s = m_nodedef->get(material).extra_dug_item;
3309 s32 extra_rarity = m_nodedef->get(material).extra_dug_item_rarity;
3310 if(extra_dug_s != "" && extra_rarity != 0
3311 && myrand() % extra_rarity == 0)
3313 std::istringstream is(extra_dug_s, std::ios::binary);
3314 item = InventoryItem::deSerialize(is, this);
3320 // Add a item to inventory
3321 player->inventory.addItem("main", item);
3322 srp->m_inventory_not_sent = true;
3328 (this takes some time so it is done after the quick stuff)
3331 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
3333 m_env->getMap().removeNodeAndUpdate(p_under, modified_blocks);
3336 Set blocks not sent to far players
3338 for(core::list<u16>::Iterator
3339 i = far_players.begin();
3340 i != far_players.end(); i++)
3343 RemoteClient *client = getClient(peer_id);
3346 client->SetBlocksNotSent(modified_blocks);
3352 scriptapi_environment_on_dignode(m_lua, p_under, n, srp);
3356 3: place block or right-click object
3358 else if(action == 3)
3360 if(pointed.type == POINTEDTHING_NODE)
3362 InventoryList *ilist = player->inventory.getList("main");
3367 InventoryItem *item = ilist->getItem(item_i);
3369 // If there is no item, it is not possible to add it anywhere
3374 Handle material items
3376 if(std::string("MaterialItem") == item->getName())
3378 bool cannot_place_node = !build_priv;
3381 // Don't add a node if this is not a free space
3382 MapNode n2 = m_env->getMap().getNode(p_above);
3383 if(m_nodedef->get(n2).buildable_to == false)
3385 infostream<<"Client "<<peer_id<<" tried to place"
3386 <<" node in invalid position."<<std::endl;
3387 cannot_place_node = true;
3390 catch(InvalidPositionException &e)
3392 infostream<<"Server: Ignoring ADDNODE: Node not found"
3393 <<" Adding block to emerge queue."
3395 m_emerge_queue.addBlock(peer_id,
3396 getNodeBlockPos(p_above), BLOCK_EMERGE_FLAG_FROMDISK);
3397 cannot_place_node = true;
3400 if(cannot_place_node)
3402 // Client probably has wrong data.
3403 // Set block not sent, so that client will get
3405 RemoteClient *client = getClient(peer_id);
3406 v3s16 blockpos = getNodeBlockPos(p_above);
3407 client->SetBlockNotSent(blockpos);
3411 // Reset build time counter
3412 getClient(peer_id)->m_time_from_building = 0.0;
3415 MaterialItem *mitem = (MaterialItem*)item;
3417 n.setContent(mitem->getMaterial());
3419 actionstream<<player->getName()<<" places material "
3420 <<(int)mitem->getMaterial()
3421 <<" at "<<PP(p_under)<<std::endl;
3423 // Calculate direction for wall mounted stuff
3424 if(m_nodedef->get(n).wall_mounted)
3425 n.param2 = packDir(p_under - p_above);
3427 // Calculate the direction for furnaces and chests and stuff
3428 if(m_nodedef->get(n).param_type == CPT_FACEDIR_SIMPLE)
3430 v3f playerpos = player->getPosition();
3431 v3f blockpos = intToFloat(p_above, BS) - playerpos;
3432 blockpos = blockpos.normalize();
3434 if (fabs(blockpos.X) > fabs(blockpos.Z)) {
3448 Send to all close-by players
3450 core::list<u16> far_players;
3451 sendAddNode(p_above, n, 0, &far_players, 30);
3456 InventoryList *ilist = player->inventory.getList("main");
3457 if(g_settings->getBool("creative_mode") == false && ilist)
3459 // Remove from inventory and send inventory
3460 if(mitem->getCount() <= 1)
3461 ilist->deleteItem(item_i);
3464 srp->m_inventory_not_sent = true;
3470 This takes some time so it is done after the quick stuff
3472 core::map<v3s16, MapBlock*> modified_blocks;
3474 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
3476 std::string p_name = std::string(player->getName());
3477 m_env->getMap().addNodeAndUpdate(p_above, n, modified_blocks, p_name);
3480 Set blocks not sent to far players
3482 for(core::list<u16>::Iterator
3483 i = far_players.begin();
3484 i != far_players.end(); i++)
3487 RemoteClient *client = getClient(peer_id);
3490 client->SetBlocksNotSent(modified_blocks);
3496 scriptapi_environment_on_placenode(m_lua, p_above, n, srp);
3499 Calculate special events
3502 /*if(n.d == LEGN(m_nodedef, "CONTENT_MESE"))
3505 for(s16 z=-1; z<=1; z++)
3506 for(s16 y=-1; y<=1; y++)
3507 for(s16 x=-1; x<=1; x++)
3514 Place other item (not a block)
3520 infostream<<"Not allowing player to place item: "
3521 "no build privileges"<<std::endl;
3525 // Calculate a position for it
3526 v3f pos = player_pos;
3527 if(pointed.type == POINTEDTHING_NOTHING)
3529 infostream<<"Not allowing player to place item: "
3530 "pointing to nothing"<<std::endl;
3533 else if(pointed.type == POINTEDTHING_NODE)
3535 pos = intToFloat(p_above, BS);
3537 else if(pointed.type == POINTEDTHING_OBJECT)
3539 pos = pointed_object->getBasePosition();
3542 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
3543 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
3547 //pos.Y -= BS*0.25; // let it drop a bit
3550 Check that the block is loaded so that the item
3551 can properly be added to the static list too
3553 v3s16 blockpos = getNodeBlockPos(floatToInt(pos, BS));
3554 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3557 infostream<<"Error while placing item: "
3558 "block not found"<<std::endl;
3562 actionstream<<player->getName()<<" places "<<item->getName()
3563 <<" at "<<PP(pos)<<std::endl;
3568 bool remove = item->dropOrPlace(m_env, srp, pos, true, -1);
3569 if(remove && g_settings->getBool("creative_mode") == false)
3571 InventoryList *ilist = player->inventory.getList("main");
3573 // Remove from inventory and send inventory
3574 ilist->deleteItem(item_i);
3575 srp->m_inventory_not_sent = true;
3580 else if(pointed.type == POINTEDTHING_OBJECT)
3582 // Right click object
3587 // Skip if object has been removed
3588 if(pointed_object->m_removed)
3591 actionstream<<player->getName()<<" right-clicks object "
3592 <<pointed.object_id<<std::endl;
3595 pointed_object->rightClick(srp);
3603 else if(action == 4)
3605 InventoryList *ilist = player->inventory.getList("main");
3610 InventoryItem *item = ilist->getItem(item_i);
3612 // If there is no item, it is not possible to add it anywhere
3616 // Requires build privs
3619 infostream<<"Not allowing player to use item: "
3620 "no build privileges"<<std::endl;
3624 actionstream<<player->getName()<<" uses "<<item->getName()
3625 <<", pointing at "<<pointed.dump()<<std::endl;
3627 bool remove = item->use(m_env, srp, pointed);
3629 if(remove && g_settings->getBool("creative_mode") == false)
3631 InventoryList *ilist = player->inventory.getList("main");
3633 // Remove from inventory and send inventory
3634 ilist->deleteItem(item_i);
3635 srp->m_inventory_not_sent = true;
3642 Catch invalid actions
3646 infostream<<"WARNING: Server: Invalid action "
3647 <<action<<std::endl;
3650 // Complete add_to_inventory_later
3651 srp->completeAddToInventoryLater(item_i);
3655 infostream<<"Server::ProcessData(): Ignoring "
3656 "unknown command "<<command<<std::endl;
3660 catch(SendFailedException &e)
3662 errorstream<<"Server::ProcessData(): SendFailedException: "
3668 void Server::onMapEditEvent(MapEditEvent *event)
3670 //infostream<<"Server::onMapEditEvent()"<<std::endl;
3671 if(m_ignore_map_edit_events)
3673 MapEditEvent *e = event->clone();
3674 m_unsent_map_edit_queue.push_back(e);
3677 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3679 if(id == "current_player")
3681 assert(c->current_player);
3682 return &(c->current_player->inventory);
3686 std::string id0 = fn.next(":");
3688 if(id0 == "nodemeta")
3691 p.X = stoi(fn.next(","));
3692 p.Y = stoi(fn.next(","));
3693 p.Z = stoi(fn.next(","));
3694 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3696 return meta->getInventory();
3697 infostream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3698 <<"no metadata found"<<std::endl;
3702 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3705 void Server::inventoryModified(InventoryContext *c, std::string id)
3707 if(id == "current_player")
3709 assert(c->current_player);
3710 ServerRemotePlayer *srp =
3711 static_cast<ServerRemotePlayer*>(c->current_player);
3712 srp->m_inventory_not_sent = true;
3717 std::string id0 = fn.next(":");
3719 if(id0 == "nodemeta")
3722 p.X = stoi(fn.next(","));
3723 p.Y = stoi(fn.next(","));
3724 p.Z = stoi(fn.next(","));
3725 v3s16 blockpos = getNodeBlockPos(p);
3727 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3729 meta->inventoryModified();
3731 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3733 block->raiseModified(MOD_STATE_WRITE_NEEDED);
3735 setBlockNotSent(blockpos);
3740 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3743 core::list<PlayerInfo> Server::getPlayerInfo()
3745 DSTACK(__FUNCTION_NAME);
3746 JMutexAutoLock envlock(m_env_mutex);
3747 JMutexAutoLock conlock(m_con_mutex);
3749 core::list<PlayerInfo> list;
3751 core::list<Player*> players = m_env->getPlayers();
3753 core::list<Player*>::Iterator i;
3754 for(i = players.begin();
3755 i != players.end(); i++)
3759 Player *player = *i;
3762 // Copy info from connection to info struct
3763 info.id = player->peer_id;
3764 info.address = m_con.GetPeerAddress(player->peer_id);
3765 info.avg_rtt = m_con.GetPeerAvgRTT(player->peer_id);
3767 catch(con::PeerNotFoundException &e)
3769 // Set dummy peer info
3771 info.address = Address(0,0,0,0,0);
3775 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3776 info.position = player->getPosition();
3778 list.push_back(info);
3785 void Server::peerAdded(con::Peer *peer)
3787 DSTACK(__FUNCTION_NAME);
3788 infostream<<"Server::peerAdded(): peer->id="
3789 <<peer->id<<std::endl;
3792 c.type = PEER_ADDED;
3793 c.peer_id = peer->id;
3795 m_peer_change_queue.push_back(c);
3798 void Server::deletingPeer(con::Peer *peer, bool timeout)
3800 DSTACK(__FUNCTION_NAME);
3801 infostream<<"Server::deletingPeer(): peer->id="
3802 <<peer->id<<", timeout="<<timeout<<std::endl;
3805 c.type = PEER_REMOVED;
3806 c.peer_id = peer->id;
3807 c.timeout = timeout;
3808 m_peer_change_queue.push_back(c);
3815 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3817 DSTACK(__FUNCTION_NAME);
3818 std::ostringstream os(std::ios_base::binary);
3820 writeU16(os, TOCLIENT_HP);
3824 std::string s = os.str();
3825 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3827 con.Send(peer_id, 0, data, true);
3830 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3831 const std::wstring &reason)
3833 DSTACK(__FUNCTION_NAME);
3834 std::ostringstream os(std::ios_base::binary);
3836 writeU16(os, TOCLIENT_ACCESS_DENIED);
3837 os<<serializeWideString(reason);
3840 std::string s = os.str();
3841 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3843 con.Send(peer_id, 0, data, true);
3846 void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
3847 bool set_camera_point_target, v3f camera_point_target)
3849 DSTACK(__FUNCTION_NAME);
3850 std::ostringstream os(std::ios_base::binary);
3852 writeU16(os, TOCLIENT_DEATHSCREEN);
3853 writeU8(os, set_camera_point_target);
3854 writeV3F1000(os, camera_point_target);
3857 std::string s = os.str();
3858 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3860 con.Send(peer_id, 0, data, true);
3863 void Server::SendToolDef(con::Connection &con, u16 peer_id,
3864 IToolDefManager *tooldef)
3866 DSTACK(__FUNCTION_NAME);
3867 std::ostringstream os(std::ios_base::binary);
3871 u32 length of the next item
3872 serialized ToolDefManager
3874 writeU16(os, TOCLIENT_TOOLDEF);
3875 std::ostringstream tmp_os(std::ios::binary);
3876 tooldef->serialize(tmp_os);
3877 os<<serializeLongString(tmp_os.str());
3880 std::string s = os.str();
3881 infostream<<"Server::SendToolDef(): Sending tool definitions: size="
3882 <<s.size()<<std::endl;
3883 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3885 con.Send(peer_id, 0, data, true);
3888 void Server::SendNodeDef(con::Connection &con, u16 peer_id,
3889 INodeDefManager *nodedef)
3891 DSTACK(__FUNCTION_NAME);
3892 std::ostringstream os(std::ios_base::binary);
3896 u32 length of the next item
3897 serialized NodeDefManager
3899 writeU16(os, TOCLIENT_NODEDEF);
3900 std::ostringstream tmp_os(std::ios::binary);
3901 nodedef->serialize(tmp_os);
3902 os<<serializeLongString(tmp_os.str());
3905 std::string s = os.str();
3906 infostream<<"Server::SendNodeDef(): Sending node definitions: size="
3907 <<s.size()<<std::endl;
3908 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3910 con.Send(peer_id, 0, data, true);
3913 void Server::SendCraftItemDef(con::Connection &con, u16 peer_id,
3914 ICraftItemDefManager *craftitemdef)
3916 DSTACK(__FUNCTION_NAME);
3917 std::ostringstream os(std::ios_base::binary);
3921 u32 length of the next item
3922 serialized CraftItemDefManager
3924 writeU16(os, TOCLIENT_CRAFTITEMDEF);
3925 std::ostringstream tmp_os(std::ios::binary);
3926 craftitemdef->serialize(tmp_os);
3927 os<<serializeLongString(tmp_os.str());
3930 std::string s = os.str();
3931 infostream<<"Server::SendCraftItemDef(): Sending craft item definitions: size="
3932 <<s.size()<<std::endl;
3933 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3935 con.Send(peer_id, 0, data, true);
3939 Non-static send methods
3942 void Server::SendObjectData(float dtime)
3944 DSTACK(__FUNCTION_NAME);
3946 core::map<v3s16, bool> stepped_blocks;
3948 for(core::map<u16, RemoteClient*>::Iterator
3949 i = m_clients.getIterator();
3950 i.atEnd() == false; i++)
3952 u16 peer_id = i.getNode()->getKey();
3953 RemoteClient *client = i.getNode()->getValue();
3954 assert(client->peer_id == peer_id);
3956 if(client->serialization_version == SER_FMT_VER_INVALID)
3959 client->SendObjectData(this, dtime, stepped_blocks);
3963 void Server::SendPlayerInfos()
3965 DSTACK(__FUNCTION_NAME);
3967 //JMutexAutoLock envlock(m_env_mutex);
3969 // Get connected players
3970 core::list<Player*> players = m_env->getPlayers(true);
3972 u32 player_count = players.getSize();
3973 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3975 SharedBuffer<u8> data(datasize);
3976 writeU16(&data[0], TOCLIENT_PLAYERINFO);
3979 core::list<Player*>::Iterator i;
3980 for(i = players.begin();
3981 i != players.end(); i++)
3983 Player *player = *i;
3985 /*infostream<<"Server sending player info for player with "
3986 "peer_id="<<player->peer_id<<std::endl;*/
3988 writeU16(&data[start], player->peer_id);
3989 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3990 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3991 start += 2+PLAYERNAME_SIZE;
3994 //JMutexAutoLock conlock(m_con_mutex);
3997 m_con.SendToAll(0, data, true);
4000 void Server::SendInventory(u16 peer_id)
4002 DSTACK(__FUNCTION_NAME);
4004 ServerRemotePlayer* player =
4005 static_cast<ServerRemotePlayer*>(m_env->getPlayer(peer_id));
4008 player->m_inventory_not_sent = false;
4014 std::ostringstream os;
4015 //os.imbue(std::locale("C"));
4017 player->inventory.serialize(os);
4019 std::string s = os.str();
4021 SharedBuffer<u8> data(s.size()+2);
4022 writeU16(&data[0], TOCLIENT_INVENTORY);
4023 memcpy(&data[2], s.c_str(), s.size());
4026 m_con.Send(peer_id, 0, data, true);
4029 std::string getWieldedItemString(const Player *player)
4031 const InventoryItem *item = player->getWieldItem();
4033 return std::string("");
4034 std::ostringstream os(std::ios_base::binary);
4035 item->serialize(os);
4039 void Server::SendWieldedItem(const Player* player)
4041 DSTACK(__FUNCTION_NAME);
4045 std::ostringstream os(std::ios_base::binary);
4047 writeU16(os, TOCLIENT_PLAYERITEM);
4049 writeU16(os, player->peer_id);
4050 os<<serializeString(getWieldedItemString(player));
4053 std::string s = os.str();
4054 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4056 m_con.SendToAll(0, data, true);
4059 void Server::SendPlayerItems()
4061 DSTACK(__FUNCTION_NAME);
4063 std::ostringstream os(std::ios_base::binary);
4064 core::list<Player *> players = m_env->getPlayers(true);
4066 writeU16(os, TOCLIENT_PLAYERITEM);
4067 writeU16(os, players.size());
4068 core::list<Player *>::Iterator i;
4069 for(i = players.begin(); i != players.end(); ++i)
4072 writeU16(os, p->peer_id);
4073 os<<serializeString(getWieldedItemString(p));
4077 std::string s = os.str();
4078 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4080 m_con.SendToAll(0, data, true);
4083 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
4085 DSTACK(__FUNCTION_NAME);
4087 std::ostringstream os(std::ios_base::binary);
4091 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
4092 os.write((char*)buf, 2);
4095 writeU16(buf, message.size());
4096 os.write((char*)buf, 2);
4099 for(u32 i=0; i<message.size(); i++)
4103 os.write((char*)buf, 2);
4107 std::string s = os.str();
4108 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4110 m_con.Send(peer_id, 0, data, true);
4113 void Server::BroadcastChatMessage(const std::wstring &message)
4115 for(core::map<u16, RemoteClient*>::Iterator
4116 i = m_clients.getIterator();
4117 i.atEnd() == false; i++)
4119 // Get client and check that it is valid
4120 RemoteClient *client = i.getNode()->getValue();
4121 assert(client->peer_id == i.getNode()->getKey());
4122 if(client->serialization_version == SER_FMT_VER_INVALID)
4125 SendChatMessage(client->peer_id, message);
4129 void Server::SendPlayerHP(Player *player)
4131 SendHP(m_con, player->peer_id, player->hp);
4134 void Server::SendMovePlayer(Player *player)
4136 DSTACK(__FUNCTION_NAME);
4137 std::ostringstream os(std::ios_base::binary);
4139 writeU16(os, TOCLIENT_MOVE_PLAYER);
4140 writeV3F1000(os, player->getPosition());
4141 writeF1000(os, player->getPitch());
4142 writeF1000(os, player->getYaw());
4145 v3f pos = player->getPosition();
4146 f32 pitch = player->getPitch();
4147 f32 yaw = player->getYaw();
4148 infostream<<"Server sending TOCLIENT_MOVE_PLAYER"
4149 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
4156 std::string s = os.str();
4157 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4159 m_con.Send(player->peer_id, 0, data, true);
4162 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
4163 core::list<u16> *far_players, float far_d_nodes)
4165 float maxd = far_d_nodes*BS;
4166 v3f p_f = intToFloat(p, BS);
4170 SharedBuffer<u8> reply(replysize);
4171 writeU16(&reply[0], TOCLIENT_REMOVENODE);
4172 writeS16(&reply[2], p.X);
4173 writeS16(&reply[4], p.Y);
4174 writeS16(&reply[6], p.Z);
4176 for(core::map<u16, RemoteClient*>::Iterator
4177 i = m_clients.getIterator();
4178 i.atEnd() == false; i++)
4180 // Get client and check that it is valid
4181 RemoteClient *client = i.getNode()->getValue();
4182 assert(client->peer_id == i.getNode()->getKey());
4183 if(client->serialization_version == SER_FMT_VER_INVALID)
4186 // Don't send if it's the same one
4187 if(client->peer_id == ignore_id)
4193 Player *player = m_env->getPlayer(client->peer_id);
4196 // If player is far away, only set modified blocks not sent
4197 v3f player_pos = player->getPosition();
4198 if(player_pos.getDistanceFrom(p_f) > maxd)
4200 far_players->push_back(client->peer_id);
4207 m_con.Send(client->peer_id, 0, reply, true);
4211 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
4212 core::list<u16> *far_players, float far_d_nodes)
4214 float maxd = far_d_nodes*BS;
4215 v3f p_f = intToFloat(p, BS);
4217 for(core::map<u16, RemoteClient*>::Iterator
4218 i = m_clients.getIterator();
4219 i.atEnd() == false; i++)
4221 // Get client and check that it is valid
4222 RemoteClient *client = i.getNode()->getValue();
4223 assert(client->peer_id == i.getNode()->getKey());
4224 if(client->serialization_version == SER_FMT_VER_INVALID)
4227 // Don't send if it's the same one
4228 if(client->peer_id == ignore_id)
4234 Player *player = m_env->getPlayer(client->peer_id);
4237 // If player is far away, only set modified blocks not sent
4238 v3f player_pos = player->getPosition();
4239 if(player_pos.getDistanceFrom(p_f) > maxd)
4241 far_players->push_back(client->peer_id);
4248 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
4249 SharedBuffer<u8> reply(replysize);
4250 writeU16(&reply[0], TOCLIENT_ADDNODE);
4251 writeS16(&reply[2], p.X);
4252 writeS16(&reply[4], p.Y);
4253 writeS16(&reply[6], p.Z);
4254 n.serialize(&reply[8], client->serialization_version);
4257 m_con.Send(client->peer_id, 0, reply, true);
4261 void Server::setBlockNotSent(v3s16 p)
4263 for(core::map<u16, RemoteClient*>::Iterator
4264 i = m_clients.getIterator();
4265 i.atEnd()==false; i++)
4267 RemoteClient *client = i.getNode()->getValue();
4268 client->SetBlockNotSent(p);
4272 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
4274 DSTACK(__FUNCTION_NAME);
4276 v3s16 p = block->getPos();
4280 bool completely_air = true;
4281 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4282 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4283 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4285 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
4287 completely_air = false;
4288 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
4293 infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
4295 infostream<<"[completely air] ";
4296 infostream<<std::endl;
4300 Create a packet with the block in the right format
4303 std::ostringstream os(std::ios_base::binary);
4304 block->serialize(os, ver);
4305 std::string s = os.str();
4306 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
4308 u32 replysize = 8 + blockdata.getSize();
4309 SharedBuffer<u8> reply(replysize);
4310 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
4311 writeS16(&reply[2], p.X);
4312 writeS16(&reply[4], p.Y);
4313 writeS16(&reply[6], p.Z);
4314 memcpy(&reply[8], *blockdata, blockdata.getSize());
4316 /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4317 <<": \tpacket size: "<<replysize<<std::endl;*/
4322 m_con.Send(peer_id, 1, reply, true);
4325 void Server::SendBlocks(float dtime)
4327 DSTACK(__FUNCTION_NAME);
4329 JMutexAutoLock envlock(m_env_mutex);
4330 JMutexAutoLock conlock(m_con_mutex);
4332 //TimeTaker timer("Server::SendBlocks");
4334 core::array<PrioritySortedBlockTransfer> queue;
4336 s32 total_sending = 0;
4339 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
4341 for(core::map<u16, RemoteClient*>::Iterator
4342 i = m_clients.getIterator();
4343 i.atEnd() == false; i++)
4345 RemoteClient *client = i.getNode()->getValue();
4346 assert(client->peer_id == i.getNode()->getKey());
4348 total_sending += client->SendingCount();
4350 if(client->serialization_version == SER_FMT_VER_INVALID)
4353 client->GetNextBlocks(this, dtime, queue);
4358 // Lowest priority number comes first.
4359 // Lowest is most important.
4362 for(u32 i=0; i<queue.size(); i++)
4364 //TODO: Calculate limit dynamically
4365 if(total_sending >= g_settings->getS32
4366 ("max_simultaneous_block_sends_server_total"))
4369 PrioritySortedBlockTransfer q = queue[i];
4371 MapBlock *block = NULL;
4374 block = m_env->getMap().getBlockNoCreate(q.pos);
4376 catch(InvalidPositionException &e)
4381 RemoteClient *client = getClient(q.peer_id);
4383 SendBlockNoLock(q.peer_id, block, client->serialization_version);
4385 client->SentBlock(q.pos);
4391 struct SendableTexture
4397 SendableTexture(const std::string &name_="", const std::string path_="",
4398 const std::string &data_=""):
4405 void Server::SendTextures(u16 peer_id)
4407 DSTACK(__FUNCTION_NAME);
4409 infostream<<"Server::SendTextures(): Sending textures to client"<<std::endl;
4413 // Put 5kB in one bunch (this is not accurate)
4414 u32 bytes_per_bunch = 5000;
4416 core::array< core::list<SendableTexture> > texture_bunches;
4417 texture_bunches.push_back(core::list<SendableTexture>());
4419 u32 texture_size_bunch_total = 0;
4420 core::list<ModSpec> mods = getMods(m_modspaths);
4421 for(core::list<ModSpec>::Iterator i = mods.begin();
4422 i != mods.end(); i++){
4424 std::string texturepath = mod.path + DIR_DELIM + "textures";
4425 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(texturepath);
4426 for(u32 j=0; j<dirlist.size(); j++){
4427 if(dirlist[j].dir) // Ignode dirs
4429 std::string tname = dirlist[j].name;
4430 std::string tpath = texturepath + DIR_DELIM + tname;
4432 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
4433 if(fis.good() == false){
4434 errorstream<<"Server::SendTextures(): Could not open \""
4435 <<tname<<"\" for reading"<<std::endl;
4438 std::ostringstream tmp_os(std::ios_base::binary);
4442 fis.read(buf, 1024);
4443 std::streamsize len = fis.gcount();
4444 tmp_os.write(buf, len);
4445 texture_size_bunch_total += len;
4454 errorstream<<"Server::SendTextures(): Failed to read \""
4455 <<tname<<"\""<<std::endl;
4458 /*infostream<<"Server::SendTextures(): Loaded \""
4459 <<tname<<"\""<<std::endl;*/
4461 texture_bunches[texture_bunches.size()-1].push_back(
4462 SendableTexture(tname, tpath, tmp_os.str()));
4464 // Start next bunch if got enough data
4465 if(texture_size_bunch_total >= bytes_per_bunch){
4466 texture_bunches.push_back(core::list<SendableTexture>());
4467 texture_size_bunch_total = 0;
4472 /* Create and send packets */
4474 u32 num_bunches = texture_bunches.size();
4475 for(u32 i=0; i<num_bunches; i++)
4479 u16 total number of texture bunches
4480 u16 index of this bunch
4481 u32 number of textures in this bunch
4489 std::ostringstream os(std::ios_base::binary);
4491 writeU16(os, TOCLIENT_TEXTURES);
4492 writeU16(os, num_bunches);
4494 writeU32(os, texture_bunches[i].size());
4496 for(core::list<SendableTexture>::Iterator
4497 j = texture_bunches[i].begin();
4498 j != texture_bunches[i].end(); j++){
4499 os<<serializeString(j->name);
4500 os<<serializeLongString(j->data);
4504 std::string s = os.str();
4505 infostream<<"Server::SendTextures(): bunch "<<i<<"/"<<num_bunches
4506 <<" textures="<<texture_bunches[i].size()
4507 <<" size=" <<s.size()<<std::endl;
4508 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4510 m_con.Send(peer_id, 0, data, true);
4518 void Server::HandlePlayerHP(Player *player, s16 damage)
4520 if(player->hp > damage)
4522 player->hp -= damage;
4523 SendPlayerHP(player);
4527 infostream<<"Server::HandlePlayerHP(): Player "
4528 <<player->getName()<<" dies"<<std::endl;
4532 //TODO: Throw items around
4534 // Handle players that are not connected
4535 if(player->peer_id == PEER_ID_INEXISTENT){
4536 RespawnPlayer(player);
4540 SendPlayerHP(player);
4542 RemoteClient *client = getClient(player->peer_id);
4543 if(client->net_proto_version >= 3)
4545 SendDeathscreen(m_con, player->peer_id, false, v3f(0,0,0));
4549 RespawnPlayer(player);
4554 void Server::RespawnPlayer(Player *player)
4557 ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
4558 bool repositioned = scriptapi_on_respawnplayer(m_lua, srp);
4560 v3f pos = findSpawnPos(m_env->getServerMap());
4561 player->setPosition(pos);
4562 srp->m_last_good_position = pos;
4563 srp->m_last_good_position_age = 0;
4565 SendMovePlayer(player);
4566 SendPlayerHP(player);
4569 void Server::UpdateCrafting(u16 peer_id)
4571 DSTACK(__FUNCTION_NAME);
4573 Player* player = m_env->getPlayer(peer_id);
4575 ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
4577 // No crafting in creative mode
4578 if(g_settings->getBool("creative_mode"))
4581 // Get the InventoryLists of the player in which we will operate
4582 InventoryList *clist = player->inventory.getList("craft");
4584 InventoryList *rlist = player->inventory.getList("craftresult");
4586 InventoryList *mlist = player->inventory.getList("main");
4589 // If the result list is not a preview and is not empty, try to
4590 // throw the item into main list
4591 if(!player->craftresult_is_preview && rlist->getUsedSlots() != 0)
4593 // Grab item out of craftresult
4594 InventoryItem *item = rlist->changeItem(0, NULL);
4595 // Try to put in main
4596 InventoryItem *leftover = mlist->addItem(item);
4597 // If there are leftovers, put them back to craftresult and
4599 delete rlist->addItem(leftover);
4600 // Inventory was modified
4601 srp->m_inventory_not_sent = true;
4604 // If result list is empty, we will make it preview what would be
4606 if(rlist->getUsedSlots() == 0)
4607 player->craftresult_is_preview = true;
4609 // If it is a preview, clear the possible old preview in it
4610 if(player->craftresult_is_preview)
4611 rlist->clearItems();
4613 // If it is a preview, find out what is the crafting result
4615 if(player->craftresult_is_preview)
4617 // Mangle crafting grid to an another format
4618 std::vector<InventoryItem*> items;
4619 for(u16 i=0; i<9; i++){
4620 if(clist->getItem(i) == NULL)
4621 items.push_back(NULL);
4623 items.push_back(clist->getItem(i)->clone());
4625 CraftPointerInput cpi(3, items);
4627 // Find out what is crafted and add it to result item slot
4628 InventoryItem *result = m_craftdef->getCraftResult(cpi, this);
4630 rlist->addItem(result);
4634 RemoteClient* Server::getClient(u16 peer_id)
4636 DSTACK(__FUNCTION_NAME);
4637 //JMutexAutoLock lock(m_con_mutex);
4638 core::map<u16, RemoteClient*>::Node *n;
4639 n = m_clients.find(peer_id);
4640 // A client should exist for all peers
4642 return n->getValue();
4645 std::wstring Server::getStatusString()
4647 std::wostringstream os(std::ios_base::binary);
4650 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4652 os<<L", uptime="<<m_uptime.get();
4653 // Information about clients
4655 for(core::map<u16, RemoteClient*>::Iterator
4656 i = m_clients.getIterator();
4657 i.atEnd() == false; i++)
4659 // Get client and check that it is valid
4660 RemoteClient *client = i.getNode()->getValue();
4661 assert(client->peer_id == i.getNode()->getKey());
4662 if(client->serialization_version == SER_FMT_VER_INVALID)
4665 Player *player = m_env->getPlayer(client->peer_id);
4666 // Get name of player
4667 std::wstring name = L"unknown";
4669 name = narrow_to_wide(player->getName());
4670 // Add name to information string
4674 if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
4675 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4676 if(g_settings->get("motd") != "")
4677 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4681 // Saves g_settings to configpath given at initialization
4682 void Server::saveConfig()
4684 if(m_configpath != "")
4685 g_settings->updateConfigFile(m_configpath.c_str());
4688 void Server::notifyPlayer(const char *name, const std::wstring msg)
4690 Player *player = m_env->getPlayer(name);
4693 SendChatMessage(player->peer_id, std::wstring(L"Server: -!- ")+msg);
4696 void Server::notifyPlayers(const std::wstring msg)
4698 BroadcastChatMessage(msg);
4701 void Server::queueBlockEmerge(v3s16 blockpos, bool allow_generate)
4705 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
4706 m_emerge_queue.addBlock(PEER_ID_INEXISTENT, blockpos, flags);
4709 // IGameDef interface
4711 IToolDefManager* Server::getToolDefManager()
4715 INodeDefManager* Server::getNodeDefManager()
4719 ICraftDefManager* Server::getCraftDefManager()
4723 ICraftItemDefManager* Server::getCraftItemDefManager()
4725 return m_craftitemdef;
4727 ITextureSource* Server::getTextureSource()
4731 u16 Server::allocateUnknownNodeId(const std::string &name)
4733 return m_nodedef->allocateDummy(name);
4736 IWritableToolDefManager* Server::getWritableToolDefManager()
4740 IWritableNodeDefManager* Server::getWritableNodeDefManager()
4744 IWritableCraftDefManager* Server::getWritableCraftDefManager()
4748 IWritableCraftItemDefManager* Server::getWritableCraftItemDefManager()
4750 return m_craftitemdef;
4753 v3f findSpawnPos(ServerMap &map)
4755 //return v3f(50,50,50)*BS;
4760 nodepos = v2s16(0,0);
4765 // Try to find a good place a few times
4766 for(s32 i=0; i<1000; i++)
4769 // We're going to try to throw the player to this position
4770 v2s16 nodepos2d = v2s16(-range + (myrand()%(range*2)),
4771 -range + (myrand()%(range*2)));
4772 //v2s16 sectorpos = getNodeSectorPos(nodepos2d);
4773 // Get ground height at point (fallbacks to heightmap function)
4774 s16 groundheight = map.findGroundLevel(nodepos2d);
4775 // Don't go underwater
4776 if(groundheight < WATER_LEVEL)
4778 //infostream<<"-> Underwater"<<std::endl;
4781 // Don't go to high places
4782 if(groundheight > WATER_LEVEL + 4)
4784 //infostream<<"-> Underwater"<<std::endl;
4788 nodepos = v3s16(nodepos2d.X, groundheight-2, nodepos2d.Y);
4789 bool is_good = false;
4791 for(s32 i=0; i<10; i++){
4792 v3s16 blockpos = getNodeBlockPos(nodepos);
4793 map.emergeBlock(blockpos, true);
4794 MapNode n = map.getNodeNoEx(nodepos);
4795 if(n.getContent() == CONTENT_AIR){
4806 // Found a good place
4807 //infostream<<"Searched through "<<i<<" places."<<std::endl;
4813 return intToFloat(nodepos, BS);
4816 Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id)
4819 Try to get an existing player
4821 Player *player = m_env->getPlayer(name);
4824 // If player is already connected, cancel
4825 if(player->peer_id != 0)
4827 infostream<<"emergePlayer(): Player already connected"<<std::endl;
4832 player->peer_id = peer_id;
4834 // Reset inventory to creative if in creative mode
4835 if(g_settings->getBool("creative_mode"))
4837 // Warning: double code below
4838 // Backup actual inventory
4839 player->inventory_backup = new Inventory();
4840 *(player->inventory_backup) = player->inventory;
4841 // Set creative inventory
4842 craft_set_creative_inventory(player, this);
4849 If player with the wanted peer_id already exists, cancel.
4851 if(m_env->getPlayer(peer_id) != NULL)
4853 infostream<<"emergePlayer(): Player with wrong name but same"
4854 " peer_id already exists"<<std::endl;
4862 // Add authentication stuff
4863 m_authmanager.add(name);
4864 m_authmanager.setPassword(name, password);
4865 m_authmanager.setPrivs(name,
4866 stringToPrivs(g_settings->get("default_privs")));
4868 /* Set player position */
4870 infostream<<"Server: Finding spawn place for player \""
4871 <<name<<"\""<<std::endl;
4873 v3f pos = findSpawnPos(m_env->getServerMap());
4875 player = new ServerRemotePlayer(m_env, pos, peer_id, name);
4877 /* Add player to environment */
4878 m_env->addPlayer(player);
4881 ServerRemotePlayer *srp = static_cast<ServerRemotePlayer*>(player);
4882 scriptapi_on_newplayer(m_lua, srp);
4884 /* Add stuff to inventory */
4885 if(g_settings->getBool("creative_mode"))
4887 // Warning: double code above
4888 // Backup actual inventory
4889 player->inventory_backup = new Inventory();
4890 *(player->inventory_backup) = player->inventory;
4891 // Set creative inventory
4892 craft_set_creative_inventory(player, this);
4897 } // create new player
4900 void Server::handlePeerChange(PeerChange &c)
4902 JMutexAutoLock envlock(m_env_mutex);
4903 JMutexAutoLock conlock(m_con_mutex);
4905 if(c.type == PEER_ADDED)
4912 core::map<u16, RemoteClient*>::Node *n;
4913 n = m_clients.find(c.peer_id);
4914 // The client shouldn't already exist
4918 RemoteClient *client = new RemoteClient();
4919 client->peer_id = c.peer_id;
4920 m_clients.insert(client->peer_id, client);
4923 else if(c.type == PEER_REMOVED)
4930 core::map<u16, RemoteClient*>::Node *n;
4931 n = m_clients.find(c.peer_id);
4932 // The client should exist
4936 Mark objects to be not known by the client
4938 RemoteClient *client = n->getValue();
4940 for(core::map<u16, bool>::Iterator
4941 i = client->m_known_objects.getIterator();
4942 i.atEnd()==false; i++)
4945 u16 id = i.getNode()->getKey();
4946 ServerActiveObject* obj = m_env->getActiveObject(id);
4948 if(obj && obj->m_known_by_count > 0)
4949 obj->m_known_by_count--;
4952 // Collect information about leaving in chat
4953 std::wstring message;
4955 Player *player = m_env->getPlayer(c.peer_id);
4958 std::wstring name = narrow_to_wide(player->getName());
4961 message += L" left game";
4963 message += L" (timed out)";
4969 m_env->removePlayer(c.peer_id);
4972 // Set player client disconnected
4974 Player *player = m_env->getPlayer(c.peer_id);
4976 player->peer_id = 0;
4983 std::ostringstream os(std::ios_base::binary);
4984 for(core::map<u16, RemoteClient*>::Iterator
4985 i = m_clients.getIterator();
4986 i.atEnd() == false; i++)
4988 RemoteClient *client = i.getNode()->getValue();
4989 assert(client->peer_id == i.getNode()->getKey());
4990 if(client->serialization_version == SER_FMT_VER_INVALID)
4993 Player *player = m_env->getPlayer(client->peer_id);
4996 // Get name of player
4997 os<<player->getName()<<" ";
5000 actionstream<<player->getName()<<" "
5001 <<(c.timeout?"times out.":"leaves game.")
5002 <<" List of players: "
5003 <<os.str()<<std::endl;
5008 delete m_clients[c.peer_id];
5009 m_clients.remove(c.peer_id);
5011 // Send player info to all remaining clients
5014 // Send leave chat message to all remaining clients
5015 BroadcastChatMessage(message);
5024 void Server::handlePeerChanges()
5026 while(m_peer_change_queue.size() > 0)
5028 PeerChange c = m_peer_change_queue.pop_front();
5030 infostream<<"Server: Handling peer change: "
5031 <<"id="<<c.peer_id<<", timeout="<<c.timeout
5034 handlePeerChange(c);
5038 u64 Server::getPlayerPrivs(Player *player)
5042 std::string playername = player->getName();
5043 // Local player gets all privileges regardless of
5044 // what's set on their account.
5045 if(g_settings->get("name") == playername)
5051 return getPlayerAuthPrivs(playername);
5055 void dedicated_server_loop(Server &server, bool &kill)
5057 DSTACK(__FUNCTION_NAME);
5059 infostream<<DTIME<<std::endl;
5060 infostream<<"========================"<<std::endl;
5061 infostream<<"Running dedicated server"<<std::endl;
5062 infostream<<"========================"<<std::endl;
5063 infostream<<std::endl;
5065 IntervalLimiter m_profiler_interval;
5069 // This is kind of a hack but can be done like this
5070 // because server.step() is very light
5072 ScopeProfiler sp(g_profiler, "dedicated server sleep");
5077 if(server.getShutdownRequested() || kill)
5079 infostream<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
5086 float profiler_print_interval =
5087 g_settings->getFloat("profiler_print_interval");
5088 if(profiler_print_interval != 0)
5090 if(m_profiler_interval.step(0.030, profiler_print_interval))
5092 infostream<<"Profiler:"<<std::endl;
5093 g_profiler->print(infostream);
5094 g_profiler->clear();
5101 static int counter = 0;
5107 core::list<PlayerInfo> list = server.getPlayerInfo();
5108 core::list<PlayerInfo>::Iterator i;
5109 static u32 sum_old = 0;
5110 u32 sum = PIChecksum(list);
5113 infostream<<DTIME<<"Player info:"<<std::endl;
5114 for(i=list.begin(); i!=list.end(); i++)
5116 i->PrintLine(&infostream);