3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
23 #include "clientserver.h"
25 #include "jmutexautolock.h"
27 #include "constants.h"
29 #include "materials.h"
32 #include "servercommand.h"
34 #include "content_mapnode.h"
35 #include "content_craft.h"
36 #include "content_nodemeta.h"
38 #include "serverobject.h"
43 #include "scriptapi.h"
48 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
50 #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
52 class MapEditEventIgnorer
55 MapEditEventIgnorer(bool *flag):
64 ~MapEditEventIgnorer()
77 void * ServerThread::Thread()
81 log_register_thread("ServerThread");
83 DSTACK(__FUNCTION_NAME);
85 BEGIN_DEBUG_EXCEPTION_HANDLER
90 //TimeTaker timer("AsyncRunStep() + Receive()");
93 //TimeTaker timer("AsyncRunStep()");
94 m_server->AsyncRunStep();
97 //infostream<<"Running m_server->Receive()"<<std::endl;
100 catch(con::NoIncomingDataException &e)
103 catch(con::PeerNotFoundException &e)
105 infostream<<"Server: PeerNotFoundException"<<std::endl;
109 END_DEBUG_EXCEPTION_HANDLER(errorstream)
114 void * EmergeThread::Thread()
118 log_register_thread("EmergeThread");
120 DSTACK(__FUNCTION_NAME);
122 BEGIN_DEBUG_EXCEPTION_HANDLER
124 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
127 Get block info from queue, emerge them and send them
130 After queue is empty, exit.
134 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
138 SharedPtr<QueuedBlockEmerge> q(qptr);
144 Do not generate over-limit
146 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
147 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
148 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
149 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
150 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
151 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
154 //infostream<<"EmergeThread::Thread(): running"<<std::endl;
156 //TimeTaker timer("block emerge");
159 Try to emerge it from somewhere.
161 If it is only wanted as optional, only loading from disk
166 Check if any peer wants it as non-optional. In that case it
169 Also decrement the emerge queue count in clients.
172 bool only_from_disk = true;
175 core::map<u16, u8>::Iterator i;
176 for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
178 //u16 peer_id = i.getNode()->getKey();
181 u8 flags = i.getNode()->getValue();
182 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
183 only_from_disk = false;
188 if(enable_mapgen_debug_info)
189 infostream<<"EmergeThread: p="
190 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
191 <<"only_from_disk="<<only_from_disk<<std::endl;
193 ServerMap &map = ((ServerMap&)m_server->m_env->getMap());
195 //core::map<v3s16, MapBlock*> changed_blocks;
196 //core::map<v3s16, MapBlock*> lighting_invalidated_blocks;
198 MapBlock *block = NULL;
199 bool got_block = true;
200 core::map<v3s16, MapBlock*> modified_blocks;
203 Fetch block from map or generate a single block
206 JMutexAutoLock envlock(m_server->m_env_mutex);
208 // Load sector if it isn't loaded
209 if(map.getSectorNoGenerateNoEx(p2d) == NULL)
210 //map.loadSectorFull(p2d);
211 map.loadSectorMeta(p2d);
213 block = map.getBlockNoCreateNoEx(p);
214 if(!block || block->isDummy() || !block->isGenerated())
216 if(enable_mapgen_debug_info)
217 infostream<<"EmergeThread: not in memory, loading"<<std::endl;
219 // Get, load or create sector
220 /*ServerMapSector *sector =
221 (ServerMapSector*)map.createSector(p2d);*/
223 // Load/generate block
225 /*block = map.emergeBlock(p, sector, changed_blocks,
226 lighting_invalidated_blocks);*/
228 block = map.loadBlock(p);
230 if(only_from_disk == false)
232 if(block == NULL || block->isGenerated() == false)
234 if(enable_mapgen_debug_info)
235 infostream<<"EmergeThread: generating"<<std::endl;
236 block = map.generateBlock(p, modified_blocks);
240 if(enable_mapgen_debug_info)
241 infostream<<"EmergeThread: ended up with: "
242 <<analyze_block(block)<<std::endl;
251 Ignore map edit events, they will not need to be
252 sent to anybody because the block hasn't been sent
255 MapEditEventIgnorer ign(&m_server->m_ignore_map_edit_events);
257 // Activate objects and stuff
258 m_server->m_env->activateBlock(block, 3600);
263 /*if(block->getLightingExpired()){
264 lighting_invalidated_blocks[block->getPos()] = block;
268 // TODO: Some additional checking and lighting updating,
273 JMutexAutoLock envlock(m_server->m_env_mutex);
278 Collect a list of blocks that have been modified in
279 addition to the fetched one.
283 if(lighting_invalidated_blocks.size() > 0)
285 /*infostream<<"lighting "<<lighting_invalidated_blocks.size()
286 <<" blocks"<<std::endl;*/
288 // 50-100ms for single block generation
289 //TimeTaker timer("** EmergeThread updateLighting");
291 // Update lighting without locking the environment mutex,
292 // add modified blocks to changed blocks
293 map.updateLighting(lighting_invalidated_blocks, modified_blocks);
296 // Add all from changed_blocks to modified_blocks
297 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
298 i.atEnd() == false; i++)
300 MapBlock *block = i.getNode()->getValue();
301 modified_blocks.insert(block->getPos(), block);
305 // If we got no block, there should be no invalidated blocks
308 //assert(lighting_invalidated_blocks.size() == 0);
314 Set sent status of modified blocks on clients
317 // NOTE: Server's clients are also behind the connection mutex
318 JMutexAutoLock lock(m_server->m_con_mutex);
321 Add the originally fetched block to the modified list
325 modified_blocks.insert(p, block);
329 Set the modified blocks unsent for all the clients
332 for(core::map<u16, RemoteClient*>::Iterator
333 i = m_server->m_clients.getIterator();
334 i.atEnd() == false; i++)
336 RemoteClient *client = i.getNode()->getValue();
338 if(modified_blocks.size() > 0)
340 // Remove block from sent history
341 client->SetBlocksNotSent(modified_blocks);
347 END_DEBUG_EXCEPTION_HANDLER(errorstream)
352 void RemoteClient::GetNextBlocks(Server *server, float dtime,
353 core::array<PrioritySortedBlockTransfer> &dest)
355 DSTACK(__FUNCTION_NAME);
358 TimeTaker timer("RemoteClient::GetNextBlocks", &timer_result);*/
361 m_nothing_to_send_pause_timer -= dtime;
362 m_nearest_unsent_reset_timer += dtime;
364 if(m_nothing_to_send_pause_timer >= 0)
369 // Won't send anything if already sending
370 if(m_blocks_sending.size() >= g_settings->getU16
371 ("max_simultaneous_block_sends_per_client"))
373 //infostream<<"Not sending any blocks, Queue full."<<std::endl;
377 //TimeTaker timer("RemoteClient::GetNextBlocks");
379 Player *player = server->m_env->getPlayer(peer_id);
381 assert(player != NULL);
383 v3f playerpos = player->getPosition();
384 v3f playerspeed = player->getSpeed();
385 v3f playerspeeddir(0,0,0);
386 if(playerspeed.getLength() > 1.0*BS)
387 playerspeeddir = playerspeed / playerspeed.getLength();
388 // Predict to next block
389 v3f playerpos_predicted = playerpos + playerspeeddir*MAP_BLOCKSIZE*BS;
391 v3s16 center_nodepos = floatToInt(playerpos_predicted, BS);
393 v3s16 center = getNodeBlockPos(center_nodepos);
395 // Camera position and direction
396 v3f camera_pos = player->getEyePosition();
397 v3f camera_dir = v3f(0,0,1);
398 camera_dir.rotateYZBy(player->getPitch());
399 camera_dir.rotateXZBy(player->getYaw());
401 /*infostream<<"camera_dir=("<<camera_dir.X<<","<<camera_dir.Y<<","
402 <<camera_dir.Z<<")"<<std::endl;*/
405 Get the starting value of the block finder radius.
408 if(m_last_center != center)
410 m_nearest_unsent_d = 0;
411 m_last_center = center;
414 /*infostream<<"m_nearest_unsent_reset_timer="
415 <<m_nearest_unsent_reset_timer<<std::endl;*/
417 // Reset periodically to workaround for some bugs or stuff
418 if(m_nearest_unsent_reset_timer > 20.0)
420 m_nearest_unsent_reset_timer = 0;
421 m_nearest_unsent_d = 0;
422 //infostream<<"Resetting m_nearest_unsent_d for "
423 // <<server->getPlayerName(peer_id)<<std::endl;
426 //s16 last_nearest_unsent_d = m_nearest_unsent_d;
427 s16 d_start = m_nearest_unsent_d;
429 //infostream<<"d_start="<<d_start<<std::endl;
431 u16 max_simul_sends_setting = g_settings->getU16
432 ("max_simultaneous_block_sends_per_client");
433 u16 max_simul_sends_usually = max_simul_sends_setting;
436 Check the time from last addNode/removeNode.
438 Decrease send rate if player is building stuff.
440 m_time_from_building += dtime;
441 if(m_time_from_building < g_settings->getFloat(
442 "full_block_send_enable_min_time_from_building"))
444 max_simul_sends_usually
445 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
449 Number of blocks sending + number of blocks selected for sending
451 u32 num_blocks_selected = m_blocks_sending.size();
454 next time d will be continued from the d from which the nearest
455 unsent block was found this time.
457 This is because not necessarily any of the blocks found this
458 time are actually sent.
460 s32 new_nearest_unsent_d = -1;
462 s16 d_max = g_settings->getS16("max_block_send_distance");
463 s16 d_max_gen = g_settings->getS16("max_block_generate_distance");
465 // Don't loop very much at a time
466 s16 max_d_increment_at_time = 2;
467 if(d_max > d_start + max_d_increment_at_time)
468 d_max = d_start + max_d_increment_at_time;
469 /*if(d_max_gen > d_start+2)
470 d_max_gen = d_start+2;*/
472 //infostream<<"Starting from "<<d_start<<std::endl;
474 s32 nearest_emerged_d = -1;
475 s32 nearest_emergefull_d = -1;
476 s32 nearest_sent_d = -1;
477 bool queue_is_full = false;
480 for(d = d_start; d <= d_max; d++)
482 /*errorstream<<"checking d="<<d<<" for "
483 <<server->getPlayerName(peer_id)<<std::endl;*/
484 //infostream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
487 If m_nearest_unsent_d was changed by the EmergeThread
488 (it can change it to 0 through SetBlockNotSent),
490 Else update m_nearest_unsent_d
492 /*if(m_nearest_unsent_d != last_nearest_unsent_d)
494 d = m_nearest_unsent_d;
495 last_nearest_unsent_d = m_nearest_unsent_d;
499 Get the border/face dot coordinates of a "d-radiused"
502 core::list<v3s16> list;
503 getFacePositions(list, d);
505 core::list<v3s16>::Iterator li;
506 for(li=list.begin(); li!=list.end(); li++)
508 v3s16 p = *li + center;
512 - Don't allow too many simultaneous transfers
513 - EXCEPT when the blocks are very close
515 Also, don't send blocks that are already flying.
518 // Start with the usual maximum
519 u16 max_simul_dynamic = max_simul_sends_usually;
521 // If block is very close, allow full maximum
522 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
523 max_simul_dynamic = max_simul_sends_setting;
525 // Don't select too many blocks for sending
526 if(num_blocks_selected >= max_simul_dynamic)
528 queue_is_full = true;
529 goto queue_full_break;
532 // Don't send blocks that are currently being transferred
533 if(m_blocks_sending.find(p) != NULL)
539 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
540 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
541 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
542 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
543 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
544 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
547 // If this is true, inexistent block will be made from scratch
548 bool generate = d <= d_max_gen;
551 /*// Limit the generating area vertically to 2/3
552 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
555 // Limit the send area vertically to 1/2
556 if(abs(p.Y - center.Y) > d_max / 2)
562 If block is far away, don't generate it unless it is
568 // Block center y in nodes
569 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
570 // Don't generate if it's very high or very low
571 if(y < -64 || y > 64)
575 v2s16 p2d_nodes_center(
579 // Get ground height in nodes
580 s16 gh = server->m_env->getServerMap().findGroundLevel(
583 // If differs a lot, don't generate
584 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
586 // Actually, don't even send it
592 //infostream<<"d="<<d<<std::endl;
595 Don't generate or send if not in sight
596 FIXME This only works if the client uses a small enough
597 FOV setting. The default of 72 degrees is fine.
600 float camera_fov = (72.0*PI/180) * 4./3.;
601 if(isBlockInSight(p, camera_pos, camera_dir, camera_fov, 10000*BS) == false)
607 Don't send already sent blocks
610 if(m_blocks_sent.find(p) != NULL)
617 Check if map has this block
619 MapBlock *block = server->m_env->getMap().getBlockNoCreateNoEx(p);
621 bool surely_not_found_on_disk = false;
622 bool block_is_invalid = false;
625 // Reset usage timer, this block will be of use in the future.
626 block->resetUsageTimer();
628 // Block is dummy if data doesn't exist.
629 // It means it has been not found from disk and not generated
632 surely_not_found_on_disk = true;
635 // Block is valid if lighting is up-to-date and data exists
636 if(block->isValid() == false)
638 block_is_invalid = true;
641 /*if(block->isFullyGenerated() == false)
643 block_is_invalid = true;
648 ServerMap *map = (ServerMap*)(&server->m_env->getMap());
649 v2s16 chunkpos = map->sector_to_chunk(p2d);
650 if(map->chunkNonVolatile(chunkpos) == false)
651 block_is_invalid = true;
653 if(block->isGenerated() == false)
654 block_is_invalid = true;
657 If block is not close, don't send it unless it is near
660 Block is near ground level if night-time mesh
661 differs from day-time mesh.
665 if(block->dayNightDiffed() == false)
672 If block has been marked to not exist on disk (dummy)
673 and generating new ones is not wanted, skip block.
675 if(generate == false && surely_not_found_on_disk == true)
682 Add inexistent block to emerge queue.
684 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
686 //TODO: Get value from somewhere
687 // Allow only one block in emerge queue
688 //if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
689 // Allow two blocks in queue per client
690 //if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
692 // Make it more responsive when needing to generate stuff
693 if(surely_not_found_on_disk)
695 if(server->m_emerge_queue.peerItemCount(peer_id) < max_emerge)
697 //infostream<<"Adding block to emerge queue"<<std::endl;
699 // Add it to the emerge queue and trigger the thread
702 if(generate == false)
703 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
705 server->m_emerge_queue.addBlock(peer_id, p, flags);
706 server->m_emergethread.trigger();
708 if(nearest_emerged_d == -1)
709 nearest_emerged_d = d;
711 if(nearest_emergefull_d == -1)
712 nearest_emergefull_d = d;
719 if(nearest_sent_d == -1)
723 Add block to send queue
726 /*errorstream<<"sending from d="<<d<<" to "
727 <<server->getPlayerName(peer_id)<<std::endl;*/
729 PrioritySortedBlockTransfer q((float)d, p, peer_id);
733 num_blocks_selected += 1;
738 //infostream<<"Stopped at "<<d<<std::endl;
740 // If nothing was found for sending and nothing was queued for
741 // emerging, continue next time browsing from here
742 if(nearest_emerged_d != -1){
743 new_nearest_unsent_d = nearest_emerged_d;
744 } else if(nearest_emergefull_d != -1){
745 new_nearest_unsent_d = nearest_emergefull_d;
747 if(d > g_settings->getS16("max_block_send_distance")){
748 new_nearest_unsent_d = 0;
749 m_nothing_to_send_pause_timer = 2.0;
750 /*infostream<<"GetNextBlocks(): d wrapped around for "
751 <<server->getPlayerName(peer_id)
752 <<"; setting to 0 and pausing"<<std::endl;*/
754 if(nearest_sent_d != -1)
755 new_nearest_unsent_d = nearest_sent_d;
757 new_nearest_unsent_d = d;
761 if(new_nearest_unsent_d != -1)
762 m_nearest_unsent_d = new_nearest_unsent_d;
764 /*timer_result = timer.stop(true);
765 if(timer_result != 0)
766 infostream<<"GetNextBlocks duration: "<<timer_result<<" (!=0)"<<std::endl;*/
769 void RemoteClient::SendObjectData(
772 core::map<v3s16, bool> &stepped_blocks
775 DSTACK(__FUNCTION_NAME);
777 // Can't send anything without knowing version
778 if(serialization_version == SER_FMT_VER_INVALID)
780 infostream<<"RemoteClient::SendObjectData(): Not sending, no version."
786 Send a TOCLIENT_OBJECTDATA packet.
790 u16 number of player positions
802 std::ostringstream os(std::ios_base::binary);
806 writeU16(buf, TOCLIENT_OBJECTDATA);
807 os.write((char*)buf, 2);
810 Get and write player data
813 // Get connected players
814 core::list<Player*> players = server->m_env->getPlayers(true);
816 // Write player count
817 u16 playercount = players.size();
818 writeU16(buf, playercount);
819 os.write((char*)buf, 2);
821 core::list<Player*>::Iterator i;
822 for(i = players.begin();
823 i != players.end(); i++)
827 v3f pf = player->getPosition();
828 v3f sf = player->getSpeed();
830 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
831 v3s32 speed_i (sf.X*100, sf.Y*100, sf.Z*100);
832 s32 pitch_i (player->getPitch() * 100);
833 s32 yaw_i (player->getYaw() * 100);
835 writeU16(buf, player->peer_id);
836 os.write((char*)buf, 2);
837 writeV3S32(buf, position_i);
838 os.write((char*)buf, 12);
839 writeV3S32(buf, speed_i);
840 os.write((char*)buf, 12);
841 writeS32(buf, pitch_i);
842 os.write((char*)buf, 4);
843 writeS32(buf, yaw_i);
844 os.write((char*)buf, 4);
848 Get and write object data (dummy, for compatibility)
853 os.write((char*)buf, 2);
859 //infostream<<"Server: Sending object data to "<<peer_id<<std::endl;
862 std::string s = os.str();
863 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
864 // Send as unreliable
865 server->m_con.Send(peer_id, 0, data, false);
868 void RemoteClient::GotBlock(v3s16 p)
870 if(m_blocks_sending.find(p) != NULL)
871 m_blocks_sending.remove(p);
874 /*infostream<<"RemoteClient::GotBlock(): Didn't find in"
875 " m_blocks_sending"<<std::endl;*/
876 m_excess_gotblocks++;
878 m_blocks_sent.insert(p, true);
881 void RemoteClient::SentBlock(v3s16 p)
883 if(m_blocks_sending.find(p) == NULL)
884 m_blocks_sending.insert(p, 0.0);
886 infostream<<"RemoteClient::SentBlock(): Sent block"
887 " already in m_blocks_sending"<<std::endl;
890 void RemoteClient::SetBlockNotSent(v3s16 p)
892 m_nearest_unsent_d = 0;
894 if(m_blocks_sending.find(p) != NULL)
895 m_blocks_sending.remove(p);
896 if(m_blocks_sent.find(p) != NULL)
897 m_blocks_sent.remove(p);
900 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
902 m_nearest_unsent_d = 0;
904 for(core::map<v3s16, MapBlock*>::Iterator
905 i = blocks.getIterator();
906 i.atEnd()==false; i++)
908 v3s16 p = i.getNode()->getKey();
910 if(m_blocks_sending.find(p) != NULL)
911 m_blocks_sending.remove(p);
912 if(m_blocks_sent.find(p) != NULL)
913 m_blocks_sent.remove(p);
921 PlayerInfo::PlayerInfo()
927 void PlayerInfo::PrintLine(std::ostream *s)
930 (*s)<<"\""<<name<<"\" ("
931 <<(position.X/10)<<","<<(position.Y/10)
932 <<","<<(position.Z/10)<<") ";
934 (*s)<<" avg_rtt="<<avg_rtt;
938 u32 PIChecksum(core::list<PlayerInfo> &l)
940 core::list<PlayerInfo>::Iterator i;
943 for(i=l.begin(); i!=l.end(); i++)
945 checksum += a * (i->id+1);
946 checksum ^= 0x435aafcd;
957 ModSpec(const std::string &name_="", const std::string path_=""):
963 static core::list<ModSpec> getMods(core::list<std::string> &modspaths)
965 core::list<ModSpec> mods;
966 for(core::list<std::string>::Iterator i = modspaths.begin();
967 i != modspaths.end(); i++){
968 std::string modspath = *i;
969 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(modspath);
970 for(u32 j=0; j<dirlist.size(); j++){
973 std::string modname = dirlist[j].name;
974 std::string modpath = modspath + DIR_DELIM + modname;
975 mods.push_back(ModSpec(modname, modpath));
986 std::string mapsavedir,
987 std::string configpath
990 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
991 m_authmanager(mapsavedir+DIR_DELIM+"auth.txt"),
992 m_banmanager(mapsavedir+DIR_DELIM+"ipban.txt"),
994 m_toolmgr(createToolDefManager()),
995 m_nodedef(createNodeDefManager()),
996 m_craftdef(createCraftDefManager()),
998 m_emergethread(this),
1000 m_time_of_day_send_timer(0),
1002 m_mapsavedir(mapsavedir),
1003 m_configpath(configpath),
1004 m_shutdown_requested(false),
1005 m_ignore_map_edit_events(false),
1006 m_ignore_map_edit_events_peer_id(0)
1008 m_liquid_transform_timer = 0.0;
1009 m_print_info_timer = 0.0;
1010 m_objectdata_timer = 0.0;
1011 m_emergethread_trigger_timer = 0.0;
1012 m_savemap_timer = 0.0;
1016 m_step_dtime_mutex.Init();
1019 JMutexAutoLock envlock(m_env_mutex);
1020 JMutexAutoLock conlock(m_con_mutex);
1022 infostream<<"m_nodedef="<<m_nodedef<<std::endl;
1024 // Initialize default node definitions
1025 content_mapnode_init(m_nodedef);
1027 // Add default global mod path
1028 m_modspaths.push_back(porting::path_data + DIR_DELIM + "mods");
1030 // Initialize scripting
1032 infostream<<"Server: Initializing scripting"<<std::endl;
1033 m_lua = script_init();
1036 scriptapi_export(m_lua, this);
1037 // Load and run scripts
1038 core::list<ModSpec> mods = getMods(m_modspaths);
1039 for(core::list<ModSpec>::Iterator i = mods.begin();
1040 i != mods.end(); i++){
1042 infostream<<"Server: Loading mod \""<<mod.name<<"\""<<std::endl;
1043 std::string scriptpath = mod.path + DIR_DELIM + "init.lua";
1044 bool success = script_load(m_lua, scriptpath.c_str());
1046 errorstream<<"Server: Failed to load and run "
1047 <<scriptpath<<std::endl;
1052 // Initialize Environment
1054 m_env = new ServerEnvironment(new ServerMap(mapsavedir, this), m_lua, this);
1056 // Give environment reference to scripting api
1057 scriptapi_add_environment(m_lua, m_env);
1059 // Register us to receive map edit events
1060 m_env->getMap().addEventReceiver(this);
1062 // If file exists, load environment metadata
1063 if(fs::PathExists(m_mapsavedir+DIR_DELIM+"env_meta.txt"))
1065 infostream<<"Server: Loading environment metadata"<<std::endl;
1066 m_env->loadMeta(m_mapsavedir);
1070 infostream<<"Server: Loading players"<<std::endl;
1071 m_env->deSerializePlayers(m_mapsavedir);
1076 infostream<<"Server::~Server()"<<std::endl;
1079 Send shutdown message
1082 JMutexAutoLock conlock(m_con_mutex);
1084 std::wstring line = L"*** Server shutting down";
1087 Send the message to clients
1089 for(core::map<u16, RemoteClient*>::Iterator
1090 i = m_clients.getIterator();
1091 i.atEnd() == false; i++)
1093 // Get client and check that it is valid
1094 RemoteClient *client = i.getNode()->getValue();
1095 assert(client->peer_id == i.getNode()->getKey());
1096 if(client->serialization_version == SER_FMT_VER_INVALID)
1100 SendChatMessage(client->peer_id, line);
1102 catch(con::PeerNotFoundException &e)
1108 JMutexAutoLock envlock(m_env_mutex);
1113 infostream<<"Server: Saving players"<<std::endl;
1114 m_env->serializePlayers(m_mapsavedir);
1117 Save environment metadata
1119 infostream<<"Server: Saving environment metadata"<<std::endl;
1120 m_env->saveMeta(m_mapsavedir);
1132 JMutexAutoLock clientslock(m_con_mutex);
1134 for(core::map<u16, RemoteClient*>::Iterator
1135 i = m_clients.getIterator();
1136 i.atEnd() == false; i++)
1139 // NOTE: These are removed by env destructor
1141 u16 peer_id = i.getNode()->getKey();
1142 JMutexAutoLock envlock(m_env_mutex);
1143 m_env->removePlayer(peer_id);
1147 delete i.getNode()->getValue();
1151 // Delete Environment
1157 // Deinitialize scripting
1158 infostream<<"Server: Deinitializing scripting"<<std::endl;
1159 script_deinit(m_lua);
1162 void Server::start(unsigned short port)
1164 DSTACK(__FUNCTION_NAME);
1165 // Stop thread if already running
1168 // Initialize connection
1169 m_con.SetTimeoutMs(30);
1173 m_thread.setRun(true);
1176 infostream<<"Server: Started on port "<<port<<std::endl;
1181 DSTACK(__FUNCTION_NAME);
1183 infostream<<"Server: Stopping and waiting threads"<<std::endl;
1185 // Stop threads (set run=false first so both start stopping)
1186 m_thread.setRun(false);
1187 m_emergethread.setRun(false);
1189 m_emergethread.stop();
1191 infostream<<"Server: Threads stopped"<<std::endl;
1194 void Server::step(float dtime)
1196 DSTACK(__FUNCTION_NAME);
1201 JMutexAutoLock lock(m_step_dtime_mutex);
1202 m_step_dtime += dtime;
1206 void Server::AsyncRunStep()
1208 DSTACK(__FUNCTION_NAME);
1210 g_profiler->add("Server::AsyncRunStep (num)", 1);
1214 JMutexAutoLock lock1(m_step_dtime_mutex);
1215 dtime = m_step_dtime;
1219 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
1220 // Send blocks to clients
1227 g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
1229 //infostream<<"Server steps "<<dtime<<std::endl;
1230 //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1233 JMutexAutoLock lock1(m_step_dtime_mutex);
1234 m_step_dtime -= dtime;
1241 m_uptime.set(m_uptime.get() + dtime);
1245 // Process connection's timeouts
1246 JMutexAutoLock lock2(m_con_mutex);
1247 ScopeProfiler sp(g_profiler, "Server: connection timeout processing");
1248 m_con.RunTimeouts(dtime);
1252 // This has to be called so that the client list gets synced
1253 // with the peer list of the connection
1254 handlePeerChanges();
1258 Update m_time_of_day and overall game time
1261 JMutexAutoLock envlock(m_env_mutex);
1263 m_time_counter += dtime;
1264 f32 speed = g_settings->getFloat("time_speed") * 24000./(24.*3600);
1265 u32 units = (u32)(m_time_counter*speed);
1266 m_time_counter -= (f32)units / speed;
1268 m_env->setTimeOfDay((m_env->getTimeOfDay() + units) % 24000);
1270 //infostream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1273 Send to clients at constant intervals
1276 m_time_of_day_send_timer -= dtime;
1277 if(m_time_of_day_send_timer < 0.0)
1279 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
1281 //JMutexAutoLock envlock(m_env_mutex);
1282 JMutexAutoLock conlock(m_con_mutex);
1284 for(core::map<u16, RemoteClient*>::Iterator
1285 i = m_clients.getIterator();
1286 i.atEnd() == false; i++)
1288 RemoteClient *client = i.getNode()->getValue();
1289 //Player *player = m_env->getPlayer(client->peer_id);
1291 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1292 m_env->getTimeOfDay());
1294 m_con.Send(client->peer_id, 0, data, true);
1300 JMutexAutoLock lock(m_env_mutex);
1302 ScopeProfiler sp(g_profiler, "SEnv step");
1303 ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
1307 const float map_timer_and_unload_dtime = 2.92;
1308 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
1310 JMutexAutoLock lock(m_env_mutex);
1311 // Run Map's timers and unload unused data
1312 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
1313 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
1314 g_settings->getFloat("server_unload_unused_data_timeout"));
1324 m_liquid_transform_timer += dtime;
1325 if(m_liquid_transform_timer >= 1.00)
1327 m_liquid_transform_timer -= 1.00;
1329 JMutexAutoLock lock(m_env_mutex);
1331 ScopeProfiler sp(g_profiler, "Server: liquid transform");
1333 core::map<v3s16, MapBlock*> modified_blocks;
1334 m_env->getMap().transformLiquids(modified_blocks);
1339 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1340 ServerMap &map = ((ServerMap&)m_env->getMap());
1341 map.updateLighting(modified_blocks, lighting_modified_blocks);
1343 // Add blocks modified by lighting to modified_blocks
1344 for(core::map<v3s16, MapBlock*>::Iterator
1345 i = lighting_modified_blocks.getIterator();
1346 i.atEnd() == false; i++)
1348 MapBlock *block = i.getNode()->getValue();
1349 modified_blocks.insert(block->getPos(), block);
1353 Set the modified blocks unsent for all the clients
1356 JMutexAutoLock lock2(m_con_mutex);
1358 for(core::map<u16, RemoteClient*>::Iterator
1359 i = m_clients.getIterator();
1360 i.atEnd() == false; i++)
1362 RemoteClient *client = i.getNode()->getValue();
1364 if(modified_blocks.size() > 0)
1366 // Remove block from sent history
1367 client->SetBlocksNotSent(modified_blocks);
1372 // Periodically print some info
1374 float &counter = m_print_info_timer;
1380 JMutexAutoLock lock2(m_con_mutex);
1382 if(m_clients.size() != 0)
1383 infostream<<"Players:"<<std::endl;
1384 for(core::map<u16, RemoteClient*>::Iterator
1385 i = m_clients.getIterator();
1386 i.atEnd() == false; i++)
1388 //u16 peer_id = i.getNode()->getKey();
1389 RemoteClient *client = i.getNode()->getValue();
1390 Player *player = m_env->getPlayer(client->peer_id);
1393 infostream<<"* "<<player->getName()<<"\t";
1394 client->PrintInfo(infostream);
1399 //if(g_settings->getBool("enable_experimental"))
1403 Check added and deleted active objects
1406 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
1407 JMutexAutoLock envlock(m_env_mutex);
1408 JMutexAutoLock conlock(m_con_mutex);
1410 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
1412 // Radius inside which objects are active
1413 s16 radius = g_settings->getS16("active_object_send_range_blocks");
1414 radius *= MAP_BLOCKSIZE;
1416 for(core::map<u16, RemoteClient*>::Iterator
1417 i = m_clients.getIterator();
1418 i.atEnd() == false; i++)
1420 RemoteClient *client = i.getNode()->getValue();
1421 Player *player = m_env->getPlayer(client->peer_id);
1424 // This can happen if the client timeouts somehow
1425 /*infostream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
1427 <<" has no associated player"<<std::endl;*/
1430 v3s16 pos = floatToInt(player->getPosition(), BS);
1432 core::map<u16, bool> removed_objects;
1433 core::map<u16, bool> added_objects;
1434 m_env->getRemovedActiveObjects(pos, radius,
1435 client->m_known_objects, removed_objects);
1436 m_env->getAddedActiveObjects(pos, radius,
1437 client->m_known_objects, added_objects);
1439 // Ignore if nothing happened
1440 if(removed_objects.size() == 0 && added_objects.size() == 0)
1442 //infostream<<"active objects: none changed"<<std::endl;
1446 std::string data_buffer;
1450 // Handle removed objects
1451 writeU16((u8*)buf, removed_objects.size());
1452 data_buffer.append(buf, 2);
1453 for(core::map<u16, bool>::Iterator
1454 i = removed_objects.getIterator();
1455 i.atEnd()==false; i++)
1458 u16 id = i.getNode()->getKey();
1459 ServerActiveObject* obj = m_env->getActiveObject(id);
1461 // Add to data buffer for sending
1462 writeU16((u8*)buf, i.getNode()->getKey());
1463 data_buffer.append(buf, 2);
1465 // Remove from known objects
1466 client->m_known_objects.remove(i.getNode()->getKey());
1468 if(obj && obj->m_known_by_count > 0)
1469 obj->m_known_by_count--;
1472 // Handle added objects
1473 writeU16((u8*)buf, added_objects.size());
1474 data_buffer.append(buf, 2);
1475 for(core::map<u16, bool>::Iterator
1476 i = added_objects.getIterator();
1477 i.atEnd()==false; i++)
1480 u16 id = i.getNode()->getKey();
1481 ServerActiveObject* obj = m_env->getActiveObject(id);
1484 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1486 infostream<<"WARNING: "<<__FUNCTION_NAME
1487 <<": NULL object"<<std::endl;
1489 type = obj->getType();
1491 // Add to data buffer for sending
1492 writeU16((u8*)buf, id);
1493 data_buffer.append(buf, 2);
1494 writeU8((u8*)buf, type);
1495 data_buffer.append(buf, 1);
1498 data_buffer.append(serializeLongString(
1499 obj->getClientInitializationData()));
1501 data_buffer.append(serializeLongString(""));
1503 // Add to known objects
1504 client->m_known_objects.insert(i.getNode()->getKey(), false);
1507 obj->m_known_by_count++;
1511 SharedBuffer<u8> reply(2 + data_buffer.size());
1512 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1513 memcpy((char*)&reply[2], data_buffer.c_str(),
1514 data_buffer.size());
1516 m_con.Send(client->peer_id, 0, reply, true);
1518 infostream<<"Server: Sent object remove/add: "
1519 <<removed_objects.size()<<" removed, "
1520 <<added_objects.size()<<" added, "
1521 <<"packet size is "<<reply.getSize()<<std::endl;
1526 Collect a list of all the objects known by the clients
1527 and report it back to the environment.
1530 core::map<u16, bool> all_known_objects;
1532 for(core::map<u16, RemoteClient*>::Iterator
1533 i = m_clients.getIterator();
1534 i.atEnd() == false; i++)
1536 RemoteClient *client = i.getNode()->getValue();
1537 // Go through all known objects of client
1538 for(core::map<u16, bool>::Iterator
1539 i = client->m_known_objects.getIterator();
1540 i.atEnd()==false; i++)
1542 u16 id = i.getNode()->getKey();
1543 all_known_objects[id] = true;
1547 m_env->setKnownActiveObjects(whatever);
1553 Send object messages
1556 JMutexAutoLock envlock(m_env_mutex);
1557 JMutexAutoLock conlock(m_con_mutex);
1559 //ScopeProfiler sp(g_profiler, "Server: sending object messages");
1562 // Value = data sent by object
1563 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1565 // Get active object messages from environment
1568 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
1572 core::list<ActiveObjectMessage>* message_list = NULL;
1573 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1574 n = buffered_messages.find(aom.id);
1577 message_list = new core::list<ActiveObjectMessage>;
1578 buffered_messages.insert(aom.id, message_list);
1582 message_list = n->getValue();
1584 message_list->push_back(aom);
1587 // Route data to every client
1588 for(core::map<u16, RemoteClient*>::Iterator
1589 i = m_clients.getIterator();
1590 i.atEnd()==false; i++)
1592 RemoteClient *client = i.getNode()->getValue();
1593 std::string reliable_data;
1594 std::string unreliable_data;
1595 // Go through all objects in message buffer
1596 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1597 j = buffered_messages.getIterator();
1598 j.atEnd()==false; j++)
1600 // If object is not known by client, skip it
1601 u16 id = j.getNode()->getKey();
1602 if(client->m_known_objects.find(id) == NULL)
1604 // Get message list of object
1605 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1606 // Go through every message
1607 for(core::list<ActiveObjectMessage>::Iterator
1608 k = list->begin(); k != list->end(); k++)
1610 // Compose the full new data with header
1611 ActiveObjectMessage aom = *k;
1612 std::string new_data;
1615 writeU16((u8*)&buf[0], aom.id);
1616 new_data.append(buf, 2);
1618 new_data += serializeString(aom.datastring);
1619 // Add data to buffer
1621 reliable_data += new_data;
1623 unreliable_data += new_data;
1627 reliable_data and unreliable_data are now ready.
1630 if(reliable_data.size() > 0)
1632 SharedBuffer<u8> reply(2 + reliable_data.size());
1633 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1634 memcpy((char*)&reply[2], reliable_data.c_str(),
1635 reliable_data.size());
1637 m_con.Send(client->peer_id, 0, reply, true);
1639 if(unreliable_data.size() > 0)
1641 SharedBuffer<u8> reply(2 + unreliable_data.size());
1642 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1643 memcpy((char*)&reply[2], unreliable_data.c_str(),
1644 unreliable_data.size());
1645 // Send as unreliable
1646 m_con.Send(client->peer_id, 0, reply, false);
1649 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1651 infostream<<"Server: Size of object message data: "
1652 <<"reliable: "<<reliable_data.size()
1653 <<", unreliable: "<<unreliable_data.size()
1658 // Clear buffered_messages
1659 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1660 i = buffered_messages.getIterator();
1661 i.atEnd()==false; i++)
1663 delete i.getNode()->getValue();
1667 } // enable_experimental
1670 Send queued-for-sending map edit events.
1673 // Don't send too many at a time
1676 // Single change sending is disabled if queue size is not small
1677 bool disable_single_change_sending = false;
1678 if(m_unsent_map_edit_queue.size() >= 4)
1679 disable_single_change_sending = true;
1681 bool got_any_events = false;
1683 // We'll log the amount of each
1686 while(m_unsent_map_edit_queue.size() != 0)
1688 got_any_events = true;
1690 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1692 // Players far away from the change are stored here.
1693 // Instead of sending the changes, MapBlocks are set not sent
1695 core::list<u16> far_players;
1697 if(event->type == MEET_ADDNODE)
1699 //infostream<<"Server: MEET_ADDNODE"<<std::endl;
1700 prof.add("MEET_ADDNODE", 1);
1701 if(disable_single_change_sending)
1702 sendAddNode(event->p, event->n, event->already_known_by_peer,
1705 sendAddNode(event->p, event->n, event->already_known_by_peer,
1708 else if(event->type == MEET_REMOVENODE)
1710 //infostream<<"Server: MEET_REMOVENODE"<<std::endl;
1711 prof.add("MEET_REMOVENODE", 1);
1712 if(disable_single_change_sending)
1713 sendRemoveNode(event->p, event->already_known_by_peer,
1716 sendRemoveNode(event->p, event->already_known_by_peer,
1719 else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
1721 infostream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
1722 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
1723 setBlockNotSent(event->p);
1725 else if(event->type == MEET_OTHER)
1727 infostream<<"Server: MEET_OTHER"<<std::endl;
1728 prof.add("MEET_OTHER", 1);
1729 for(core::map<v3s16, bool>::Iterator
1730 i = event->modified_blocks.getIterator();
1731 i.atEnd()==false; i++)
1733 v3s16 p = i.getNode()->getKey();
1739 prof.add("unknown", 1);
1740 infostream<<"WARNING: Server: Unknown MapEditEvent "
1741 <<((u32)event->type)<<std::endl;
1745 Set blocks not sent to far players
1747 if(far_players.size() > 0)
1749 // Convert list format to that wanted by SetBlocksNotSent
1750 core::map<v3s16, MapBlock*> modified_blocks2;
1751 for(core::map<v3s16, bool>::Iterator
1752 i = event->modified_blocks.getIterator();
1753 i.atEnd()==false; i++)
1755 v3s16 p = i.getNode()->getKey();
1756 modified_blocks2.insert(p,
1757 m_env->getMap().getBlockNoCreateNoEx(p));
1759 // Set blocks not sent
1760 for(core::list<u16>::Iterator
1761 i = far_players.begin();
1762 i != far_players.end(); i++)
1765 RemoteClient *client = getClient(peer_id);
1768 client->SetBlocksNotSent(modified_blocks2);
1774 /*// Don't send too many at a time
1776 if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
1782 infostream<<"Server: MapEditEvents:"<<std::endl;
1783 prof.print(infostream);
1789 Send object positions
1792 float &counter = m_objectdata_timer;
1794 if(counter >= g_settings->getFloat("objectdata_interval"))
1796 JMutexAutoLock lock1(m_env_mutex);
1797 JMutexAutoLock lock2(m_con_mutex);
1799 //ScopeProfiler sp(g_profiler, "Server: sending player positions");
1801 SendObjectData(counter);
1808 Trigger emergethread (it somehow gets to a non-triggered but
1809 bysy state sometimes)
1812 float &counter = m_emergethread_trigger_timer;
1818 m_emergethread.trigger();
1822 // Save map, players and auth stuff
1824 float &counter = m_savemap_timer;
1826 if(counter >= g_settings->getFloat("server_map_save_interval"))
1830 ScopeProfiler sp(g_profiler, "Server: saving stuff");
1833 if(m_authmanager.isModified())
1834 m_authmanager.save();
1837 if(m_banmanager.isModified())
1838 m_banmanager.save();
1841 JMutexAutoLock lock(m_env_mutex);
1843 /*// Unload unused data (delete from memory)
1844 m_env->getMap().unloadUnusedData(
1845 g_settings->getFloat("server_unload_unused_sectors_timeout"));
1847 /*u32 deleted_count = m_env->getMap().unloadUnusedData(
1848 g_settings->getFloat("server_unload_unused_sectors_timeout"));
1851 // Save only changed parts
1852 m_env->getMap().save(true);
1854 /*if(deleted_count > 0)
1856 infostream<<"Server: Unloaded "<<deleted_count
1857 <<" blocks from memory"<<std::endl;
1861 m_env->serializePlayers(m_mapsavedir);
1863 // Save environment metadata
1864 m_env->saveMeta(m_mapsavedir);
1869 void Server::Receive()
1871 DSTACK(__FUNCTION_NAME);
1872 SharedBuffer<u8> data;
1877 JMutexAutoLock conlock(m_con_mutex);
1878 datasize = m_con.Receive(peer_id, data);
1881 // This has to be called so that the client list gets synced
1882 // with the peer list of the connection
1883 handlePeerChanges();
1885 ProcessData(*data, datasize, peer_id);
1887 catch(con::InvalidIncomingDataException &e)
1889 infostream<<"Server::Receive(): "
1890 "InvalidIncomingDataException: what()="
1891 <<e.what()<<std::endl;
1893 catch(con::PeerNotFoundException &e)
1895 //NOTE: This is not needed anymore
1897 // The peer has been disconnected.
1898 // Find the associated player and remove it.
1900 /*JMutexAutoLock envlock(m_env_mutex);
1902 infostream<<"ServerThread: peer_id="<<peer_id
1903 <<" has apparently closed connection. "
1904 <<"Removing player."<<std::endl;
1906 m_env->removePlayer(peer_id);*/
1910 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1912 DSTACK(__FUNCTION_NAME);
1913 // Environment is locked first.
1914 JMutexAutoLock envlock(m_env_mutex);
1915 JMutexAutoLock conlock(m_con_mutex);
1918 Address address = m_con.GetPeerAddress(peer_id);
1920 // drop player if is ip is banned
1921 if(m_banmanager.isIpBanned(address.serializeString())){
1922 SendAccessDenied(m_con, peer_id,
1923 L"Your ip is banned. Banned name was "
1924 +narrow_to_wide(m_banmanager.getBanName(
1925 address.serializeString())));
1926 m_con.DeletePeer(peer_id);
1930 catch(con::PeerNotFoundException &e)
1932 infostream<<"Server::ProcessData(): Cancelling: peer "
1933 <<peer_id<<" not found"<<std::endl;
1937 u8 peer_ser_ver = getClient(peer_id)->serialization_version;
1945 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1947 if(command == TOSERVER_INIT)
1949 // [0] u16 TOSERVER_INIT
1950 // [2] u8 SER_FMT_VER_HIGHEST
1951 // [3] u8[20] player_name
1952 // [23] u8[28] password <--- can be sent without this, from old versions
1954 if(datasize < 2+1+PLAYERNAME_SIZE)
1957 infostream<<"Server: Got TOSERVER_INIT from "
1958 <<peer_id<<std::endl;
1960 // First byte after command is maximum supported
1961 // serialization version
1962 u8 client_max = data[2];
1963 u8 our_max = SER_FMT_VER_HIGHEST;
1964 // Use the highest version supported by both
1965 u8 deployed = core::min_(client_max, our_max);
1966 // If it's lower than the lowest supported, give up.
1967 if(deployed < SER_FMT_VER_LOWEST)
1968 deployed = SER_FMT_VER_INVALID;
1970 //peer->serialization_version = deployed;
1971 getClient(peer_id)->pending_serialization_version = deployed;
1973 if(deployed == SER_FMT_VER_INVALID)
1975 infostream<<"Server: Cannot negotiate "
1976 "serialization version with peer "
1977 <<peer_id<<std::endl;
1978 SendAccessDenied(m_con, peer_id,
1979 L"Your client is too old (map format)");
1984 Read and check network protocol version
1987 u16 net_proto_version = 0;
1988 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
1990 net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
1993 getClient(peer_id)->net_proto_version = net_proto_version;
1995 if(net_proto_version == 0)
1997 SendAccessDenied(m_con, peer_id,
1998 L"Your client is too old. Please upgrade.");
2002 /* Uhh... this should actually be a warning but let's do it like this */
2003 if(g_settings->getBool("strict_protocol_version_checking"))
2005 if(net_proto_version < PROTOCOL_VERSION)
2007 SendAccessDenied(m_con, peer_id,
2008 L"Your client is too old. Please upgrade.");
2018 char playername[PLAYERNAME_SIZE];
2019 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
2021 playername[i] = data[3+i];
2023 playername[PLAYERNAME_SIZE-1] = 0;
2025 if(playername[0]=='\0')
2027 infostream<<"Server: Player has empty name"<<std::endl;
2028 SendAccessDenied(m_con, peer_id,
2033 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
2035 infostream<<"Server: Player has invalid name"<<std::endl;
2036 SendAccessDenied(m_con, peer_id,
2037 L"Name contains unallowed characters");
2042 char password[PASSWORD_SIZE];
2043 if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
2045 // old version - assume blank password
2050 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2052 password[i] = data[23+i];
2054 password[PASSWORD_SIZE-1] = 0;
2057 std::string checkpwd;
2058 if(m_authmanager.exists(playername))
2060 checkpwd = m_authmanager.getPassword(playername);
2064 checkpwd = g_settings->get("default_password");
2067 /*infostream<<"Server: Client gave password '"<<password
2068 <<"', the correct one is '"<<checkpwd<<"'"<<std::endl;*/
2070 if(password != checkpwd && m_authmanager.exists(playername))
2072 infostream<<"Server: peer_id="<<peer_id
2073 <<": supplied invalid password for "
2074 <<playername<<std::endl;
2075 SendAccessDenied(m_con, peer_id, L"Invalid password");
2079 // Add player to auth manager
2080 if(m_authmanager.exists(playername) == false)
2082 infostream<<"Server: adding player "<<playername
2083 <<" to auth manager"<<std::endl;
2084 m_authmanager.add(playername);
2085 m_authmanager.setPassword(playername, checkpwd);
2086 m_authmanager.setPrivs(playername,
2087 stringToPrivs(g_settings->get("default_privs")));
2088 m_authmanager.save();
2091 // Enforce user limit.
2092 // Don't enforce for users that have some admin right
2093 if(m_clients.size() >= g_settings->getU16("max_users") &&
2094 (m_authmanager.getPrivs(playername)
2095 & (PRIV_SERVER|PRIV_BAN|PRIV_PRIVS)) == 0 &&
2096 playername != g_settings->get("name"))
2098 SendAccessDenied(m_con, peer_id, L"Too many users.");
2103 Player *player = emergePlayer(playername, password, peer_id);
2105 // If failed, cancel
2108 infostream<<"Server: peer_id="<<peer_id
2109 <<": failed to emerge player"<<std::endl;
2114 Answer with a TOCLIENT_INIT
2117 SharedBuffer<u8> reply(2+1+6+8);
2118 writeU16(&reply[0], TOCLIENT_INIT);
2119 writeU8(&reply[2], deployed);
2120 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
2121 writeU64(&reply[2+1+6], m_env->getServerMap().getSeed());
2124 m_con.Send(peer_id, 0, reply, true);
2128 Send complete position information
2130 SendMovePlayer(player);
2135 if(command == TOSERVER_INIT2)
2137 infostream<<"Server: Got TOSERVER_INIT2 from "
2138 <<peer_id<<std::endl;
2141 getClient(peer_id)->serialization_version
2142 = getClient(peer_id)->pending_serialization_version;
2145 Send some initialization data
2148 // Send tool definitions
2149 SendToolDef(m_con, peer_id, m_toolmgr);
2151 // Send node definitions
2152 SendNodeDef(m_con, peer_id, m_nodedef);
2155 SendTextures(peer_id);
2157 // Send player info to all players
2160 // Send inventory to player
2161 UpdateCrafting(peer_id);
2162 SendInventory(peer_id);
2164 // Send player items to all players
2167 Player *player = m_env->getPlayer(peer_id);
2170 SendPlayerHP(player);
2174 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2175 m_env->getTimeOfDay());
2176 m_con.Send(peer_id, 0, data, true);
2179 // Send information about server to player in chat
2180 SendChatMessage(peer_id, getStatusString());
2182 // Send information about joining in chat
2184 std::wstring name = L"unknown";
2185 Player *player = m_env->getPlayer(peer_id);
2187 name = narrow_to_wide(player->getName());
2189 std::wstring message;
2192 message += L" joined game";
2193 BroadcastChatMessage(message);
2196 // Warnings about protocol version can be issued here
2197 if(getClient(peer_id)->net_proto_version < PROTOCOL_VERSION)
2199 SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT IS OLD AND MAY WORK PROPERLY WITH THIS SERVER");
2203 Check HP, respawn if necessary
2205 HandlePlayerHP(player, 0);
2211 std::ostringstream os(std::ios_base::binary);
2212 for(core::map<u16, RemoteClient*>::Iterator
2213 i = m_clients.getIterator();
2214 i.atEnd() == false; i++)
2216 RemoteClient *client = i.getNode()->getValue();
2217 assert(client->peer_id == i.getNode()->getKey());
2218 if(client->serialization_version == SER_FMT_VER_INVALID)
2221 Player *player = m_env->getPlayer(client->peer_id);
2224 // Get name of player
2225 os<<player->getName()<<" ";
2228 actionstream<<player->getName()<<" joins game. List of players: "
2229 <<os.str()<<std::endl;
2235 if(peer_ser_ver == SER_FMT_VER_INVALID)
2237 infostream<<"Server::ProcessData(): Cancelling: Peer"
2238 " serialization format invalid or not initialized."
2239 " Skipping incoming command="<<command<<std::endl;
2243 Player *player = m_env->getPlayer(peer_id);
2246 infostream<<"Server::ProcessData(): Cancelling: "
2247 "No player for peer_id="<<peer_id
2251 if(command == TOSERVER_PLAYERPOS)
2253 if(datasize < 2+12+12+4+4)
2257 v3s32 ps = readV3S32(&data[start+2]);
2258 v3s32 ss = readV3S32(&data[start+2+12]);
2259 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2260 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2261 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2262 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2263 pitch = wrapDegrees(pitch);
2264 yaw = wrapDegrees(yaw);
2266 player->setPosition(position);
2267 player->setSpeed(speed);
2268 player->setPitch(pitch);
2269 player->setYaw(yaw);
2271 /*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2272 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2273 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2275 else if(command == TOSERVER_GOTBLOCKS)
2288 u16 count = data[2];
2289 for(u16 i=0; i<count; i++)
2291 if((s16)datasize < 2+1+(i+1)*6)
2292 throw con::InvalidIncomingDataException
2293 ("GOTBLOCKS length is too short");
2294 v3s16 p = readV3S16(&data[2+1+i*6]);
2295 /*infostream<<"Server: GOTBLOCKS ("
2296 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2297 RemoteClient *client = getClient(peer_id);
2298 client->GotBlock(p);
2301 else if(command == TOSERVER_DELETEDBLOCKS)
2314 u16 count = data[2];
2315 for(u16 i=0; i<count; i++)
2317 if((s16)datasize < 2+1+(i+1)*6)
2318 throw con::InvalidIncomingDataException
2319 ("DELETEDBLOCKS length is too short");
2320 v3s16 p = readV3S16(&data[2+1+i*6]);
2321 /*infostream<<"Server: DELETEDBLOCKS ("
2322 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2323 RemoteClient *client = getClient(peer_id);
2324 client->SetBlockNotSent(p);
2327 else if(command == TOSERVER_CLICK_OBJECT)
2329 infostream<<"Server: CLICK_OBJECT not supported anymore"<<std::endl;
2332 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2337 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2343 [2] u8 button (0=left, 1=right)
2347 u8 button = readU8(&data[2]);
2348 u16 id = readS16(&data[3]);
2349 u16 item_i = readU16(&data[5]);
2351 ServerActiveObject *obj = m_env->getActiveObject(id);
2355 infostream<<"Server: CLICK_ACTIVEOBJECT: object not found"
2360 // Skip if object has been removed
2364 //TODO: Check that object is reasonably close
2366 // Get ServerRemotePlayer
2367 ServerRemotePlayer *srp = (ServerRemotePlayer*)player;
2369 // Update wielded item
2370 srp->wieldItem(item_i);
2372 // Left click, pick/punch
2375 actionstream<<player->getName()<<" punches object "
2376 <<obj->getId()<<std::endl;
2383 Try creating inventory item
2385 InventoryItem *item = obj->createPickedUpItem();
2389 InventoryList *ilist = player->inventory.getList("main");
2392 actionstream<<player->getName()<<" picked up "
2393 <<item->getName()<<std::endl;
2394 if(g_settings->getBool("creative_mode") == false)
2396 // Skip if inventory has no free space
2397 if(ilist->roomForItem(item) == false)
2399 infostream<<"Player inventory has no free space"<<std::endl;
2403 // Add to inventory and send inventory
2404 ilist->addItem(item);
2405 UpdateCrafting(player->peer_id);
2406 SendInventory(player->peer_id);
2409 // Remove object from environment
2410 obj->m_removed = true;
2416 Item cannot be picked up. Punch it instead.
2419 actionstream<<player->getName()<<" punches object "
2420 <<obj->getId()<<std::endl;
2422 ToolItem *titem = NULL;
2423 std::string toolname = "";
2425 InventoryList *mlist = player->inventory.getList("main");
2428 InventoryItem *item = mlist->getItem(item_i);
2429 if(item && (std::string)item->getName() == "ToolItem")
2431 titem = (ToolItem*)item;
2432 toolname = titem->getToolName();
2436 v3f playerpos = player->getPosition();
2437 v3f objpos = obj->getBasePosition();
2438 v3f dir = (objpos - playerpos).normalize();
2440 u16 wear = obj->punch(toolname, dir, player->getName());
2444 bool weared_out = titem->addWear(wear);
2446 mlist->deleteItem(item_i);
2447 SendInventory(player->peer_id);
2452 // Right click, do something with object
2455 actionstream<<player->getName()<<" right clicks object "
2456 <<obj->getId()<<std::endl;
2459 obj->rightClick(srp);
2463 Update player state to client
2465 SendPlayerHP(player);
2466 UpdateCrafting(player->peer_id);
2467 SendInventory(player->peer_id);
2469 else if(command == TOSERVER_GROUND_ACTION)
2477 [3] v3s16 nodepos_undersurface
2478 [9] v3s16 nodepos_abovesurface
2483 2: stop digging (all parameters ignored)
2484 3: digging completed
2486 u8 action = readU8(&data[2]);
2488 p_under.X = readS16(&data[3]);
2489 p_under.Y = readS16(&data[5]);
2490 p_under.Z = readS16(&data[7]);
2492 p_over.X = readS16(&data[9]);
2493 p_over.Y = readS16(&data[11]);
2494 p_over.Z = readS16(&data[13]);
2495 u16 item_i = readU16(&data[15]);
2497 //TODO: Check that target is reasonably close
2505 NOTE: This can be used in the future to check if
2506 somebody is cheating, by checking the timing.
2508 bool cannot_punch_node = false;
2510 MapNode n(CONTENT_IGNORE);
2514 n = m_env->getMap().getNode(p_under);
2516 catch(InvalidPositionException &e)
2518 infostream<<"Server: Not punching: Node not found."
2519 <<" Adding block to emerge queue."
2521 m_emerge_queue.addBlock(peer_id,
2522 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2523 cannot_punch_node = true;
2526 if(cannot_punch_node)
2532 ServerRemotePlayer *srp = (ServerRemotePlayer*)player;
2533 scriptapi_environment_on_punchnode(m_lua, p_under, n, srp);
2540 else if(action == 2)
2543 RemoteClient *client = getClient(peer_id);
2544 JMutexAutoLock digmutex(client->m_dig_mutex);
2545 client->m_dig_tool_item = -1;
2550 3: Digging completed
2552 else if(action == 3)
2554 // Mandatory parameter; actually used for nothing
2555 core::map<v3s16, MapBlock*> modified_blocks;
2557 content_t material = CONTENT_IGNORE;
2558 u8 mineral = MINERAL_NONE;
2560 bool cannot_remove_node = false;
2562 MapNode n(CONTENT_IGNORE);
2565 n = m_env->getMap().getNode(p_under);
2567 mineral = n.getMineral(m_nodedef);
2568 // Get material at position
2569 material = n.getContent();
2570 // If not yet cancelled
2571 if(cannot_remove_node == false)
2573 // If it's not diggable, do nothing
2574 if(m_nodedef->get(material).diggable == false)
2576 infostream<<"Server: Not finishing digging: "
2577 <<"Node not diggable"
2579 cannot_remove_node = true;
2582 // If not yet cancelled
2583 if(cannot_remove_node == false)
2585 // Get node metadata
2586 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p_under);
2587 if(meta && meta->nodeRemovalDisabled() == true)
2589 infostream<<"Server: Not finishing digging: "
2590 <<"Node metadata disables removal"
2592 cannot_remove_node = true;
2596 catch(InvalidPositionException &e)
2598 infostream<<"Server: Not finishing digging: Node not found."
2599 <<" Adding block to emerge queue."
2601 m_emerge_queue.addBlock(peer_id,
2602 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2603 cannot_remove_node = true;
2606 // Make sure the player is allowed to do it
2607 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2609 infostream<<"Player "<<player->getName()<<" cannot remove node"
2610 <<" because privileges are "<<getPlayerPrivs(player)
2612 cannot_remove_node = true;
2616 If node can't be removed, set block to be re-sent to
2619 if(cannot_remove_node)
2621 infostream<<"Server: Not finishing digging."<<std::endl;
2623 // Client probably has wrong data.
2624 // Set block not sent, so that client will get
2626 infostream<<"Client "<<peer_id<<" tried to dig "
2627 <<"node; but node cannot be removed."
2628 <<" setting MapBlock not sent."<<std::endl;
2629 RemoteClient *client = getClient(peer_id);
2630 v3s16 blockpos = getNodeBlockPos(p_under);
2631 client->SetBlockNotSent(blockpos);
2636 actionstream<<player->getName()<<" digs "<<PP(p_under)
2637 <<", gets material "<<(int)material<<", mineral "
2638 <<(int)mineral<<std::endl;
2641 Send the removal to all close-by players.
2642 - If other player is close, send REMOVENODE
2643 - Otherwise set blocks not sent
2645 core::list<u16> far_players;
2646 sendRemoveNode(p_under, peer_id, &far_players, 30);
2649 Update and send inventory
2652 if(g_settings->getBool("creative_mode") == false)
2657 InventoryList *mlist = player->inventory.getList("main");
2660 InventoryItem *item = mlist->getItem(item_i);
2661 if(item && (std::string)item->getName() == "ToolItem")
2663 ToolItem *titem = (ToolItem*)item;
2664 std::string toolname = titem->getToolName();
2666 // Get digging properties for material and tool
2667 ToolDiggingProperties tp =
2668 m_toolmgr->getDiggingProperties(toolname);
2669 DiggingProperties prop =
2670 getDiggingProperties(material, &tp, m_nodedef);
2672 if(prop.diggable == false)
2674 infostream<<"Server: WARNING: Player digged"
2675 <<" with impossible material + tool"
2676 <<" combination"<<std::endl;
2679 bool weared_out = titem->addWear(prop.wear);
2683 mlist->deleteItem(item_i);
2689 Add dug item to inventory
2692 InventoryItem *item = NULL;
2694 if(mineral != MINERAL_NONE)
2695 item = getDiggedMineralItem(mineral, this);
2700 const std::string &dug_s = m_nodedef->get(material).dug_item;
2703 std::istringstream is(dug_s, std::ios::binary);
2704 item = InventoryItem::deSerialize(is, this);
2710 // Add a item to inventory
2711 player->inventory.addItem("main", item);
2714 UpdateCrafting(player->peer_id);
2715 SendInventory(player->peer_id);
2720 if(mineral != MINERAL_NONE)
2721 item = getDiggedMineralItem(mineral, this);
2726 const std::string &extra_dug_s = m_nodedef->get(material).extra_dug_item;
2727 s32 extra_rarity = m_nodedef->get(material).extra_dug_item_rarity;
2728 if(extra_dug_s != "" && extra_rarity != 0
2729 && myrand() % extra_rarity == 0)
2731 std::istringstream is(extra_dug_s, std::ios::binary);
2732 item = InventoryItem::deSerialize(is, this);
2738 // Add a item to inventory
2739 player->inventory.addItem("main", item);
2742 UpdateCrafting(player->peer_id);
2743 SendInventory(player->peer_id);
2749 (this takes some time so it is done after the quick stuff)
2752 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
2754 m_env->getMap().removeNodeAndUpdate(p_under, modified_blocks);
2757 Set blocks not sent to far players
2759 for(core::list<u16>::Iterator
2760 i = far_players.begin();
2761 i != far_players.end(); i++)
2764 RemoteClient *client = getClient(peer_id);
2767 client->SetBlocksNotSent(modified_blocks);
2773 ServerRemotePlayer *srp = (ServerRemotePlayer*)player;
2774 scriptapi_environment_on_dignode(m_lua, p_under, n, srp);
2780 else if(action == 1)
2783 InventoryList *ilist = player->inventory.getList("main");
2788 InventoryItem *item = ilist->getItem(item_i);
2790 // If there is no item, it is not possible to add it anywhere
2795 Handle material items
2797 if(std::string("MaterialItem") == item->getName())
2800 // Don't add a node if this is not a free space
2801 MapNode n2 = m_env->getMap().getNode(p_over);
2802 bool no_enough_privs =
2803 ((getPlayerPrivs(player) & PRIV_BUILD)==0);
2805 infostream<<"Player "<<player->getName()<<" cannot add node"
2806 <<" because privileges are "<<getPlayerPrivs(player)
2809 if(m_nodedef->get(n2).buildable_to == false
2812 // Client probably has wrong data.
2813 // Set block not sent, so that client will get
2815 infostream<<"Client "<<peer_id<<" tried to place"
2816 <<" node in invalid position; setting"
2817 <<" MapBlock not sent."<<std::endl;
2818 RemoteClient *client = getClient(peer_id);
2819 v3s16 blockpos = getNodeBlockPos(p_over);
2820 client->SetBlockNotSent(blockpos);
2824 catch(InvalidPositionException &e)
2826 infostream<<"Server: Ignoring ADDNODE: Node not found"
2827 <<" Adding block to emerge queue."
2829 m_emerge_queue.addBlock(peer_id,
2830 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2834 // Reset build time counter
2835 getClient(peer_id)->m_time_from_building = 0.0;
2838 MaterialItem *mitem = (MaterialItem*)item;
2840 n.setContent(mitem->getMaterial());
2842 actionstream<<player->getName()<<" places material "
2843 <<(int)mitem->getMaterial()
2844 <<" at "<<PP(p_under)<<std::endl;
2846 // Calculate direction for wall mounted stuff
2847 if(m_nodedef->get(n).wall_mounted)
2848 n.param2 = packDir(p_under - p_over);
2850 // Calculate the direction for furnaces and chests and stuff
2851 if(m_nodedef->get(n).param_type == CPT_FACEDIR_SIMPLE)
2853 v3f playerpos = player->getPosition();
2854 v3f blockpos = intToFloat(p_over, BS) - playerpos;
2855 blockpos = blockpos.normalize();
2857 if (fabs(blockpos.X) > fabs(blockpos.Z)) {
2871 Send to all close-by players
2873 core::list<u16> far_players;
2874 sendAddNode(p_over, n, 0, &far_players, 30);
2879 InventoryList *ilist = player->inventory.getList("main");
2880 if(g_settings->getBool("creative_mode") == false && ilist)
2882 // Remove from inventory and send inventory
2883 if(mitem->getCount() == 1)
2884 ilist->deleteItem(item_i);
2888 UpdateCrafting(peer_id);
2889 SendInventory(peer_id);
2895 This takes some time so it is done after the quick stuff
2897 core::map<v3s16, MapBlock*> modified_blocks;
2899 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
2901 std::string p_name = std::string(player->getName());
2902 m_env->getMap().addNodeAndUpdate(p_over, n, modified_blocks, p_name);
2905 Set blocks not sent to far players
2907 for(core::list<u16>::Iterator
2908 i = far_players.begin();
2909 i != far_players.end(); i++)
2912 RemoteClient *client = getClient(peer_id);
2915 client->SetBlocksNotSent(modified_blocks);
2921 ServerRemotePlayer *srp = (ServerRemotePlayer*)player;
2922 scriptapi_environment_on_placenode(m_lua, p_over, n, srp);
2925 Calculate special events
2928 /*if(n.d == LEGN(m_nodedef, "CONTENT_MESE"))
2931 for(s16 z=-1; z<=1; z++)
2932 for(s16 y=-1; y<=1; y++)
2933 for(s16 x=-1; x<=1; x++)
2940 Place other item (not a block)
2944 v3s16 blockpos = getNodeBlockPos(p_over);
2947 Check that the block is loaded so that the item
2948 can properly be added to the static list too
2950 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
2953 infostream<<"Error while placing object: "
2954 "block not found"<<std::endl;
2959 If in creative mode, item dropping is disabled unless
2960 player has build privileges
2962 if(g_settings->getBool("creative_mode") &&
2963 (getPlayerPrivs(player) & PRIV_BUILD) == 0)
2965 infostream<<"Not allowing player to drop item: "
2966 "creative mode and no build privs"<<std::endl;
2970 // Calculate a position for it
2971 v3f pos = intToFloat(p_over, BS);
2973 /*pos.Y -= BS*0.25; // let it drop a bit
2975 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2976 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;*/
2981 ServerActiveObject *obj = item->createSAO(m_env, 0, pos);
2985 infostream<<"WARNING: item resulted in NULL object, "
2986 <<"not placing onto map"
2991 actionstream<<player->getName()<<" places "<<item->getName()
2992 <<" at "<<PP(p_over)<<std::endl;
2994 // Add the object to the environment
2995 m_env->addActiveObject(obj);
2997 infostream<<"Placed object"<<std::endl;
2999 if(g_settings->getBool("creative_mode") == false)
3001 // Delete the right amount of items from the slot
3002 u16 dropcount = item->getDropCount();
3004 // Delete item if all gone
3005 if(item->getCount() <= dropcount)
3007 if(item->getCount() < dropcount)
3008 infostream<<"WARNING: Server: dropped more items"
3009 <<" than the slot contains"<<std::endl;
3011 InventoryList *ilist = player->inventory.getList("main");
3013 // Remove from inventory and send inventory
3014 ilist->deleteItem(item_i);
3016 // Else decrement it
3018 item->remove(dropcount);
3021 UpdateCrafting(peer_id);
3022 SendInventory(peer_id);
3030 Catch invalid actions
3034 infostream<<"WARNING: Server: Invalid action "
3035 <<action<<std::endl;
3039 else if(command == TOSERVER_RELEASE)
3048 infostream<<"TOSERVER_RELEASE ignored"<<std::endl;
3051 else if(command == TOSERVER_SIGNTEXT)
3053 infostream<<"Server: TOSERVER_SIGNTEXT not supported anymore"
3057 else if(command == TOSERVER_SIGNNODETEXT)
3059 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
3067 std::string datastring((char*)&data[2], datasize-2);
3068 std::istringstream is(datastring, std::ios_base::binary);
3071 is.read((char*)buf, 6);
3072 v3s16 p = readV3S16(buf);
3073 is.read((char*)buf, 2);
3074 u16 textlen = readU16(buf);
3076 for(u16 i=0; i<textlen; i++)
3078 is.read((char*)buf, 1);
3079 text += (char)buf[0];
3082 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3085 if(meta->typeId() != LEGN(m_nodedef, "CONTENT_SIGN_WALL"))
3087 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
3088 signmeta->setText(text);
3090 actionstream<<player->getName()<<" writes \""<<text<<"\" to sign "
3091 <<" at "<<PP(p)<<std::endl;
3093 v3s16 blockpos = getNodeBlockPos(p);
3094 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3097 block->raiseModified(MOD_STATE_WRITE_NEEDED,
3101 setBlockNotSent(blockpos);
3103 else if(command == TOSERVER_INVENTORY_ACTION)
3105 /*// Ignore inventory changes if in creative mode
3106 if(g_settings->getBool("creative_mode") == true)
3108 infostream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
3112 // Strip command and create a stream
3113 std::string datastring((char*)&data[2], datasize-2);
3114 infostream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
3115 std::istringstream is(datastring, std::ios_base::binary);
3117 InventoryAction *a = InventoryAction::deSerialize(is);
3122 c.current_player = player;
3125 Handle craftresult specially if not in creative mode
3127 bool disable_action = false;
3128 if(a->getType() == IACTION_MOVE
3129 && g_settings->getBool("creative_mode") == false)
3131 IMoveAction *ma = (IMoveAction*)a;
3132 if(ma->to_inv == "current_player" &&
3133 ma->from_inv == "current_player")
3135 InventoryList *rlist = player->inventory.getList("craftresult");
3137 InventoryList *clist = player->inventory.getList("craft");
3139 InventoryList *mlist = player->inventory.getList("main");
3142 Craftresult is no longer preview if something
3145 if(ma->to_list == "craftresult"
3146 && ma->from_list != "craftresult")
3148 // If it currently is a preview, remove
3150 if(player->craftresult_is_preview)
3152 rlist->deleteItem(0);
3154 player->craftresult_is_preview = false;
3157 Crafting takes place if this condition is true.
3159 if(player->craftresult_is_preview &&
3160 ma->from_list == "craftresult")
3162 player->craftresult_is_preview = false;
3163 clist->decrementMaterials(1);
3165 /* Print out action */
3166 InventoryList *list =
3167 player->inventory.getList("craftresult");
3169 InventoryItem *item = list->getItem(0);
3170 std::string itemname = "NULL";
3172 itemname = item->getName();
3173 actionstream<<player->getName()<<" crafts "
3174 <<itemname<<std::endl;
3177 If the craftresult is placed on itself, move it to
3178 main inventory instead of doing the action
3180 if(ma->to_list == "craftresult"
3181 && ma->from_list == "craftresult")
3183 disable_action = true;
3185 InventoryItem *item1 = rlist->changeItem(0, NULL);
3186 mlist->addItem(item1);
3189 // Disallow moving items if not allowed to build
3190 else if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
3194 // if it's a locking chest, only allow the owner or server admins to move items
3195 else if (ma->from_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
3197 Strfnd fn(ma->from_inv);
3198 std::string id0 = fn.next(":");
3199 if(id0 == "nodemeta")
3202 p.X = stoi(fn.next(","));
3203 p.Y = stoi(fn.next(","));
3204 p.Z = stoi(fn.next(","));
3205 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3206 if(meta && meta->typeId() == LEGN(m_nodedef, "CONTENT_LOCKABLE_CHEST")) {
3207 LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
3208 if (lcm->getOwner() != player->getName())
3213 else if (ma->to_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
3215 Strfnd fn(ma->to_inv);
3216 std::string id0 = fn.next(":");
3217 if(id0 == "nodemeta")
3220 p.X = stoi(fn.next(","));
3221 p.Y = stoi(fn.next(","));
3222 p.Z = stoi(fn.next(","));
3223 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3224 if(meta && meta->typeId() == LEGN(m_nodedef, "CONTENT_LOCKABLE_CHEST")) {
3225 LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
3226 if (lcm->getOwner() != player->getName())
3233 if(disable_action == false)
3235 // Feed action to player inventory
3243 UpdateCrafting(player->peer_id);
3244 SendInventory(player->peer_id);
3249 infostream<<"TOSERVER_INVENTORY_ACTION: "
3250 <<"InventoryAction::deSerialize() returned NULL"
3254 else if(command == TOSERVER_CHAT_MESSAGE)
3262 std::string datastring((char*)&data[2], datasize-2);
3263 std::istringstream is(datastring, std::ios_base::binary);
3266 is.read((char*)buf, 2);
3267 u16 len = readU16(buf);
3269 std::wstring message;
3270 for(u16 i=0; i<len; i++)
3272 is.read((char*)buf, 2);
3273 message += (wchar_t)readU16(buf);
3276 // Get player name of this client
3277 std::wstring name = narrow_to_wide(player->getName());
3279 // Line to send to players
3281 // Whether to send to the player that sent the line
3282 bool send_to_sender = false;
3283 // Whether to send to other players
3284 bool send_to_others = false;
3286 // Local player gets all privileges regardless of
3287 // what's set on their account.
3288 u64 privs = getPlayerPrivs(player);
3291 if(message[0] == L'/')
3293 size_t strip_size = 1;
3294 if (message[1] == L'#') // support old-style commans
3296 message = message.substr(strip_size);
3298 WStrfnd f1(message);
3299 f1.next(L" "); // Skip over /#whatever
3300 std::wstring paramstring = f1.next(L"");
3302 ServerCommandContext *ctx = new ServerCommandContext(
3303 str_split(message, L' '),
3310 std::wstring reply(processServerCommand(ctx));
3311 send_to_sender = ctx->flags & SEND_TO_SENDER;
3312 send_to_others = ctx->flags & SEND_TO_OTHERS;
3314 if (ctx->flags & SEND_NO_PREFIX)
3317 line += L"Server: " + reply;
3324 if(privs & PRIV_SHOUT)
3330 send_to_others = true;
3334 line += L"Server: You are not allowed to shout";
3335 send_to_sender = true;
3342 actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
3345 Send the message to clients
3347 for(core::map<u16, RemoteClient*>::Iterator
3348 i = m_clients.getIterator();
3349 i.atEnd() == false; i++)
3351 // Get client and check that it is valid
3352 RemoteClient *client = i.getNode()->getValue();
3353 assert(client->peer_id == i.getNode()->getKey());
3354 if(client->serialization_version == SER_FMT_VER_INVALID)
3358 bool sender_selected = (peer_id == client->peer_id);
3359 if(sender_selected == true && send_to_sender == false)
3361 if(sender_selected == false && send_to_others == false)
3364 SendChatMessage(client->peer_id, line);
3368 else if(command == TOSERVER_DAMAGE)
3370 std::string datastring((char*)&data[2], datasize-2);
3371 std::istringstream is(datastring, std::ios_base::binary);
3372 u8 damage = readU8(is);
3374 if(g_settings->getBool("enable_damage"))
3376 actionstream<<player->getName()<<" damaged by "
3377 <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
3380 HandlePlayerHP(player, damage);
3384 SendPlayerHP(player);
3387 else if(command == TOSERVER_PASSWORD)
3390 [0] u16 TOSERVER_PASSWORD
3391 [2] u8[28] old password
3392 [30] u8[28] new password
3395 if(datasize != 2+PASSWORD_SIZE*2)
3397 /*char password[PASSWORD_SIZE];
3398 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3399 password[i] = data[2+i];
3400 password[PASSWORD_SIZE-1] = 0;*/
3402 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3410 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3412 char c = data[2+PASSWORD_SIZE+i];
3418 infostream<<"Server: Client requests a password change from "
3419 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
3421 std::string playername = player->getName();
3423 if(m_authmanager.exists(playername) == false)
3425 infostream<<"Server: playername not found in authmanager"<<std::endl;
3426 // Wrong old password supplied!!
3427 SendChatMessage(peer_id, L"playername not found in authmanager");
3431 std::string checkpwd = m_authmanager.getPassword(playername);
3433 if(oldpwd != checkpwd)
3435 infostream<<"Server: invalid old password"<<std::endl;
3436 // Wrong old password supplied!!
3437 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
3441 actionstream<<player->getName()<<" changes password"<<std::endl;
3443 m_authmanager.setPassword(playername, newpwd);
3445 infostream<<"Server: password change successful for "<<playername
3447 SendChatMessage(peer_id, L"Password change successful");
3449 else if(command == TOSERVER_PLAYERITEM)
3454 u16 item = readU16(&data[2]);
3455 player->wieldItem(item);
3456 SendWieldedItem(player);
3458 else if(command == TOSERVER_RESPAWN)
3463 RespawnPlayer(player);
3465 actionstream<<player->getName()<<" respawns at "
3466 <<PP(player->getPosition()/BS)<<std::endl;
3470 infostream<<"Server::ProcessData(): Ignoring "
3471 "unknown command "<<command<<std::endl;
3475 catch(SendFailedException &e)
3477 errorstream<<"Server::ProcessData(): SendFailedException: "
3483 void Server::onMapEditEvent(MapEditEvent *event)
3485 //infostream<<"Server::onMapEditEvent()"<<std::endl;
3486 if(m_ignore_map_edit_events)
3488 MapEditEvent *e = event->clone();
3489 m_unsent_map_edit_queue.push_back(e);
3492 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3494 if(id == "current_player")
3496 assert(c->current_player);
3497 return &(c->current_player->inventory);
3501 std::string id0 = fn.next(":");
3503 if(id0 == "nodemeta")
3506 p.X = stoi(fn.next(","));
3507 p.Y = stoi(fn.next(","));
3508 p.Z = stoi(fn.next(","));
3509 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3511 return meta->getInventory();
3512 infostream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3513 <<"no metadata found"<<std::endl;
3517 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3520 void Server::inventoryModified(InventoryContext *c, std::string id)
3522 if(id == "current_player")
3524 assert(c->current_player);
3526 UpdateCrafting(c->current_player->peer_id);
3527 SendInventory(c->current_player->peer_id);
3532 std::string id0 = fn.next(":");
3534 if(id0 == "nodemeta")
3537 p.X = stoi(fn.next(","));
3538 p.Y = stoi(fn.next(","));
3539 p.Z = stoi(fn.next(","));
3540 v3s16 blockpos = getNodeBlockPos(p);
3542 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3544 meta->inventoryModified();
3546 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3548 block->raiseModified(MOD_STATE_WRITE_NEEDED);
3550 setBlockNotSent(blockpos);
3555 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3558 core::list<PlayerInfo> Server::getPlayerInfo()
3560 DSTACK(__FUNCTION_NAME);
3561 JMutexAutoLock envlock(m_env_mutex);
3562 JMutexAutoLock conlock(m_con_mutex);
3564 core::list<PlayerInfo> list;
3566 core::list<Player*> players = m_env->getPlayers();
3568 core::list<Player*>::Iterator i;
3569 for(i = players.begin();
3570 i != players.end(); i++)
3574 Player *player = *i;
3577 // Copy info from connection to info struct
3578 info.id = player->peer_id;
3579 info.address = m_con.GetPeerAddress(player->peer_id);
3580 info.avg_rtt = m_con.GetPeerAvgRTT(player->peer_id);
3582 catch(con::PeerNotFoundException &e)
3584 // Set dummy peer info
3586 info.address = Address(0,0,0,0,0);
3590 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3591 info.position = player->getPosition();
3593 list.push_back(info);
3600 void Server::peerAdded(con::Peer *peer)
3602 DSTACK(__FUNCTION_NAME);
3603 infostream<<"Server::peerAdded(): peer->id="
3604 <<peer->id<<std::endl;
3607 c.type = PEER_ADDED;
3608 c.peer_id = peer->id;
3610 m_peer_change_queue.push_back(c);
3613 void Server::deletingPeer(con::Peer *peer, bool timeout)
3615 DSTACK(__FUNCTION_NAME);
3616 infostream<<"Server::deletingPeer(): peer->id="
3617 <<peer->id<<", timeout="<<timeout<<std::endl;
3620 c.type = PEER_REMOVED;
3621 c.peer_id = peer->id;
3622 c.timeout = timeout;
3623 m_peer_change_queue.push_back(c);
3630 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3632 DSTACK(__FUNCTION_NAME);
3633 std::ostringstream os(std::ios_base::binary);
3635 writeU16(os, TOCLIENT_HP);
3639 std::string s = os.str();
3640 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3642 con.Send(peer_id, 0, data, true);
3645 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3646 const std::wstring &reason)
3648 DSTACK(__FUNCTION_NAME);
3649 std::ostringstream os(std::ios_base::binary);
3651 writeU16(os, TOCLIENT_ACCESS_DENIED);
3652 os<<serializeWideString(reason);
3655 std::string s = os.str();
3656 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3658 con.Send(peer_id, 0, data, true);
3661 void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
3662 bool set_camera_point_target, v3f camera_point_target)
3664 DSTACK(__FUNCTION_NAME);
3665 std::ostringstream os(std::ios_base::binary);
3667 writeU16(os, TOCLIENT_DEATHSCREEN);
3668 writeU8(os, set_camera_point_target);
3669 writeV3F1000(os, camera_point_target);
3672 std::string s = os.str();
3673 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3675 con.Send(peer_id, 0, data, true);
3678 void Server::SendToolDef(con::Connection &con, u16 peer_id,
3679 IToolDefManager *tooldef)
3681 DSTACK(__FUNCTION_NAME);
3682 std::ostringstream os(std::ios_base::binary);
3686 u32 length of the next item
3687 serialized ToolDefManager
3689 writeU16(os, TOCLIENT_TOOLDEF);
3690 std::ostringstream tmp_os(std::ios::binary);
3691 tooldef->serialize(tmp_os);
3692 os<<serializeLongString(tmp_os.str());
3695 std::string s = os.str();
3696 infostream<<"Server::SendToolDef(): Sending tool definitions: size="
3697 <<s.size()<<std::endl;
3698 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3700 con.Send(peer_id, 0, data, true);
3703 void Server::SendNodeDef(con::Connection &con, u16 peer_id,
3704 INodeDefManager *nodedef)
3706 DSTACK(__FUNCTION_NAME);
3707 std::ostringstream os(std::ios_base::binary);
3711 u32 length of the next item
3712 serialized NodeDefManager
3714 writeU16(os, TOCLIENT_NODEDEF);
3715 std::ostringstream tmp_os(std::ios::binary);
3716 nodedef->serialize(tmp_os);
3717 os<<serializeLongString(tmp_os.str());
3720 std::string s = os.str();
3721 infostream<<"Server::SendNodeDef(): Sending node definitions: size="
3722 <<s.size()<<std::endl;
3723 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3725 con.Send(peer_id, 0, data, true);
3729 Non-static send methods
3732 void Server::SendObjectData(float dtime)
3734 DSTACK(__FUNCTION_NAME);
3736 core::map<v3s16, bool> stepped_blocks;
3738 for(core::map<u16, RemoteClient*>::Iterator
3739 i = m_clients.getIterator();
3740 i.atEnd() == false; i++)
3742 u16 peer_id = i.getNode()->getKey();
3743 RemoteClient *client = i.getNode()->getValue();
3744 assert(client->peer_id == peer_id);
3746 if(client->serialization_version == SER_FMT_VER_INVALID)
3749 client->SendObjectData(this, dtime, stepped_blocks);
3753 void Server::SendPlayerInfos()
3755 DSTACK(__FUNCTION_NAME);
3757 //JMutexAutoLock envlock(m_env_mutex);
3759 // Get connected players
3760 core::list<Player*> players = m_env->getPlayers(true);
3762 u32 player_count = players.getSize();
3763 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3765 SharedBuffer<u8> data(datasize);
3766 writeU16(&data[0], TOCLIENT_PLAYERINFO);
3769 core::list<Player*>::Iterator i;
3770 for(i = players.begin();
3771 i != players.end(); i++)
3773 Player *player = *i;
3775 /*infostream<<"Server sending player info for player with "
3776 "peer_id="<<player->peer_id<<std::endl;*/
3778 writeU16(&data[start], player->peer_id);
3779 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3780 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3781 start += 2+PLAYERNAME_SIZE;
3784 //JMutexAutoLock conlock(m_con_mutex);
3787 m_con.SendToAll(0, data, true);
3790 void Server::SendInventory(u16 peer_id)
3792 DSTACK(__FUNCTION_NAME);
3794 Player* player = m_env->getPlayer(peer_id);
3801 std::ostringstream os;
3802 //os.imbue(std::locale("C"));
3804 player->inventory.serialize(os);
3806 std::string s = os.str();
3808 SharedBuffer<u8> data(s.size()+2);
3809 writeU16(&data[0], TOCLIENT_INVENTORY);
3810 memcpy(&data[2], s.c_str(), s.size());
3813 m_con.Send(peer_id, 0, data, true);
3816 std::string getWieldedItemString(const Player *player)
3818 const InventoryItem *item = player->getWieldItem();
3820 return std::string("");
3821 std::ostringstream os(std::ios_base::binary);
3822 item->serialize(os);
3826 void Server::SendWieldedItem(const Player* player)
3828 DSTACK(__FUNCTION_NAME);
3832 std::ostringstream os(std::ios_base::binary);
3834 writeU16(os, TOCLIENT_PLAYERITEM);
3836 writeU16(os, player->peer_id);
3837 os<<serializeString(getWieldedItemString(player));
3840 std::string s = os.str();
3841 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3843 m_con.SendToAll(0, data, true);
3846 void Server::SendPlayerItems()
3848 DSTACK(__FUNCTION_NAME);
3850 std::ostringstream os(std::ios_base::binary);
3851 core::list<Player *> players = m_env->getPlayers(true);
3853 writeU16(os, TOCLIENT_PLAYERITEM);
3854 writeU16(os, players.size());
3855 core::list<Player *>::Iterator i;
3856 for(i = players.begin(); i != players.end(); ++i)
3859 writeU16(os, p->peer_id);
3860 os<<serializeString(getWieldedItemString(p));
3864 std::string s = os.str();
3865 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3867 m_con.SendToAll(0, data, true);
3870 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3872 DSTACK(__FUNCTION_NAME);
3874 std::ostringstream os(std::ios_base::binary);
3878 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3879 os.write((char*)buf, 2);
3882 writeU16(buf, message.size());
3883 os.write((char*)buf, 2);
3886 for(u32 i=0; i<message.size(); i++)
3890 os.write((char*)buf, 2);
3894 std::string s = os.str();
3895 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3897 m_con.Send(peer_id, 0, data, true);
3900 void Server::BroadcastChatMessage(const std::wstring &message)
3902 for(core::map<u16, RemoteClient*>::Iterator
3903 i = m_clients.getIterator();
3904 i.atEnd() == false; i++)
3906 // Get client and check that it is valid
3907 RemoteClient *client = i.getNode()->getValue();
3908 assert(client->peer_id == i.getNode()->getKey());
3909 if(client->serialization_version == SER_FMT_VER_INVALID)
3912 SendChatMessage(client->peer_id, message);
3916 void Server::SendPlayerHP(Player *player)
3918 SendHP(m_con, player->peer_id, player->hp);
3921 void Server::SendMovePlayer(Player *player)
3923 DSTACK(__FUNCTION_NAME);
3924 std::ostringstream os(std::ios_base::binary);
3926 writeU16(os, TOCLIENT_MOVE_PLAYER);
3927 writeV3F1000(os, player->getPosition());
3928 writeF1000(os, player->getPitch());
3929 writeF1000(os, player->getYaw());
3932 v3f pos = player->getPosition();
3933 f32 pitch = player->getPitch();
3934 f32 yaw = player->getYaw();
3935 infostream<<"Server sending TOCLIENT_MOVE_PLAYER"
3936 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3943 std::string s = os.str();
3944 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3946 m_con.Send(player->peer_id, 0, data, true);
3949 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3950 core::list<u16> *far_players, float far_d_nodes)
3952 float maxd = far_d_nodes*BS;
3953 v3f p_f = intToFloat(p, BS);
3957 SharedBuffer<u8> reply(replysize);
3958 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3959 writeS16(&reply[2], p.X);
3960 writeS16(&reply[4], p.Y);
3961 writeS16(&reply[6], p.Z);
3963 for(core::map<u16, RemoteClient*>::Iterator
3964 i = m_clients.getIterator();
3965 i.atEnd() == false; i++)
3967 // Get client and check that it is valid
3968 RemoteClient *client = i.getNode()->getValue();
3969 assert(client->peer_id == i.getNode()->getKey());
3970 if(client->serialization_version == SER_FMT_VER_INVALID)
3973 // Don't send if it's the same one
3974 if(client->peer_id == ignore_id)
3980 Player *player = m_env->getPlayer(client->peer_id);
3983 // If player is far away, only set modified blocks not sent
3984 v3f player_pos = player->getPosition();
3985 if(player_pos.getDistanceFrom(p_f) > maxd)
3987 far_players->push_back(client->peer_id);
3994 m_con.Send(client->peer_id, 0, reply, true);
3998 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3999 core::list<u16> *far_players, float far_d_nodes)
4001 float maxd = far_d_nodes*BS;
4002 v3f p_f = intToFloat(p, BS);
4004 for(core::map<u16, RemoteClient*>::Iterator
4005 i = m_clients.getIterator();
4006 i.atEnd() == false; i++)
4008 // Get client and check that it is valid
4009 RemoteClient *client = i.getNode()->getValue();
4010 assert(client->peer_id == i.getNode()->getKey());
4011 if(client->serialization_version == SER_FMT_VER_INVALID)
4014 // Don't send if it's the same one
4015 if(client->peer_id == ignore_id)
4021 Player *player = m_env->getPlayer(client->peer_id);
4024 // If player is far away, only set modified blocks not sent
4025 v3f player_pos = player->getPosition();
4026 if(player_pos.getDistanceFrom(p_f) > maxd)
4028 far_players->push_back(client->peer_id);
4035 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
4036 SharedBuffer<u8> reply(replysize);
4037 writeU16(&reply[0], TOCLIENT_ADDNODE);
4038 writeS16(&reply[2], p.X);
4039 writeS16(&reply[4], p.Y);
4040 writeS16(&reply[6], p.Z);
4041 n.serialize(&reply[8], client->serialization_version);
4044 m_con.Send(client->peer_id, 0, reply, true);
4048 void Server::setBlockNotSent(v3s16 p)
4050 for(core::map<u16, RemoteClient*>::Iterator
4051 i = m_clients.getIterator();
4052 i.atEnd()==false; i++)
4054 RemoteClient *client = i.getNode()->getValue();
4055 client->SetBlockNotSent(p);
4059 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
4061 DSTACK(__FUNCTION_NAME);
4063 v3s16 p = block->getPos();
4067 bool completely_air = true;
4068 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4069 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4070 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4072 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
4074 completely_air = false;
4075 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
4080 infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
4082 infostream<<"[completely air] ";
4083 infostream<<std::endl;
4087 Create a packet with the block in the right format
4090 std::ostringstream os(std::ios_base::binary);
4091 block->serialize(os, ver);
4092 std::string s = os.str();
4093 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
4095 u32 replysize = 8 + blockdata.getSize();
4096 SharedBuffer<u8> reply(replysize);
4097 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
4098 writeS16(&reply[2], p.X);
4099 writeS16(&reply[4], p.Y);
4100 writeS16(&reply[6], p.Z);
4101 memcpy(&reply[8], *blockdata, blockdata.getSize());
4103 /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4104 <<": \tpacket size: "<<replysize<<std::endl;*/
4109 m_con.Send(peer_id, 1, reply, true);
4112 void Server::SendBlocks(float dtime)
4114 DSTACK(__FUNCTION_NAME);
4116 JMutexAutoLock envlock(m_env_mutex);
4117 JMutexAutoLock conlock(m_con_mutex);
4119 //TimeTaker timer("Server::SendBlocks");
4121 core::array<PrioritySortedBlockTransfer> queue;
4123 s32 total_sending = 0;
4126 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
4128 for(core::map<u16, RemoteClient*>::Iterator
4129 i = m_clients.getIterator();
4130 i.atEnd() == false; i++)
4132 RemoteClient *client = i.getNode()->getValue();
4133 assert(client->peer_id == i.getNode()->getKey());
4135 total_sending += client->SendingCount();
4137 if(client->serialization_version == SER_FMT_VER_INVALID)
4140 client->GetNextBlocks(this, dtime, queue);
4145 // Lowest priority number comes first.
4146 // Lowest is most important.
4149 for(u32 i=0; i<queue.size(); i++)
4151 //TODO: Calculate limit dynamically
4152 if(total_sending >= g_settings->getS32
4153 ("max_simultaneous_block_sends_server_total"))
4156 PrioritySortedBlockTransfer q = queue[i];
4158 MapBlock *block = NULL;
4161 block = m_env->getMap().getBlockNoCreate(q.pos);
4163 catch(InvalidPositionException &e)
4168 RemoteClient *client = getClient(q.peer_id);
4170 SendBlockNoLock(q.peer_id, block, client->serialization_version);
4172 client->SentBlock(q.pos);
4178 struct SendableTexture
4184 SendableTexture(const std::string &name_="", const std::string path_="",
4185 const std::string &data_=""):
4192 void Server::SendTextures(u16 peer_id)
4194 DSTACK(__FUNCTION_NAME);
4196 infostream<<"Server::SendTextures(): Sending textures to client"<<std::endl;
4200 // Put 5kB in one bunch (this is not accurate)
4201 u32 bytes_per_bunch = 5000;
4203 core::array< core::list<SendableTexture> > texture_bunches;
4204 texture_bunches.push_back(core::list<SendableTexture>());
4206 u32 texture_size_bunch_total = 0;
4207 core::list<ModSpec> mods = getMods(m_modspaths);
4208 for(core::list<ModSpec>::Iterator i = mods.begin();
4209 i != mods.end(); i++){
4211 std::string texturepath = mod.path + DIR_DELIM + "textures";
4212 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(texturepath);
4213 for(u32 j=0; j<dirlist.size(); j++){
4214 if(dirlist[j].dir) // Ignode dirs
4216 std::string tname = dirlist[j].name;
4217 std::string tpath = texturepath + DIR_DELIM + tname;
4219 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
4220 if(fis.good() == false){
4221 errorstream<<"Server::SendTextures(): Could not open \""
4222 <<tname<<"\" for reading"<<std::endl;
4225 std::ostringstream tmp_os(std::ios_base::binary);
4229 fis.read(buf, 1024);
4230 std::streamsize len = fis.gcount();
4231 tmp_os.write(buf, len);
4232 texture_size_bunch_total += len;
4241 errorstream<<"Server::SendTextures(): Failed to read \""
4242 <<tname<<"\""<<std::endl;
4245 /*infostream<<"Server::SendTextures(): Loaded \""
4246 <<tname<<"\""<<std::endl;*/
4248 texture_bunches[texture_bunches.size()-1].push_back(
4249 SendableTexture(tname, tpath, tmp_os.str()));
4251 // Start next bunch if got enough data
4252 if(texture_size_bunch_total >= bytes_per_bunch){
4253 texture_bunches.push_back(core::list<SendableTexture>());
4254 texture_size_bunch_total = 0;
4259 /* Create and send packets */
4261 u32 num_bunches = texture_bunches.size();
4262 for(u32 i=0; i<num_bunches; i++)
4266 u16 total number of texture bunches
4267 u16 index of this bunch
4268 u32 number of textures in this bunch
4276 std::ostringstream os(std::ios_base::binary);
4278 writeU16(os, TOCLIENT_TEXTURES);
4279 writeU16(os, num_bunches);
4281 writeU32(os, texture_bunches[i].size());
4283 for(core::list<SendableTexture>::Iterator
4284 j = texture_bunches[i].begin();
4285 j != texture_bunches[i].end(); j++){
4286 os<<serializeString(j->name);
4287 os<<serializeLongString(j->data);
4291 std::string s = os.str();
4292 infostream<<"Server::SendTextures(): bunch "<<i<<"/"<<num_bunches
4293 <<" textures="<<texture_bunches[i].size()
4294 <<" size=" <<s.size()<<std::endl;
4295 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4297 m_con.Send(peer_id, 0, data, true);
4305 void Server::HandlePlayerHP(Player *player, s16 damage)
4307 if(player->hp > damage)
4309 player->hp -= damage;
4310 SendPlayerHP(player);
4314 infostream<<"Server::HandlePlayerHP(): Player "
4315 <<player->getName()<<" dies"<<std::endl;
4319 //TODO: Throw items around
4321 // Handle players that are not connected
4322 if(player->peer_id == PEER_ID_INEXISTENT){
4323 RespawnPlayer(player);
4327 SendPlayerHP(player);
4329 RemoteClient *client = getClient(player->peer_id);
4330 if(client->net_proto_version >= 3)
4332 SendDeathscreen(m_con, player->peer_id, false, v3f(0,0,0));
4336 RespawnPlayer(player);
4341 void Server::RespawnPlayer(Player *player)
4343 v3f pos = findSpawnPos(m_env->getServerMap());
4344 player->setPosition(pos);
4346 SendMovePlayer(player);
4347 SendPlayerHP(player);
4350 void Server::UpdateCrafting(u16 peer_id)
4352 DSTACK(__FUNCTION_NAME);
4354 Player* player = m_env->getPlayer(peer_id);
4358 Calculate crafting stuff
4360 if(g_settings->getBool("creative_mode") == false)
4362 InventoryList *clist = player->inventory.getList("craft");
4363 InventoryList *rlist = player->inventory.getList("craftresult");
4365 if(rlist && rlist->getUsedSlots() == 0)
4366 player->craftresult_is_preview = true;
4368 if(rlist && player->craftresult_is_preview)
4370 rlist->clearItems();
4372 if(clist && rlist && player->craftresult_is_preview)
4374 // Get result of crafting grid
4376 std::vector<InventoryItem*> items;
4377 for(u16 i=0; i<9; i++){
4378 if(clist->getItem(i) == NULL)
4379 items.push_back(NULL);
4381 items.push_back(clist->getItem(i)->clone());
4383 CraftPointerInput cpi(3, items);
4385 InventoryItem *result = m_craftdef->getCraftResult(cpi, this);
4386 //InventoryItem *result = craft_get_result(items, this);
4388 rlist->addItem(result);
4391 } // if creative_mode == false
4394 RemoteClient* Server::getClient(u16 peer_id)
4396 DSTACK(__FUNCTION_NAME);
4397 //JMutexAutoLock lock(m_con_mutex);
4398 core::map<u16, RemoteClient*>::Node *n;
4399 n = m_clients.find(peer_id);
4400 // A client should exist for all peers
4402 return n->getValue();
4405 std::wstring Server::getStatusString()
4407 std::wostringstream os(std::ios_base::binary);
4410 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4412 os<<L", uptime="<<m_uptime.get();
4413 // Information about clients
4415 for(core::map<u16, RemoteClient*>::Iterator
4416 i = m_clients.getIterator();
4417 i.atEnd() == false; i++)
4419 // Get client and check that it is valid
4420 RemoteClient *client = i.getNode()->getValue();
4421 assert(client->peer_id == i.getNode()->getKey());
4422 if(client->serialization_version == SER_FMT_VER_INVALID)
4425 Player *player = m_env->getPlayer(client->peer_id);
4426 // Get name of player
4427 std::wstring name = L"unknown";
4429 name = narrow_to_wide(player->getName());
4430 // Add name to information string
4434 if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
4435 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4436 if(g_settings->get("motd") != "")
4437 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4441 // Saves g_settings to configpath given at initialization
4442 void Server::saveConfig()
4444 if(m_configpath != "")
4445 g_settings->updateConfigFile(m_configpath.c_str());
4448 void Server::notifyPlayer(const char *name, const std::wstring msg)
4450 Player *player = m_env->getPlayer(name);
4453 SendChatMessage(player->peer_id, std::wstring(L"Server: -!- ")+msg);
4456 void Server::notifyPlayers(const std::wstring msg)
4458 BroadcastChatMessage(msg);
4461 // IGameDef interface
4463 IToolDefManager* Server::getToolDefManager()
4467 INodeDefManager* Server::getNodeDefManager()
4471 ICraftDefManager* Server::getCraftDefManager()
4475 ITextureSource* Server::getTextureSource()
4479 u16 Server::allocateUnknownNodeId(const std::string &name)
4481 return m_nodedef->allocateDummy(name);
4484 IWritableToolDefManager* Server::getWritableToolDefManager()
4488 IWritableNodeDefManager* Server::getWritableNodeDefManager()
4492 IWritableCraftDefManager* Server::getWritableCraftDefManager()
4497 v3f findSpawnPos(ServerMap &map)
4499 //return v3f(50,50,50)*BS;
4504 nodepos = v2s16(0,0);
4509 // Try to find a good place a few times
4510 for(s32 i=0; i<1000; i++)
4513 // We're going to try to throw the player to this position
4514 v2s16 nodepos2d = v2s16(-range + (myrand()%(range*2)),
4515 -range + (myrand()%(range*2)));
4516 //v2s16 sectorpos = getNodeSectorPos(nodepos2d);
4517 // Get ground height at point (fallbacks to heightmap function)
4518 s16 groundheight = map.findGroundLevel(nodepos2d);
4519 // Don't go underwater
4520 if(groundheight < WATER_LEVEL)
4522 //infostream<<"-> Underwater"<<std::endl;
4525 // Don't go to high places
4526 if(groundheight > WATER_LEVEL + 4)
4528 //infostream<<"-> Underwater"<<std::endl;
4532 nodepos = v3s16(nodepos2d.X, groundheight-2, nodepos2d.Y);
4533 bool is_good = false;
4535 for(s32 i=0; i<10; i++){
4536 v3s16 blockpos = getNodeBlockPos(nodepos);
4537 map.emergeBlock(blockpos, true);
4538 MapNode n = map.getNodeNoEx(nodepos);
4539 if(n.getContent() == CONTENT_AIR){
4550 // Found a good place
4551 //infostream<<"Searched through "<<i<<" places."<<std::endl;
4557 return intToFloat(nodepos, BS);
4560 Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id)
4563 Try to get an existing player
4565 Player *player = m_env->getPlayer(name);
4568 // If player is already connected, cancel
4569 if(player->peer_id != 0)
4571 infostream<<"emergePlayer(): Player already connected"<<std::endl;
4576 player->peer_id = peer_id;
4578 // Reset inventory to creative if in creative mode
4579 if(g_settings->getBool("creative_mode"))
4581 // Warning: double code below
4582 // Backup actual inventory
4583 player->inventory_backup = new Inventory();
4584 *(player->inventory_backup) = player->inventory;
4585 // Set creative inventory
4586 craft_set_creative_inventory(player, this);
4593 If player with the wanted peer_id already exists, cancel.
4595 if(m_env->getPlayer(peer_id) != NULL)
4597 infostream<<"emergePlayer(): Player with wrong name but same"
4598 " peer_id already exists"<<std::endl;
4606 // Add authentication stuff
4607 m_authmanager.add(name);
4608 m_authmanager.setPassword(name, password);
4609 m_authmanager.setPrivs(name,
4610 stringToPrivs(g_settings->get("default_privs")));
4616 infostream<<"Server: Finding spawn place for player \""
4617 <<name<<"\""<<std::endl;
4619 v3f pos = findSpawnPos(m_env->getServerMap());
4621 player = new ServerRemotePlayer(m_env, pos, peer_id, name);
4624 Add player to environment
4627 m_env->addPlayer(player);
4630 Add stuff to inventory
4633 if(g_settings->getBool("creative_mode"))
4635 // Warning: double code above
4636 // Backup actual inventory
4637 player->inventory_backup = new Inventory();
4638 *(player->inventory_backup) = player->inventory;
4639 // Set creative inventory
4640 craft_set_creative_inventory(player, this);
4642 else if(g_settings->getBool("give_initial_stuff"))
4644 craft_give_initial_stuff(player, this);
4649 } // create new player
4652 void Server::handlePeerChange(PeerChange &c)
4654 JMutexAutoLock envlock(m_env_mutex);
4655 JMutexAutoLock conlock(m_con_mutex);
4657 if(c.type == PEER_ADDED)
4664 core::map<u16, RemoteClient*>::Node *n;
4665 n = m_clients.find(c.peer_id);
4666 // The client shouldn't already exist
4670 RemoteClient *client = new RemoteClient();
4671 client->peer_id = c.peer_id;
4672 m_clients.insert(client->peer_id, client);
4675 else if(c.type == PEER_REMOVED)
4682 core::map<u16, RemoteClient*>::Node *n;
4683 n = m_clients.find(c.peer_id);
4684 // The client should exist
4688 Mark objects to be not known by the client
4690 RemoteClient *client = n->getValue();
4692 for(core::map<u16, bool>::Iterator
4693 i = client->m_known_objects.getIterator();
4694 i.atEnd()==false; i++)
4697 u16 id = i.getNode()->getKey();
4698 ServerActiveObject* obj = m_env->getActiveObject(id);
4700 if(obj && obj->m_known_by_count > 0)
4701 obj->m_known_by_count--;
4704 // Collect information about leaving in chat
4705 std::wstring message;
4707 Player *player = m_env->getPlayer(c.peer_id);
4710 std::wstring name = narrow_to_wide(player->getName());
4713 message += L" left game";
4715 message += L" (timed out)";
4721 m_env->removePlayer(c.peer_id);
4724 // Set player client disconnected
4726 Player *player = m_env->getPlayer(c.peer_id);
4728 player->peer_id = 0;
4735 std::ostringstream os(std::ios_base::binary);
4736 for(core::map<u16, RemoteClient*>::Iterator
4737 i = m_clients.getIterator();
4738 i.atEnd() == false; i++)
4740 RemoteClient *client = i.getNode()->getValue();
4741 assert(client->peer_id == i.getNode()->getKey());
4742 if(client->serialization_version == SER_FMT_VER_INVALID)
4745 Player *player = m_env->getPlayer(client->peer_id);
4748 // Get name of player
4749 os<<player->getName()<<" ";
4752 actionstream<<player->getName()<<" "
4753 <<(c.timeout?"times out.":"leaves game.")
4754 <<" List of players: "
4755 <<os.str()<<std::endl;
4760 delete m_clients[c.peer_id];
4761 m_clients.remove(c.peer_id);
4763 // Send player info to all remaining clients
4766 // Send leave chat message to all remaining clients
4767 BroadcastChatMessage(message);
4776 void Server::handlePeerChanges()
4778 while(m_peer_change_queue.size() > 0)
4780 PeerChange c = m_peer_change_queue.pop_front();
4782 infostream<<"Server: Handling peer change: "
4783 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4786 handlePeerChange(c);
4790 u64 Server::getPlayerPrivs(Player *player)
4794 std::string playername = player->getName();
4795 // Local player gets all privileges regardless of
4796 // what's set on their account.
4797 if(g_settings->get("name") == playername)
4803 return getPlayerAuthPrivs(playername);
4807 void dedicated_server_loop(Server &server, bool &kill)
4809 DSTACK(__FUNCTION_NAME);
4811 infostream<<DTIME<<std::endl;
4812 infostream<<"========================"<<std::endl;
4813 infostream<<"Running dedicated server"<<std::endl;
4814 infostream<<"========================"<<std::endl;
4815 infostream<<std::endl;
4817 IntervalLimiter m_profiler_interval;
4821 // This is kind of a hack but can be done like this
4822 // because server.step() is very light
4824 ScopeProfiler sp(g_profiler, "dedicated server sleep");
4829 if(server.getShutdownRequested() || kill)
4831 infostream<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4838 float profiler_print_interval =
4839 g_settings->getFloat("profiler_print_interval");
4840 if(profiler_print_interval != 0)
4842 if(m_profiler_interval.step(0.030, profiler_print_interval))
4844 infostream<<"Profiler:"<<std::endl;
4845 g_profiler->print(infostream);
4846 g_profiler->clear();
4853 static int counter = 0;
4859 core::list<PlayerInfo> list = server.getPlayerInfo();
4860 core::list<PlayerInfo>::Iterator i;
4861 static u32 sum_old = 0;
4862 u32 sum = PIChecksum(list);
4865 infostream<<DTIME<<"Player info:"<<std::endl;
4866 for(i=list.begin(); i!=list.end(); i++)
4868 i->PrintLine(&infostream);