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"
47 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
49 #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
51 class MapEditEventIgnorer
54 MapEditEventIgnorer(bool *flag):
63 ~MapEditEventIgnorer()
76 void * ServerThread::Thread()
80 log_register_thread("ServerThread");
82 DSTACK(__FUNCTION_NAME);
84 BEGIN_DEBUG_EXCEPTION_HANDLER
89 //TimeTaker timer("AsyncRunStep() + Receive()");
92 //TimeTaker timer("AsyncRunStep()");
93 m_server->AsyncRunStep();
96 //infostream<<"Running m_server->Receive()"<<std::endl;
99 catch(con::NoIncomingDataException &e)
102 catch(con::PeerNotFoundException &e)
104 infostream<<"Server: PeerNotFoundException"<<std::endl;
108 END_DEBUG_EXCEPTION_HANDLER(errorstream)
113 void * EmergeThread::Thread()
117 log_register_thread("EmergeThread");
119 DSTACK(__FUNCTION_NAME);
121 BEGIN_DEBUG_EXCEPTION_HANDLER
123 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
126 Get block info from queue, emerge them and send them
129 After queue is empty, exit.
133 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
137 SharedPtr<QueuedBlockEmerge> q(qptr);
143 Do not generate over-limit
145 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
146 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
147 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
148 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
149 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
150 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
153 //infostream<<"EmergeThread::Thread(): running"<<std::endl;
155 //TimeTaker timer("block emerge");
158 Try to emerge it from somewhere.
160 If it is only wanted as optional, only loading from disk
165 Check if any peer wants it as non-optional. In that case it
168 Also decrement the emerge queue count in clients.
171 bool only_from_disk = true;
174 core::map<u16, u8>::Iterator i;
175 for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
177 //u16 peer_id = i.getNode()->getKey();
180 u8 flags = i.getNode()->getValue();
181 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
182 only_from_disk = false;
187 if(enable_mapgen_debug_info)
188 infostream<<"EmergeThread: p="
189 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
190 <<"only_from_disk="<<only_from_disk<<std::endl;
192 ServerMap &map = ((ServerMap&)m_server->m_env->getMap());
194 //core::map<v3s16, MapBlock*> changed_blocks;
195 //core::map<v3s16, MapBlock*> lighting_invalidated_blocks;
197 MapBlock *block = NULL;
198 bool got_block = true;
199 core::map<v3s16, MapBlock*> modified_blocks;
202 Fetch block from map or generate a single block
205 JMutexAutoLock envlock(m_server->m_env_mutex);
207 // Load sector if it isn't loaded
208 if(map.getSectorNoGenerateNoEx(p2d) == NULL)
209 //map.loadSectorFull(p2d);
210 map.loadSectorMeta(p2d);
212 block = map.getBlockNoCreateNoEx(p);
213 if(!block || block->isDummy() || !block->isGenerated())
215 if(enable_mapgen_debug_info)
216 infostream<<"EmergeThread: not in memory, loading"<<std::endl;
218 // Get, load or create sector
219 /*ServerMapSector *sector =
220 (ServerMapSector*)map.createSector(p2d);*/
222 // Load/generate block
224 /*block = map.emergeBlock(p, sector, changed_blocks,
225 lighting_invalidated_blocks);*/
227 block = map.loadBlock(p);
229 if(only_from_disk == false)
231 if(block == NULL || block->isGenerated() == false)
233 if(enable_mapgen_debug_info)
234 infostream<<"EmergeThread: generating"<<std::endl;
235 block = map.generateBlock(p, modified_blocks);
239 if(enable_mapgen_debug_info)
240 infostream<<"EmergeThread: ended up with: "
241 <<analyze_block(block)<<std::endl;
250 Ignore map edit events, they will not need to be
251 sent to anybody because the block hasn't been sent
254 MapEditEventIgnorer ign(&m_server->m_ignore_map_edit_events);
256 // Activate objects and stuff
257 m_server->m_env->activateBlock(block, 3600);
262 /*if(block->getLightingExpired()){
263 lighting_invalidated_blocks[block->getPos()] = block;
267 // TODO: Some additional checking and lighting updating,
272 JMutexAutoLock envlock(m_server->m_env_mutex);
277 Collect a list of blocks that have been modified in
278 addition to the fetched one.
282 if(lighting_invalidated_blocks.size() > 0)
284 /*infostream<<"lighting "<<lighting_invalidated_blocks.size()
285 <<" blocks"<<std::endl;*/
287 // 50-100ms for single block generation
288 //TimeTaker timer("** EmergeThread updateLighting");
290 // Update lighting without locking the environment mutex,
291 // add modified blocks to changed blocks
292 map.updateLighting(lighting_invalidated_blocks, modified_blocks);
295 // Add all from changed_blocks to modified_blocks
296 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
297 i.atEnd() == false; i++)
299 MapBlock *block = i.getNode()->getValue();
300 modified_blocks.insert(block->getPos(), block);
304 // If we got no block, there should be no invalidated blocks
307 //assert(lighting_invalidated_blocks.size() == 0);
313 Set sent status of modified blocks on clients
316 // NOTE: Server's clients are also behind the connection mutex
317 JMutexAutoLock lock(m_server->m_con_mutex);
320 Add the originally fetched block to the modified list
324 modified_blocks.insert(p, block);
328 Set the modified blocks unsent for all the clients
331 for(core::map<u16, RemoteClient*>::Iterator
332 i = m_server->m_clients.getIterator();
333 i.atEnd() == false; i++)
335 RemoteClient *client = i.getNode()->getValue();
337 if(modified_blocks.size() > 0)
339 // Remove block from sent history
340 client->SetBlocksNotSent(modified_blocks);
346 END_DEBUG_EXCEPTION_HANDLER(errorstream)
351 void RemoteClient::GetNextBlocks(Server *server, float dtime,
352 core::array<PrioritySortedBlockTransfer> &dest)
354 DSTACK(__FUNCTION_NAME);
357 TimeTaker timer("RemoteClient::GetNextBlocks", &timer_result);*/
360 m_nothing_to_send_pause_timer -= dtime;
361 m_nearest_unsent_reset_timer += dtime;
363 if(m_nothing_to_send_pause_timer >= 0)
368 // Won't send anything if already sending
369 if(m_blocks_sending.size() >= g_settings->getU16
370 ("max_simultaneous_block_sends_per_client"))
372 //infostream<<"Not sending any blocks, Queue full."<<std::endl;
376 //TimeTaker timer("RemoteClient::GetNextBlocks");
378 Player *player = server->m_env->getPlayer(peer_id);
380 assert(player != NULL);
382 v3f playerpos = player->getPosition();
383 v3f playerspeed = player->getSpeed();
384 v3f playerspeeddir(0,0,0);
385 if(playerspeed.getLength() > 1.0*BS)
386 playerspeeddir = playerspeed / playerspeed.getLength();
387 // Predict to next block
388 v3f playerpos_predicted = playerpos + playerspeeddir*MAP_BLOCKSIZE*BS;
390 v3s16 center_nodepos = floatToInt(playerpos_predicted, BS);
392 v3s16 center = getNodeBlockPos(center_nodepos);
394 // Camera position and direction
395 v3f camera_pos = player->getEyePosition();
396 v3f camera_dir = v3f(0,0,1);
397 camera_dir.rotateYZBy(player->getPitch());
398 camera_dir.rotateXZBy(player->getYaw());
400 /*infostream<<"camera_dir=("<<camera_dir.X<<","<<camera_dir.Y<<","
401 <<camera_dir.Z<<")"<<std::endl;*/
404 Get the starting value of the block finder radius.
407 if(m_last_center != center)
409 m_nearest_unsent_d = 0;
410 m_last_center = center;
413 /*infostream<<"m_nearest_unsent_reset_timer="
414 <<m_nearest_unsent_reset_timer<<std::endl;*/
416 // Reset periodically to workaround for some bugs or stuff
417 if(m_nearest_unsent_reset_timer > 20.0)
419 m_nearest_unsent_reset_timer = 0;
420 m_nearest_unsent_d = 0;
421 //infostream<<"Resetting m_nearest_unsent_d for "
422 // <<server->getPlayerName(peer_id)<<std::endl;
425 //s16 last_nearest_unsent_d = m_nearest_unsent_d;
426 s16 d_start = m_nearest_unsent_d;
428 //infostream<<"d_start="<<d_start<<std::endl;
430 u16 max_simul_sends_setting = g_settings->getU16
431 ("max_simultaneous_block_sends_per_client");
432 u16 max_simul_sends_usually = max_simul_sends_setting;
435 Check the time from last addNode/removeNode.
437 Decrease send rate if player is building stuff.
439 m_time_from_building += dtime;
440 if(m_time_from_building < g_settings->getFloat(
441 "full_block_send_enable_min_time_from_building"))
443 max_simul_sends_usually
444 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
448 Number of blocks sending + number of blocks selected for sending
450 u32 num_blocks_selected = m_blocks_sending.size();
453 next time d will be continued from the d from which the nearest
454 unsent block was found this time.
456 This is because not necessarily any of the blocks found this
457 time are actually sent.
459 s32 new_nearest_unsent_d = -1;
461 s16 d_max = g_settings->getS16("max_block_send_distance");
462 s16 d_max_gen = g_settings->getS16("max_block_generate_distance");
464 // Don't loop very much at a time
465 s16 max_d_increment_at_time = 2;
466 if(d_max > d_start + max_d_increment_at_time)
467 d_max = d_start + max_d_increment_at_time;
468 /*if(d_max_gen > d_start+2)
469 d_max_gen = d_start+2;*/
471 //infostream<<"Starting from "<<d_start<<std::endl;
473 s32 nearest_emerged_d = -1;
474 s32 nearest_emergefull_d = -1;
475 s32 nearest_sent_d = -1;
476 bool queue_is_full = false;
479 for(d = d_start; d <= d_max; d++)
481 /*errorstream<<"checking d="<<d<<" for "
482 <<server->getPlayerName(peer_id)<<std::endl;*/
483 //infostream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
486 If m_nearest_unsent_d was changed by the EmergeThread
487 (it can change it to 0 through SetBlockNotSent),
489 Else update m_nearest_unsent_d
491 /*if(m_nearest_unsent_d != last_nearest_unsent_d)
493 d = m_nearest_unsent_d;
494 last_nearest_unsent_d = m_nearest_unsent_d;
498 Get the border/face dot coordinates of a "d-radiused"
501 core::list<v3s16> list;
502 getFacePositions(list, d);
504 core::list<v3s16>::Iterator li;
505 for(li=list.begin(); li!=list.end(); li++)
507 v3s16 p = *li + center;
511 - Don't allow too many simultaneous transfers
512 - EXCEPT when the blocks are very close
514 Also, don't send blocks that are already flying.
517 // Start with the usual maximum
518 u16 max_simul_dynamic = max_simul_sends_usually;
520 // If block is very close, allow full maximum
521 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
522 max_simul_dynamic = max_simul_sends_setting;
524 // Don't select too many blocks for sending
525 if(num_blocks_selected >= max_simul_dynamic)
527 queue_is_full = true;
528 goto queue_full_break;
531 // Don't send blocks that are currently being transferred
532 if(m_blocks_sending.find(p) != NULL)
538 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
539 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
540 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
541 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
542 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
543 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
546 // If this is true, inexistent block will be made from scratch
547 bool generate = d <= d_max_gen;
550 /*// Limit the generating area vertically to 2/3
551 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
554 // Limit the send area vertically to 1/2
555 if(abs(p.Y - center.Y) > d_max / 2)
561 If block is far away, don't generate it unless it is
567 // Block center y in nodes
568 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
569 // Don't generate if it's very high or very low
570 if(y < -64 || y > 64)
574 v2s16 p2d_nodes_center(
578 // Get ground height in nodes
579 s16 gh = server->m_env->getServerMap().findGroundLevel(
582 // If differs a lot, don't generate
583 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
585 // Actually, don't even send it
591 //infostream<<"d="<<d<<std::endl;
594 Don't generate or send if not in sight
595 FIXME This only works if the client uses a small enough
596 FOV setting. The default of 72 degrees is fine.
599 float camera_fov = (72.0*PI/180) * 4./3.;
600 if(isBlockInSight(p, camera_pos, camera_dir, camera_fov, 10000*BS) == false)
606 Don't send already sent blocks
609 if(m_blocks_sent.find(p) != NULL)
616 Check if map has this block
618 MapBlock *block = server->m_env->getMap().getBlockNoCreateNoEx(p);
620 bool surely_not_found_on_disk = false;
621 bool block_is_invalid = false;
624 // Reset usage timer, this block will be of use in the future.
625 block->resetUsageTimer();
627 // Block is dummy if data doesn't exist.
628 // It means it has been not found from disk and not generated
631 surely_not_found_on_disk = true;
634 // Block is valid if lighting is up-to-date and data exists
635 if(block->isValid() == false)
637 block_is_invalid = true;
640 /*if(block->isFullyGenerated() == false)
642 block_is_invalid = true;
647 ServerMap *map = (ServerMap*)(&server->m_env->getMap());
648 v2s16 chunkpos = map->sector_to_chunk(p2d);
649 if(map->chunkNonVolatile(chunkpos) == false)
650 block_is_invalid = true;
652 if(block->isGenerated() == false)
653 block_is_invalid = true;
656 If block is not close, don't send it unless it is near
659 Block is near ground level if night-time mesh
660 differs from day-time mesh.
664 if(block->dayNightDiffed() == false)
671 If block has been marked to not exist on disk (dummy)
672 and generating new ones is not wanted, skip block.
674 if(generate == false && surely_not_found_on_disk == true)
681 Add inexistent block to emerge queue.
683 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
685 //TODO: Get value from somewhere
686 // Allow only one block in emerge queue
687 //if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
688 // Allow two blocks in queue per client
689 //if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
690 if(server->m_emerge_queue.peerItemCount(peer_id) < 25)
692 //infostream<<"Adding block to emerge queue"<<std::endl;
694 // Add it to the emerge queue and trigger the thread
697 if(generate == false)
698 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
700 server->m_emerge_queue.addBlock(peer_id, p, flags);
701 server->m_emergethread.trigger();
703 if(nearest_emerged_d == -1)
704 nearest_emerged_d = d;
706 if(nearest_emergefull_d == -1)
707 nearest_emergefull_d = d;
714 if(nearest_sent_d == -1)
718 Add block to send queue
721 /*errorstream<<"sending from d="<<d<<" to "
722 <<server->getPlayerName(peer_id)<<std::endl;*/
724 PrioritySortedBlockTransfer q((float)d, p, peer_id);
728 num_blocks_selected += 1;
733 //infostream<<"Stopped at "<<d<<std::endl;
735 // If nothing was found for sending and nothing was queued for
736 // emerging, continue next time browsing from here
737 if(nearest_emerged_d != -1){
738 new_nearest_unsent_d = nearest_emerged_d;
739 } else if(nearest_emergefull_d != -1){
740 new_nearest_unsent_d = nearest_emergefull_d;
742 if(d > g_settings->getS16("max_block_send_distance")){
743 new_nearest_unsent_d = 0;
744 m_nothing_to_send_pause_timer = 2.0;
745 /*infostream<<"GetNextBlocks(): d wrapped around for "
746 <<server->getPlayerName(peer_id)
747 <<"; setting to 0 and pausing"<<std::endl;*/
749 if(nearest_sent_d != -1)
750 new_nearest_unsent_d = nearest_sent_d;
752 new_nearest_unsent_d = d;
756 if(new_nearest_unsent_d != -1)
757 m_nearest_unsent_d = new_nearest_unsent_d;
759 /*timer_result = timer.stop(true);
760 if(timer_result != 0)
761 infostream<<"GetNextBlocks duration: "<<timer_result<<" (!=0)"<<std::endl;*/
764 void RemoteClient::SendObjectData(
767 core::map<v3s16, bool> &stepped_blocks
770 DSTACK(__FUNCTION_NAME);
772 // Can't send anything without knowing version
773 if(serialization_version == SER_FMT_VER_INVALID)
775 infostream<<"RemoteClient::SendObjectData(): Not sending, no version."
781 Send a TOCLIENT_OBJECTDATA packet.
785 u16 number of player positions
797 std::ostringstream os(std::ios_base::binary);
801 writeU16(buf, TOCLIENT_OBJECTDATA);
802 os.write((char*)buf, 2);
805 Get and write player data
808 // Get connected players
809 core::list<Player*> players = server->m_env->getPlayers(true);
811 // Write player count
812 u16 playercount = players.size();
813 writeU16(buf, playercount);
814 os.write((char*)buf, 2);
816 core::list<Player*>::Iterator i;
817 for(i = players.begin();
818 i != players.end(); i++)
822 v3f pf = player->getPosition();
823 v3f sf = player->getSpeed();
825 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
826 v3s32 speed_i (sf.X*100, sf.Y*100, sf.Z*100);
827 s32 pitch_i (player->getPitch() * 100);
828 s32 yaw_i (player->getYaw() * 100);
830 writeU16(buf, player->peer_id);
831 os.write((char*)buf, 2);
832 writeV3S32(buf, position_i);
833 os.write((char*)buf, 12);
834 writeV3S32(buf, speed_i);
835 os.write((char*)buf, 12);
836 writeS32(buf, pitch_i);
837 os.write((char*)buf, 4);
838 writeS32(buf, yaw_i);
839 os.write((char*)buf, 4);
843 Get and write object data (dummy, for compatibility)
848 os.write((char*)buf, 2);
854 //infostream<<"Server: Sending object data to "<<peer_id<<std::endl;
857 std::string s = os.str();
858 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
859 // Send as unreliable
860 server->m_con.Send(peer_id, 0, data, false);
863 void RemoteClient::GotBlock(v3s16 p)
865 if(m_blocks_sending.find(p) != NULL)
866 m_blocks_sending.remove(p);
869 /*infostream<<"RemoteClient::GotBlock(): Didn't find in"
870 " m_blocks_sending"<<std::endl;*/
871 m_excess_gotblocks++;
873 m_blocks_sent.insert(p, true);
876 void RemoteClient::SentBlock(v3s16 p)
878 if(m_blocks_sending.find(p) == NULL)
879 m_blocks_sending.insert(p, 0.0);
881 infostream<<"RemoteClient::SentBlock(): Sent block"
882 " already in m_blocks_sending"<<std::endl;
885 void RemoteClient::SetBlockNotSent(v3s16 p)
887 m_nearest_unsent_d = 0;
889 if(m_blocks_sending.find(p) != NULL)
890 m_blocks_sending.remove(p);
891 if(m_blocks_sent.find(p) != NULL)
892 m_blocks_sent.remove(p);
895 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
897 m_nearest_unsent_d = 0;
899 for(core::map<v3s16, MapBlock*>::Iterator
900 i = blocks.getIterator();
901 i.atEnd()==false; i++)
903 v3s16 p = i.getNode()->getKey();
905 if(m_blocks_sending.find(p) != NULL)
906 m_blocks_sending.remove(p);
907 if(m_blocks_sent.find(p) != NULL)
908 m_blocks_sent.remove(p);
916 PlayerInfo::PlayerInfo()
922 void PlayerInfo::PrintLine(std::ostream *s)
925 (*s)<<"\""<<name<<"\" ("
926 <<(position.X/10)<<","<<(position.Y/10)
927 <<","<<(position.Z/10)<<") ";
929 (*s)<<" avg_rtt="<<avg_rtt;
933 u32 PIChecksum(core::list<PlayerInfo> &l)
935 core::list<PlayerInfo>::Iterator i;
938 for(i=l.begin(); i!=l.end(); i++)
940 checksum += a * (i->id+1);
941 checksum ^= 0x435aafcd;
952 std::string mapsavedir,
953 std::string configpath
956 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
957 m_authmanager(mapsavedir+DIR_DELIM+"auth.txt"),
958 m_banmanager(mapsavedir+DIR_DELIM+"ipban.txt"),
960 m_toolmgr(createToolDefManager()),
961 m_nodemgr(createNodeDefManager(NULL)),
963 m_emergethread(this),
965 m_time_of_day_send_timer(0),
967 m_mapsavedir(mapsavedir),
968 m_configpath(configpath),
969 m_shutdown_requested(false),
970 m_ignore_map_edit_events(false),
971 m_ignore_map_edit_events_peer_id(0)
973 m_liquid_transform_timer = 0.0;
974 m_print_info_timer = 0.0;
975 m_objectdata_timer = 0.0;
976 m_emergethread_trigger_timer = 0.0;
977 m_savemap_timer = 0.0;
981 m_step_dtime_mutex.Init();
984 JMutexAutoLock envlock(m_env_mutex);
985 JMutexAutoLock conlock(m_con_mutex);
987 infostream<<"m_nodemgr="<<m_nodemgr<<std::endl;
989 // Initialize default node definitions
990 content_mapnode_init(NULL, m_nodemgr);
992 // Initialize scripting
994 infostream<<"Server: Initializing scripting"<<std::endl;
995 m_lua = script_init();
998 scriptapi_export(m_lua, this);
999 // Load and run scripts
1000 core::list<std::string> modspaths;
1001 modspaths.push_back(porting::path_data + DIR_DELIM + "mods");
1002 for(core::list<std::string>::Iterator i = modspaths.begin();
1003 i != modspaths.end(); i++){
1004 std::string modspath = *i;
1005 std::vector<fs::DirListNode> dirlist = fs::GetDirListing(modspath);
1006 for(u32 j=0; j<dirlist.size(); j++){
1009 std::string modname = dirlist[j].name;
1010 infostream<<"Server: Loading mod \""<<modname<<"\" script..."
1012 std::string scriptpath = modspath + DIR_DELIM + modname
1013 + DIR_DELIM + "init.lua";
1014 bool success = script_load(m_lua, scriptpath.c_str());
1016 errorstream<<"Server: Failed to load and run "
1017 <<scriptpath<<std::endl;
1023 // Initialize Environment
1025 m_env = new ServerEnvironment(new ServerMap(mapsavedir, this), m_lua, this);
1027 // Give environment reference to scripting api
1028 scriptapi_add_environment(m_lua, m_env);
1030 // Register us to receive map edit events
1031 m_env->getMap().addEventReceiver(this);
1033 // If file exists, load environment metadata
1034 if(fs::PathExists(m_mapsavedir+DIR_DELIM+"env_meta.txt"))
1036 infostream<<"Server: Loading environment metadata"<<std::endl;
1037 m_env->loadMeta(m_mapsavedir);
1041 infostream<<"Server: Loading players"<<std::endl;
1042 m_env->deSerializePlayers(m_mapsavedir);
1047 infostream<<"Server::~Server()"<<std::endl;
1050 Send shutdown message
1053 JMutexAutoLock conlock(m_con_mutex);
1055 std::wstring line = L"*** Server shutting down";
1058 Send the message to clients
1060 for(core::map<u16, RemoteClient*>::Iterator
1061 i = m_clients.getIterator();
1062 i.atEnd() == false; i++)
1064 // Get client and check that it is valid
1065 RemoteClient *client = i.getNode()->getValue();
1066 assert(client->peer_id == i.getNode()->getKey());
1067 if(client->serialization_version == SER_FMT_VER_INVALID)
1071 SendChatMessage(client->peer_id, line);
1073 catch(con::PeerNotFoundException &e)
1079 JMutexAutoLock envlock(m_env_mutex);
1084 infostream<<"Server: Saving players"<<std::endl;
1085 m_env->serializePlayers(m_mapsavedir);
1088 Save environment metadata
1090 infostream<<"Server: Saving environment metadata"<<std::endl;
1091 m_env->saveMeta(m_mapsavedir);
1103 JMutexAutoLock clientslock(m_con_mutex);
1105 for(core::map<u16, RemoteClient*>::Iterator
1106 i = m_clients.getIterator();
1107 i.atEnd() == false; i++)
1110 // NOTE: These are removed by env destructor
1112 u16 peer_id = i.getNode()->getKey();
1113 JMutexAutoLock envlock(m_env_mutex);
1114 m_env->removePlayer(peer_id);
1118 delete i.getNode()->getValue();
1122 // Delete Environment
1128 // Deinitialize scripting
1129 infostream<<"Server: Deinitializing scripting"<<std::endl;
1130 script_deinit(m_lua);
1133 void Server::start(unsigned short port)
1135 DSTACK(__FUNCTION_NAME);
1136 // Stop thread if already running
1139 // Initialize connection
1140 m_con.SetTimeoutMs(30);
1144 m_thread.setRun(true);
1147 infostream<<"Server: Started on port "<<port<<std::endl;
1152 DSTACK(__FUNCTION_NAME);
1154 infostream<<"Server: Stopping and waiting threads"<<std::endl;
1156 // Stop threads (set run=false first so both start stopping)
1157 m_thread.setRun(false);
1158 m_emergethread.setRun(false);
1160 m_emergethread.stop();
1162 infostream<<"Server: Threads stopped"<<std::endl;
1165 void Server::step(float dtime)
1167 DSTACK(__FUNCTION_NAME);
1172 JMutexAutoLock lock(m_step_dtime_mutex);
1173 m_step_dtime += dtime;
1177 void Server::AsyncRunStep()
1179 DSTACK(__FUNCTION_NAME);
1181 g_profiler->add("Server::AsyncRunStep (num)", 1);
1185 JMutexAutoLock lock1(m_step_dtime_mutex);
1186 dtime = m_step_dtime;
1190 ScopeProfiler sp(g_profiler, "Server: sel and send blocks to clients");
1191 // Send blocks to clients
1198 g_profiler->add("Server::AsyncRunStep with dtime (num)", 1);
1200 //infostream<<"Server steps "<<dtime<<std::endl;
1201 //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1204 JMutexAutoLock lock1(m_step_dtime_mutex);
1205 m_step_dtime -= dtime;
1212 m_uptime.set(m_uptime.get() + dtime);
1216 // Process connection's timeouts
1217 JMutexAutoLock lock2(m_con_mutex);
1218 ScopeProfiler sp(g_profiler, "Server: connection timeout processing");
1219 m_con.RunTimeouts(dtime);
1223 // This has to be called so that the client list gets synced
1224 // with the peer list of the connection
1225 handlePeerChanges();
1229 Update m_time_of_day and overall game time
1232 JMutexAutoLock envlock(m_env_mutex);
1234 m_time_counter += dtime;
1235 f32 speed = g_settings->getFloat("time_speed") * 24000./(24.*3600);
1236 u32 units = (u32)(m_time_counter*speed);
1237 m_time_counter -= (f32)units / speed;
1239 m_env->setTimeOfDay((m_env->getTimeOfDay() + units) % 24000);
1241 //infostream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1244 Send to clients at constant intervals
1247 m_time_of_day_send_timer -= dtime;
1248 if(m_time_of_day_send_timer < 0.0)
1250 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
1252 //JMutexAutoLock envlock(m_env_mutex);
1253 JMutexAutoLock conlock(m_con_mutex);
1255 for(core::map<u16, RemoteClient*>::Iterator
1256 i = m_clients.getIterator();
1257 i.atEnd() == false; i++)
1259 RemoteClient *client = i.getNode()->getValue();
1260 //Player *player = m_env->getPlayer(client->peer_id);
1262 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1263 m_env->getTimeOfDay());
1265 m_con.Send(client->peer_id, 0, data, true);
1271 JMutexAutoLock lock(m_env_mutex);
1273 ScopeProfiler sp(g_profiler, "SEnv step");
1274 ScopeProfiler sp2(g_profiler, "SEnv step avg", SPT_AVG);
1278 const float map_timer_and_unload_dtime = 5.15;
1279 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
1281 JMutexAutoLock lock(m_env_mutex);
1282 // Run Map's timers and unload unused data
1283 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
1284 m_env->getMap().timerUpdate(map_timer_and_unload_dtime,
1285 g_settings->getFloat("server_unload_unused_data_timeout"));
1295 m_liquid_transform_timer += dtime;
1296 if(m_liquid_transform_timer >= 1.00)
1298 m_liquid_transform_timer -= 1.00;
1300 JMutexAutoLock lock(m_env_mutex);
1302 ScopeProfiler sp(g_profiler, "Server: liquid transform");
1304 core::map<v3s16, MapBlock*> modified_blocks;
1305 m_env->getMap().transformLiquids(modified_blocks);
1310 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1311 ServerMap &map = ((ServerMap&)m_env->getMap());
1312 map.updateLighting(modified_blocks, lighting_modified_blocks);
1314 // Add blocks modified by lighting to modified_blocks
1315 for(core::map<v3s16, MapBlock*>::Iterator
1316 i = lighting_modified_blocks.getIterator();
1317 i.atEnd() == false; i++)
1319 MapBlock *block = i.getNode()->getValue();
1320 modified_blocks.insert(block->getPos(), block);
1324 Set the modified blocks unsent for all the clients
1327 JMutexAutoLock lock2(m_con_mutex);
1329 for(core::map<u16, RemoteClient*>::Iterator
1330 i = m_clients.getIterator();
1331 i.atEnd() == false; i++)
1333 RemoteClient *client = i.getNode()->getValue();
1335 if(modified_blocks.size() > 0)
1337 // Remove block from sent history
1338 client->SetBlocksNotSent(modified_blocks);
1343 // Periodically print some info
1345 float &counter = m_print_info_timer;
1351 JMutexAutoLock lock2(m_con_mutex);
1353 if(m_clients.size() != 0)
1354 infostream<<"Players:"<<std::endl;
1355 for(core::map<u16, RemoteClient*>::Iterator
1356 i = m_clients.getIterator();
1357 i.atEnd() == false; i++)
1359 //u16 peer_id = i.getNode()->getKey();
1360 RemoteClient *client = i.getNode()->getValue();
1361 Player *player = m_env->getPlayer(client->peer_id);
1364 infostream<<"* "<<player->getName()<<"\t";
1365 client->PrintInfo(infostream);
1370 //if(g_settings->getBool("enable_experimental"))
1374 Check added and deleted active objects
1377 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
1378 JMutexAutoLock envlock(m_env_mutex);
1379 JMutexAutoLock conlock(m_con_mutex);
1381 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objs");
1383 // Radius inside which objects are active
1384 s16 radius = g_settings->getS16("active_object_send_range_blocks");
1385 radius *= MAP_BLOCKSIZE;
1387 for(core::map<u16, RemoteClient*>::Iterator
1388 i = m_clients.getIterator();
1389 i.atEnd() == false; i++)
1391 RemoteClient *client = i.getNode()->getValue();
1392 Player *player = m_env->getPlayer(client->peer_id);
1395 // This can happen if the client timeouts somehow
1396 /*infostream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
1398 <<" has no associated player"<<std::endl;*/
1401 v3s16 pos = floatToInt(player->getPosition(), BS);
1403 core::map<u16, bool> removed_objects;
1404 core::map<u16, bool> added_objects;
1405 m_env->getRemovedActiveObjects(pos, radius,
1406 client->m_known_objects, removed_objects);
1407 m_env->getAddedActiveObjects(pos, radius,
1408 client->m_known_objects, added_objects);
1410 // Ignore if nothing happened
1411 if(removed_objects.size() == 0 && added_objects.size() == 0)
1413 //infostream<<"active objects: none changed"<<std::endl;
1417 std::string data_buffer;
1421 // Handle removed objects
1422 writeU16((u8*)buf, removed_objects.size());
1423 data_buffer.append(buf, 2);
1424 for(core::map<u16, bool>::Iterator
1425 i = removed_objects.getIterator();
1426 i.atEnd()==false; i++)
1429 u16 id = i.getNode()->getKey();
1430 ServerActiveObject* obj = m_env->getActiveObject(id);
1432 // Add to data buffer for sending
1433 writeU16((u8*)buf, i.getNode()->getKey());
1434 data_buffer.append(buf, 2);
1436 // Remove from known objects
1437 client->m_known_objects.remove(i.getNode()->getKey());
1439 if(obj && obj->m_known_by_count > 0)
1440 obj->m_known_by_count--;
1443 // Handle added objects
1444 writeU16((u8*)buf, added_objects.size());
1445 data_buffer.append(buf, 2);
1446 for(core::map<u16, bool>::Iterator
1447 i = added_objects.getIterator();
1448 i.atEnd()==false; i++)
1451 u16 id = i.getNode()->getKey();
1452 ServerActiveObject* obj = m_env->getActiveObject(id);
1455 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1457 infostream<<"WARNING: "<<__FUNCTION_NAME
1458 <<": NULL object"<<std::endl;
1460 type = obj->getType();
1462 // Add to data buffer for sending
1463 writeU16((u8*)buf, id);
1464 data_buffer.append(buf, 2);
1465 writeU8((u8*)buf, type);
1466 data_buffer.append(buf, 1);
1469 data_buffer.append(serializeLongString(
1470 obj->getClientInitializationData()));
1472 data_buffer.append(serializeLongString(""));
1474 // Add to known objects
1475 client->m_known_objects.insert(i.getNode()->getKey(), false);
1478 obj->m_known_by_count++;
1482 SharedBuffer<u8> reply(2 + data_buffer.size());
1483 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1484 memcpy((char*)&reply[2], data_buffer.c_str(),
1485 data_buffer.size());
1487 m_con.Send(client->peer_id, 0, reply, true);
1489 infostream<<"Server: Sent object remove/add: "
1490 <<removed_objects.size()<<" removed, "
1491 <<added_objects.size()<<" added, "
1492 <<"packet size is "<<reply.getSize()<<std::endl;
1497 Collect a list of all the objects known by the clients
1498 and report it back to the environment.
1501 core::map<u16, bool> all_known_objects;
1503 for(core::map<u16, RemoteClient*>::Iterator
1504 i = m_clients.getIterator();
1505 i.atEnd() == false; i++)
1507 RemoteClient *client = i.getNode()->getValue();
1508 // Go through all known objects of client
1509 for(core::map<u16, bool>::Iterator
1510 i = client->m_known_objects.getIterator();
1511 i.atEnd()==false; i++)
1513 u16 id = i.getNode()->getKey();
1514 all_known_objects[id] = true;
1518 m_env->setKnownActiveObjects(whatever);
1524 Send object messages
1527 JMutexAutoLock envlock(m_env_mutex);
1528 JMutexAutoLock conlock(m_con_mutex);
1530 //ScopeProfiler sp(g_profiler, "Server: sending object messages");
1533 // Value = data sent by object
1534 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1536 // Get active object messages from environment
1539 ActiveObjectMessage aom = m_env->getActiveObjectMessage();
1543 core::list<ActiveObjectMessage>* message_list = NULL;
1544 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1545 n = buffered_messages.find(aom.id);
1548 message_list = new core::list<ActiveObjectMessage>;
1549 buffered_messages.insert(aom.id, message_list);
1553 message_list = n->getValue();
1555 message_list->push_back(aom);
1558 // Route data to every client
1559 for(core::map<u16, RemoteClient*>::Iterator
1560 i = m_clients.getIterator();
1561 i.atEnd()==false; i++)
1563 RemoteClient *client = i.getNode()->getValue();
1564 std::string reliable_data;
1565 std::string unreliable_data;
1566 // Go through all objects in message buffer
1567 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1568 j = buffered_messages.getIterator();
1569 j.atEnd()==false; j++)
1571 // If object is not known by client, skip it
1572 u16 id = j.getNode()->getKey();
1573 if(client->m_known_objects.find(id) == NULL)
1575 // Get message list of object
1576 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1577 // Go through every message
1578 for(core::list<ActiveObjectMessage>::Iterator
1579 k = list->begin(); k != list->end(); k++)
1581 // Compose the full new data with header
1582 ActiveObjectMessage aom = *k;
1583 std::string new_data;
1586 writeU16((u8*)&buf[0], aom.id);
1587 new_data.append(buf, 2);
1589 new_data += serializeString(aom.datastring);
1590 // Add data to buffer
1592 reliable_data += new_data;
1594 unreliable_data += new_data;
1598 reliable_data and unreliable_data are now ready.
1601 if(reliable_data.size() > 0)
1603 SharedBuffer<u8> reply(2 + reliable_data.size());
1604 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1605 memcpy((char*)&reply[2], reliable_data.c_str(),
1606 reliable_data.size());
1608 m_con.Send(client->peer_id, 0, reply, true);
1610 if(unreliable_data.size() > 0)
1612 SharedBuffer<u8> reply(2 + unreliable_data.size());
1613 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1614 memcpy((char*)&reply[2], unreliable_data.c_str(),
1615 unreliable_data.size());
1616 // Send as unreliable
1617 m_con.Send(client->peer_id, 0, reply, false);
1620 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1622 infostream<<"Server: Size of object message data: "
1623 <<"reliable: "<<reliable_data.size()
1624 <<", unreliable: "<<unreliable_data.size()
1629 // Clear buffered_messages
1630 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1631 i = buffered_messages.getIterator();
1632 i.atEnd()==false; i++)
1634 delete i.getNode()->getValue();
1638 } // enable_experimental
1641 Send queued-for-sending map edit events.
1644 // Don't send too many at a time
1647 // Single change sending is disabled if queue size is not small
1648 bool disable_single_change_sending = false;
1649 if(m_unsent_map_edit_queue.size() >= 4)
1650 disable_single_change_sending = true;
1652 bool got_any_events = false;
1654 // We'll log the amount of each
1657 while(m_unsent_map_edit_queue.size() != 0)
1659 got_any_events = true;
1661 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1663 // Players far away from the change are stored here.
1664 // Instead of sending the changes, MapBlocks are set not sent
1666 core::list<u16> far_players;
1668 if(event->type == MEET_ADDNODE)
1670 //infostream<<"Server: MEET_ADDNODE"<<std::endl;
1671 prof.add("MEET_ADDNODE", 1);
1672 if(disable_single_change_sending)
1673 sendAddNode(event->p, event->n, event->already_known_by_peer,
1676 sendAddNode(event->p, event->n, event->already_known_by_peer,
1679 else if(event->type == MEET_REMOVENODE)
1681 //infostream<<"Server: MEET_REMOVENODE"<<std::endl;
1682 prof.add("MEET_REMOVENODE", 1);
1683 if(disable_single_change_sending)
1684 sendRemoveNode(event->p, event->already_known_by_peer,
1687 sendRemoveNode(event->p, event->already_known_by_peer,
1690 else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
1692 infostream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
1693 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
1694 setBlockNotSent(event->p);
1696 else if(event->type == MEET_OTHER)
1698 infostream<<"Server: MEET_OTHER"<<std::endl;
1699 prof.add("MEET_OTHER", 1);
1700 for(core::map<v3s16, bool>::Iterator
1701 i = event->modified_blocks.getIterator();
1702 i.atEnd()==false; i++)
1704 v3s16 p = i.getNode()->getKey();
1710 prof.add("unknown", 1);
1711 infostream<<"WARNING: Server: Unknown MapEditEvent "
1712 <<((u32)event->type)<<std::endl;
1716 Set blocks not sent to far players
1718 if(far_players.size() > 0)
1720 // Convert list format to that wanted by SetBlocksNotSent
1721 core::map<v3s16, MapBlock*> modified_blocks2;
1722 for(core::map<v3s16, bool>::Iterator
1723 i = event->modified_blocks.getIterator();
1724 i.atEnd()==false; i++)
1726 v3s16 p = i.getNode()->getKey();
1727 modified_blocks2.insert(p,
1728 m_env->getMap().getBlockNoCreateNoEx(p));
1730 // Set blocks not sent
1731 for(core::list<u16>::Iterator
1732 i = far_players.begin();
1733 i != far_players.end(); i++)
1736 RemoteClient *client = getClient(peer_id);
1739 client->SetBlocksNotSent(modified_blocks2);
1745 /*// Don't send too many at a time
1747 if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
1753 infostream<<"Server: MapEditEvents:"<<std::endl;
1754 prof.print(infostream);
1760 Send object positions
1763 float &counter = m_objectdata_timer;
1765 if(counter >= g_settings->getFloat("objectdata_interval"))
1767 JMutexAutoLock lock1(m_env_mutex);
1768 JMutexAutoLock lock2(m_con_mutex);
1770 //ScopeProfiler sp(g_profiler, "Server: sending player positions");
1772 SendObjectData(counter);
1779 Trigger emergethread (it somehow gets to a non-triggered but
1780 bysy state sometimes)
1783 float &counter = m_emergethread_trigger_timer;
1789 m_emergethread.trigger();
1793 // Save map, players and auth stuff
1795 float &counter = m_savemap_timer;
1797 if(counter >= g_settings->getFloat("server_map_save_interval"))
1801 ScopeProfiler sp(g_profiler, "Server: saving stuff");
1804 if(m_authmanager.isModified())
1805 m_authmanager.save();
1808 if(m_banmanager.isModified())
1809 m_banmanager.save();
1812 JMutexAutoLock lock(m_env_mutex);
1814 /*// Unload unused data (delete from memory)
1815 m_env->getMap().unloadUnusedData(
1816 g_settings->getFloat("server_unload_unused_sectors_timeout"));
1818 /*u32 deleted_count = m_env->getMap().unloadUnusedData(
1819 g_settings->getFloat("server_unload_unused_sectors_timeout"));
1822 // Save only changed parts
1823 m_env->getMap().save(true);
1825 /*if(deleted_count > 0)
1827 infostream<<"Server: Unloaded "<<deleted_count
1828 <<" blocks from memory"<<std::endl;
1832 m_env->serializePlayers(m_mapsavedir);
1834 // Save environment metadata
1835 m_env->saveMeta(m_mapsavedir);
1840 void Server::Receive()
1842 DSTACK(__FUNCTION_NAME);
1843 SharedBuffer<u8> data;
1848 JMutexAutoLock conlock(m_con_mutex);
1849 datasize = m_con.Receive(peer_id, data);
1852 // This has to be called so that the client list gets synced
1853 // with the peer list of the connection
1854 handlePeerChanges();
1856 ProcessData(*data, datasize, peer_id);
1858 catch(con::InvalidIncomingDataException &e)
1860 infostream<<"Server::Receive(): "
1861 "InvalidIncomingDataException: what()="
1862 <<e.what()<<std::endl;
1864 catch(con::PeerNotFoundException &e)
1866 //NOTE: This is not needed anymore
1868 // The peer has been disconnected.
1869 // Find the associated player and remove it.
1871 /*JMutexAutoLock envlock(m_env_mutex);
1873 infostream<<"ServerThread: peer_id="<<peer_id
1874 <<" has apparently closed connection. "
1875 <<"Removing player."<<std::endl;
1877 m_env->removePlayer(peer_id);*/
1881 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1883 DSTACK(__FUNCTION_NAME);
1884 // Environment is locked first.
1885 JMutexAutoLock envlock(m_env_mutex);
1886 JMutexAutoLock conlock(m_con_mutex);
1889 Address address = m_con.GetPeerAddress(peer_id);
1891 // drop player if is ip is banned
1892 if(m_banmanager.isIpBanned(address.serializeString())){
1893 SendAccessDenied(m_con, peer_id,
1894 L"Your ip is banned. Banned name was "
1895 +narrow_to_wide(m_banmanager.getBanName(
1896 address.serializeString())));
1897 m_con.DeletePeer(peer_id);
1901 catch(con::PeerNotFoundException &e)
1903 infostream<<"Server::ProcessData(): Cancelling: peer "
1904 <<peer_id<<" not found"<<std::endl;
1908 u8 peer_ser_ver = getClient(peer_id)->serialization_version;
1916 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1918 if(command == TOSERVER_INIT)
1920 // [0] u16 TOSERVER_INIT
1921 // [2] u8 SER_FMT_VER_HIGHEST
1922 // [3] u8[20] player_name
1923 // [23] u8[28] password <--- can be sent without this, from old versions
1925 if(datasize < 2+1+PLAYERNAME_SIZE)
1928 infostream<<"Server: Got TOSERVER_INIT from "
1929 <<peer_id<<std::endl;
1931 // First byte after command is maximum supported
1932 // serialization version
1933 u8 client_max = data[2];
1934 u8 our_max = SER_FMT_VER_HIGHEST;
1935 // Use the highest version supported by both
1936 u8 deployed = core::min_(client_max, our_max);
1937 // If it's lower than the lowest supported, give up.
1938 if(deployed < SER_FMT_VER_LOWEST)
1939 deployed = SER_FMT_VER_INVALID;
1941 //peer->serialization_version = deployed;
1942 getClient(peer_id)->pending_serialization_version = deployed;
1944 if(deployed == SER_FMT_VER_INVALID)
1946 infostream<<"Server: Cannot negotiate "
1947 "serialization version with peer "
1948 <<peer_id<<std::endl;
1949 SendAccessDenied(m_con, peer_id,
1950 L"Your client is too old (map format)");
1955 Read and check network protocol version
1958 u16 net_proto_version = 0;
1959 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
1961 net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
1964 getClient(peer_id)->net_proto_version = net_proto_version;
1966 if(net_proto_version == 0)
1968 SendAccessDenied(m_con, peer_id,
1969 L"Your client is too old. Please upgrade.");
1973 /* Uhh... this should actually be a warning but let's do it like this */
1974 if(g_settings->getBool("strict_protocol_version_checking"))
1976 if(net_proto_version < PROTOCOL_VERSION)
1978 SendAccessDenied(m_con, peer_id,
1979 L"Your client is too old. Please upgrade.");
1989 char playername[PLAYERNAME_SIZE];
1990 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
1992 playername[i] = data[3+i];
1994 playername[PLAYERNAME_SIZE-1] = 0;
1996 if(playername[0]=='\0')
1998 infostream<<"Server: Player has empty name"<<std::endl;
1999 SendAccessDenied(m_con, peer_id,
2004 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
2006 infostream<<"Server: Player has invalid name"<<std::endl;
2007 SendAccessDenied(m_con, peer_id,
2008 L"Name contains unallowed characters");
2013 char password[PASSWORD_SIZE];
2014 if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
2016 // old version - assume blank password
2021 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
2023 password[i] = data[23+i];
2025 password[PASSWORD_SIZE-1] = 0;
2028 std::string checkpwd;
2029 if(m_authmanager.exists(playername))
2031 checkpwd = m_authmanager.getPassword(playername);
2035 checkpwd = g_settings->get("default_password");
2038 /*infostream<<"Server: Client gave password '"<<password
2039 <<"', the correct one is '"<<checkpwd<<"'"<<std::endl;*/
2041 if(password != checkpwd && m_authmanager.exists(playername))
2043 infostream<<"Server: peer_id="<<peer_id
2044 <<": supplied invalid password for "
2045 <<playername<<std::endl;
2046 SendAccessDenied(m_con, peer_id, L"Invalid password");
2050 // Add player to auth manager
2051 if(m_authmanager.exists(playername) == false)
2053 infostream<<"Server: adding player "<<playername
2054 <<" to auth manager"<<std::endl;
2055 m_authmanager.add(playername);
2056 m_authmanager.setPassword(playername, checkpwd);
2057 m_authmanager.setPrivs(playername,
2058 stringToPrivs(g_settings->get("default_privs")));
2059 m_authmanager.save();
2062 // Enforce user limit.
2063 // Don't enforce for users that have some admin right
2064 if(m_clients.size() >= g_settings->getU16("max_users") &&
2065 (m_authmanager.getPrivs(playername)
2066 & (PRIV_SERVER|PRIV_BAN|PRIV_PRIVS)) == 0 &&
2067 playername != g_settings->get("name"))
2069 SendAccessDenied(m_con, peer_id, L"Too many users.");
2074 Player *player = emergePlayer(playername, password, peer_id);
2076 // If failed, cancel
2079 infostream<<"Server: peer_id="<<peer_id
2080 <<": failed to emerge player"<<std::endl;
2085 Answer with a TOCLIENT_INIT
2088 SharedBuffer<u8> reply(2+1+6+8);
2089 writeU16(&reply[0], TOCLIENT_INIT);
2090 writeU8(&reply[2], deployed);
2091 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
2092 writeU64(&reply[2+1+6], m_env->getServerMap().getSeed());
2095 m_con.Send(peer_id, 0, reply, true);
2099 Send complete position information
2101 SendMovePlayer(player);
2106 if(command == TOSERVER_INIT2)
2108 infostream<<"Server: Got TOSERVER_INIT2 from "
2109 <<peer_id<<std::endl;
2112 getClient(peer_id)->serialization_version
2113 = getClient(peer_id)->pending_serialization_version;
2116 Send some initialization data
2119 // Send tool definitions
2120 SendToolDef(m_con, peer_id, m_toolmgr);
2122 // Send player info to all players
2125 // Send inventory to player
2126 UpdateCrafting(peer_id);
2127 SendInventory(peer_id);
2129 // Send player items to all players
2132 Player *player = m_env->getPlayer(peer_id);
2135 SendPlayerHP(player);
2139 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2140 m_env->getTimeOfDay());
2141 m_con.Send(peer_id, 0, data, true);
2144 // Send information about server to player in chat
2145 SendChatMessage(peer_id, getStatusString());
2147 // Send information about joining in chat
2149 std::wstring name = L"unknown";
2150 Player *player = m_env->getPlayer(peer_id);
2152 name = narrow_to_wide(player->getName());
2154 std::wstring message;
2157 message += L" joined game";
2158 BroadcastChatMessage(message);
2161 // Warnings about protocol version can be issued here
2162 if(getClient(peer_id)->net_proto_version < PROTOCOL_VERSION)
2164 SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT IS OLD AND MAY WORK PROPERLY WITH THIS SERVER");
2168 Check HP, respawn if necessary
2170 HandlePlayerHP(player, 0);
2176 std::ostringstream os(std::ios_base::binary);
2177 for(core::map<u16, RemoteClient*>::Iterator
2178 i = m_clients.getIterator();
2179 i.atEnd() == false; i++)
2181 RemoteClient *client = i.getNode()->getValue();
2182 assert(client->peer_id == i.getNode()->getKey());
2183 if(client->serialization_version == SER_FMT_VER_INVALID)
2186 Player *player = m_env->getPlayer(client->peer_id);
2189 // Get name of player
2190 os<<player->getName()<<" ";
2193 actionstream<<player->getName()<<" joins game. List of players: "
2194 <<os.str()<<std::endl;
2200 if(peer_ser_ver == SER_FMT_VER_INVALID)
2202 infostream<<"Server::ProcessData(): Cancelling: Peer"
2203 " serialization format invalid or not initialized."
2204 " Skipping incoming command="<<command<<std::endl;
2208 Player *player = m_env->getPlayer(peer_id);
2211 infostream<<"Server::ProcessData(): Cancelling: "
2212 "No player for peer_id="<<peer_id
2216 if(command == TOSERVER_PLAYERPOS)
2218 if(datasize < 2+12+12+4+4)
2222 v3s32 ps = readV3S32(&data[start+2]);
2223 v3s32 ss = readV3S32(&data[start+2+12]);
2224 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2225 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2226 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2227 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2228 pitch = wrapDegrees(pitch);
2229 yaw = wrapDegrees(yaw);
2231 player->setPosition(position);
2232 player->setSpeed(speed);
2233 player->setPitch(pitch);
2234 player->setYaw(yaw);
2236 /*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2237 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2238 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2240 else if(command == TOSERVER_GOTBLOCKS)
2253 u16 count = data[2];
2254 for(u16 i=0; i<count; i++)
2256 if((s16)datasize < 2+1+(i+1)*6)
2257 throw con::InvalidIncomingDataException
2258 ("GOTBLOCKS length is too short");
2259 v3s16 p = readV3S16(&data[2+1+i*6]);
2260 /*infostream<<"Server: GOTBLOCKS ("
2261 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2262 RemoteClient *client = getClient(peer_id);
2263 client->GotBlock(p);
2266 else if(command == TOSERVER_DELETEDBLOCKS)
2279 u16 count = data[2];
2280 for(u16 i=0; i<count; i++)
2282 if((s16)datasize < 2+1+(i+1)*6)
2283 throw con::InvalidIncomingDataException
2284 ("DELETEDBLOCKS length is too short");
2285 v3s16 p = readV3S16(&data[2+1+i*6]);
2286 /*infostream<<"Server: DELETEDBLOCKS ("
2287 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2288 RemoteClient *client = getClient(peer_id);
2289 client->SetBlockNotSent(p);
2292 else if(command == TOSERVER_CLICK_OBJECT)
2294 infostream<<"Server: CLICK_OBJECT not supported anymore"<<std::endl;
2297 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2302 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2308 [2] u8 button (0=left, 1=right)
2312 u8 button = readU8(&data[2]);
2313 u16 id = readS16(&data[3]);
2314 u16 item_i = readU16(&data[5]);
2316 ServerActiveObject *obj = m_env->getActiveObject(id);
2320 infostream<<"Server: CLICK_ACTIVEOBJECT: object not found"
2325 // Skip if object has been removed
2329 //TODO: Check that object is reasonably close
2331 // Get ServerRemotePlayer
2332 ServerRemotePlayer *srp = (ServerRemotePlayer*)player;
2334 // Update wielded item
2335 srp->wieldItem(item_i);
2337 // Left click, pick/punch
2340 actionstream<<player->getName()<<" punches object "
2341 <<obj->getId()<<std::endl;
2348 Try creating inventory item
2350 InventoryItem *item = obj->createPickedUpItem();
2354 InventoryList *ilist = player->inventory.getList("main");
2357 actionstream<<player->getName()<<" picked up "
2358 <<item->getName()<<std::endl;
2359 if(g_settings->getBool("creative_mode") == false)
2361 // Skip if inventory has no free space
2362 if(ilist->roomForItem(item) == false)
2364 infostream<<"Player inventory has no free space"<<std::endl;
2368 // Add to inventory and send inventory
2369 ilist->addItem(item);
2370 UpdateCrafting(player->peer_id);
2371 SendInventory(player->peer_id);
2374 // Remove object from environment
2375 obj->m_removed = true;
2381 Item cannot be picked up. Punch it instead.
2384 actionstream<<player->getName()<<" punches object "
2385 <<obj->getId()<<std::endl;
2387 ToolItem *titem = NULL;
2388 std::string toolname = "";
2390 InventoryList *mlist = player->inventory.getList("main");
2393 InventoryItem *item = mlist->getItem(item_i);
2394 if(item && (std::string)item->getName() == "ToolItem")
2396 titem = (ToolItem*)item;
2397 toolname = titem->getToolName();
2401 v3f playerpos = player->getPosition();
2402 v3f objpos = obj->getBasePosition();
2403 v3f dir = (objpos - playerpos).normalize();
2405 u16 wear = obj->punch(toolname, dir, player->getName());
2409 bool weared_out = titem->addWear(wear);
2411 mlist->deleteItem(item_i);
2412 SendInventory(player->peer_id);
2417 // Right click, do something with object
2420 actionstream<<player->getName()<<" right clicks object "
2421 <<obj->getId()<<std::endl;
2424 obj->rightClick(srp);
2428 Update player state to client
2430 SendPlayerHP(player);
2431 UpdateCrafting(player->peer_id);
2432 SendInventory(player->peer_id);
2434 else if(command == TOSERVER_GROUND_ACTION)
2442 [3] v3s16 nodepos_undersurface
2443 [9] v3s16 nodepos_abovesurface
2448 2: stop digging (all parameters ignored)
2449 3: digging completed
2451 u8 action = readU8(&data[2]);
2453 p_under.X = readS16(&data[3]);
2454 p_under.Y = readS16(&data[5]);
2455 p_under.Z = readS16(&data[7]);
2457 p_over.X = readS16(&data[9]);
2458 p_over.Y = readS16(&data[11]);
2459 p_over.Z = readS16(&data[13]);
2460 u16 item_i = readU16(&data[15]);
2462 //TODO: Check that target is reasonably close
2470 NOTE: This can be used in the future to check if
2471 somebody is cheating, by checking the timing.
2478 else if(action == 2)
2481 RemoteClient *client = getClient(peer_id);
2482 JMutexAutoLock digmutex(client->m_dig_mutex);
2483 client->m_dig_tool_item = -1;
2488 3: Digging completed
2490 else if(action == 3)
2492 // Mandatory parameter; actually used for nothing
2493 core::map<v3s16, MapBlock*> modified_blocks;
2495 content_t material = CONTENT_IGNORE;
2496 u8 mineral = MINERAL_NONE;
2498 bool cannot_remove_node = false;
2502 MapNode n = m_env->getMap().getNode(p_under);
2504 mineral = n.getMineral(m_nodemgr);
2505 // Get material at position
2506 material = n.getContent();
2507 // If not yet cancelled
2508 if(cannot_remove_node == false)
2510 // If it's not diggable, do nothing
2511 if(m_nodemgr->get(material).diggable == false)
2513 infostream<<"Server: Not finishing digging: "
2514 <<"Node not diggable"
2516 cannot_remove_node = true;
2519 // If not yet cancelled
2520 if(cannot_remove_node == false)
2522 // Get node metadata
2523 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p_under);
2524 if(meta && meta->nodeRemovalDisabled() == true)
2526 infostream<<"Server: Not finishing digging: "
2527 <<"Node metadata disables removal"
2529 cannot_remove_node = true;
2533 catch(InvalidPositionException &e)
2535 infostream<<"Server: Not finishing digging: Node not found."
2536 <<" Adding block to emerge queue."
2538 m_emerge_queue.addBlock(peer_id,
2539 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2540 cannot_remove_node = true;
2543 // Make sure the player is allowed to do it
2544 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2546 infostream<<"Player "<<player->getName()<<" cannot remove node"
2547 <<" because privileges are "<<getPlayerPrivs(player)
2549 cannot_remove_node = true;
2553 If node can't be removed, set block to be re-sent to
2556 if(cannot_remove_node)
2558 infostream<<"Server: Not finishing digging."<<std::endl;
2560 // Client probably has wrong data.
2561 // Set block not sent, so that client will get
2563 infostream<<"Client "<<peer_id<<" tried to dig "
2564 <<"node; but node cannot be removed."
2565 <<" setting MapBlock not sent."<<std::endl;
2566 RemoteClient *client = getClient(peer_id);
2567 v3s16 blockpos = getNodeBlockPos(p_under);
2568 client->SetBlockNotSent(blockpos);
2573 actionstream<<player->getName()<<" digs "<<PP(p_under)
2574 <<", gets material "<<(int)material<<", mineral "
2575 <<(int)mineral<<std::endl;
2578 Send the removal to all close-by players.
2579 - If other player is close, send REMOVENODE
2580 - Otherwise set blocks not sent
2582 core::list<u16> far_players;
2583 sendRemoveNode(p_under, peer_id, &far_players, 30);
2586 Update and send inventory
2589 if(g_settings->getBool("creative_mode") == false)
2594 InventoryList *mlist = player->inventory.getList("main");
2597 InventoryItem *item = mlist->getItem(item_i);
2598 if(item && (std::string)item->getName() == "ToolItem")
2600 ToolItem *titem = (ToolItem*)item;
2601 std::string toolname = titem->getToolName();
2603 // Get digging properties for material and tool
2604 ToolDiggingProperties tp =
2605 m_toolmgr->getDiggingProperties(toolname);
2606 DiggingProperties prop =
2607 getDiggingProperties(material, &tp, m_nodemgr);
2609 if(prop.diggable == false)
2611 infostream<<"Server: WARNING: Player digged"
2612 <<" with impossible material + tool"
2613 <<" combination"<<std::endl;
2616 bool weared_out = titem->addWear(prop.wear);
2620 mlist->deleteItem(item_i);
2626 Add dug item to inventory
2629 InventoryItem *item = NULL;
2631 if(mineral != MINERAL_NONE)
2632 item = getDiggedMineralItem(mineral, this);
2637 const std::string &dug_s = m_nodemgr->get(material).dug_item;
2640 std::istringstream is(dug_s, std::ios::binary);
2641 item = InventoryItem::deSerialize(is, this);
2647 // Add a item to inventory
2648 player->inventory.addItem("main", item);
2651 UpdateCrafting(player->peer_id);
2652 SendInventory(player->peer_id);
2657 if(mineral != MINERAL_NONE)
2658 item = getDiggedMineralItem(mineral, this);
2663 const std::string &extra_dug_s = m_nodemgr->get(material).extra_dug_item;
2664 s32 extra_rarity = m_nodemgr->get(material).extra_dug_item_rarity;
2665 if(extra_dug_s != "" && extra_rarity != 0
2666 && myrand() % extra_rarity == 0)
2668 std::istringstream is(extra_dug_s, std::ios::binary);
2669 item = InventoryItem::deSerialize(is, this);
2675 // Add a item to inventory
2676 player->inventory.addItem("main", item);
2679 UpdateCrafting(player->peer_id);
2680 SendInventory(player->peer_id);
2686 (this takes some time so it is done after the quick stuff)
2689 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
2691 m_env->getMap().removeNodeAndUpdate(p_under, modified_blocks);
2694 Set blocks not sent to far players
2696 for(core::list<u16>::Iterator
2697 i = far_players.begin();
2698 i != far_players.end(); i++)
2701 RemoteClient *client = getClient(peer_id);
2704 client->SetBlocksNotSent(modified_blocks);
2711 else if(action == 1)
2714 InventoryList *ilist = player->inventory.getList("main");
2719 InventoryItem *item = ilist->getItem(item_i);
2721 // If there is no item, it is not possible to add it anywhere
2726 Handle material items
2728 if(std::string("MaterialItem") == item->getName())
2731 // Don't add a node if this is not a free space
2732 MapNode n2 = m_env->getMap().getNode(p_over);
2733 bool no_enough_privs =
2734 ((getPlayerPrivs(player) & PRIV_BUILD)==0);
2736 infostream<<"Player "<<player->getName()<<" cannot add node"
2737 <<" because privileges are "<<getPlayerPrivs(player)
2740 if(m_nodemgr->get(n2).buildable_to == false
2743 // Client probably has wrong data.
2744 // Set block not sent, so that client will get
2746 infostream<<"Client "<<peer_id<<" tried to place"
2747 <<" node in invalid position; setting"
2748 <<" MapBlock not sent."<<std::endl;
2749 RemoteClient *client = getClient(peer_id);
2750 v3s16 blockpos = getNodeBlockPos(p_over);
2751 client->SetBlockNotSent(blockpos);
2755 catch(InvalidPositionException &e)
2757 infostream<<"Server: Ignoring ADDNODE: Node not found"
2758 <<" Adding block to emerge queue."
2760 m_emerge_queue.addBlock(peer_id,
2761 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2765 // Reset build time counter
2766 getClient(peer_id)->m_time_from_building = 0.0;
2769 MaterialItem *mitem = (MaterialItem*)item;
2771 n.setContent(mitem->getMaterial());
2773 actionstream<<player->getName()<<" places material "
2774 <<(int)mitem->getMaterial()
2775 <<" at "<<PP(p_under)<<std::endl;
2777 // Calculate direction for wall mounted stuff
2778 if(m_nodemgr->get(n).wall_mounted)
2779 n.param2 = packDir(p_under - p_over);
2781 // Calculate the direction for furnaces and chests and stuff
2782 if(m_nodemgr->get(n).param_type == CPT_FACEDIR_SIMPLE)
2784 v3f playerpos = player->getPosition();
2785 v3f blockpos = intToFloat(p_over, BS) - playerpos;
2786 blockpos = blockpos.normalize();
2788 if (fabs(blockpos.X) > fabs(blockpos.Z)) {
2802 Send to all close-by players
2804 core::list<u16> far_players;
2805 sendAddNode(p_over, n, 0, &far_players, 30);
2810 InventoryList *ilist = player->inventory.getList("main");
2811 if(g_settings->getBool("creative_mode") == false && ilist)
2813 // Remove from inventory and send inventory
2814 if(mitem->getCount() == 1)
2815 ilist->deleteItem(item_i);
2819 UpdateCrafting(peer_id);
2820 SendInventory(peer_id);
2826 This takes some time so it is done after the quick stuff
2828 core::map<v3s16, MapBlock*> modified_blocks;
2830 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
2832 std::string p_name = std::string(player->getName());
2833 m_env->getMap().addNodeAndUpdate(p_over, n, modified_blocks, p_name);
2836 Set blocks not sent to far players
2838 for(core::list<u16>::Iterator
2839 i = far_players.begin();
2840 i != far_players.end(); i++)
2843 RemoteClient *client = getClient(peer_id);
2846 client->SetBlocksNotSent(modified_blocks);
2850 Calculate special events
2853 /*if(n.d == CONTENT_MESE)
2856 for(s16 z=-1; z<=1; z++)
2857 for(s16 y=-1; y<=1; y++)
2858 for(s16 x=-1; x<=1; x++)
2865 Place other item (not a block)
2869 v3s16 blockpos = getNodeBlockPos(p_over);
2872 Check that the block is loaded so that the item
2873 can properly be added to the static list too
2875 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
2878 infostream<<"Error while placing object: "
2879 "block not found"<<std::endl;
2884 If in creative mode, item dropping is disabled unless
2885 player has build privileges
2887 if(g_settings->getBool("creative_mode") &&
2888 (getPlayerPrivs(player) & PRIV_BUILD) == 0)
2890 infostream<<"Not allowing player to drop item: "
2891 "creative mode and no build privs"<<std::endl;
2895 // Calculate a position for it
2896 v3f pos = intToFloat(p_over, BS);
2898 /*pos.Y -= BS*0.25; // let it drop a bit
2900 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2901 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;*/
2906 ServerActiveObject *obj = item->createSAO(m_env, 0, pos);
2910 infostream<<"WARNING: item resulted in NULL object, "
2911 <<"not placing onto map"
2916 actionstream<<player->getName()<<" places "<<item->getName()
2917 <<" at "<<PP(p_over)<<std::endl;
2919 // Add the object to the environment
2920 m_env->addActiveObject(obj);
2922 infostream<<"Placed object"<<std::endl;
2924 if(g_settings->getBool("creative_mode") == false)
2926 // Delete the right amount of items from the slot
2927 u16 dropcount = item->getDropCount();
2929 // Delete item if all gone
2930 if(item->getCount() <= dropcount)
2932 if(item->getCount() < dropcount)
2933 infostream<<"WARNING: Server: dropped more items"
2934 <<" than the slot contains"<<std::endl;
2936 InventoryList *ilist = player->inventory.getList("main");
2938 // Remove from inventory and send inventory
2939 ilist->deleteItem(item_i);
2941 // Else decrement it
2943 item->remove(dropcount);
2946 UpdateCrafting(peer_id);
2947 SendInventory(peer_id);
2955 Catch invalid actions
2959 infostream<<"WARNING: Server: Invalid action "
2960 <<action<<std::endl;
2964 else if(command == TOSERVER_RELEASE)
2973 infostream<<"TOSERVER_RELEASE ignored"<<std::endl;
2976 else if(command == TOSERVER_SIGNTEXT)
2978 infostream<<"Server: TOSERVER_SIGNTEXT not supported anymore"
2982 else if(command == TOSERVER_SIGNNODETEXT)
2984 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2992 std::string datastring((char*)&data[2], datasize-2);
2993 std::istringstream is(datastring, std::ios_base::binary);
2996 is.read((char*)buf, 6);
2997 v3s16 p = readV3S16(buf);
2998 is.read((char*)buf, 2);
2999 u16 textlen = readU16(buf);
3001 for(u16 i=0; i<textlen; i++)
3003 is.read((char*)buf, 1);
3004 text += (char)buf[0];
3007 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3010 if(meta->typeId() != CONTENT_SIGN_WALL)
3012 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
3013 signmeta->setText(text);
3015 actionstream<<player->getName()<<" writes \""<<text<<"\" to sign "
3016 <<" at "<<PP(p)<<std::endl;
3018 v3s16 blockpos = getNodeBlockPos(p);
3019 MapBlock *block = m_env->getMap().getBlockNoCreateNoEx(blockpos);
3022 block->setChangedFlag();
3025 for(core::map<u16, RemoteClient*>::Iterator
3026 i = m_clients.getIterator();
3027 i.atEnd()==false; i++)
3029 RemoteClient *client = i.getNode()->getValue();
3030 client->SetBlockNotSent(blockpos);
3033 else if(command == TOSERVER_INVENTORY_ACTION)
3035 /*// Ignore inventory changes if in creative mode
3036 if(g_settings->getBool("creative_mode") == true)
3038 infostream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
3042 // Strip command and create a stream
3043 std::string datastring((char*)&data[2], datasize-2);
3044 infostream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
3045 std::istringstream is(datastring, std::ios_base::binary);
3047 InventoryAction *a = InventoryAction::deSerialize(is);
3052 c.current_player = player;
3055 Handle craftresult specially if not in creative mode
3057 bool disable_action = false;
3058 if(a->getType() == IACTION_MOVE
3059 && g_settings->getBool("creative_mode") == false)
3061 IMoveAction *ma = (IMoveAction*)a;
3062 if(ma->to_inv == "current_player" &&
3063 ma->from_inv == "current_player")
3065 InventoryList *rlist = player->inventory.getList("craftresult");
3067 InventoryList *clist = player->inventory.getList("craft");
3069 InventoryList *mlist = player->inventory.getList("main");
3072 Craftresult is no longer preview if something
3075 if(ma->to_list == "craftresult"
3076 && ma->from_list != "craftresult")
3078 // If it currently is a preview, remove
3080 if(player->craftresult_is_preview)
3082 rlist->deleteItem(0);
3084 player->craftresult_is_preview = false;
3087 Crafting takes place if this condition is true.
3089 if(player->craftresult_is_preview &&
3090 ma->from_list == "craftresult")
3092 player->craftresult_is_preview = false;
3093 clist->decrementMaterials(1);
3095 /* Print out action */
3096 InventoryList *list =
3097 player->inventory.getList("craftresult");
3099 InventoryItem *item = list->getItem(0);
3100 std::string itemname = "NULL";
3102 itemname = item->getName();
3103 actionstream<<player->getName()<<" crafts "
3104 <<itemname<<std::endl;
3107 If the craftresult is placed on itself, move it to
3108 main inventory instead of doing the action
3110 if(ma->to_list == "craftresult"
3111 && ma->from_list == "craftresult")
3113 disable_action = true;
3115 InventoryItem *item1 = rlist->changeItem(0, NULL);
3116 mlist->addItem(item1);
3119 // Disallow moving items if not allowed to build
3120 else if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
3124 // if it's a locking chest, only allow the owner or server admins to move items
3125 else if (ma->from_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
3127 Strfnd fn(ma->from_inv);
3128 std::string id0 = fn.next(":");
3129 if(id0 == "nodemeta")
3132 p.X = stoi(fn.next(","));
3133 p.Y = stoi(fn.next(","));
3134 p.Z = stoi(fn.next(","));
3135 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3136 if(meta && meta->typeId() == CONTENT_LOCKABLE_CHEST) {
3137 LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
3138 if (lcm->getOwner() != player->getName())
3143 else if (ma->to_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
3145 Strfnd fn(ma->to_inv);
3146 std::string id0 = fn.next(":");
3147 if(id0 == "nodemeta")
3150 p.X = stoi(fn.next(","));
3151 p.Y = stoi(fn.next(","));
3152 p.Z = stoi(fn.next(","));
3153 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3154 if(meta && meta->typeId() == CONTENT_LOCKABLE_CHEST) {
3155 LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
3156 if (lcm->getOwner() != player->getName())
3163 if(disable_action == false)
3165 // Feed action to player inventory
3173 UpdateCrafting(player->peer_id);
3174 SendInventory(player->peer_id);
3179 infostream<<"TOSERVER_INVENTORY_ACTION: "
3180 <<"InventoryAction::deSerialize() returned NULL"
3184 else if(command == TOSERVER_CHAT_MESSAGE)
3192 std::string datastring((char*)&data[2], datasize-2);
3193 std::istringstream is(datastring, std::ios_base::binary);
3196 is.read((char*)buf, 2);
3197 u16 len = readU16(buf);
3199 std::wstring message;
3200 for(u16 i=0; i<len; i++)
3202 is.read((char*)buf, 2);
3203 message += (wchar_t)readU16(buf);
3206 // Get player name of this client
3207 std::wstring name = narrow_to_wide(player->getName());
3209 // Line to send to players
3211 // Whether to send to the player that sent the line
3212 bool send_to_sender = false;
3213 // Whether to send to other players
3214 bool send_to_others = false;
3216 // Local player gets all privileges regardless of
3217 // what's set on their account.
3218 u64 privs = getPlayerPrivs(player);
3221 if(message[0] == L'/')
3223 size_t strip_size = 1;
3224 if (message[1] == L'#') // support old-style commans
3226 message = message.substr(strip_size);
3228 WStrfnd f1(message);
3229 f1.next(L" "); // Skip over /#whatever
3230 std::wstring paramstring = f1.next(L"");
3232 ServerCommandContext *ctx = new ServerCommandContext(
3233 str_split(message, L' '),
3240 std::wstring reply(processServerCommand(ctx));
3241 send_to_sender = ctx->flags & SEND_TO_SENDER;
3242 send_to_others = ctx->flags & SEND_TO_OTHERS;
3244 if (ctx->flags & SEND_NO_PREFIX)
3247 line += L"Server: " + reply;
3254 if(privs & PRIV_SHOUT)
3260 send_to_others = true;
3264 line += L"Server: You are not allowed to shout";
3265 send_to_sender = true;
3272 actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
3275 Send the message to clients
3277 for(core::map<u16, RemoteClient*>::Iterator
3278 i = m_clients.getIterator();
3279 i.atEnd() == false; i++)
3281 // Get client and check that it is valid
3282 RemoteClient *client = i.getNode()->getValue();
3283 assert(client->peer_id == i.getNode()->getKey());
3284 if(client->serialization_version == SER_FMT_VER_INVALID)
3288 bool sender_selected = (peer_id == client->peer_id);
3289 if(sender_selected == true && send_to_sender == false)
3291 if(sender_selected == false && send_to_others == false)
3294 SendChatMessage(client->peer_id, line);
3298 else if(command == TOSERVER_DAMAGE)
3300 std::string datastring((char*)&data[2], datasize-2);
3301 std::istringstream is(datastring, std::ios_base::binary);
3302 u8 damage = readU8(is);
3304 if(g_settings->getBool("enable_damage"))
3306 actionstream<<player->getName()<<" damaged by "
3307 <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
3310 HandlePlayerHP(player, damage);
3314 SendPlayerHP(player);
3317 else if(command == TOSERVER_PASSWORD)
3320 [0] u16 TOSERVER_PASSWORD
3321 [2] u8[28] old password
3322 [30] u8[28] new password
3325 if(datasize != 2+PASSWORD_SIZE*2)
3327 /*char password[PASSWORD_SIZE];
3328 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3329 password[i] = data[2+i];
3330 password[PASSWORD_SIZE-1] = 0;*/
3332 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3340 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3342 char c = data[2+PASSWORD_SIZE+i];
3348 infostream<<"Server: Client requests a password change from "
3349 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
3351 std::string playername = player->getName();
3353 if(m_authmanager.exists(playername) == false)
3355 infostream<<"Server: playername not found in authmanager"<<std::endl;
3356 // Wrong old password supplied!!
3357 SendChatMessage(peer_id, L"playername not found in authmanager");
3361 std::string checkpwd = m_authmanager.getPassword(playername);
3363 if(oldpwd != checkpwd)
3365 infostream<<"Server: invalid old password"<<std::endl;
3366 // Wrong old password supplied!!
3367 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
3371 actionstream<<player->getName()<<" changes password"<<std::endl;
3373 m_authmanager.setPassword(playername, newpwd);
3375 infostream<<"Server: password change successful for "<<playername
3377 SendChatMessage(peer_id, L"Password change successful");
3379 else if(command == TOSERVER_PLAYERITEM)
3384 u16 item = readU16(&data[2]);
3385 player->wieldItem(item);
3386 SendWieldedItem(player);
3388 else if(command == TOSERVER_RESPAWN)
3393 RespawnPlayer(player);
3395 actionstream<<player->getName()<<" respawns at "
3396 <<PP(player->getPosition()/BS)<<std::endl;
3400 infostream<<"Server::ProcessData(): Ignoring "
3401 "unknown command "<<command<<std::endl;
3405 catch(SendFailedException &e)
3407 errorstream<<"Server::ProcessData(): SendFailedException: "
3413 void Server::onMapEditEvent(MapEditEvent *event)
3415 //infostream<<"Server::onMapEditEvent()"<<std::endl;
3416 if(m_ignore_map_edit_events)
3418 MapEditEvent *e = event->clone();
3419 m_unsent_map_edit_queue.push_back(e);
3422 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3424 if(id == "current_player")
3426 assert(c->current_player);
3427 return &(c->current_player->inventory);
3431 std::string id0 = fn.next(":");
3433 if(id0 == "nodemeta")
3436 p.X = stoi(fn.next(","));
3437 p.Y = stoi(fn.next(","));
3438 p.Z = stoi(fn.next(","));
3439 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3441 return meta->getInventory();
3442 infostream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3443 <<"no metadata found"<<std::endl;
3447 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3450 void Server::inventoryModified(InventoryContext *c, std::string id)
3452 if(id == "current_player")
3454 assert(c->current_player);
3456 UpdateCrafting(c->current_player->peer_id);
3457 SendInventory(c->current_player->peer_id);
3462 std::string id0 = fn.next(":");
3464 if(id0 == "nodemeta")
3467 p.X = stoi(fn.next(","));
3468 p.Y = stoi(fn.next(","));
3469 p.Z = stoi(fn.next(","));
3470 v3s16 blockpos = getNodeBlockPos(p);
3472 NodeMetadata *meta = m_env->getMap().getNodeMetadata(p);
3474 meta->inventoryModified();
3476 for(core::map<u16, RemoteClient*>::Iterator
3477 i = m_clients.getIterator();
3478 i.atEnd()==false; i++)
3480 RemoteClient *client = i.getNode()->getValue();
3481 client->SetBlockNotSent(blockpos);
3487 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3490 core::list<PlayerInfo> Server::getPlayerInfo()
3492 DSTACK(__FUNCTION_NAME);
3493 JMutexAutoLock envlock(m_env_mutex);
3494 JMutexAutoLock conlock(m_con_mutex);
3496 core::list<PlayerInfo> list;
3498 core::list<Player*> players = m_env->getPlayers();
3500 core::list<Player*>::Iterator i;
3501 for(i = players.begin();
3502 i != players.end(); i++)
3506 Player *player = *i;
3509 // Copy info from connection to info struct
3510 info.id = player->peer_id;
3511 info.address = m_con.GetPeerAddress(player->peer_id);
3512 info.avg_rtt = m_con.GetPeerAvgRTT(player->peer_id);
3514 catch(con::PeerNotFoundException &e)
3516 // Set dummy peer info
3518 info.address = Address(0,0,0,0,0);
3522 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3523 info.position = player->getPosition();
3525 list.push_back(info);
3532 void Server::peerAdded(con::Peer *peer)
3534 DSTACK(__FUNCTION_NAME);
3535 infostream<<"Server::peerAdded(): peer->id="
3536 <<peer->id<<std::endl;
3539 c.type = PEER_ADDED;
3540 c.peer_id = peer->id;
3542 m_peer_change_queue.push_back(c);
3545 void Server::deletingPeer(con::Peer *peer, bool timeout)
3547 DSTACK(__FUNCTION_NAME);
3548 infostream<<"Server::deletingPeer(): peer->id="
3549 <<peer->id<<", timeout="<<timeout<<std::endl;
3552 c.type = PEER_REMOVED;
3553 c.peer_id = peer->id;
3554 c.timeout = timeout;
3555 m_peer_change_queue.push_back(c);
3562 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3564 DSTACK(__FUNCTION_NAME);
3565 std::ostringstream os(std::ios_base::binary);
3567 writeU16(os, TOCLIENT_HP);
3571 std::string s = os.str();
3572 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3574 con.Send(peer_id, 0, data, true);
3577 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3578 const std::wstring &reason)
3580 DSTACK(__FUNCTION_NAME);
3581 std::ostringstream os(std::ios_base::binary);
3583 writeU16(os, TOCLIENT_ACCESS_DENIED);
3584 os<<serializeWideString(reason);
3587 std::string s = os.str();
3588 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3590 con.Send(peer_id, 0, data, true);
3593 void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
3594 bool set_camera_point_target, v3f camera_point_target)
3596 DSTACK(__FUNCTION_NAME);
3597 std::ostringstream os(std::ios_base::binary);
3599 writeU16(os, TOCLIENT_DEATHSCREEN);
3600 writeU8(os, set_camera_point_target);
3601 writeV3F1000(os, camera_point_target);
3604 std::string s = os.str();
3605 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3607 con.Send(peer_id, 0, data, true);
3610 void Server::SendToolDef(con::Connection &con, u16 peer_id,
3611 IToolDefManager *tooldef)
3613 DSTACK(__FUNCTION_NAME);
3614 std::ostringstream os(std::ios_base::binary);
3618 u32 length of the next item
3619 serialized ToolDefManager
3621 writeU16(os, TOCLIENT_TOOLDEF);
3622 std::ostringstream tmp_os(std::ios::binary);
3623 tooldef->serialize(tmp_os);
3624 os<<serializeLongString(tmp_os.str());
3627 std::string s = os.str();
3628 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3630 con.Send(peer_id, 0, data, true);
3634 Non-static send methods
3637 void Server::SendObjectData(float dtime)
3639 DSTACK(__FUNCTION_NAME);
3641 core::map<v3s16, bool> stepped_blocks;
3643 for(core::map<u16, RemoteClient*>::Iterator
3644 i = m_clients.getIterator();
3645 i.atEnd() == false; i++)
3647 u16 peer_id = i.getNode()->getKey();
3648 RemoteClient *client = i.getNode()->getValue();
3649 assert(client->peer_id == peer_id);
3651 if(client->serialization_version == SER_FMT_VER_INVALID)
3654 client->SendObjectData(this, dtime, stepped_blocks);
3658 void Server::SendPlayerInfos()
3660 DSTACK(__FUNCTION_NAME);
3662 //JMutexAutoLock envlock(m_env_mutex);
3664 // Get connected players
3665 core::list<Player*> players = m_env->getPlayers(true);
3667 u32 player_count = players.getSize();
3668 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3670 SharedBuffer<u8> data(datasize);
3671 writeU16(&data[0], TOCLIENT_PLAYERINFO);
3674 core::list<Player*>::Iterator i;
3675 for(i = players.begin();
3676 i != players.end(); i++)
3678 Player *player = *i;
3680 /*infostream<<"Server sending player info for player with "
3681 "peer_id="<<player->peer_id<<std::endl;*/
3683 writeU16(&data[start], player->peer_id);
3684 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3685 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3686 start += 2+PLAYERNAME_SIZE;
3689 //JMutexAutoLock conlock(m_con_mutex);
3692 m_con.SendToAll(0, data, true);
3695 void Server::SendInventory(u16 peer_id)
3697 DSTACK(__FUNCTION_NAME);
3699 Player* player = m_env->getPlayer(peer_id);
3706 std::ostringstream os;
3707 //os.imbue(std::locale("C"));
3709 player->inventory.serialize(os);
3711 std::string s = os.str();
3713 SharedBuffer<u8> data(s.size()+2);
3714 writeU16(&data[0], TOCLIENT_INVENTORY);
3715 memcpy(&data[2], s.c_str(), s.size());
3718 m_con.Send(peer_id, 0, data, true);
3721 std::string getWieldedItemString(const Player *player)
3723 const InventoryItem *item = player->getWieldItem();
3725 return std::string("");
3726 std::ostringstream os(std::ios_base::binary);
3727 item->serialize(os);
3731 void Server::SendWieldedItem(const Player* player)
3733 DSTACK(__FUNCTION_NAME);
3737 std::ostringstream os(std::ios_base::binary);
3739 writeU16(os, TOCLIENT_PLAYERITEM);
3741 writeU16(os, player->peer_id);
3742 os<<serializeString(getWieldedItemString(player));
3745 std::string s = os.str();
3746 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3748 m_con.SendToAll(0, data, true);
3751 void Server::SendPlayerItems()
3753 DSTACK(__FUNCTION_NAME);
3755 std::ostringstream os(std::ios_base::binary);
3756 core::list<Player *> players = m_env->getPlayers(true);
3758 writeU16(os, TOCLIENT_PLAYERITEM);
3759 writeU16(os, players.size());
3760 core::list<Player *>::Iterator i;
3761 for(i = players.begin(); i != players.end(); ++i)
3764 writeU16(os, p->peer_id);
3765 os<<serializeString(getWieldedItemString(p));
3769 std::string s = os.str();
3770 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3772 m_con.SendToAll(0, data, true);
3775 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3777 DSTACK(__FUNCTION_NAME);
3779 std::ostringstream os(std::ios_base::binary);
3783 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3784 os.write((char*)buf, 2);
3787 writeU16(buf, message.size());
3788 os.write((char*)buf, 2);
3791 for(u32 i=0; i<message.size(); i++)
3795 os.write((char*)buf, 2);
3799 std::string s = os.str();
3800 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3802 m_con.Send(peer_id, 0, data, true);
3805 void Server::BroadcastChatMessage(const std::wstring &message)
3807 for(core::map<u16, RemoteClient*>::Iterator
3808 i = m_clients.getIterator();
3809 i.atEnd() == false; i++)
3811 // Get client and check that it is valid
3812 RemoteClient *client = i.getNode()->getValue();
3813 assert(client->peer_id == i.getNode()->getKey());
3814 if(client->serialization_version == SER_FMT_VER_INVALID)
3817 SendChatMessage(client->peer_id, message);
3821 void Server::SendPlayerHP(Player *player)
3823 SendHP(m_con, player->peer_id, player->hp);
3826 void Server::SendMovePlayer(Player *player)
3828 DSTACK(__FUNCTION_NAME);
3829 std::ostringstream os(std::ios_base::binary);
3831 writeU16(os, TOCLIENT_MOVE_PLAYER);
3832 writeV3F1000(os, player->getPosition());
3833 writeF1000(os, player->getPitch());
3834 writeF1000(os, player->getYaw());
3837 v3f pos = player->getPosition();
3838 f32 pitch = player->getPitch();
3839 f32 yaw = player->getYaw();
3840 infostream<<"Server sending TOCLIENT_MOVE_PLAYER"
3841 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3848 std::string s = os.str();
3849 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3851 m_con.Send(player->peer_id, 0, data, true);
3854 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3855 core::list<u16> *far_players, float far_d_nodes)
3857 float maxd = far_d_nodes*BS;
3858 v3f p_f = intToFloat(p, BS);
3862 SharedBuffer<u8> reply(replysize);
3863 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3864 writeS16(&reply[2], p.X);
3865 writeS16(&reply[4], p.Y);
3866 writeS16(&reply[6], p.Z);
3868 for(core::map<u16, RemoteClient*>::Iterator
3869 i = m_clients.getIterator();
3870 i.atEnd() == false; i++)
3872 // Get client and check that it is valid
3873 RemoteClient *client = i.getNode()->getValue();
3874 assert(client->peer_id == i.getNode()->getKey());
3875 if(client->serialization_version == SER_FMT_VER_INVALID)
3878 // Don't send if it's the same one
3879 if(client->peer_id == ignore_id)
3885 Player *player = m_env->getPlayer(client->peer_id);
3888 // If player is far away, only set modified blocks not sent
3889 v3f player_pos = player->getPosition();
3890 if(player_pos.getDistanceFrom(p_f) > maxd)
3892 far_players->push_back(client->peer_id);
3899 m_con.Send(client->peer_id, 0, reply, true);
3903 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3904 core::list<u16> *far_players, float far_d_nodes)
3906 float maxd = far_d_nodes*BS;
3907 v3f p_f = intToFloat(p, BS);
3909 for(core::map<u16, RemoteClient*>::Iterator
3910 i = m_clients.getIterator();
3911 i.atEnd() == false; i++)
3913 // Get client and check that it is valid
3914 RemoteClient *client = i.getNode()->getValue();
3915 assert(client->peer_id == i.getNode()->getKey());
3916 if(client->serialization_version == SER_FMT_VER_INVALID)
3919 // Don't send if it's the same one
3920 if(client->peer_id == ignore_id)
3926 Player *player = m_env->getPlayer(client->peer_id);
3929 // If player is far away, only set modified blocks not sent
3930 v3f player_pos = player->getPosition();
3931 if(player_pos.getDistanceFrom(p_f) > maxd)
3933 far_players->push_back(client->peer_id);
3940 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3941 SharedBuffer<u8> reply(replysize);
3942 writeU16(&reply[0], TOCLIENT_ADDNODE);
3943 writeS16(&reply[2], p.X);
3944 writeS16(&reply[4], p.Y);
3945 writeS16(&reply[6], p.Z);
3946 n.serialize(&reply[8], client->serialization_version);
3949 m_con.Send(client->peer_id, 0, reply, true);
3953 void Server::setBlockNotSent(v3s16 p)
3955 for(core::map<u16, RemoteClient*>::Iterator
3956 i = m_clients.getIterator();
3957 i.atEnd()==false; i++)
3959 RemoteClient *client = i.getNode()->getValue();
3960 client->SetBlockNotSent(p);
3964 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3966 DSTACK(__FUNCTION_NAME);
3968 v3s16 p = block->getPos();
3972 bool completely_air = true;
3973 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3974 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3975 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3977 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
3979 completely_air = false;
3980 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
3985 infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
3987 infostream<<"[completely air] ";
3988 infostream<<std::endl;
3992 Create a packet with the block in the right format
3995 std::ostringstream os(std::ios_base::binary);
3996 block->serialize(os, ver);
3997 std::string s = os.str();
3998 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
4000 u32 replysize = 8 + blockdata.getSize();
4001 SharedBuffer<u8> reply(replysize);
4002 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
4003 writeS16(&reply[2], p.X);
4004 writeS16(&reply[4], p.Y);
4005 writeS16(&reply[6], p.Z);
4006 memcpy(&reply[8], *blockdata, blockdata.getSize());
4008 /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
4009 <<": \tpacket size: "<<replysize<<std::endl;*/
4014 m_con.Send(peer_id, 1, reply, true);
4017 void Server::SendBlocks(float dtime)
4019 DSTACK(__FUNCTION_NAME);
4021 JMutexAutoLock envlock(m_env_mutex);
4022 JMutexAutoLock conlock(m_con_mutex);
4024 //TimeTaker timer("Server::SendBlocks");
4026 core::array<PrioritySortedBlockTransfer> queue;
4028 s32 total_sending = 0;
4031 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
4033 for(core::map<u16, RemoteClient*>::Iterator
4034 i = m_clients.getIterator();
4035 i.atEnd() == false; i++)
4037 RemoteClient *client = i.getNode()->getValue();
4038 assert(client->peer_id == i.getNode()->getKey());
4040 total_sending += client->SendingCount();
4042 if(client->serialization_version == SER_FMT_VER_INVALID)
4045 client->GetNextBlocks(this, dtime, queue);
4050 // Lowest priority number comes first.
4051 // Lowest is most important.
4054 for(u32 i=0; i<queue.size(); i++)
4056 //TODO: Calculate limit dynamically
4057 if(total_sending >= g_settings->getS32
4058 ("max_simultaneous_block_sends_server_total"))
4061 PrioritySortedBlockTransfer q = queue[i];
4063 MapBlock *block = NULL;
4066 block = m_env->getMap().getBlockNoCreate(q.pos);
4068 catch(InvalidPositionException &e)
4073 RemoteClient *client = getClient(q.peer_id);
4075 SendBlockNoLock(q.peer_id, block, client->serialization_version);
4077 client->SentBlock(q.pos);
4087 void Server::HandlePlayerHP(Player *player, s16 damage)
4089 if(player->hp > damage)
4091 player->hp -= damage;
4092 SendPlayerHP(player);
4096 infostream<<"Server::HandlePlayerHP(): Player "
4097 <<player->getName()<<" dies"<<std::endl;
4101 //TODO: Throw items around
4103 // Handle players that are not connected
4104 if(player->peer_id == PEER_ID_INEXISTENT){
4105 RespawnPlayer(player);
4109 SendPlayerHP(player);
4111 RemoteClient *client = getClient(player->peer_id);
4112 if(client->net_proto_version >= 3)
4114 SendDeathscreen(m_con, player->peer_id, false, v3f(0,0,0));
4118 RespawnPlayer(player);
4123 void Server::RespawnPlayer(Player *player)
4125 v3f pos = findSpawnPos(m_env->getServerMap());
4126 player->setPosition(pos);
4128 SendMovePlayer(player);
4129 SendPlayerHP(player);
4132 void Server::UpdateCrafting(u16 peer_id)
4134 DSTACK(__FUNCTION_NAME);
4136 Player* player = m_env->getPlayer(peer_id);
4140 Calculate crafting stuff
4142 if(g_settings->getBool("creative_mode") == false)
4144 InventoryList *clist = player->inventory.getList("craft");
4145 InventoryList *rlist = player->inventory.getList("craftresult");
4147 if(rlist && rlist->getUsedSlots() == 0)
4148 player->craftresult_is_preview = true;
4150 if(rlist && player->craftresult_is_preview)
4152 rlist->clearItems();
4154 if(clist && rlist && player->craftresult_is_preview)
4156 InventoryItem *items[9];
4157 for(u16 i=0; i<9; i++)
4159 items[i] = clist->getItem(i);
4162 // Get result of crafting grid
4163 InventoryItem *result = craft_get_result(items, this);
4165 rlist->addItem(result);
4168 } // if creative_mode == false
4171 RemoteClient* Server::getClient(u16 peer_id)
4173 DSTACK(__FUNCTION_NAME);
4174 //JMutexAutoLock lock(m_con_mutex);
4175 core::map<u16, RemoteClient*>::Node *n;
4176 n = m_clients.find(peer_id);
4177 // A client should exist for all peers
4179 return n->getValue();
4182 std::wstring Server::getStatusString()
4184 std::wostringstream os(std::ios_base::binary);
4187 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4189 os<<L", uptime="<<m_uptime.get();
4190 // Information about clients
4192 for(core::map<u16, RemoteClient*>::Iterator
4193 i = m_clients.getIterator();
4194 i.atEnd() == false; i++)
4196 // Get client and check that it is valid
4197 RemoteClient *client = i.getNode()->getValue();
4198 assert(client->peer_id == i.getNode()->getKey());
4199 if(client->serialization_version == SER_FMT_VER_INVALID)
4202 Player *player = m_env->getPlayer(client->peer_id);
4203 // Get name of player
4204 std::wstring name = L"unknown";
4206 name = narrow_to_wide(player->getName());
4207 // Add name to information string
4211 if(((ServerMap*)(&m_env->getMap()))->isSavingEnabled() == false)
4212 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4213 if(g_settings->get("motd") != "")
4214 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4218 // Saves g_settings to configpath given at initialization
4219 void Server::saveConfig()
4221 if(m_configpath != "")
4222 g_settings->updateConfigFile(m_configpath.c_str());
4225 void Server::notifyPlayer(const char *name, const std::wstring msg)
4227 Player *player = m_env->getPlayer(name);
4230 SendChatMessage(player->peer_id, std::wstring(L"Server: -!- ")+msg);
4233 void Server::notifyPlayers(const std::wstring msg)
4235 BroadcastChatMessage(msg);
4238 // IGameDef interface
4240 IToolDefManager* Server::getToolDefManager()
4244 INodeDefManager* Server::getNodeDefManager()
4248 ITextureSource* Server::getTextureSource()
4253 IWritableToolDefManager* Server::getWritableToolDefManager()
4257 IWritableNodeDefManager* Server::getWritableNodeDefManager()
4262 v3f findSpawnPos(ServerMap &map)
4264 //return v3f(50,50,50)*BS;
4269 nodepos = v2s16(0,0);
4274 // Try to find a good place a few times
4275 for(s32 i=0; i<1000; i++)
4278 // We're going to try to throw the player to this position
4279 v2s16 nodepos2d = v2s16(-range + (myrand()%(range*2)),
4280 -range + (myrand()%(range*2)));
4281 //v2s16 sectorpos = getNodeSectorPos(nodepos2d);
4282 // Get ground height at point (fallbacks to heightmap function)
4283 s16 groundheight = map.findGroundLevel(nodepos2d);
4284 // Don't go underwater
4285 if(groundheight < WATER_LEVEL)
4287 //infostream<<"-> Underwater"<<std::endl;
4290 // Don't go to high places
4291 if(groundheight > WATER_LEVEL + 4)
4293 //infostream<<"-> Underwater"<<std::endl;
4297 nodepos = v3s16(nodepos2d.X, groundheight-2, nodepos2d.Y);
4298 bool is_good = false;
4300 for(s32 i=0; i<10; i++){
4301 v3s16 blockpos = getNodeBlockPos(nodepos);
4302 map.emergeBlock(blockpos, true);
4303 MapNode n = map.getNodeNoEx(nodepos);
4304 if(n.getContent() == CONTENT_AIR){
4315 // Found a good place
4316 //infostream<<"Searched through "<<i<<" places."<<std::endl;
4322 return intToFloat(nodepos, BS);
4325 Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id)
4328 Try to get an existing player
4330 Player *player = m_env->getPlayer(name);
4333 // If player is already connected, cancel
4334 if(player->peer_id != 0)
4336 infostream<<"emergePlayer(): Player already connected"<<std::endl;
4341 player->peer_id = peer_id;
4343 // Reset inventory to creative if in creative mode
4344 if(g_settings->getBool("creative_mode"))
4346 // Warning: double code below
4347 // Backup actual inventory
4348 player->inventory_backup = new Inventory();
4349 *(player->inventory_backup) = player->inventory;
4350 // Set creative inventory
4351 craft_set_creative_inventory(player, this);
4358 If player with the wanted peer_id already exists, cancel.
4360 if(m_env->getPlayer(peer_id) != NULL)
4362 infostream<<"emergePlayer(): Player with wrong name but same"
4363 " peer_id already exists"<<std::endl;
4371 // Add authentication stuff
4372 m_authmanager.add(name);
4373 m_authmanager.setPassword(name, password);
4374 m_authmanager.setPrivs(name,
4375 stringToPrivs(g_settings->get("default_privs")));
4381 infostream<<"Server: Finding spawn place for player \""
4382 <<name<<"\""<<std::endl;
4384 v3f pos = findSpawnPos(m_env->getServerMap());
4386 player = new ServerRemotePlayer(m_env, pos, peer_id, name);
4389 Add player to environment
4392 m_env->addPlayer(player);
4395 Add stuff to inventory
4398 if(g_settings->getBool("creative_mode"))
4400 // Warning: double code above
4401 // Backup actual inventory
4402 player->inventory_backup = new Inventory();
4403 *(player->inventory_backup) = player->inventory;
4404 // Set creative inventory
4405 craft_set_creative_inventory(player, this);
4407 else if(g_settings->getBool("give_initial_stuff"))
4409 craft_give_initial_stuff(player, this);
4414 } // create new player
4417 void Server::handlePeerChange(PeerChange &c)
4419 JMutexAutoLock envlock(m_env_mutex);
4420 JMutexAutoLock conlock(m_con_mutex);
4422 if(c.type == PEER_ADDED)
4429 core::map<u16, RemoteClient*>::Node *n;
4430 n = m_clients.find(c.peer_id);
4431 // The client shouldn't already exist
4435 RemoteClient *client = new RemoteClient();
4436 client->peer_id = c.peer_id;
4437 m_clients.insert(client->peer_id, client);
4440 else if(c.type == PEER_REMOVED)
4447 core::map<u16, RemoteClient*>::Node *n;
4448 n = m_clients.find(c.peer_id);
4449 // The client should exist
4453 Mark objects to be not known by the client
4455 RemoteClient *client = n->getValue();
4457 for(core::map<u16, bool>::Iterator
4458 i = client->m_known_objects.getIterator();
4459 i.atEnd()==false; i++)
4462 u16 id = i.getNode()->getKey();
4463 ServerActiveObject* obj = m_env->getActiveObject(id);
4465 if(obj && obj->m_known_by_count > 0)
4466 obj->m_known_by_count--;
4469 // Collect information about leaving in chat
4470 std::wstring message;
4472 Player *player = m_env->getPlayer(c.peer_id);
4475 std::wstring name = narrow_to_wide(player->getName());
4478 message += L" left game";
4480 message += L" (timed out)";
4486 m_env->removePlayer(c.peer_id);
4489 // Set player client disconnected
4491 Player *player = m_env->getPlayer(c.peer_id);
4493 player->peer_id = 0;
4500 std::ostringstream os(std::ios_base::binary);
4501 for(core::map<u16, RemoteClient*>::Iterator
4502 i = m_clients.getIterator();
4503 i.atEnd() == false; i++)
4505 RemoteClient *client = i.getNode()->getValue();
4506 assert(client->peer_id == i.getNode()->getKey());
4507 if(client->serialization_version == SER_FMT_VER_INVALID)
4510 Player *player = m_env->getPlayer(client->peer_id);
4513 // Get name of player
4514 os<<player->getName()<<" ";
4517 actionstream<<player->getName()<<" "
4518 <<(c.timeout?"times out.":"leaves game.")
4519 <<" List of players: "
4520 <<os.str()<<std::endl;
4525 delete m_clients[c.peer_id];
4526 m_clients.remove(c.peer_id);
4528 // Send player info to all remaining clients
4531 // Send leave chat message to all remaining clients
4532 BroadcastChatMessage(message);
4541 void Server::handlePeerChanges()
4543 while(m_peer_change_queue.size() > 0)
4545 PeerChange c = m_peer_change_queue.pop_front();
4547 infostream<<"Server: Handling peer change: "
4548 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4551 handlePeerChange(c);
4555 u64 Server::getPlayerPrivs(Player *player)
4559 std::string playername = player->getName();
4560 // Local player gets all privileges regardless of
4561 // what's set on their account.
4562 if(g_settings->get("name") == playername)
4568 return getPlayerAuthPrivs(playername);
4572 void dedicated_server_loop(Server &server, bool &kill)
4574 DSTACK(__FUNCTION_NAME);
4576 infostream<<DTIME<<std::endl;
4577 infostream<<"========================"<<std::endl;
4578 infostream<<"Running dedicated server"<<std::endl;
4579 infostream<<"========================"<<std::endl;
4580 infostream<<std::endl;
4582 IntervalLimiter m_profiler_interval;
4586 // This is kind of a hack but can be done like this
4587 // because server.step() is very light
4589 ScopeProfiler sp(g_profiler, "dedicated server sleep");
4594 if(server.getShutdownRequested() || kill)
4596 infostream<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4603 float profiler_print_interval =
4604 g_settings->getFloat("profiler_print_interval");
4605 if(profiler_print_interval != 0)
4607 if(m_profiler_interval.step(0.030, profiler_print_interval))
4609 infostream<<"Profiler:"<<std::endl;
4610 g_profiler->print(infostream);
4611 g_profiler->clear();
4618 static int counter = 0;
4624 core::list<PlayerInfo> list = server.getPlayerInfo();
4625 core::list<PlayerInfo>::Iterator i;
4626 static u32 sum_old = 0;
4627 u32 sum = PIChecksum(list);
4630 infostream<<DTIME<<"Player info:"<<std::endl;
4631 for(i=list.begin(); i!=list.end(); i++)
4633 i->PrintLine(&infostream);