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 scriptapi_environment_on_punchnode(m_lua, p_under, n);
2539 else if(action == 2)
2542 RemoteClient *client = getClient(peer_id);
2543 JMutexAutoLock digmutex(client->m_dig_mutex);
2544 client->m_dig_tool_item = -1;
2549 3: Digging completed
2551 else if(action == 3)
2553 // Mandatory parameter; actually used for nothing
2554 core::map<v3s16, MapBlock*> modified_blocks;
2556 content_t material = CONTENT_IGNORE;
2557 u8 mineral = MINERAL_NONE;
2559 bool cannot_remove_node = false;
2561 MapNode n(CONTENT_IGNORE);
2564 n = m_env->getMap().getNode(p_under);
2566 mineral = n.getMineral(m_nodedef);
2567 // Get material at position
2568 material = n.getContent();
2569 // If not yet cancelled
2570 if(cannot_remove_node == false)
2572 // If it's not diggable, do nothing
2573 if(m_nodedef->get(material).diggable == false)
2575 infostream<<"Server: Not finishing digging: "
2576 <<"Node not diggable"
2578 cannot_remove_node = true;
2581 // If not yet cancelled
2582 if(cannot_remove_node == false)
2584 // Get node metadata
2585 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p_under);
2586 if(meta && meta->nodeRemovalDisabled() == true)
2588 infostream<<"Server: Not finishing digging: "
2589 <<"Node metadata disables removal"
2591 cannot_remove_node = true;
2595 catch(InvalidPositionException &e)
2597 infostream<<"Server: Not finishing digging: Node not found."
2598 <<" Adding block to emerge queue."
2600 m_emerge_queue.addBlock(peer_id,
2601 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2602 cannot_remove_node = true;
2605 // Make sure the player is allowed to do it
2606 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2608 infostream<<"Player "<<player->getName()<<" cannot remove node"
2609 <<" because privileges are "<<getPlayerPrivs(player)
2611 cannot_remove_node = true;
2615 If node can't be removed, set block to be re-sent to
2618 if(cannot_remove_node)
2620 infostream<<"Server: Not finishing digging."<<std::endl;
2622 // Client probably has wrong data.
2623 // Set block not sent, so that client will get
2625 infostream<<"Client "<<peer_id<<" tried to dig "
2626 <<"node; but node cannot be removed."
2627 <<" setting MapBlock not sent."<<std::endl;
2628 RemoteClient *client = getClient(peer_id);
2629 v3s16 blockpos = getNodeBlockPos(p_under);
2630 client->SetBlockNotSent(blockpos);
2635 actionstream<<player->getName()<<" digs "<<PP(p_under)
2636 <<", gets material "<<(int)material<<", mineral "
2637 <<(int)mineral<<std::endl;
2640 Send the removal to all close-by players.
2641 - If other player is close, send REMOVENODE
2642 - Otherwise set blocks not sent
2644 core::list<u16> far_players;
2645 sendRemoveNode(p_under, peer_id, &far_players, 30);
2648 Update and send inventory
2651 if(g_settings->getBool("creative_mode") == false)
2656 InventoryList *mlist = player->inventory.getList("main");
2659 InventoryItem *item = mlist->getItem(item_i);
2660 if(item && (std::string)item->getName() == "ToolItem")
2662 ToolItem *titem = (ToolItem*)item;
2663 std::string toolname = titem->getToolName();
2665 // Get digging properties for material and tool
2666 ToolDiggingProperties tp =
2667 m_toolmgr->getDiggingProperties(toolname);
2668 DiggingProperties prop =
2669 getDiggingProperties(material, &tp, m_nodedef);
2671 if(prop.diggable == false)
2673 infostream<<"Server: WARNING: Player digged"
2674 <<" with impossible material + tool"
2675 <<" combination"<<std::endl;
2678 bool weared_out = titem->addWear(prop.wear);
2682 mlist->deleteItem(item_i);
2688 Add dug item to inventory
2691 InventoryItem *item = NULL;
2693 if(mineral != MINERAL_NONE)
2694 item = getDiggedMineralItem(mineral, this);
2699 const std::string &dug_s = m_nodedef->get(material).dug_item;
2702 std::istringstream is(dug_s, std::ios::binary);
2703 item = InventoryItem::deSerialize(is, this);
2709 // Add a item to inventory
2710 player->inventory.addItem("main", item);
2713 UpdateCrafting(player->peer_id);
2714 SendInventory(player->peer_id);
2719 if(mineral != MINERAL_NONE)
2720 item = getDiggedMineralItem(mineral, this);
2725 const std::string &extra_dug_s = m_nodedef->get(material).extra_dug_item;
2726 s32 extra_rarity = m_nodedef->get(material).extra_dug_item_rarity;
2727 if(extra_dug_s != "" && extra_rarity != 0
2728 && myrand() % extra_rarity == 0)
2730 std::istringstream is(extra_dug_s, std::ios::binary);
2731 item = InventoryItem::deSerialize(is, this);
2737 // Add a item to inventory
2738 player->inventory.addItem("main", item);
2741 UpdateCrafting(player->peer_id);
2742 SendInventory(player->peer_id);
2748 (this takes some time so it is done after the quick stuff)
2751 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
2753 m_env->getMap().removeNodeAndUpdate(p_under, modified_blocks);
2756 Set blocks not sent to far players
2758 for(core::list<u16>::Iterator
2759 i = far_players.begin();
2760 i != far_players.end(); i++)
2763 RemoteClient *client = getClient(peer_id);
2766 client->SetBlocksNotSent(modified_blocks);
2772 scriptapi_environment_on_dignode(m_lua, p_under, n);
2778 else if(action == 1)
2781 InventoryList *ilist = player->inventory.getList("main");
2786 InventoryItem *item = ilist->getItem(item_i);
2788 // If there is no item, it is not possible to add it anywhere
2793 Handle material items
2795 if(std::string("MaterialItem") == item->getName())
2798 // Don't add a node if this is not a free space
2799 MapNode n2 = m_env->getMap().getNode(p_over);
2800 bool no_enough_privs =
2801 ((getPlayerPrivs(player) & PRIV_BUILD)==0);
2803 infostream<<"Player "<<player->getName()<<" cannot add node"
2804 <<" because privileges are "<<getPlayerPrivs(player)
2807 if(m_nodedef->get(n2).buildable_to == false
2810 // Client probably has wrong data.
2811 // Set block not sent, so that client will get
2813 infostream<<"Client "<<peer_id<<" tried to place"
2814 <<" node in invalid position; setting"
2815 <<" MapBlock not sent."<<std::endl;
2816 RemoteClient *client = getClient(peer_id);
2817 v3s16 blockpos = getNodeBlockPos(p_over);
2818 client->SetBlockNotSent(blockpos);
2822 catch(InvalidPositionException &e)
2824 infostream<<"Server: Ignoring ADDNODE: Node not found"
2825 <<" Adding block to emerge queue."
2827 m_emerge_queue.addBlock(peer_id,
2828 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2832 // Reset build time counter
2833 getClient(peer_id)->m_time_from_building = 0.0;
2836 MaterialItem *mitem = (MaterialItem*)item;
2838 n.setContent(mitem->getMaterial());
2840 actionstream<<player->getName()<<" places material "
2841 <<(int)mitem->getMaterial()
2842 <<" at "<<PP(p_under)<<std::endl;
2844 // Calculate direction for wall mounted stuff
2845 if(m_nodedef->get(n).wall_mounted)
2846 n.param2 = packDir(p_under - p_over);
2848 // Calculate the direction for furnaces and chests and stuff
2849 if(m_nodedef->get(n).param_type == CPT_FACEDIR_SIMPLE)
2851 v3f playerpos = player->getPosition();
2852 v3f blockpos = intToFloat(p_over, BS) - playerpos;
2853 blockpos = blockpos.normalize();
2855 if (fabs(blockpos.X) > fabs(blockpos.Z)) {
2869 Send to all close-by players
2871 core::list<u16> far_players;
2872 sendAddNode(p_over, n, 0, &far_players, 30);
2877 InventoryList *ilist = player->inventory.getList("main");
2878 if(g_settings->getBool("creative_mode") == false && ilist)
2880 // Remove from inventory and send inventory
2881 if(mitem->getCount() == 1)
2882 ilist->deleteItem(item_i);
2886 UpdateCrafting(peer_id);
2887 SendInventory(peer_id);
2893 This takes some time so it is done after the quick stuff
2895 core::map<v3s16, MapBlock*> modified_blocks;
2897 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
2899 std::string p_name = std::string(player->getName());
2900 m_env->getMap().addNodeAndUpdate(p_over, n, modified_blocks, p_name);
2903 Set blocks not sent to far players
2905 for(core::list<u16>::Iterator
2906 i = far_players.begin();
2907 i != far_players.end(); i++)
2910 RemoteClient *client = getClient(peer_id);
2913 client->SetBlocksNotSent(modified_blocks);
2919 scriptapi_environment_on_placenode(m_lua, p_over, n);
2922 Calculate special events
2925 /*if(n.d == LEGN(m_nodedef, "CONTENT_MESE"))
2928 for(s16 z=-1; z<=1; z++)
2929 for(s16 y=-1; y<=1; y++)
2930 for(s16 x=-1; x<=1; x++)
2937 Place other item (not a block)
2941 v3s16 blockpos = getNodeBlockPos(p_over);
2944 Check that the block is loaded so that the item
2945 can properly be added to the static list too
2947 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
2950 infostream<<"Error while placing object: "
2951 "block not found"<<std::endl;
2956 If in creative mode, item dropping is disabled unless
2957 player has build privileges
2959 if(g_settings->getBool("creative_mode") &&
2960 (getPlayerPrivs(player) & PRIV_BUILD) == 0)
2962 infostream<<"Not allowing player to drop item: "
2963 "creative mode and no build privs"<<std::endl;
2967 // Calculate a position for it
2968 v3f pos = intToFloat(p_over, BS);
2970 /*pos.Y -= BS*0.25; // let it drop a bit
2972 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2973 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;*/
2978 ServerActiveObject *obj = item->createSAO(m_env, 0, pos);
2982 infostream<<"WARNING: item resulted in NULL object, "
2983 <<"not placing onto map"
2988 actionstream<<player->getName()<<" places "<<item->getName()
2989 <<" at "<<PP(p_over)<<std::endl;
2991 // Add the object to the environment
2992 m_env->addActiveObject(obj);
2994 infostream<<"Placed object"<<std::endl;
2996 if(g_settings->getBool("creative_mode") == false)
2998 // Delete the right amount of items from the slot
2999 u16 dropcount = item->getDropCount();
3001 // Delete item if all gone
3002 if(item->getCount() <= dropcount)
3004 if(item->getCount() < dropcount)
3005 infostream<<"WARNING: Server: dropped more items"
3006 <<" than the slot contains"<<std::endl;
3008 InventoryList *ilist = player->inventory.getList("main");
3010 // Remove from inventory and send inventory
3011 ilist->deleteItem(item_i);
3013 // Else decrement it
3015 item->remove(dropcount);
3018 UpdateCrafting(peer_id);
3019 SendInventory(peer_id);
3027 Catch invalid actions
3031 infostream<<"WARNING: Server: Invalid action "
3032 <<action<<std::endl;
3036 else if(command == TOSERVER_RELEASE)
3045 infostream<<"TOSERVER_RELEASE ignored"<<std::endl;
3048 else if(command == TOSERVER_SIGNTEXT)
3050 infostream<<"Server: TOSERVER_SIGNTEXT not supported anymore"
3054 else if(command == TOSERVER_SIGNNODETEXT)
3056 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
3064 std::string datastring((char*)&data[2], datasize-2);
3065 std::istringstream is(datastring, std::ios_base::binary);
3068 is.read((char*)buf, 6);
3069 v3s16 p = readV3S16(buf);
3070 is.read((char*)buf, 2);
3071 u16 textlen = readU16(buf);
3073 for(u16 i=0; i<textlen; i++)
3075 is.read((char*)buf, 1);
3076 text += (char)buf[0];
3079 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3082 if(meta->typeId() != LEGN(m_nodedef, "CONTENT_SIGN_WALL"))
3084 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
3085 signmeta->setText(text);
3087 actionstream<<player->getName()<<" writes \""<<text<<"\" to sign "
3088 <<" at "<<PP(p)<<std::endl;
3090 v3s16 blockpos = getNodeBlockPos(p);
3091 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3094 block->raiseModified(MOD_STATE_WRITE_NEEDED,
3098 setBlockNotSent(blockpos);
3100 else if(command == TOSERVER_INVENTORY_ACTION)
3102 /*// Ignore inventory changes if in creative mode
3103 if(g_settings->getBool("creative_mode") == true)
3105 infostream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
3109 // Strip command and create a stream
3110 std::string datastring((char*)&data[2], datasize-2);
3111 infostream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
3112 std::istringstream is(datastring, std::ios_base::binary);
3114 InventoryAction *a = InventoryAction::deSerialize(is);
3119 c.current_player = player;
3122 Handle craftresult specially if not in creative mode
3124 bool disable_action = false;
3125 if(a->getType() == IACTION_MOVE
3126 && g_settings->getBool("creative_mode") == false)
3128 IMoveAction *ma = (IMoveAction*)a;
3129 if(ma->to_inv == "current_player" &&
3130 ma->from_inv == "current_player")
3132 InventoryList *rlist = player->inventory.getList("craftresult");
3134 InventoryList *clist = player->inventory.getList("craft");
3136 InventoryList *mlist = player->inventory.getList("main");
3139 Craftresult is no longer preview if something
3142 if(ma->to_list == "craftresult"
3143 && ma->from_list != "craftresult")
3145 // If it currently is a preview, remove
3147 if(player->craftresult_is_preview)
3149 rlist->deleteItem(0);
3151 player->craftresult_is_preview = false;
3154 Crafting takes place if this condition is true.
3156 if(player->craftresult_is_preview &&
3157 ma->from_list == "craftresult")
3159 player->craftresult_is_preview = false;
3160 clist->decrementMaterials(1);
3162 /* Print out action */
3163 InventoryList *list =
3164 player->inventory.getList("craftresult");
3166 InventoryItem *item = list->getItem(0);
3167 std::string itemname = "NULL";
3169 itemname = item->getName();
3170 actionstream<<player->getName()<<" crafts "
3171 <<itemname<<std::endl;
3174 If the craftresult is placed on itself, move it to
3175 main inventory instead of doing the action
3177 if(ma->to_list == "craftresult"
3178 && ma->from_list == "craftresult")
3180 disable_action = true;
3182 InventoryItem *item1 = rlist->changeItem(0, NULL);
3183 mlist->addItem(item1);
3186 // Disallow moving items if not allowed to build
3187 else if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
3191 // if it's a locking chest, only allow the owner or server admins to move items
3192 else if (ma->from_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
3194 Strfnd fn(ma->from_inv);
3195 std::string id0 = fn.next(":");
3196 if(id0 == "nodemeta")
3199 p.X = stoi(fn.next(","));
3200 p.Y = stoi(fn.next(","));
3201 p.Z = stoi(fn.next(","));
3202 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3203 if(meta && meta->typeId() == LEGN(m_nodedef, "CONTENT_LOCKABLE_CHEST")) {
3204 LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
3205 if (lcm->getOwner() != player->getName())
3210 else if (ma->to_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
3212 Strfnd fn(ma->to_inv);
3213 std::string id0 = fn.next(":");
3214 if(id0 == "nodemeta")
3217 p.X = stoi(fn.next(","));
3218 p.Y = stoi(fn.next(","));
3219 p.Z = stoi(fn.next(","));
3220 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3221 if(meta && meta->typeId() == LEGN(m_nodedef, "CONTENT_LOCKABLE_CHEST")) {
3222 LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
3223 if (lcm->getOwner() != player->getName())
3230 if(disable_action == false)
3232 // Feed action to player inventory
3240 UpdateCrafting(player->peer_id);
3241 SendInventory(player->peer_id);
3246 infostream<<"TOSERVER_INVENTORY_ACTION: "
3247 <<"InventoryAction::deSerialize() returned NULL"
3251 else if(command == TOSERVER_CHAT_MESSAGE)
3259 std::string datastring((char*)&data[2], datasize-2);
3260 std::istringstream is(datastring, std::ios_base::binary);
3263 is.read((char*)buf, 2);
3264 u16 len = readU16(buf);
3266 std::wstring message;
3267 for(u16 i=0; i<len; i++)
3269 is.read((char*)buf, 2);
3270 message += (wchar_t)readU16(buf);
3273 // Get player name of this client
3274 std::wstring name = narrow_to_wide(player->getName());
3276 // Line to send to players
3278 // Whether to send to the player that sent the line
3279 bool send_to_sender = false;
3280 // Whether to send to other players
3281 bool send_to_others = false;
3283 // Local player gets all privileges regardless of
3284 // what's set on their account.
3285 u64 privs = getPlayerPrivs(player);
3288 if(message[0] == L'/')
3290 size_t strip_size = 1;
3291 if (message[1] == L'#') // support old-style commans
3293 message = message.substr(strip_size);
3295 WStrfnd f1(message);
3296 f1.next(L" "); // Skip over /#whatever
3297 std::wstring paramstring = f1.next(L"");
3299 ServerCommandContext *ctx = new ServerCommandContext(
3300 str_split(message, L' '),
3307 std::wstring reply(processServerCommand(ctx));
3308 send_to_sender = ctx->flags & SEND_TO_SENDER;
3309 send_to_others = ctx->flags & SEND_TO_OTHERS;
3311 if (ctx->flags & SEND_NO_PREFIX)
3314 line += L"Server: " + reply;
3321 if(privs & PRIV_SHOUT)
3327 send_to_others = true;
3331 line += L"Server: You are not allowed to shout";
3332 send_to_sender = true;
3339 actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
3342 Send the message to clients
3344 for(core::map<u16, RemoteClient*>::Iterator
3345 i = m_clients.getIterator();
3346 i.atEnd() == false; i++)
3348 // Get client and check that it is valid
3349 RemoteClient *client = i.getNode()->getValue();
3350 assert(client->peer_id == i.getNode()->getKey());
3351 if(client->serialization_version == SER_FMT_VER_INVALID)
3355 bool sender_selected = (peer_id == client->peer_id);
3356 if(sender_selected == true && send_to_sender == false)
3358 if(sender_selected == false && send_to_others == false)
3361 SendChatMessage(client->peer_id, line);
3365 else if(command == TOSERVER_DAMAGE)
3367 std::string datastring((char*)&data[2], datasize-2);
3368 std::istringstream is(datastring, std::ios_base::binary);
3369 u8 damage = readU8(is);
3371 if(g_settings->getBool("enable_damage"))
3373 actionstream<<player->getName()<<" damaged by "
3374 <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
3377 HandlePlayerHP(player, damage);
3381 SendPlayerHP(player);
3384 else if(command == TOSERVER_PASSWORD)
3387 [0] u16 TOSERVER_PASSWORD
3388 [2] u8[28] old password
3389 [30] u8[28] new password
3392 if(datasize != 2+PASSWORD_SIZE*2)
3394 /*char password[PASSWORD_SIZE];
3395 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3396 password[i] = data[2+i];
3397 password[PASSWORD_SIZE-1] = 0;*/
3399 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3407 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3409 char c = data[2+PASSWORD_SIZE+i];
3415 infostream<<"Server: Client requests a password change from "
3416 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
3418 std::string playername = player->getName();
3420 if(m_authmanager.exists(playername) == false)
3422 infostream<<"Server: playername not found in authmanager"<<std::endl;
3423 // Wrong old password supplied!!
3424 SendChatMessage(peer_id, L"playername not found in authmanager");
3428 std::string checkpwd = m_authmanager.getPassword(playername);
3430 if(oldpwd != checkpwd)
3432 infostream<<"Server: invalid old password"<<std::endl;
3433 // Wrong old password supplied!!
3434 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
3438 actionstream<<player->getName()<<" changes password"<<std::endl;
3440 m_authmanager.setPassword(playername, newpwd);
3442 infostream<<"Server: password change successful for "<<playername
3444 SendChatMessage(peer_id, L"Password change successful");
3446 else if(command == TOSERVER_PLAYERITEM)
3451 u16 item = readU16(&data[2]);
3452 player->wieldItem(item);
3453 SendWieldedItem(player);
3455 else if(command == TOSERVER_RESPAWN)
3460 RespawnPlayer(player);
3462 actionstream<<player->getName()<<" respawns at "
3463 <<PP(player->getPosition()/BS)<<std::endl;
3467 infostream<<"Server::ProcessData(): Ignoring "
3468 "unknown command "<<command<<std::endl;
3472 catch(SendFailedException &e)
3474 errorstream<<"Server::ProcessData(): SendFailedException: "
3480 void Server::onMapEditEvent(MapEditEvent *event)
3482 //infostream<<"Server::onMapEditEvent()"<<std::endl;
3483 if(m_ignore_map_edit_events)
3485 MapEditEvent *e = event->clone();
3486 m_unsent_map_edit_queue.push_back(e);
3489 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3491 if(id == "current_player")
3493 assert(c->current_player);
3494 return &(c->current_player->inventory);
3498 std::string id0 = fn.next(":");
3500 if(id0 == "nodemeta")
3503 p.X = stoi(fn.next(","));
3504 p.Y = stoi(fn.next(","));
3505 p.Z = stoi(fn.next(","));
3506 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3508 return meta->getInventory();
3509 infostream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3510 <<"no metadata found"<<std::endl;
3514 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3517 void Server::inventoryModified(InventoryContext *c, std::string id)
3519 if(id == "current_player")
3521 assert(c->current_player);
3523 UpdateCrafting(c->current_player->peer_id);
3524 SendInventory(c->current_player->peer_id);
3529 std::string id0 = fn.next(":");
3531 if(id0 == "nodemeta")
3534 p.X = stoi(fn.next(","));
3535 p.Y = stoi(fn.next(","));
3536 p.Z = stoi(fn.next(","));
3537 v3s16 blockpos = getNodeBlockPos(p);
3539 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3541 meta->inventoryModified();
3543 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3545 block->raiseModified(MOD_STATE_WRITE_NEEDED);
3547 setBlockNotSent(blockpos);
3552 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3555 core::list<PlayerInfo> Server::getPlayerInfo()
3557 DSTACK(__FUNCTION_NAME);
3558 JMutexAutoLock envlock(m_env_mutex);
3559 JMutexAutoLock conlock(m_con_mutex);
3561 core::list<PlayerInfo> list;
3563 core::list<Player*> players = m_env->getPlayers();
3565 core::list<Player*>::Iterator i;
3566 for(i = players.begin();
3567 i != players.end(); i++)
3571 Player *player = *i;
3574 // Copy info from connection to info struct
3575 info.id = player->peer_id;
3576 info.address = m_con.GetPeerAddress(player->peer_id);
3577 info.avg_rtt = m_con.GetPeerAvgRTT(player->peer_id);
3579 catch(con::PeerNotFoundException &e)
3581 // Set dummy peer info
3583 info.address = Address(0,0,0,0,0);
3587 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3588 info.position = player->getPosition();
3590 list.push_back(info);
3597 void Server::peerAdded(con::Peer *peer)
3599 DSTACK(__FUNCTION_NAME);
3600 infostream<<"Server::peerAdded(): peer->id="
3601 <<peer->id<<std::endl;
3604 c.type = PEER_ADDED;
3605 c.peer_id = peer->id;
3607 m_peer_change_queue.push_back(c);
3610 void Server::deletingPeer(con::Peer *peer, bool timeout)
3612 DSTACK(__FUNCTION_NAME);
3613 infostream<<"Server::deletingPeer(): peer->id="
3614 <<peer->id<<", timeout="<<timeout<<std::endl;
3617 c.type = PEER_REMOVED;
3618 c.peer_id = peer->id;
3619 c.timeout = timeout;
3620 m_peer_change_queue.push_back(c);
3627 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3629 DSTACK(__FUNCTION_NAME);
3630 std::ostringstream os(std::ios_base::binary);
3632 writeU16(os, TOCLIENT_HP);
3636 std::string s = os.str();
3637 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3639 con.Send(peer_id, 0, data, true);
3642 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3643 const std::wstring &reason)
3645 DSTACK(__FUNCTION_NAME);
3646 std::ostringstream os(std::ios_base::binary);
3648 writeU16(os, TOCLIENT_ACCESS_DENIED);
3649 os<<serializeWideString(reason);
3652 std::string s = os.str();
3653 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3655 con.Send(peer_id, 0, data, true);
3658 void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
3659 bool set_camera_point_target, v3f camera_point_target)
3661 DSTACK(__FUNCTION_NAME);
3662 std::ostringstream os(std::ios_base::binary);
3664 writeU16(os, TOCLIENT_DEATHSCREEN);
3665 writeU8(os, set_camera_point_target);
3666 writeV3F1000(os, camera_point_target);
3669 std::string s = os.str();
3670 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3672 con.Send(peer_id, 0, data, true);
3675 void Server::SendToolDef(con::Connection &con, u16 peer_id,
3676 IToolDefManager *tooldef)
3678 DSTACK(__FUNCTION_NAME);
3679 std::ostringstream os(std::ios_base::binary);
3683 u32 length of the next item
3684 serialized ToolDefManager
3686 writeU16(os, TOCLIENT_TOOLDEF);
3687 std::ostringstream tmp_os(std::ios::binary);
3688 tooldef->serialize(tmp_os);
3689 os<<serializeLongString(tmp_os.str());
3692 std::string s = os.str();
3693 infostream<<"Server::SendToolDef(): Sending tool definitions: size="
3694 <<s.size()<<std::endl;
3695 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3697 con.Send(peer_id, 0, data, true);
3700 void Server::SendNodeDef(con::Connection &con, u16 peer_id,
3701 INodeDefManager *nodedef)
3703 DSTACK(__FUNCTION_NAME);
3704 std::ostringstream os(std::ios_base::binary);
3708 u32 length of the next item
3709 serialized NodeDefManager
3711 writeU16(os, TOCLIENT_NODEDEF);
3712 std::ostringstream tmp_os(std::ios::binary);
3713 nodedef->serialize(tmp_os);
3714 os<<serializeLongString(tmp_os.str());
3717 std::string s = os.str();
3718 infostream<<"Server::SendNodeDef(): Sending node definitions: size="
3719 <<s.size()<<std::endl;
3720 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3722 con.Send(peer_id, 0, data, true);
3726 Non-static send methods
3729 void Server::SendObjectData(float dtime)
3731 DSTACK(__FUNCTION_NAME);
3733 core::map<v3s16, bool> stepped_blocks;
3735 for(core::map<u16, RemoteClient*>::Iterator
3736 i = m_clients.getIterator();
3737 i.atEnd() == false; i++)
3739 u16 peer_id = i.getNode()->getKey();
3740 RemoteClient *client = i.getNode()->getValue();
3741 assert(client->peer_id == peer_id);
3743 if(client->serialization_version == SER_FMT_VER_INVALID)
3746 client->SendObjectData(this, dtime, stepped_blocks);
3750 void Server::SendPlayerInfos()
3752 DSTACK(__FUNCTION_NAME);
3754 //JMutexAutoLock envlock(m_env_mutex);
3756 // Get connected players
3757 core::list<Player*> players = m_env->getPlayers(true);
3759 u32 player_count = players.getSize();
3760 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3762 SharedBuffer<u8> data(datasize);
3763 writeU16(&data[0], TOCLIENT_PLAYERINFO);
3766 core::list<Player*>::Iterator i;
3767 for(i = players.begin();
3768 i != players.end(); i++)
3770 Player *player = *i;
3772 /*infostream<<"Server sending player info for player with "
3773 "peer_id="<<player->peer_id<<std::endl;*/
3775 writeU16(&data[start], player->peer_id);
3776 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3777 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3778 start += 2+PLAYERNAME_SIZE;
3781 //JMutexAutoLock conlock(m_con_mutex);
3784 m_con.SendToAll(0, data, true);
3787 void Server::SendInventory(u16 peer_id)
3789 DSTACK(__FUNCTION_NAME);
3791 Player* player = m_env->getPlayer(peer_id);
3798 std::ostringstream os;
3799 //os.imbue(std::locale("C"));
3801 player->inventory.serialize(os);
3803 std::string s = os.str();
3805 SharedBuffer<u8> data(s.size()+2);
3806 writeU16(&data[0], TOCLIENT_INVENTORY);
3807 memcpy(&data[2], s.c_str(), s.size());
3810 m_con.Send(peer_id, 0, data, true);
3813 std::string getWieldedItemString(const Player *player)
3815 const InventoryItem *item = player->getWieldItem();
3817 return std::string("");
3818 std::ostringstream os(std::ios_base::binary);
3819 item->serialize(os);
3823 void Server::SendWieldedItem(const Player* player)
3825 DSTACK(__FUNCTION_NAME);
3829 std::ostringstream os(std::ios_base::binary);
3831 writeU16(os, TOCLIENT_PLAYERITEM);
3833 writeU16(os, player->peer_id);
3834 os<<serializeString(getWieldedItemString(player));
3837 std::string s = os.str();
3838 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3840 m_con.SendToAll(0, data, true);
3843 void Server::SendPlayerItems()
3845 DSTACK(__FUNCTION_NAME);
3847 std::ostringstream os(std::ios_base::binary);
3848 core::list<Player *> players = m_env->getPlayers(true);
3850 writeU16(os, TOCLIENT_PLAYERITEM);
3851 writeU16(os, players.size());
3852 core::list<Player *>::Iterator i;
3853 for(i = players.begin(); i != players.end(); ++i)
3856 writeU16(os, p->peer_id);
3857 os<<serializeString(getWieldedItemString(p));
3861 std::string s = os.str();
3862 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3864 m_con.SendToAll(0, data, true);
3867 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3869 DSTACK(__FUNCTION_NAME);
3871 std::ostringstream os(std::ios_base::binary);
3875 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3876 os.write((char*)buf, 2);
3879 writeU16(buf, message.size());
3880 os.write((char*)buf, 2);
3883 for(u32 i=0; i<message.size(); i++)
3887 os.write((char*)buf, 2);
3891 std::string s = os.str();
3892 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3894 m_con.Send(peer_id, 0, data, true);
3897 void Server::BroadcastChatMessage(const std::wstring &message)
3899 for(core::map<u16, RemoteClient*>::Iterator
3900 i = m_clients.getIterator();
3901 i.atEnd() == false; i++)
3903 // Get client and check that it is valid
3904 RemoteClient *client = i.getNode()->getValue();
3905 assert(client->peer_id == i.getNode()->getKey());
3906 if(client->serialization_version == SER_FMT_VER_INVALID)
3909 SendChatMessage(client->peer_id, message);
3913 void Server::SendPlayerHP(Player *player)
3915 SendHP(m_con, player->peer_id, player->hp);
3918 void Server::SendMovePlayer(Player *player)
3920 DSTACK(__FUNCTION_NAME);
3921 std::ostringstream os(std::ios_base::binary);
3923 writeU16(os, TOCLIENT_MOVE_PLAYER);
3924 writeV3F1000(os, player->getPosition());
3925 writeF1000(os, player->getPitch());
3926 writeF1000(os, player->getYaw());
3929 v3f pos = player->getPosition();
3930 f32 pitch = player->getPitch();
3931 f32 yaw = player->getYaw();
3932 infostream<<"Server sending TOCLIENT_MOVE_PLAYER"
3933 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3940 std::string s = os.str();
3941 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3943 m_con.Send(player->peer_id, 0, data, true);
3946 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3947 core::list<u16> *far_players, float far_d_nodes)
3949 float maxd = far_d_nodes*BS;
3950 v3f p_f = intToFloat(p, BS);
3954 SharedBuffer<u8> reply(replysize);
3955 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3956 writeS16(&reply[2], p.X);
3957 writeS16(&reply[4], p.Y);
3958 writeS16(&reply[6], p.Z);
3960 for(core::map<u16, RemoteClient*>::Iterator
3961 i = m_clients.getIterator();
3962 i.atEnd() == false; i++)
3964 // Get client and check that it is valid
3965 RemoteClient *client = i.getNode()->getValue();
3966 assert(client->peer_id == i.getNode()->getKey());
3967 if(client->serialization_version == SER_FMT_VER_INVALID)
3970 // Don't send if it's the same one
3971 if(client->peer_id == ignore_id)
3977 Player *player = m_env->getPlayer(client->peer_id);
3980 // If player is far away, only set modified blocks not sent
3981 v3f player_pos = player->getPosition();
3982 if(player_pos.getDistanceFrom(p_f) > maxd)
3984 far_players->push_back(client->peer_id);
3991 m_con.Send(client->peer_id, 0, reply, true);
3995 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3996 core::list<u16> *far_players, float far_d_nodes)
3998 float maxd = far_d_nodes*BS;
3999 v3f p_f = intToFloat(p, BS);
4001 for(core::map<u16, RemoteClient*>::Iterator
4002 i = m_clients.getIterator();
4003 i.atEnd() == false; i++)
4005 // Get client and check that it is valid
4006 RemoteClient *client = i.getNode()->getValue();
4007 assert(client->peer_id == i.getNode()->getKey());
4008 if(client->serialization_version == SER_FMT_VER_INVALID)
4011 // Don't send if it's the same one
4012 if(client->peer_id == ignore_id)
4018 Player *player = m_env->getPlayer(client->peer_id);
4021 // If player is far away, only set modified blocks not sent
4022 v3f player_pos = player->getPosition();
4023 if(player_pos.getDistanceFrom(p_f) > maxd)
4025 far_players->push_back(client->peer_id);
4032 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
4033 SharedBuffer<u8> reply(replysize);
4034 writeU16(&reply[0], TOCLIENT_ADDNODE);
4035 writeS16(&reply[2], p.X);
4036 writeS16(&reply[4], p.Y);
4037 writeS16(&reply[6], p.Z);
4038 n.serialize(&reply[8], client->serialization_version);
4041 m_con.Send(client->peer_id, 0, reply, true);
4045 void Server::setBlockNotSent(v3s16 p)
4047 for(core::map<u16, RemoteClient*>::Iterator
4048 i = m_clients.getIterator();
4049 i.atEnd()==false; i++)
4051 RemoteClient *client = i.getNode()->getValue();
4052 client->SetBlockNotSent(p);
4056 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
4058 DSTACK(__FUNCTION_NAME);
4060 v3s16 p = block->getPos();
4064 bool completely_air = true;
4065 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
4066 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
4067 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
4069 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
4071 completely_air = false;
4072 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
4077 infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
4079 infostream<<"[completely air] ";
4080 infostream<<std::endl;
4084 Create a packet with the block in the right format
4087 std::ostringstream os(std::ios_base::binary);
4088 block->serialize(os, ver);
4089 std::string s = os.str();
4090 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
4092 u32 replysize = 8 + blockdata.getSize();
4093 SharedBuffer<u8> reply(replysize);
4094 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
4095 writeS16(&reply[2], p.X);
4096 writeS16(&reply[4], p.Y);
4097 writeS16(&reply[6], p.Z);
4098 memcpy(&reply[8], *blockdata, blockdata.getSize());
4100 /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4101 <<": \tpacket size: "<<replysize<<std::endl;*/
4106 m_con.Send(peer_id, 1, reply, true);
4109 void Server::SendBlocks(float dtime)
4111 DSTACK(__FUNCTION_NAME);
4113 JMutexAutoLock envlock(m_env_mutex);
4114 JMutexAutoLock conlock(m_con_mutex);
4116 //TimeTaker timer("Server::SendBlocks");
4118 core::array<PrioritySortedBlockTransfer> queue;
4120 s32 total_sending = 0;
4123 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
4125 for(core::map<u16, RemoteClient*>::Iterator
4126 i = m_clients.getIterator();
4127 i.atEnd() == false; i++)
4129 RemoteClient *client = i.getNode()->getValue();
4130 assert(client->peer_id == i.getNode()->getKey());
4132 total_sending += client->SendingCount();
4134 if(client->serialization_version == SER_FMT_VER_INVALID)
4137 client->GetNextBlocks(this, dtime, queue);
4142 // Lowest priority number comes first.
4143 // Lowest is most important.
4146 for(u32 i=0; i<queue.size(); i++)
4148 //TODO: Calculate limit dynamically
4149 if(total_sending >= g_settings->getS32
4150 ("max_simultaneous_block_sends_server_total"))
4153 PrioritySortedBlockTransfer q = queue[i];
4155 MapBlock *block = NULL;
4158 block = m_env->getMap().getBlockNoCreate(q.pos);
4160 catch(InvalidPositionException &e)
4165 RemoteClient *client = getClient(q.peer_id);
4167 SendBlockNoLock(q.peer_id, block, client->serialization_version);
4169 client->SentBlock(q.pos);
4175 struct SendableTexture
4181 SendableTexture(const std::string &name_="", const std::string path_="",
4182 const std::string &data_=""):
4189 void Server::SendTextures(u16 peer_id)
4191 DSTACK(__FUNCTION_NAME);
4193 infostream<<"Server::SendTextures(): Sending textures to client"<<std::endl;
4197 // Put 5kB in one bunch (this is not accurate)
4198 u32 bytes_per_bunch = 5000;
4200 core::array< core::list<SendableTexture> > texture_bunches;
4201 texture_bunches.push_back(core::list<SendableTexture>());
4203 u32 texture_size_bunch_total = 0;
4204 core::list<ModSpec> mods = getMods(m_modspaths);
4205 for(core::list<ModSpec>::Iterator i = mods.begin();
4206 i != mods.end(); i++){
4208 std::string texturepath = mod.path + DIR_DELIM + "textures";
4209 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(texturepath);
4210 for(u32 j=0; j<dirlist.size(); j++){
4211 if(dirlist[j].dir) // Ignode dirs
4213 std::string tname = dirlist[j].name;
4214 std::string tpath = texturepath + DIR_DELIM + tname;
4216 std::ifstream fis(tpath.c_str(), std::ios_base::binary);
4217 if(fis.good() == false){
4218 errorstream<<"Server::SendTextures(): Could not open \""
4219 <<tname<<"\" for reading"<<std::endl;
4222 std::ostringstream tmp_os(std::ios_base::binary);
4226 fis.read(buf, 1024);
4227 std::streamsize len = fis.gcount();
4228 tmp_os.write(buf, len);
4229 texture_size_bunch_total += len;
4238 errorstream<<"Server::SendTextures(): Failed to read \""
4239 <<tname<<"\""<<std::endl;
4242 /*infostream<<"Server::SendTextures(): Loaded \""
4243 <<tname<<"\""<<std::endl;*/
4245 texture_bunches[texture_bunches.size()-1].push_back(
4246 SendableTexture(tname, tpath, tmp_os.str()));
4248 // Start next bunch if got enough data
4249 if(texture_size_bunch_total >= bytes_per_bunch){
4250 texture_bunches.push_back(core::list<SendableTexture>());
4251 texture_size_bunch_total = 0;
4256 /* Create and send packets */
4258 u32 num_bunches = texture_bunches.size();
4259 for(u32 i=0; i<num_bunches; i++)
4263 u16 total number of texture bunches
4264 u16 index of this bunch
4265 u32 number of textures in this bunch
4273 std::ostringstream os(std::ios_base::binary);
4275 writeU16(os, TOCLIENT_TEXTURES);
4276 writeU16(os, num_bunches);
4278 writeU32(os, texture_bunches[i].size());
4280 for(core::list<SendableTexture>::Iterator
4281 j = texture_bunches[i].begin();
4282 j != texture_bunches[i].end(); j++){
4283 os<<serializeString(j->name);
4284 os<<serializeLongString(j->data);
4288 std::string s = os.str();
4289 infostream<<"Server::SendTextures(): bunch "<<i<<"/"<<num_bunches
4290 <<" textures="<<texture_bunches[i].size()
4291 <<" size=" <<s.size()<<std::endl;
4292 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
4294 m_con.Send(peer_id, 0, data, true);
4302 void Server::HandlePlayerHP(Player *player, s16 damage)
4304 if(player->hp > damage)
4306 player->hp -= damage;
4307 SendPlayerHP(player);
4311 infostream<<"Server::HandlePlayerHP(): Player "
4312 <<player->getName()<<" dies"<<std::endl;
4316 //TODO: Throw items around
4318 // Handle players that are not connected
4319 if(player->peer_id == PEER_ID_INEXISTENT){
4320 RespawnPlayer(player);
4324 SendPlayerHP(player);
4326 RemoteClient *client = getClient(player->peer_id);
4327 if(client->net_proto_version >= 3)
4329 SendDeathscreen(m_con, player->peer_id, false, v3f(0,0,0));
4333 RespawnPlayer(player);
4338 void Server::RespawnPlayer(Player *player)
4340 v3f pos = findSpawnPos(m_env->getServerMap());
4341 player->setPosition(pos);
4343 SendMovePlayer(player);
4344 SendPlayerHP(player);
4347 void Server::UpdateCrafting(u16 peer_id)
4349 DSTACK(__FUNCTION_NAME);
4351 Player* player = m_env->getPlayer(peer_id);
4355 Calculate crafting stuff
4357 if(g_settings->getBool("creative_mode") == false)
4359 InventoryList *clist = player->inventory.getList("craft");
4360 InventoryList *rlist = player->inventory.getList("craftresult");
4362 if(rlist && rlist->getUsedSlots() == 0)
4363 player->craftresult_is_preview = true;
4365 if(rlist && player->craftresult_is_preview)
4367 rlist->clearItems();
4369 if(clist && rlist && player->craftresult_is_preview)
4371 // Get result of crafting grid
4373 std::vector<InventoryItem*> items;
4374 for(u16 i=0; i<9; i++){
4375 if(clist->getItem(i) == NULL)
4376 items.push_back(NULL);
4378 items.push_back(clist->getItem(i)->clone());
4380 CraftPointerInput cpi(3, items);
4382 InventoryItem *result = m_craftdef->getCraftResult(cpi, this);
4383 //InventoryItem *result = craft_get_result(items, this);
4385 rlist->addItem(result);
4388 } // if creative_mode == false
4391 RemoteClient* Server::getClient(u16 peer_id)
4393 DSTACK(__FUNCTION_NAME);
4394 //JMutexAutoLock lock(m_con_mutex);
4395 core::map<u16, RemoteClient*>::Node *n;
4396 n = m_clients.find(peer_id);
4397 // A client should exist for all peers
4399 return n->getValue();
4402 std::wstring Server::getStatusString()
4404 std::wostringstream os(std::ios_base::binary);
4407 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4409 os<<L", uptime="<<m_uptime.get();
4410 // Information about clients
4412 for(core::map<u16, RemoteClient*>::Iterator
4413 i = m_clients.getIterator();
4414 i.atEnd() == false; i++)
4416 // Get client and check that it is valid
4417 RemoteClient *client = i.getNode()->getValue();
4418 assert(client->peer_id == i.getNode()->getKey());
4419 if(client->serialization_version == SER_FMT_VER_INVALID)
4422 Player *player = m_env->getPlayer(client->peer_id);
4423 // Get name of player
4424 std::wstring name = L"unknown";
4426 name = narrow_to_wide(player->getName());
4427 // Add name to information string
4431 if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
4432 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4433 if(g_settings->get("motd") != "")
4434 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4438 // Saves g_settings to configpath given at initialization
4439 void Server::saveConfig()
4441 if(m_configpath != "")
4442 g_settings->updateConfigFile(m_configpath.c_str());
4445 void Server::notifyPlayer(const char *name, const std::wstring msg)
4447 Player *player = m_env->getPlayer(name);
4450 SendChatMessage(player->peer_id, std::wstring(L"Server: -!- ")+msg);
4453 void Server::notifyPlayers(const std::wstring msg)
4455 BroadcastChatMessage(msg);
4458 // IGameDef interface
4460 IToolDefManager* Server::getToolDefManager()
4464 INodeDefManager* Server::getNodeDefManager()
4468 ICraftDefManager* Server::getCraftDefManager()
4472 ITextureSource* Server::getTextureSource()
4476 u16 Server::allocateUnknownNodeId(const std::string &name)
4478 return m_nodedef->allocateDummy(name);
4481 IWritableToolDefManager* Server::getWritableToolDefManager()
4485 IWritableNodeDefManager* Server::getWritableNodeDefManager()
4489 IWritableCraftDefManager* Server::getWritableCraftDefManager()
4494 v3f findSpawnPos(ServerMap &map)
4496 //return v3f(50,50,50)*BS;
4501 nodepos = v2s16(0,0);
4506 // Try to find a good place a few times
4507 for(s32 i=0; i<1000; i++)
4510 // We're going to try to throw the player to this position
4511 v2s16 nodepos2d = v2s16(-range + (myrand()%(range*2)),
4512 -range + (myrand()%(range*2)));
4513 //v2s16 sectorpos = getNodeSectorPos(nodepos2d);
4514 // Get ground height at point (fallbacks to heightmap function)
4515 s16 groundheight = map.findGroundLevel(nodepos2d);
4516 // Don't go underwater
4517 if(groundheight < WATER_LEVEL)
4519 //infostream<<"-> Underwater"<<std::endl;
4522 // Don't go to high places
4523 if(groundheight > WATER_LEVEL + 4)
4525 //infostream<<"-> Underwater"<<std::endl;
4529 nodepos = v3s16(nodepos2d.X, groundheight-2, nodepos2d.Y);
4530 bool is_good = false;
4532 for(s32 i=0; i<10; i++){
4533 v3s16 blockpos = getNodeBlockPos(nodepos);
4534 map.emergeBlock(blockpos, true);
4535 MapNode n = map.getNodeNoEx(nodepos);
4536 if(n.getContent() == CONTENT_AIR){
4547 // Found a good place
4548 //infostream<<"Searched through "<<i<<" places."<<std::endl;
4554 return intToFloat(nodepos, BS);
4557 Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id)
4560 Try to get an existing player
4562 Player *player = m_env->getPlayer(name);
4565 // If player is already connected, cancel
4566 if(player->peer_id != 0)
4568 infostream<<"emergePlayer(): Player already connected"<<std::endl;
4573 player->peer_id = peer_id;
4575 // Reset inventory to creative if in creative mode
4576 if(g_settings->getBool("creative_mode"))
4578 // Warning: double code below
4579 // Backup actual inventory
4580 player->inventory_backup = new Inventory();
4581 *(player->inventory_backup) = player->inventory;
4582 // Set creative inventory
4583 craft_set_creative_inventory(player, this);
4590 If player with the wanted peer_id already exists, cancel.
4592 if(m_env->getPlayer(peer_id) != NULL)
4594 infostream<<"emergePlayer(): Player with wrong name but same"
4595 " peer_id already exists"<<std::endl;
4603 // Add authentication stuff
4604 m_authmanager.add(name);
4605 m_authmanager.setPassword(name, password);
4606 m_authmanager.setPrivs(name,
4607 stringToPrivs(g_settings->get("default_privs")));
4613 infostream<<"Server: Finding spawn place for player \""
4614 <<name<<"\""<<std::endl;
4616 v3f pos = findSpawnPos(m_env->getServerMap());
4618 player = new ServerRemotePlayer(m_env, pos, peer_id, name);
4621 Add player to environment
4624 m_env->addPlayer(player);
4627 Add stuff to inventory
4630 if(g_settings->getBool("creative_mode"))
4632 // Warning: double code above
4633 // Backup actual inventory
4634 player->inventory_backup = new Inventory();
4635 *(player->inventory_backup) = player->inventory;
4636 // Set creative inventory
4637 craft_set_creative_inventory(player, this);
4639 else if(g_settings->getBool("give_initial_stuff"))
4641 craft_give_initial_stuff(player, this);
4646 } // create new player
4649 void Server::handlePeerChange(PeerChange &c)
4651 JMutexAutoLock envlock(m_env_mutex);
4652 JMutexAutoLock conlock(m_con_mutex);
4654 if(c.type == PEER_ADDED)
4661 core::map<u16, RemoteClient*>::Node *n;
4662 n = m_clients.find(c.peer_id);
4663 // The client shouldn't already exist
4667 RemoteClient *client = new RemoteClient();
4668 client->peer_id = c.peer_id;
4669 m_clients.insert(client->peer_id, client);
4672 else if(c.type == PEER_REMOVED)
4679 core::map<u16, RemoteClient*>::Node *n;
4680 n = m_clients.find(c.peer_id);
4681 // The client should exist
4685 Mark objects to be not known by the client
4687 RemoteClient *client = n->getValue();
4689 for(core::map<u16, bool>::Iterator
4690 i = client->m_known_objects.getIterator();
4691 i.atEnd()==false; i++)
4694 u16 id = i.getNode()->getKey();
4695 ServerActiveObject* obj = m_env->getActiveObject(id);
4697 if(obj && obj->m_known_by_count > 0)
4698 obj->m_known_by_count--;
4701 // Collect information about leaving in chat
4702 std::wstring message;
4704 Player *player = m_env->getPlayer(c.peer_id);
4707 std::wstring name = narrow_to_wide(player->getName());
4710 message += L" left game";
4712 message += L" (timed out)";
4718 m_env->removePlayer(c.peer_id);
4721 // Set player client disconnected
4723 Player *player = m_env->getPlayer(c.peer_id);
4725 player->peer_id = 0;
4732 std::ostringstream os(std::ios_base::binary);
4733 for(core::map<u16, RemoteClient*>::Iterator
4734 i = m_clients.getIterator();
4735 i.atEnd() == false; i++)
4737 RemoteClient *client = i.getNode()->getValue();
4738 assert(client->peer_id == i.getNode()->getKey());
4739 if(client->serialization_version == SER_FMT_VER_INVALID)
4742 Player *player = m_env->getPlayer(client->peer_id);
4745 // Get name of player
4746 os<<player->getName()<<" ";
4749 actionstream<<player->getName()<<" "
4750 <<(c.timeout?"times out.":"leaves game.")
4751 <<" List of players: "
4752 <<os.str()<<std::endl;
4757 delete m_clients[c.peer_id];
4758 m_clients.remove(c.peer_id);
4760 // Send player info to all remaining clients
4763 // Send leave chat message to all remaining clients
4764 BroadcastChatMessage(message);
4773 void Server::handlePeerChanges()
4775 while(m_peer_change_queue.size() > 0)
4777 PeerChange c = m_peer_change_queue.pop_front();
4779 infostream<<"Server: Handling peer change: "
4780 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4783 handlePeerChange(c);
4787 u64 Server::getPlayerPrivs(Player *player)
4791 std::string playername = player->getName();
4792 // Local player gets all privileges regardless of
4793 // what's set on their account.
4794 if(g_settings->get("name") == playername)
4800 return getPlayerAuthPrivs(playername);
4804 void dedicated_server_loop(Server &server, bool &kill)
4806 DSTACK(__FUNCTION_NAME);
4808 infostream<<DTIME<<std::endl;
4809 infostream<<"========================"<<std::endl;
4810 infostream<<"Running dedicated server"<<std::endl;
4811 infostream<<"========================"<<std::endl;
4812 infostream<<std::endl;
4814 IntervalLimiter m_profiler_interval;
4818 // This is kind of a hack but can be done like this
4819 // because server.step() is very light
4821 ScopeProfiler sp(g_profiler, "dedicated server sleep");
4826 if(server.getShutdownRequested() || kill)
4828 infostream<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4835 float profiler_print_interval =
4836 g_settings->getFloat("profiler_print_interval");
4837 if(profiler_print_interval != 0)
4839 if(m_profiler_interval.step(0.030, profiler_print_interval))
4841 infostream<<"Profiler:"<<std::endl;
4842 g_profiler->print(infostream);
4843 g_profiler->clear();
4850 static int counter = 0;
4856 core::list<PlayerInfo> list = server.getPlayerInfo();
4857 core::list<PlayerInfo>::Iterator i;
4858 static u32 sum_old = 0;
4859 u32 sum = PIChecksum(list);
4862 infostream<<DTIME<<"Player info:"<<std::endl;
4863 for(i=list.begin(); i!=list.end(); i++)
4865 i->PrintLine(&infostream);