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 #define PP(x) "("<<(x).X<<","<<(x).Y<<","<<(x).Z<<")"
45 #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
47 class MapEditEventIgnorer
50 MapEditEventIgnorer(bool *flag):
59 ~MapEditEventIgnorer()
72 void * ServerThread::Thread()
76 log_register_thread("ServerThread");
78 DSTACK(__FUNCTION_NAME);
80 BEGIN_DEBUG_EXCEPTION_HANDLER
85 //TimeTaker timer("AsyncRunStep() + Receive()");
88 //TimeTaker timer("AsyncRunStep()");
89 m_server->AsyncRunStep();
92 //infostream<<"Running m_server->Receive()"<<std::endl;
95 catch(con::NoIncomingDataException &e)
98 catch(con::PeerNotFoundException &e)
100 infostream<<"Server: PeerNotFoundException"<<std::endl;
104 END_DEBUG_EXCEPTION_HANDLER(errorstream)
109 void * EmergeThread::Thread()
113 log_register_thread("EmergeThread");
115 DSTACK(__FUNCTION_NAME);
117 BEGIN_DEBUG_EXCEPTION_HANDLER
119 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
122 Get block info from queue, emerge them and send them
125 After queue is empty, exit.
129 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
133 SharedPtr<QueuedBlockEmerge> q(qptr);
139 Do not generate over-limit
141 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
142 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
143 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
144 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
145 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
146 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
149 //infostream<<"EmergeThread::Thread(): running"<<std::endl;
151 //TimeTaker timer("block emerge");
154 Try to emerge it from somewhere.
156 If it is only wanted as optional, only loading from disk
161 Check if any peer wants it as non-optional. In that case it
164 Also decrement the emerge queue count in clients.
167 bool only_from_disk = true;
170 core::map<u16, u8>::Iterator i;
171 for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
173 //u16 peer_id = i.getNode()->getKey();
176 u8 flags = i.getNode()->getValue();
177 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
178 only_from_disk = false;
183 if(enable_mapgen_debug_info)
184 infostream<<"EmergeThread: p="
185 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
186 <<"only_from_disk="<<only_from_disk<<std::endl;
188 ServerMap &map = ((ServerMap&)m_server->m_env.getMap());
190 //core::map<v3s16, MapBlock*> changed_blocks;
191 //core::map<v3s16, MapBlock*> lighting_invalidated_blocks;
193 MapBlock *block = NULL;
194 bool got_block = true;
195 core::map<v3s16, MapBlock*> modified_blocks;
198 Fetch block from map or generate a single block
201 JMutexAutoLock envlock(m_server->m_env_mutex);
203 // Load sector if it isn't loaded
204 if(map.getSectorNoGenerateNoEx(p2d) == NULL)
205 //map.loadSectorFull(p2d);
206 map.loadSectorMeta(p2d);
208 block = map.getBlockNoCreateNoEx(p);
209 if(!block || block->isDummy() || !block->isGenerated())
211 if(enable_mapgen_debug_info)
212 infostream<<"EmergeThread: not in memory, loading"<<std::endl;
214 // Get, load or create sector
215 /*ServerMapSector *sector =
216 (ServerMapSector*)map.createSector(p2d);*/
218 // Load/generate block
220 /*block = map.emergeBlock(p, sector, changed_blocks,
221 lighting_invalidated_blocks);*/
223 block = map.loadBlock(p);
225 if(only_from_disk == false)
227 if(block == NULL || block->isGenerated() == false)
229 if(enable_mapgen_debug_info)
230 infostream<<"EmergeThread: generating"<<std::endl;
231 block = map.generateBlock(p, modified_blocks);
235 if(enable_mapgen_debug_info)
236 infostream<<"EmergeThread: ended up with: "
237 <<analyze_block(block)<<std::endl;
246 Ignore map edit events, they will not need to be
247 sent to anybody because the block hasn't been sent
250 MapEditEventIgnorer ign(&m_server->m_ignore_map_edit_events);
252 // Activate objects and stuff
253 m_server->m_env.activateBlock(block, 3600);
258 /*if(block->getLightingExpired()){
259 lighting_invalidated_blocks[block->getPos()] = block;
263 // TODO: Some additional checking and lighting updating,
268 JMutexAutoLock envlock(m_server->m_env_mutex);
273 Collect a list of blocks that have been modified in
274 addition to the fetched one.
278 if(lighting_invalidated_blocks.size() > 0)
280 /*infostream<<"lighting "<<lighting_invalidated_blocks.size()
281 <<" blocks"<<std::endl;*/
283 // 50-100ms for single block generation
284 //TimeTaker timer("** EmergeThread updateLighting");
286 // Update lighting without locking the environment mutex,
287 // add modified blocks to changed blocks
288 map.updateLighting(lighting_invalidated_blocks, modified_blocks);
291 // Add all from changed_blocks to modified_blocks
292 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
293 i.atEnd() == false; i++)
295 MapBlock *block = i.getNode()->getValue();
296 modified_blocks.insert(block->getPos(), block);
300 // If we got no block, there should be no invalidated blocks
303 //assert(lighting_invalidated_blocks.size() == 0);
309 Set sent status of modified blocks on clients
312 // NOTE: Server's clients are also behind the connection mutex
313 JMutexAutoLock lock(m_server->m_con_mutex);
316 Add the originally fetched block to the modified list
320 modified_blocks.insert(p, block);
324 Set the modified blocks unsent for all the clients
327 for(core::map<u16, RemoteClient*>::Iterator
328 i = m_server->m_clients.getIterator();
329 i.atEnd() == false; i++)
331 RemoteClient *client = i.getNode()->getValue();
333 if(modified_blocks.size() > 0)
335 // Remove block from sent history
336 client->SetBlocksNotSent(modified_blocks);
342 END_DEBUG_EXCEPTION_HANDLER(errorstream)
347 void RemoteClient::GetNextBlocks(Server *server, float dtime,
348 core::array<PrioritySortedBlockTransfer> &dest)
350 DSTACK(__FUNCTION_NAME);
353 TimeTaker timer("RemoteClient::GetNextBlocks", &timer_result);*/
356 m_nothing_to_send_pause_timer -= dtime;
358 if(m_nothing_to_send_pause_timer >= 0)
361 m_nearest_unsent_reset_timer = 0;
365 // Won't send anything if already sending
366 if(m_blocks_sending.size() >= g_settings->getU16
367 ("max_simultaneous_block_sends_per_client"))
369 //infostream<<"Not sending any blocks, Queue full."<<std::endl;
373 //TimeTaker timer("RemoteClient::GetNextBlocks");
375 Player *player = server->m_env.getPlayer(peer_id);
377 assert(player != NULL);
379 v3f playerpos = player->getPosition();
380 v3f playerspeed = player->getSpeed();
381 v3f playerspeeddir(0,0,0);
382 if(playerspeed.getLength() > 1.0*BS)
383 playerspeeddir = playerspeed / playerspeed.getLength();
384 // Predict to next block
385 v3f playerpos_predicted = playerpos + playerspeeddir*MAP_BLOCKSIZE*BS;
387 v3s16 center_nodepos = floatToInt(playerpos_predicted, BS);
389 v3s16 center = getNodeBlockPos(center_nodepos);
391 // Camera position and direction
392 v3f camera_pos = player->getEyePosition();
393 v3f camera_dir = v3f(0,0,1);
394 camera_dir.rotateYZBy(player->getPitch());
395 camera_dir.rotateXZBy(player->getYaw());
397 /*infostream<<"camera_dir=("<<camera_dir.X<<","<<camera_dir.Y<<","
398 <<camera_dir.Z<<")"<<std::endl;*/
401 Get the starting value of the block finder radius.
404 if(m_last_center != center)
406 m_nearest_unsent_d = 0;
407 m_last_center = center;
410 /*infostream<<"m_nearest_unsent_reset_timer="
411 <<m_nearest_unsent_reset_timer<<std::endl;*/
413 // This has to be incremented only when the nothing to send pause
415 m_nearest_unsent_reset_timer += dtime;
417 // Reset periodically to avoid possible bugs or other mishaps
418 if(m_nearest_unsent_reset_timer > 10.0)
420 m_nearest_unsent_reset_timer = 0;
421 m_nearest_unsent_d = 0;
422 /*infostream<<"Resetting m_nearest_unsent_d for "
423 <<server->getPlayerName(peer_id)<<std::endl;*/
426 //s16 last_nearest_unsent_d = m_nearest_unsent_d;
427 s16 d_start = m_nearest_unsent_d;
429 //infostream<<"d_start="<<d_start<<std::endl;
431 u16 max_simul_sends_setting = g_settings->getU16
432 ("max_simultaneous_block_sends_per_client");
433 u16 max_simul_sends_usually = max_simul_sends_setting;
436 Check the time from last addNode/removeNode.
438 Decrease send rate if player is building stuff.
440 m_time_from_building += dtime;
441 if(m_time_from_building < g_settings->getFloat(
442 "full_block_send_enable_min_time_from_building"))
444 max_simul_sends_usually
445 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
449 Number of blocks sending + number of blocks selected for sending
451 u32 num_blocks_selected = m_blocks_sending.size();
454 next time d will be continued from the d from which the nearest
455 unsent block was found this time.
457 This is because not necessarily any of the blocks found this
458 time are actually sent.
460 s32 new_nearest_unsent_d = -1;
462 s16 d_max = g_settings->getS16("max_block_send_distance");
463 s16 d_max_gen = g_settings->getS16("max_block_generate_distance");
465 // Don't loop very much at a time
466 if(d_max > d_start+1)
468 /*if(d_max_gen > d_start+2)
469 d_max_gen = d_start+2;*/
471 //infostream<<"Starting from "<<d_start<<std::endl;
473 bool sending_something = false;
475 bool no_blocks_found_for_sending = true;
477 bool queue_is_full = false;
480 for(d = d_start; d <= d_max; d++)
482 //infostream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
485 If m_nearest_unsent_d was changed by the EmergeThread
486 (it can change it to 0 through SetBlockNotSent),
488 Else update m_nearest_unsent_d
490 /*if(m_nearest_unsent_d != last_nearest_unsent_d)
492 d = m_nearest_unsent_d;
493 last_nearest_unsent_d = m_nearest_unsent_d;
497 Get the border/face dot coordinates of a "d-radiused"
500 core::list<v3s16> list;
501 getFacePositions(list, d);
503 core::list<v3s16>::Iterator li;
504 for(li=list.begin(); li!=list.end(); li++)
506 v3s16 p = *li + center;
510 - Don't allow too many simultaneous transfers
511 - EXCEPT when the blocks are very close
513 Also, don't send blocks that are already flying.
516 // Start with the usual maximum
517 u16 max_simul_dynamic = max_simul_sends_usually;
519 // If block is very close, allow full maximum
520 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
521 max_simul_dynamic = max_simul_sends_setting;
523 // Don't select too many blocks for sending
524 if(num_blocks_selected >= max_simul_dynamic)
526 queue_is_full = true;
527 goto queue_full_break;
530 // Don't send blocks that are currently being transferred
531 if(m_blocks_sending.find(p) != NULL)
537 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
538 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
539 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
540 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
541 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
542 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
545 // If this is true, inexistent block will be made from scratch
546 bool generate = d <= d_max_gen;
549 /*// Limit the generating area vertically to 2/3
550 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
553 // Limit the send area vertically to 2/3
554 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
560 If block is far away, don't generate it unless it is
566 // Block center y in nodes
567 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
568 // Don't generate if it's very high or very low
569 if(y < -64 || y > 64)
573 v2s16 p2d_nodes_center(
577 // Get ground height in nodes
578 s16 gh = server->m_env.getServerMap().findGroundLevel(
581 // If differs a lot, don't generate
582 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
584 // Actually, don't even send it
590 //infostream<<"d="<<d<<std::endl;
593 Don't generate or send if not in sight
594 FIXME This only works if the client uses a small enough
595 FOV setting. The default of 72 degrees is fine.
598 float camera_fov = (72.0*PI/180) * 4./3.;
599 if(isBlockInSight(p, camera_pos, camera_dir, camera_fov, 10000*BS) == false)
605 Don't send already sent blocks
608 if(m_blocks_sent.find(p) != NULL)
615 Check if map has this block
617 MapBlock *block = server->m_env.getMap().getBlockNoCreateNoEx(p);
619 bool surely_not_found_on_disk = false;
620 bool block_is_invalid = false;
623 // Reset usage timer, this block will be of use in the future.
624 block->resetUsageTimer();
626 // Block is dummy if data doesn't exist.
627 // It means it has been not found from disk and not generated
630 surely_not_found_on_disk = true;
633 // Block is valid if lighting is up-to-date and data exists
634 if(block->isValid() == false)
636 block_is_invalid = true;
639 /*if(block->isFullyGenerated() == false)
641 block_is_invalid = true;
646 ServerMap *map = (ServerMap*)(&server->m_env.getMap());
647 v2s16 chunkpos = map->sector_to_chunk(p2d);
648 if(map->chunkNonVolatile(chunkpos) == false)
649 block_is_invalid = true;
651 if(block->isGenerated() == false)
652 block_is_invalid = true;
655 If block is not close, don't send it unless it is near
658 Block is near ground level if night-time mesh
659 differs from day-time mesh.
663 if(block->dayNightDiffed() == false)
670 If block has been marked to not exist on disk (dummy)
671 and generating new ones is not wanted, skip block.
673 if(generate == false && surely_not_found_on_disk == true)
680 Record the lowest d from which a block has been
681 found being not sent and possibly to exist
683 if(no_blocks_found_for_sending)
686 new_nearest_unsent_d = d;
689 no_blocks_found_for_sending = false;
692 Add inexistent block to emerge queue.
694 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
696 //TODO: Get value from somewhere
697 // Allow only one block in emerge queue
698 //if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
699 // Allow two blocks in queue per client
700 if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
702 //infostream<<"Adding block to emerge queue"<<std::endl;
704 // Add it to the emerge queue and trigger the thread
707 if(generate == false)
708 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
710 server->m_emerge_queue.addBlock(peer_id, p, flags);
711 server->m_emergethread.trigger();
719 Add block to send queue
722 PrioritySortedBlockTransfer q((float)d, p, peer_id);
726 num_blocks_selected += 1;
727 sending_something = true;
732 //infostream<<"Stopped at "<<d<<std::endl;
734 if(no_blocks_found_for_sending)
736 if(queue_is_full == false)
737 new_nearest_unsent_d = d;
740 if(new_nearest_unsent_d != -1)
741 m_nearest_unsent_d = new_nearest_unsent_d;
743 if(sending_something == false)
745 m_nothing_to_send_counter++;
746 if((s16)m_nothing_to_send_counter >=
747 g_settings->getS16("max_block_send_distance"))
749 // Pause time in seconds
750 m_nothing_to_send_pause_timer = 1.0;
751 /*infostream<<"nothing to send to "
752 <<server->getPlayerName(peer_id)
753 <<" (d="<<d<<")"<<std::endl;*/
758 m_nothing_to_send_counter = 0;
761 /*timer_result = timer.stop(true);
762 if(timer_result != 0)
763 infostream<<"GetNextBlocks duration: "<<timer_result<<" (!=0)"<<std::endl;*/
766 void RemoteClient::SendObjectData(
769 core::map<v3s16, bool> &stepped_blocks
772 DSTACK(__FUNCTION_NAME);
774 // Can't send anything without knowing version
775 if(serialization_version == SER_FMT_VER_INVALID)
777 infostream<<"RemoteClient::SendObjectData(): Not sending, no version."
783 Send a TOCLIENT_OBJECTDATA packet.
787 u16 number of player positions
799 std::ostringstream os(std::ios_base::binary);
803 writeU16(buf, TOCLIENT_OBJECTDATA);
804 os.write((char*)buf, 2);
807 Get and write player data
810 // Get connected players
811 core::list<Player*> players = server->m_env.getPlayers(true);
813 // Write player count
814 u16 playercount = players.size();
815 writeU16(buf, playercount);
816 os.write((char*)buf, 2);
818 core::list<Player*>::Iterator i;
819 for(i = players.begin();
820 i != players.end(); i++)
824 v3f pf = player->getPosition();
825 v3f sf = player->getSpeed();
827 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
828 v3s32 speed_i (sf.X*100, sf.Y*100, sf.Z*100);
829 s32 pitch_i (player->getPitch() * 100);
830 s32 yaw_i (player->getYaw() * 100);
832 writeU16(buf, player->peer_id);
833 os.write((char*)buf, 2);
834 writeV3S32(buf, position_i);
835 os.write((char*)buf, 12);
836 writeV3S32(buf, speed_i);
837 os.write((char*)buf, 12);
838 writeS32(buf, pitch_i);
839 os.write((char*)buf, 4);
840 writeS32(buf, yaw_i);
841 os.write((char*)buf, 4);
845 Get and write object data
851 For making players to be able to build to their nearby
852 environment (building is not possible on blocks that are not
855 - Add blocks to emerge queue if they are not found
857 SUGGESTION: These could be ignored from the backside of the player
860 Player *player = server->m_env.getPlayer(peer_id);
864 v3f playerpos = player->getPosition();
865 v3f playerspeed = player->getSpeed();
867 v3s16 center_nodepos = floatToInt(playerpos, BS);
868 v3s16 center = getNodeBlockPos(center_nodepos);
872 os.write((char*)buf, 2);
878 //infostream<<"Server: Sending object data to "<<peer_id<<std::endl;
881 std::string s = os.str();
882 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
883 // Send as unreliable
884 server->m_con.Send(peer_id, 0, data, false);
887 void RemoteClient::GotBlock(v3s16 p)
889 if(m_blocks_sending.find(p) != NULL)
890 m_blocks_sending.remove(p);
893 /*infostream<<"RemoteClient::GotBlock(): Didn't find in"
894 " m_blocks_sending"<<std::endl;*/
895 m_excess_gotblocks++;
897 m_blocks_sent.insert(p, true);
900 void RemoteClient::SentBlock(v3s16 p)
902 if(m_blocks_sending.find(p) == NULL)
903 m_blocks_sending.insert(p, 0.0);
905 infostream<<"RemoteClient::SentBlock(): Sent block"
906 " already in m_blocks_sending"<<std::endl;
909 void RemoteClient::SetBlockNotSent(v3s16 p)
911 m_nearest_unsent_d = 0;
913 if(m_blocks_sending.find(p) != NULL)
914 m_blocks_sending.remove(p);
915 if(m_blocks_sent.find(p) != NULL)
916 m_blocks_sent.remove(p);
919 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
921 m_nearest_unsent_d = 0;
923 for(core::map<v3s16, MapBlock*>::Iterator
924 i = blocks.getIterator();
925 i.atEnd()==false; i++)
927 v3s16 p = i.getNode()->getKey();
929 if(m_blocks_sending.find(p) != NULL)
930 m_blocks_sending.remove(p);
931 if(m_blocks_sent.find(p) != NULL)
932 m_blocks_sent.remove(p);
940 PlayerInfo::PlayerInfo()
946 void PlayerInfo::PrintLine(std::ostream *s)
949 (*s)<<"\""<<name<<"\" ("
950 <<(position.X/10)<<","<<(position.Y/10)
951 <<","<<(position.Z/10)<<") ";
953 (*s)<<" avg_rtt="<<avg_rtt;
957 u32 PIChecksum(core::list<PlayerInfo> &l)
959 core::list<PlayerInfo>::Iterator i;
962 for(i=l.begin(); i!=l.end(); i++)
964 checksum += a * (i->id+1);
965 checksum ^= 0x435aafcd;
976 std::string mapsavedir,
977 std::string configpath
979 m_env(new ServerMap(mapsavedir), this),
980 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
981 m_authmanager(mapsavedir+"/auth.txt"),
982 m_banmanager(mapsavedir+"/ipban.txt"),
984 m_emergethread(this),
986 m_time_of_day_send_timer(0),
988 m_mapsavedir(mapsavedir),
989 m_configpath(configpath),
990 m_shutdown_requested(false),
991 m_ignore_map_edit_events(false),
992 m_ignore_map_edit_events_peer_id(0)
994 m_liquid_transform_timer = 0.0;
995 m_print_info_timer = 0.0;
996 m_objectdata_timer = 0.0;
997 m_emergethread_trigger_timer = 0.0;
998 m_savemap_timer = 0.0;
1002 m_step_dtime_mutex.Init();
1005 // Register us to receive map edit events
1006 m_env.getMap().addEventReceiver(this);
1008 // If file exists, load environment metadata
1009 if(fs::PathExists(m_mapsavedir+"/env_meta.txt"))
1011 infostream<<"Server: Loading environment metadata"<<std::endl;
1012 m_env.loadMeta(m_mapsavedir);
1016 infostream<<"Server: Loading players"<<std::endl;
1017 m_env.deSerializePlayers(m_mapsavedir);
1022 infostream<<"Server::~Server()"<<std::endl;
1025 Send shutdown message
1028 JMutexAutoLock conlock(m_con_mutex);
1030 std::wstring line = L"*** Server shutting down";
1033 Send the message to clients
1035 for(core::map<u16, RemoteClient*>::Iterator
1036 i = m_clients.getIterator();
1037 i.atEnd() == false; i++)
1039 // Get client and check that it is valid
1040 RemoteClient *client = i.getNode()->getValue();
1041 assert(client->peer_id == i.getNode()->getKey());
1042 if(client->serialization_version == SER_FMT_VER_INVALID)
1046 SendChatMessage(client->peer_id, line);
1048 catch(con::PeerNotFoundException &e)
1054 JMutexAutoLock envlock(m_env_mutex);
1059 infostream<<"Server: Saving players"<<std::endl;
1060 m_env.serializePlayers(m_mapsavedir);
1063 Save environment metadata
1065 infostream<<"Server: Saving environment metadata"<<std::endl;
1066 m_env.saveMeta(m_mapsavedir);
1078 JMutexAutoLock clientslock(m_con_mutex);
1080 for(core::map<u16, RemoteClient*>::Iterator
1081 i = m_clients.getIterator();
1082 i.atEnd() == false; i++)
1085 // NOTE: These are removed by env destructor
1087 u16 peer_id = i.getNode()->getKey();
1088 JMutexAutoLock envlock(m_env_mutex);
1089 m_env.removePlayer(peer_id);
1093 delete i.getNode()->getValue();
1098 void Server::start(unsigned short port)
1100 DSTACK(__FUNCTION_NAME);
1101 // Stop thread if already running
1104 // Initialize connection
1105 m_con.setTimeoutMs(30);
1109 m_thread.setRun(true);
1112 infostream<<"Server: Started on port "<<port<<std::endl;
1117 DSTACK(__FUNCTION_NAME);
1119 infostream<<"Server: Stopping and waiting threads"<<std::endl;
1121 // Stop threads (set run=false first so both start stopping)
1122 m_thread.setRun(false);
1123 m_emergethread.setRun(false);
1125 m_emergethread.stop();
1127 infostream<<"Server: Threads stopped"<<std::endl;
1130 void Server::step(float dtime)
1132 DSTACK(__FUNCTION_NAME);
1137 JMutexAutoLock lock(m_step_dtime_mutex);
1138 m_step_dtime += dtime;
1142 void Server::AsyncRunStep()
1144 DSTACK(__FUNCTION_NAME);
1148 JMutexAutoLock lock1(m_step_dtime_mutex);
1149 dtime = m_step_dtime;
1153 ScopeProfiler sp(g_profiler, "Server: selecting and sending "
1154 "blocks to clients");
1155 // Send blocks to clients
1162 //infostream<<"Server steps "<<dtime<<std::endl;
1163 //infostream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1166 JMutexAutoLock lock1(m_step_dtime_mutex);
1167 m_step_dtime -= dtime;
1174 m_uptime.set(m_uptime.get() + dtime);
1178 // Process connection's timeouts
1179 JMutexAutoLock lock2(m_con_mutex);
1180 ScopeProfiler sp(g_profiler, "Server: connection timeout processing");
1181 m_con.RunTimeouts(dtime);
1185 // This has to be called so that the client list gets synced
1186 // with the peer list of the connection
1187 ScopeProfiler sp(g_profiler, "Server: peer change handling");
1188 handlePeerChanges();
1192 Update m_time_of_day and overall game time
1195 JMutexAutoLock envlock(m_env_mutex);
1197 m_time_counter += dtime;
1198 f32 speed = g_settings->getFloat("time_speed") * 24000./(24.*3600);
1199 u32 units = (u32)(m_time_counter*speed);
1200 m_time_counter -= (f32)units / speed;
1202 m_env.setTimeOfDay((m_env.getTimeOfDay() + units) % 24000);
1204 //infostream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1207 Send to clients at constant intervals
1210 m_time_of_day_send_timer -= dtime;
1211 if(m_time_of_day_send_timer < 0.0)
1213 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
1215 //JMutexAutoLock envlock(m_env_mutex);
1216 JMutexAutoLock conlock(m_con_mutex);
1218 for(core::map<u16, RemoteClient*>::Iterator
1219 i = m_clients.getIterator();
1220 i.atEnd() == false; i++)
1222 RemoteClient *client = i.getNode()->getValue();
1223 //Player *player = m_env.getPlayer(client->peer_id);
1225 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1226 m_env.getTimeOfDay());
1228 m_con.Send(client->peer_id, 0, data, true);
1234 JMutexAutoLock lock(m_env_mutex);
1236 ScopeProfiler sp(g_profiler, "Server: environment step");
1240 const float map_timer_and_unload_dtime = 5.15;
1241 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
1243 JMutexAutoLock lock(m_env_mutex);
1244 // Run Map's timers and unload unused data
1245 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
1246 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
1247 g_settings->getFloat("server_unload_unused_data_timeout"));
1257 m_liquid_transform_timer += dtime;
1258 if(m_liquid_transform_timer >= 1.00)
1260 m_liquid_transform_timer -= 1.00;
1262 JMutexAutoLock lock(m_env_mutex);
1264 ScopeProfiler sp(g_profiler, "Server: liquid transform");
1266 core::map<v3s16, MapBlock*> modified_blocks;
1267 m_env.getMap().transformLiquids(modified_blocks);
1272 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1273 ServerMap &map = ((ServerMap&)m_env.getMap());
1274 map.updateLighting(modified_blocks, lighting_modified_blocks);
1276 // Add blocks modified by lighting to modified_blocks
1277 for(core::map<v3s16, MapBlock*>::Iterator
1278 i = lighting_modified_blocks.getIterator();
1279 i.atEnd() == false; i++)
1281 MapBlock *block = i.getNode()->getValue();
1282 modified_blocks.insert(block->getPos(), block);
1286 Set the modified blocks unsent for all the clients
1289 JMutexAutoLock lock2(m_con_mutex);
1291 for(core::map<u16, RemoteClient*>::Iterator
1292 i = m_clients.getIterator();
1293 i.atEnd() == false; i++)
1295 RemoteClient *client = i.getNode()->getValue();
1297 if(modified_blocks.size() > 0)
1299 // Remove block from sent history
1300 client->SetBlocksNotSent(modified_blocks);
1305 // Periodically print some info
1307 float &counter = m_print_info_timer;
1313 JMutexAutoLock lock2(m_con_mutex);
1315 if(m_clients.size() != 0)
1316 infostream<<"Players:"<<std::endl;
1317 for(core::map<u16, RemoteClient*>::Iterator
1318 i = m_clients.getIterator();
1319 i.atEnd() == false; i++)
1321 //u16 peer_id = i.getNode()->getKey();
1322 RemoteClient *client = i.getNode()->getValue();
1323 Player *player = m_env.getPlayer(client->peer_id);
1326 infostream<<"* "<<player->getName()<<"\t";
1327 client->PrintInfo(infostream);
1332 //if(g_settings->getBool("enable_experimental"))
1336 Check added and deleted active objects
1339 //infostream<<"Server: Checking added and deleted active objects"<<std::endl;
1340 JMutexAutoLock envlock(m_env_mutex);
1341 JMutexAutoLock conlock(m_con_mutex);
1343 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objects");
1345 // Radius inside which objects are active
1346 s16 radius = g_settings->getS16("active_object_send_range_blocks");
1347 radius *= MAP_BLOCKSIZE;
1349 for(core::map<u16, RemoteClient*>::Iterator
1350 i = m_clients.getIterator();
1351 i.atEnd() == false; i++)
1353 RemoteClient *client = i.getNode()->getValue();
1354 Player *player = m_env.getPlayer(client->peer_id);
1357 // This can happen if the client timeouts somehow
1358 /*infostream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
1360 <<" has no associated player"<<std::endl;*/
1363 v3s16 pos = floatToInt(player->getPosition(), BS);
1365 core::map<u16, bool> removed_objects;
1366 core::map<u16, bool> added_objects;
1367 m_env.getRemovedActiveObjects(pos, radius,
1368 client->m_known_objects, removed_objects);
1369 m_env.getAddedActiveObjects(pos, radius,
1370 client->m_known_objects, added_objects);
1372 // Ignore if nothing happened
1373 if(removed_objects.size() == 0 && added_objects.size() == 0)
1375 //infostream<<"active objects: none changed"<<std::endl;
1379 std::string data_buffer;
1383 // Handle removed objects
1384 writeU16((u8*)buf, removed_objects.size());
1385 data_buffer.append(buf, 2);
1386 for(core::map<u16, bool>::Iterator
1387 i = removed_objects.getIterator();
1388 i.atEnd()==false; i++)
1391 u16 id = i.getNode()->getKey();
1392 ServerActiveObject* obj = m_env.getActiveObject(id);
1394 // Add to data buffer for sending
1395 writeU16((u8*)buf, i.getNode()->getKey());
1396 data_buffer.append(buf, 2);
1398 // Remove from known objects
1399 client->m_known_objects.remove(i.getNode()->getKey());
1401 if(obj && obj->m_known_by_count > 0)
1402 obj->m_known_by_count--;
1405 // Handle added objects
1406 writeU16((u8*)buf, added_objects.size());
1407 data_buffer.append(buf, 2);
1408 for(core::map<u16, bool>::Iterator
1409 i = added_objects.getIterator();
1410 i.atEnd()==false; i++)
1413 u16 id = i.getNode()->getKey();
1414 ServerActiveObject* obj = m_env.getActiveObject(id);
1417 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1419 infostream<<"WARNING: "<<__FUNCTION_NAME
1420 <<": NULL object"<<std::endl;
1422 type = obj->getType();
1424 // Add to data buffer for sending
1425 writeU16((u8*)buf, id);
1426 data_buffer.append(buf, 2);
1427 writeU8((u8*)buf, type);
1428 data_buffer.append(buf, 1);
1431 data_buffer.append(serializeLongString(
1432 obj->getClientInitializationData()));
1434 data_buffer.append(serializeLongString(""));
1436 // Add to known objects
1437 client->m_known_objects.insert(i.getNode()->getKey(), false);
1440 obj->m_known_by_count++;
1444 SharedBuffer<u8> reply(2 + data_buffer.size());
1445 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1446 memcpy((char*)&reply[2], data_buffer.c_str(),
1447 data_buffer.size());
1449 m_con.Send(client->peer_id, 0, reply, true);
1451 infostream<<"Server: Sent object remove/add: "
1452 <<removed_objects.size()<<" removed, "
1453 <<added_objects.size()<<" added, "
1454 <<"packet size is "<<reply.getSize()<<std::endl;
1459 Collect a list of all the objects known by the clients
1460 and report it back to the environment.
1463 core::map<u16, bool> all_known_objects;
1465 for(core::map<u16, RemoteClient*>::Iterator
1466 i = m_clients.getIterator();
1467 i.atEnd() == false; i++)
1469 RemoteClient *client = i.getNode()->getValue();
1470 // Go through all known objects of client
1471 for(core::map<u16, bool>::Iterator
1472 i = client->m_known_objects.getIterator();
1473 i.atEnd()==false; i++)
1475 u16 id = i.getNode()->getKey();
1476 all_known_objects[id] = true;
1480 m_env.setKnownActiveObjects(whatever);
1486 Send object messages
1489 JMutexAutoLock envlock(m_env_mutex);
1490 JMutexAutoLock conlock(m_con_mutex);
1492 ScopeProfiler sp(g_profiler, "Server: sending object messages");
1495 // Value = data sent by object
1496 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1498 // Get active object messages from environment
1501 ActiveObjectMessage aom = m_env.getActiveObjectMessage();
1505 core::list<ActiveObjectMessage>* message_list = NULL;
1506 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1507 n = buffered_messages.find(aom.id);
1510 message_list = new core::list<ActiveObjectMessage>;
1511 buffered_messages.insert(aom.id, message_list);
1515 message_list = n->getValue();
1517 message_list->push_back(aom);
1520 // Route data to every client
1521 for(core::map<u16, RemoteClient*>::Iterator
1522 i = m_clients.getIterator();
1523 i.atEnd()==false; i++)
1525 RemoteClient *client = i.getNode()->getValue();
1526 std::string reliable_data;
1527 std::string unreliable_data;
1528 // Go through all objects in message buffer
1529 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1530 j = buffered_messages.getIterator();
1531 j.atEnd()==false; j++)
1533 // If object is not known by client, skip it
1534 u16 id = j.getNode()->getKey();
1535 if(client->m_known_objects.find(id) == NULL)
1537 // Get message list of object
1538 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1539 // Go through every message
1540 for(core::list<ActiveObjectMessage>::Iterator
1541 k = list->begin(); k != list->end(); k++)
1543 // Compose the full new data with header
1544 ActiveObjectMessage aom = *k;
1545 std::string new_data;
1548 writeU16((u8*)&buf[0], aom.id);
1549 new_data.append(buf, 2);
1551 new_data += serializeString(aom.datastring);
1552 // Add data to buffer
1554 reliable_data += new_data;
1556 unreliable_data += new_data;
1560 reliable_data and unreliable_data are now ready.
1563 if(reliable_data.size() > 0)
1565 SharedBuffer<u8> reply(2 + reliable_data.size());
1566 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1567 memcpy((char*)&reply[2], reliable_data.c_str(),
1568 reliable_data.size());
1570 m_con.Send(client->peer_id, 0, reply, true);
1572 if(unreliable_data.size() > 0)
1574 SharedBuffer<u8> reply(2 + unreliable_data.size());
1575 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1576 memcpy((char*)&reply[2], unreliable_data.c_str(),
1577 unreliable_data.size());
1578 // Send as unreliable
1579 m_con.Send(client->peer_id, 0, reply, false);
1582 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1584 infostream<<"Server: Size of object message data: "
1585 <<"reliable: "<<reliable_data.size()
1586 <<", unreliable: "<<unreliable_data.size()
1591 // Clear buffered_messages
1592 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1593 i = buffered_messages.getIterator();
1594 i.atEnd()==false; i++)
1596 delete i.getNode()->getValue();
1600 } // enable_experimental
1603 Send queued-for-sending map edit events.
1606 // Don't send too many at a time
1609 // Single change sending is disabled if queue size is not small
1610 bool disable_single_change_sending = false;
1611 if(m_unsent_map_edit_queue.size() >= 4)
1612 disable_single_change_sending = true;
1614 bool got_any_events = false;
1616 // We'll log the amount of each
1619 while(m_unsent_map_edit_queue.size() != 0)
1621 got_any_events = true;
1623 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1625 // Players far away from the change are stored here.
1626 // Instead of sending the changes, MapBlocks are set not sent
1628 core::list<u16> far_players;
1630 if(event->type == MEET_ADDNODE)
1632 //infostream<<"Server: MEET_ADDNODE"<<std::endl;
1633 prof.add("MEET_ADDNODE", 1);
1634 if(disable_single_change_sending)
1635 sendAddNode(event->p, event->n, event->already_known_by_peer,
1638 sendAddNode(event->p, event->n, event->already_known_by_peer,
1641 else if(event->type == MEET_REMOVENODE)
1643 //infostream<<"Server: MEET_REMOVENODE"<<std::endl;
1644 prof.add("MEET_REMOVENODE", 1);
1645 if(disable_single_change_sending)
1646 sendRemoveNode(event->p, event->already_known_by_peer,
1649 sendRemoveNode(event->p, event->already_known_by_peer,
1652 else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
1654 infostream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
1655 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
1656 setBlockNotSent(event->p);
1658 else if(event->type == MEET_OTHER)
1660 infostream<<"Server: MEET_OTHER"<<std::endl;
1661 prof.add("MEET_OTHER", 1);
1662 for(core::map<v3s16, bool>::Iterator
1663 i = event->modified_blocks.getIterator();
1664 i.atEnd()==false; i++)
1666 v3s16 p = i.getNode()->getKey();
1672 prof.add("unknown", 1);
1673 infostream<<"WARNING: Server: Unknown MapEditEvent "
1674 <<((u32)event->type)<<std::endl;
1678 Set blocks not sent to far players
1680 if(far_players.size() > 0)
1682 // Convert list format to that wanted by SetBlocksNotSent
1683 core::map<v3s16, MapBlock*> modified_blocks2;
1684 for(core::map<v3s16, bool>::Iterator
1685 i = event->modified_blocks.getIterator();
1686 i.atEnd()==false; i++)
1688 v3s16 p = i.getNode()->getKey();
1689 modified_blocks2.insert(p,
1690 m_env.getMap().getBlockNoCreateNoEx(p));
1692 // Set blocks not sent
1693 for(core::list<u16>::Iterator
1694 i = far_players.begin();
1695 i != far_players.end(); i++)
1698 RemoteClient *client = getClient(peer_id);
1701 client->SetBlocksNotSent(modified_blocks2);
1707 /*// Don't send too many at a time
1709 if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
1715 infostream<<"Server: MapEditEvents:"<<std::endl;
1716 prof.print(infostream);
1722 Send object positions
1723 TODO: Get rid of MapBlockObjects
1726 float &counter = m_objectdata_timer;
1728 if(counter >= g_settings->getFloat("objectdata_interval"))
1730 JMutexAutoLock lock1(m_env_mutex);
1731 JMutexAutoLock lock2(m_con_mutex);
1733 ScopeProfiler sp(g_profiler, "Server: sending mbo positions");
1735 SendObjectData(counter);
1742 Trigger emergethread (it somehow gets to a non-triggered but
1743 bysy state sometimes)
1746 float &counter = m_emergethread_trigger_timer;
1752 m_emergethread.trigger();
1756 // Save map, players and auth stuff
1758 float &counter = m_savemap_timer;
1760 if(counter >= g_settings->getFloat("server_map_save_interval"))
1764 ScopeProfiler sp(g_profiler, "Server: saving stuff");
1767 if(m_authmanager.isModified())
1768 m_authmanager.save();
1771 if(m_banmanager.isModified())
1772 m_banmanager.save();
1775 JMutexAutoLock lock(m_env_mutex);
1777 /*// Unload unused data (delete from memory)
1778 m_env.getMap().unloadUnusedData(
1779 g_settings->getFloat("server_unload_unused_sectors_timeout"));
1781 /*u32 deleted_count = m_env.getMap().unloadUnusedData(
1782 g_settings->getFloat("server_unload_unused_sectors_timeout"));
1785 // Save only changed parts
1786 m_env.getMap().save(true);
1788 /*if(deleted_count > 0)
1790 infostream<<"Server: Unloaded "<<deleted_count
1791 <<" blocks from memory"<<std::endl;
1795 m_env.serializePlayers(m_mapsavedir);
1797 // Save environment metadata
1798 m_env.saveMeta(m_mapsavedir);
1803 void Server::Receive()
1805 DSTACK(__FUNCTION_NAME);
1806 u32 data_maxsize = 10000;
1807 Buffer<u8> data(data_maxsize);
1812 JMutexAutoLock conlock(m_con_mutex);
1813 datasize = m_con.Receive(peer_id, *data, data_maxsize);
1816 // This has to be called so that the client list gets synced
1817 // with the peer list of the connection
1818 handlePeerChanges();
1820 ProcessData(*data, datasize, peer_id);
1822 catch(con::InvalidIncomingDataException &e)
1824 infostream<<"Server::Receive(): "
1825 "InvalidIncomingDataException: what()="
1826 <<e.what()<<std::endl;
1828 catch(con::PeerNotFoundException &e)
1830 //NOTE: This is not needed anymore
1832 // The peer has been disconnected.
1833 // Find the associated player and remove it.
1835 /*JMutexAutoLock envlock(m_env_mutex);
1837 infostream<<"ServerThread: peer_id="<<peer_id
1838 <<" has apparently closed connection. "
1839 <<"Removing player."<<std::endl;
1841 m_env.removePlayer(peer_id);*/
1845 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1847 DSTACK(__FUNCTION_NAME);
1848 // Environment is locked first.
1849 JMutexAutoLock envlock(m_env_mutex);
1850 JMutexAutoLock conlock(m_con_mutex);
1854 peer = m_con.GetPeer(peer_id);
1856 catch(con::PeerNotFoundException &e)
1858 infostream<<"Server::ProcessData(): Cancelling: peer "
1859 <<peer_id<<" not found"<<std::endl;
1863 // drop player if is ip is banned
1864 if(m_banmanager.isIpBanned(peer->address.serializeString())){
1865 SendAccessDenied(m_con, peer_id,
1866 L"Your ip is banned. Banned name was "
1867 +narrow_to_wide(m_banmanager.getBanName(
1868 peer->address.serializeString())));
1869 m_con.deletePeer(peer_id, false);
1873 u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1881 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1883 if(command == TOSERVER_INIT)
1885 // [0] u16 TOSERVER_INIT
1886 // [2] u8 SER_FMT_VER_HIGHEST
1887 // [3] u8[20] player_name
1888 // [23] u8[28] password <--- can be sent without this, from old versions
1890 if(datasize < 2+1+PLAYERNAME_SIZE)
1893 infostream<<"Server: Got TOSERVER_INIT from "
1894 <<peer->id<<std::endl;
1896 // First byte after command is maximum supported
1897 // serialization version
1898 u8 client_max = data[2];
1899 u8 our_max = SER_FMT_VER_HIGHEST;
1900 // Use the highest version supported by both
1901 u8 deployed = core::min_(client_max, our_max);
1902 // If it's lower than the lowest supported, give up.
1903 if(deployed < SER_FMT_VER_LOWEST)
1904 deployed = SER_FMT_VER_INVALID;
1906 //peer->serialization_version = deployed;
1907 getClient(peer->id)->pending_serialization_version = deployed;
1909 if(deployed == SER_FMT_VER_INVALID)
1911 infostream<<"Server: Cannot negotiate "
1912 "serialization version with peer "
1913 <<peer_id<<std::endl;
1914 SendAccessDenied(m_con, peer_id,
1915 L"Your client is too old (map format)");
1920 Read and check network protocol version
1923 u16 net_proto_version = 0;
1924 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
1926 net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
1929 getClient(peer->id)->net_proto_version = net_proto_version;
1931 if(net_proto_version == 0)
1933 SendAccessDenied(m_con, peer_id,
1934 L"Your client is too old. Please upgrade.");
1938 /* Uhh... this should actually be a warning but let's do it like this */
1939 if(g_settings->getBool("strict_protocol_version_checking"))
1941 if(net_proto_version < PROTOCOL_VERSION)
1943 SendAccessDenied(m_con, peer_id,
1944 L"Your client is too old. Please upgrade.");
1954 char playername[PLAYERNAME_SIZE];
1955 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
1957 playername[i] = data[3+i];
1959 playername[PLAYERNAME_SIZE-1] = 0;
1961 if(playername[0]=='\0')
1963 infostream<<"Server: Player has empty name"<<std::endl;
1964 SendAccessDenied(m_con, peer_id,
1969 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
1971 infostream<<"Server: Player has invalid name"<<std::endl;
1972 SendAccessDenied(m_con, peer_id,
1973 L"Name contains unallowed characters");
1978 char password[PASSWORD_SIZE];
1979 if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
1981 // old version - assume blank password
1986 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
1988 password[i] = data[23+i];
1990 password[PASSWORD_SIZE-1] = 0;
1993 std::string checkpwd;
1994 if(m_authmanager.exists(playername))
1996 checkpwd = m_authmanager.getPassword(playername);
2000 checkpwd = g_settings->get("default_password");
2003 /*infostream<<"Server: Client gave password '"<<password
2004 <<"', the correct one is '"<<checkpwd<<"'"<<std::endl;*/
2006 if(password != checkpwd && m_authmanager.exists(playername))
2008 infostream<<"Server: peer_id="<<peer_id
2009 <<": supplied invalid password for "
2010 <<playername<<std::endl;
2011 SendAccessDenied(m_con, peer_id, L"Invalid password");
2015 // Add player to auth manager
2016 if(m_authmanager.exists(playername) == false)
2018 infostream<<"Server: adding player "<<playername
2019 <<" to auth manager"<<std::endl;
2020 m_authmanager.add(playername);
2021 m_authmanager.setPassword(playername, checkpwd);
2022 m_authmanager.setPrivs(playername,
2023 stringToPrivs(g_settings->get("default_privs")));
2024 m_authmanager.save();
2027 // Enforce user limit.
2028 // Don't enforce for users that have some admin right
2029 if(m_clients.size() >= g_settings->getU16("max_users") &&
2030 (m_authmanager.getPrivs(playername)
2031 & (PRIV_SERVER|PRIV_BAN|PRIV_PRIVS)) == 0 &&
2032 playername != g_settings->get("name"))
2034 SendAccessDenied(m_con, peer_id, L"Too many users.");
2039 Player *player = emergePlayer(playername, password, peer_id);
2041 // If failed, cancel
2044 infostream<<"Server: peer_id="<<peer_id
2045 <<": failed to emerge player"<<std::endl;
2050 Answer with a TOCLIENT_INIT
2053 SharedBuffer<u8> reply(2+1+6+8);
2054 writeU16(&reply[0], TOCLIENT_INIT);
2055 writeU8(&reply[2], deployed);
2056 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
2057 writeU64(&reply[2+1+6], m_env.getServerMap().getSeed());
2060 m_con.Send(peer_id, 0, reply, true);
2064 Send complete position information
2066 SendMovePlayer(player);
2071 if(command == TOSERVER_INIT2)
2073 infostream<<"Server: Got TOSERVER_INIT2 from "
2074 <<peer->id<<std::endl;
2077 getClient(peer->id)->serialization_version
2078 = getClient(peer->id)->pending_serialization_version;
2081 Send some initialization data
2084 // Send player info to all players
2087 // Send inventory to player
2088 UpdateCrafting(peer->id);
2089 SendInventory(peer->id);
2091 // Send player items to all players
2094 Player *player = m_env.getPlayer(peer_id);
2097 SendPlayerHP(player);
2101 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2102 m_env.getTimeOfDay());
2103 m_con.Send(peer->id, 0, data, true);
2106 // Send information about server to player in chat
2107 SendChatMessage(peer_id, getStatusString());
2109 // Send information about joining in chat
2111 std::wstring name = L"unknown";
2112 Player *player = m_env.getPlayer(peer_id);
2114 name = narrow_to_wide(player->getName());
2116 std::wstring message;
2119 message += L" joined game";
2120 BroadcastChatMessage(message);
2123 // Warnings about protocol version can be issued here
2124 if(getClient(peer->id)->net_proto_version < PROTOCOL_VERSION)
2126 SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT IS OLD AND MAY WORK PROPERLY WITH THIS SERVER");
2130 Check HP, respawn if necessary
2132 HandlePlayerHP(player, 0);
2138 std::ostringstream os(std::ios_base::binary);
2139 for(core::map<u16, RemoteClient*>::Iterator
2140 i = m_clients.getIterator();
2141 i.atEnd() == false; i++)
2143 RemoteClient *client = i.getNode()->getValue();
2144 assert(client->peer_id == i.getNode()->getKey());
2145 if(client->serialization_version == SER_FMT_VER_INVALID)
2148 Player *player = m_env.getPlayer(client->peer_id);
2151 // Get name of player
2152 os<<player->getName()<<" ";
2155 actionstream<<player->getName()<<" joins game. List of players: "
2156 <<os.str()<<std::endl;
2162 if(peer_ser_ver == SER_FMT_VER_INVALID)
2164 infostream<<"Server::ProcessData(): Cancelling: Peer"
2165 " serialization format invalid or not initialized."
2166 " Skipping incoming command="<<command<<std::endl;
2170 Player *player = m_env.getPlayer(peer_id);
2173 infostream<<"Server::ProcessData(): Cancelling: "
2174 "No player for peer_id="<<peer_id
2178 if(command == TOSERVER_PLAYERPOS)
2180 if(datasize < 2+12+12+4+4)
2184 v3s32 ps = readV3S32(&data[start+2]);
2185 v3s32 ss = readV3S32(&data[start+2+12]);
2186 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2187 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2188 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2189 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2190 pitch = wrapDegrees(pitch);
2191 yaw = wrapDegrees(yaw);
2193 player->setPosition(position);
2194 player->setSpeed(speed);
2195 player->setPitch(pitch);
2196 player->setYaw(yaw);
2198 /*infostream<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2199 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2200 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2202 else if(command == TOSERVER_GOTBLOCKS)
2215 u16 count = data[2];
2216 for(u16 i=0; i<count; i++)
2218 if((s16)datasize < 2+1+(i+1)*6)
2219 throw con::InvalidIncomingDataException
2220 ("GOTBLOCKS length is too short");
2221 v3s16 p = readV3S16(&data[2+1+i*6]);
2222 /*infostream<<"Server: GOTBLOCKS ("
2223 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2224 RemoteClient *client = getClient(peer_id);
2225 client->GotBlock(p);
2228 else if(command == TOSERVER_DELETEDBLOCKS)
2241 u16 count = data[2];
2242 for(u16 i=0; i<count; i++)
2244 if((s16)datasize < 2+1+(i+1)*6)
2245 throw con::InvalidIncomingDataException
2246 ("DELETEDBLOCKS length is too short");
2247 v3s16 p = readV3S16(&data[2+1+i*6]);
2248 /*infostream<<"Server: DELETEDBLOCKS ("
2249 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2250 RemoteClient *client = getClient(peer_id);
2251 client->SetBlockNotSent(p);
2254 else if(command == TOSERVER_CLICK_OBJECT)
2256 infostream<<"Server: CLICK_OBJECT not supported anymore"<<std::endl;
2259 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2264 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2270 [2] u8 button (0=left, 1=right)
2274 u8 button = readU8(&data[2]);
2275 u16 id = readS16(&data[3]);
2276 u16 item_i = readU16(&data[5]);
2278 ServerActiveObject *obj = m_env.getActiveObject(id);
2282 infostream<<"Server: CLICK_ACTIVEOBJECT: object not found"
2287 // Skip if object has been removed
2291 //TODO: Check that object is reasonably close
2293 // Left click, pick object up (usually)
2297 Try creating inventory item
2299 InventoryItem *item = obj->createPickedUpItem();
2303 InventoryList *ilist = player->inventory.getList("main");
2306 actionstream<<player->getName()<<" picked up "
2307 <<item->getName()<<std::endl;
2308 if(g_settings->getBool("creative_mode") == false)
2310 // Skip if inventory has no free space
2311 if(ilist->roomForItem(item) == false)
2313 infostream<<"Player inventory has no free space"<<std::endl;
2317 // Add to inventory and send inventory
2318 ilist->addItem(item);
2319 UpdateCrafting(player->peer_id);
2320 SendInventory(player->peer_id);
2323 // Remove object from environment
2324 obj->m_removed = true;
2330 Item cannot be picked up. Punch it instead.
2333 actionstream<<player->getName()<<" punches object "
2334 <<obj->getId()<<std::endl;
2336 ToolItem *titem = NULL;
2337 std::string toolname = "";
2339 InventoryList *mlist = player->inventory.getList("main");
2342 InventoryItem *item = mlist->getItem(item_i);
2343 if(item && (std::string)item->getName() == "ToolItem")
2345 titem = (ToolItem*)item;
2346 toolname = titem->getToolName();
2350 v3f playerpos = player->getPosition();
2351 v3f objpos = obj->getBasePosition();
2352 v3f dir = (objpos - playerpos).normalize();
2354 u16 wear = obj->punch(toolname, dir, player->getName());
2358 bool weared_out = titem->addWear(wear);
2360 mlist->deleteItem(item_i);
2361 SendInventory(player->peer_id);
2365 // Right click, do something with object
2368 actionstream<<player->getName()<<" right clicks object "
2369 <<obj->getId()<<std::endl;
2371 // Track hp changes super-crappily
2372 u16 oldhp = player->hp;
2375 obj->rightClick(player);
2378 if(player->hp != oldhp)
2380 SendPlayerHP(player);
2384 else if(command == TOSERVER_GROUND_ACTION)
2392 [3] v3s16 nodepos_undersurface
2393 [9] v3s16 nodepos_abovesurface
2398 2: stop digging (all parameters ignored)
2399 3: digging completed
2401 u8 action = readU8(&data[2]);
2403 p_under.X = readS16(&data[3]);
2404 p_under.Y = readS16(&data[5]);
2405 p_under.Z = readS16(&data[7]);
2407 p_over.X = readS16(&data[9]);
2408 p_over.Y = readS16(&data[11]);
2409 p_over.Z = readS16(&data[13]);
2410 u16 item_i = readU16(&data[15]);
2412 //TODO: Check that target is reasonably close
2420 NOTE: This can be used in the future to check if
2421 somebody is cheating, by checking the timing.
2428 else if(action == 2)
2431 RemoteClient *client = getClient(peer->id);
2432 JMutexAutoLock digmutex(client->m_dig_mutex);
2433 client->m_dig_tool_item = -1;
2438 3: Digging completed
2440 else if(action == 3)
2442 // Mandatory parameter; actually used for nothing
2443 core::map<v3s16, MapBlock*> modified_blocks;
2445 content_t material = CONTENT_IGNORE;
2446 u8 mineral = MINERAL_NONE;
2448 bool cannot_remove_node = false;
2452 MapNode n = m_env.getMap().getNode(p_under);
2454 mineral = n.getMineral();
2455 // Get material at position
2456 material = n.getContent();
2457 // If not yet cancelled
2458 if(cannot_remove_node == false)
2460 // If it's not diggable, do nothing
2461 if(content_diggable(material) == false)
2463 infostream<<"Server: Not finishing digging: "
2464 <<"Node not diggable"
2466 cannot_remove_node = true;
2469 // If not yet cancelled
2470 if(cannot_remove_node == false)
2472 // Get node metadata
2473 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p_under);
2474 if(meta && meta->nodeRemovalDisabled() == true)
2476 infostream<<"Server: Not finishing digging: "
2477 <<"Node metadata disables removal"
2479 cannot_remove_node = true;
2483 catch(InvalidPositionException &e)
2485 infostream<<"Server: Not finishing digging: Node not found."
2486 <<" Adding block to emerge queue."
2488 m_emerge_queue.addBlock(peer_id,
2489 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2490 cannot_remove_node = true;
2493 // Make sure the player is allowed to do it
2494 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2496 infostream<<"Player "<<player->getName()<<" cannot remove node"
2497 <<" because privileges are "<<getPlayerPrivs(player)
2499 cannot_remove_node = true;
2503 If node can't be removed, set block to be re-sent to
2506 if(cannot_remove_node)
2508 infostream<<"Server: Not finishing digging."<<std::endl;
2510 // Client probably has wrong data.
2511 // Set block not sent, so that client will get
2513 infostream<<"Client "<<peer_id<<" tried to dig "
2514 <<"node; but node cannot be removed."
2515 <<" setting MapBlock not sent."<<std::endl;
2516 RemoteClient *client = getClient(peer_id);
2517 v3s16 blockpos = getNodeBlockPos(p_under);
2518 client->SetBlockNotSent(blockpos);
2523 actionstream<<player->getName()<<" digs "<<PP(p_under)
2524 <<", gets material "<<(int)material<<", mineral "
2525 <<(int)mineral<<std::endl;
2528 Send the removal to all close-by players.
2529 - If other player is close, send REMOVENODE
2530 - Otherwise set blocks not sent
2532 core::list<u16> far_players;
2533 sendRemoveNode(p_under, peer_id, &far_players, 30);
2536 Update and send inventory
2539 if(g_settings->getBool("creative_mode") == false)
2544 InventoryList *mlist = player->inventory.getList("main");
2547 InventoryItem *item = mlist->getItem(item_i);
2548 if(item && (std::string)item->getName() == "ToolItem")
2550 ToolItem *titem = (ToolItem*)item;
2551 std::string toolname = titem->getToolName();
2553 // Get digging properties for material and tool
2554 DiggingProperties prop =
2555 getDiggingProperties(material, toolname);
2557 if(prop.diggable == false)
2559 infostream<<"Server: WARNING: Player digged"
2560 <<" with impossible material + tool"
2561 <<" combination"<<std::endl;
2564 bool weared_out = titem->addWear(prop.wear);
2568 mlist->deleteItem(item_i);
2574 Add dug item to inventory
2577 InventoryItem *item = NULL;
2579 if(mineral != MINERAL_NONE)
2580 item = getDiggedMineralItem(mineral);
2585 std::string &dug_s = content_features(material).dug_item;
2588 std::istringstream is(dug_s, std::ios::binary);
2589 item = InventoryItem::deSerialize(is);
2595 // Add a item to inventory
2596 player->inventory.addItem("main", item);
2599 UpdateCrafting(player->peer_id);
2600 SendInventory(player->peer_id);
2605 if(mineral != MINERAL_NONE)
2606 item = getDiggedMineralItem(mineral);
2611 std::string &extra_dug_s = content_features(material).extra_dug_item;
2612 s32 extra_rarity = content_features(material).extra_dug_item_rarity;
2613 if(extra_dug_s != "" && extra_rarity != 0
2614 && myrand() % extra_rarity == 0)
2616 std::istringstream is(extra_dug_s, std::ios::binary);
2617 item = InventoryItem::deSerialize(is);
2623 // Add a item to inventory
2624 player->inventory.addItem("main", item);
2627 UpdateCrafting(player->peer_id);
2628 SendInventory(player->peer_id);
2634 (this takes some time so it is done after the quick stuff)
2637 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
2639 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2642 Set blocks not sent to far players
2644 for(core::list<u16>::Iterator
2645 i = far_players.begin();
2646 i != far_players.end(); i++)
2649 RemoteClient *client = getClient(peer_id);
2652 client->SetBlocksNotSent(modified_blocks);
2659 else if(action == 1)
2662 InventoryList *ilist = player->inventory.getList("main");
2667 InventoryItem *item = ilist->getItem(item_i);
2669 // If there is no item, it is not possible to add it anywhere
2674 Handle material items
2676 if(std::string("MaterialItem") == item->getName())
2679 // Don't add a node if this is not a free space
2680 MapNode n2 = m_env.getMap().getNode(p_over);
2681 bool no_enough_privs =
2682 ((getPlayerPrivs(player) & PRIV_BUILD)==0);
2684 infostream<<"Player "<<player->getName()<<" cannot add node"
2685 <<" because privileges are "<<getPlayerPrivs(player)
2688 if(content_features(n2).buildable_to == false
2691 // Client probably has wrong data.
2692 // Set block not sent, so that client will get
2694 infostream<<"Client "<<peer_id<<" tried to place"
2695 <<" node in invalid position; setting"
2696 <<" MapBlock not sent."<<std::endl;
2697 RemoteClient *client = getClient(peer_id);
2698 v3s16 blockpos = getNodeBlockPos(p_over);
2699 client->SetBlockNotSent(blockpos);
2703 catch(InvalidPositionException &e)
2705 infostream<<"Server: Ignoring ADDNODE: Node not found"
2706 <<" Adding block to emerge queue."
2708 m_emerge_queue.addBlock(peer_id,
2709 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2713 // Reset build time counter
2714 getClient(peer->id)->m_time_from_building = 0.0;
2717 MaterialItem *mitem = (MaterialItem*)item;
2719 n.setContent(mitem->getMaterial());
2721 actionstream<<player->getName()<<" places material "
2722 <<(int)mitem->getMaterial()
2723 <<" at "<<PP(p_under)<<std::endl;
2725 // Calculate direction for wall mounted stuff
2726 if(content_features(n).wall_mounted)
2727 n.param2 = packDir(p_under - p_over);
2729 // Calculate the direction for furnaces and chests and stuff
2730 if(content_features(n).param_type == CPT_FACEDIR_SIMPLE)
2732 v3f playerpos = player->getPosition();
2733 v3f blockpos = intToFloat(p_over, BS) - playerpos;
2734 blockpos = blockpos.normalize();
2736 if (fabs(blockpos.X) > fabs(blockpos.Z)) {
2750 Send to all close-by players
2752 core::list<u16> far_players;
2753 sendAddNode(p_over, n, 0, &far_players, 30);
2758 InventoryList *ilist = player->inventory.getList("main");
2759 if(g_settings->getBool("creative_mode") == false && ilist)
2761 // Remove from inventory and send inventory
2762 if(mitem->getCount() == 1)
2763 ilist->deleteItem(item_i);
2767 UpdateCrafting(peer_id);
2768 SendInventory(peer_id);
2774 This takes some time so it is done after the quick stuff
2776 core::map<v3s16, MapBlock*> modified_blocks;
2778 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
2780 std::string p_name = std::string(player->getName());
2781 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks, p_name);
2784 Set blocks not sent to far players
2786 for(core::list<u16>::Iterator
2787 i = far_players.begin();
2788 i != far_players.end(); i++)
2791 RemoteClient *client = getClient(peer_id);
2794 client->SetBlocksNotSent(modified_blocks);
2798 Calculate special events
2801 /*if(n.d == CONTENT_MESE)
2804 for(s16 z=-1; z<=1; z++)
2805 for(s16 y=-1; y<=1; y++)
2806 for(s16 x=-1; x<=1; x++)
2813 Place other item (not a block)
2817 v3s16 blockpos = getNodeBlockPos(p_over);
2820 Check that the block is loaded so that the item
2821 can properly be added to the static list too
2823 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2826 infostream<<"Error while placing object: "
2827 "block not found"<<std::endl;
2832 If in creative mode, item dropping is disabled unless
2833 player has build privileges
2835 if(g_settings->getBool("creative_mode") &&
2836 (getPlayerPrivs(player) & PRIV_BUILD) == 0)
2838 infostream<<"Not allowing player to drop item: "
2839 "creative mode and no build privs"<<std::endl;
2843 // Calculate a position for it
2844 v3f pos = intToFloat(p_over, BS);
2846 pos.Y -= BS*0.25; // let it drop a bit
2848 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2849 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2854 ServerActiveObject *obj = item->createSAO(&m_env, 0, pos);
2858 infostream<<"WARNING: item resulted in NULL object, "
2859 <<"not placing onto map"
2864 actionstream<<player->getName()<<" places "<<item->getName()
2865 <<" at "<<PP(p_over)<<std::endl;
2867 // Add the object to the environment
2868 m_env.addActiveObject(obj);
2870 infostream<<"Placed object"<<std::endl;
2872 if(g_settings->getBool("creative_mode") == false)
2874 // Delete the right amount of items from the slot
2875 u16 dropcount = item->getDropCount();
2877 // Delete item if all gone
2878 if(item->getCount() <= dropcount)
2880 if(item->getCount() < dropcount)
2881 infostream<<"WARNING: Server: dropped more items"
2882 <<" than the slot contains"<<std::endl;
2884 InventoryList *ilist = player->inventory.getList("main");
2886 // Remove from inventory and send inventory
2887 ilist->deleteItem(item_i);
2889 // Else decrement it
2891 item->remove(dropcount);
2894 UpdateCrafting(peer_id);
2895 SendInventory(peer_id);
2903 Catch invalid actions
2907 infostream<<"WARNING: Server: Invalid action "
2908 <<action<<std::endl;
2912 else if(command == TOSERVER_RELEASE)
2921 infostream<<"TOSERVER_RELEASE ignored"<<std::endl;
2924 else if(command == TOSERVER_SIGNTEXT)
2926 infostream<<"Server: TOSERVER_SIGNTEXT not supported anymore"
2930 else if(command == TOSERVER_SIGNNODETEXT)
2932 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2940 std::string datastring((char*)&data[2], datasize-2);
2941 std::istringstream is(datastring, std::ios_base::binary);
2944 is.read((char*)buf, 6);
2945 v3s16 p = readV3S16(buf);
2946 is.read((char*)buf, 2);
2947 u16 textlen = readU16(buf);
2949 for(u16 i=0; i<textlen; i++)
2951 is.read((char*)buf, 1);
2952 text += (char)buf[0];
2955 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2958 if(meta->typeId() != CONTENT_SIGN_WALL)
2960 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
2961 signmeta->setText(text);
2963 actionstream<<player->getName()<<" writes \""<<text<<"\" to sign "
2964 <<" at "<<PP(p)<<std::endl;
2966 v3s16 blockpos = getNodeBlockPos(p);
2967 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2970 block->setChangedFlag();
2973 for(core::map<u16, RemoteClient*>::Iterator
2974 i = m_clients.getIterator();
2975 i.atEnd()==false; i++)
2977 RemoteClient *client = i.getNode()->getValue();
2978 client->SetBlockNotSent(blockpos);
2981 else if(command == TOSERVER_INVENTORY_ACTION)
2983 /*// Ignore inventory changes if in creative mode
2984 if(g_settings->getBool("creative_mode") == true)
2986 infostream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2990 // Strip command and create a stream
2991 std::string datastring((char*)&data[2], datasize-2);
2992 infostream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2993 std::istringstream is(datastring, std::ios_base::binary);
2995 InventoryAction *a = InventoryAction::deSerialize(is);
3000 c.current_player = player;
3003 Handle craftresult specially if not in creative mode
3005 bool disable_action = false;
3006 if(a->getType() == IACTION_MOVE
3007 && g_settings->getBool("creative_mode") == false)
3009 IMoveAction *ma = (IMoveAction*)a;
3010 if(ma->to_inv == "current_player" &&
3011 ma->from_inv == "current_player")
3013 InventoryList *rlist = player->inventory.getList("craftresult");
3015 InventoryList *clist = player->inventory.getList("craft");
3017 InventoryList *mlist = player->inventory.getList("main");
3020 Craftresult is no longer preview if something
3023 if(ma->to_list == "craftresult"
3024 && ma->from_list != "craftresult")
3026 // If it currently is a preview, remove
3028 if(player->craftresult_is_preview)
3030 rlist->deleteItem(0);
3032 player->craftresult_is_preview = false;
3035 Crafting takes place if this condition is true.
3037 if(player->craftresult_is_preview &&
3038 ma->from_list == "craftresult")
3040 player->craftresult_is_preview = false;
3041 clist->decrementMaterials(1);
3043 /* Print out action */
3044 InventoryList *list =
3045 player->inventory.getList("craftresult");
3047 InventoryItem *item = list->getItem(0);
3048 actionstream<<player->getName()<<" crafts "
3049 <<item->getName()<<std::endl;
3052 If the craftresult is placed on itself, move it to
3053 main inventory instead of doing the action
3055 if(ma->to_list == "craftresult"
3056 && ma->from_list == "craftresult")
3058 disable_action = true;
3060 InventoryItem *item1 = rlist->changeItem(0, NULL);
3061 mlist->addItem(item1);
3064 // Disallow moving items if not allowed to build
3065 else if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
3069 // if it's a locking chest, only allow the owner or server admins to move items
3070 else if (ma->from_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
3072 Strfnd fn(ma->from_inv);
3073 std::string id0 = fn.next(":");
3074 if(id0 == "nodemeta")
3077 p.X = stoi(fn.next(","));
3078 p.Y = stoi(fn.next(","));
3079 p.Z = stoi(fn.next(","));
3080 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3081 if(meta && meta->typeId() == CONTENT_LOCKABLE_CHEST) {
3082 LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
3083 if (lcm->getOwner() != player->getName())
3088 else if (ma->to_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
3090 Strfnd fn(ma->to_inv);
3091 std::string id0 = fn.next(":");
3092 if(id0 == "nodemeta")
3095 p.X = stoi(fn.next(","));
3096 p.Y = stoi(fn.next(","));
3097 p.Z = stoi(fn.next(","));
3098 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3099 if(meta && meta->typeId() == CONTENT_LOCKABLE_CHEST) {
3100 LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
3101 if (lcm->getOwner() != player->getName())
3108 if(disable_action == false)
3110 // Feed action to player inventory
3118 UpdateCrafting(player->peer_id);
3119 SendInventory(player->peer_id);
3124 infostream<<"TOSERVER_INVENTORY_ACTION: "
3125 <<"InventoryAction::deSerialize() returned NULL"
3129 else if(command == TOSERVER_CHAT_MESSAGE)
3137 std::string datastring((char*)&data[2], datasize-2);
3138 std::istringstream is(datastring, std::ios_base::binary);
3141 is.read((char*)buf, 2);
3142 u16 len = readU16(buf);
3144 std::wstring message;
3145 for(u16 i=0; i<len; i++)
3147 is.read((char*)buf, 2);
3148 message += (wchar_t)readU16(buf);
3151 // Get player name of this client
3152 std::wstring name = narrow_to_wide(player->getName());
3154 // Line to send to players
3156 // Whether to send to the player that sent the line
3157 bool send_to_sender = false;
3158 // Whether to send to other players
3159 bool send_to_others = false;
3161 // Local player gets all privileges regardless of
3162 // what's set on their account.
3163 u64 privs = getPlayerPrivs(player);
3166 if(message[0] == L'/')
3168 size_t strip_size = 1;
3169 if (message[1] == L'#') // support old-style commans
3171 message = message.substr(strip_size);
3173 WStrfnd f1(message);
3174 f1.next(L" "); // Skip over /#whatever
3175 std::wstring paramstring = f1.next(L"");
3177 ServerCommandContext *ctx = new ServerCommandContext(
3178 str_split(message, L' '),
3185 std::wstring reply(processServerCommand(ctx));
3186 send_to_sender = ctx->flags & SEND_TO_SENDER;
3187 send_to_others = ctx->flags & SEND_TO_OTHERS;
3189 if (ctx->flags & SEND_NO_PREFIX)
3192 line += L"Server: " + reply;
3199 if(privs & PRIV_SHOUT)
3205 send_to_others = true;
3209 line += L"Server: You are not allowed to shout";
3210 send_to_sender = true;
3217 actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
3220 Send the message to clients
3222 for(core::map<u16, RemoteClient*>::Iterator
3223 i = m_clients.getIterator();
3224 i.atEnd() == false; i++)
3226 // Get client and check that it is valid
3227 RemoteClient *client = i.getNode()->getValue();
3228 assert(client->peer_id == i.getNode()->getKey());
3229 if(client->serialization_version == SER_FMT_VER_INVALID)
3233 bool sender_selected = (peer_id == client->peer_id);
3234 if(sender_selected == true && send_to_sender == false)
3236 if(sender_selected == false && send_to_others == false)
3239 SendChatMessage(client->peer_id, line);
3243 else if(command == TOSERVER_DAMAGE)
3245 std::string datastring((char*)&data[2], datasize-2);
3246 std::istringstream is(datastring, std::ios_base::binary);
3247 u8 damage = readU8(is);
3249 if(g_settings->getBool("enable_damage"))
3251 actionstream<<player->getName()<<" damaged by "
3252 <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
3255 HandlePlayerHP(player, damage);
3259 SendPlayerHP(player);
3262 else if(command == TOSERVER_PASSWORD)
3265 [0] u16 TOSERVER_PASSWORD
3266 [2] u8[28] old password
3267 [30] u8[28] new password
3270 if(datasize != 2+PASSWORD_SIZE*2)
3272 /*char password[PASSWORD_SIZE];
3273 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3274 password[i] = data[2+i];
3275 password[PASSWORD_SIZE-1] = 0;*/
3277 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3285 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3287 char c = data[2+PASSWORD_SIZE+i];
3293 infostream<<"Server: Client requests a password change from "
3294 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
3296 std::string playername = player->getName();
3298 if(m_authmanager.exists(playername) == false)
3300 infostream<<"Server: playername not found in authmanager"<<std::endl;
3301 // Wrong old password supplied!!
3302 SendChatMessage(peer_id, L"playername not found in authmanager");
3306 std::string checkpwd = m_authmanager.getPassword(playername);
3308 if(oldpwd != checkpwd)
3310 infostream<<"Server: invalid old password"<<std::endl;
3311 // Wrong old password supplied!!
3312 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
3316 actionstream<<player->getName()<<" changes password"<<std::endl;
3318 m_authmanager.setPassword(playername, newpwd);
3320 infostream<<"Server: password change successful for "<<playername
3322 SendChatMessage(peer_id, L"Password change successful");
3324 else if(command == TOSERVER_PLAYERITEM)
3329 u16 item = readU16(&data[2]);
3330 player->wieldItem(item);
3331 SendWieldedItem(player);
3333 else if(command == TOSERVER_RESPAWN)
3338 RespawnPlayer(player);
3340 actionstream<<player->getName()<<" respawns at "
3341 <<PP(player->getPosition()/BS)<<std::endl;
3345 infostream<<"Server::ProcessData(): Ignoring "
3346 "unknown command "<<command<<std::endl;
3350 catch(SendFailedException &e)
3352 errorstream<<"Server::ProcessData(): SendFailedException: "
3358 void Server::onMapEditEvent(MapEditEvent *event)
3360 //infostream<<"Server::onMapEditEvent()"<<std::endl;
3361 if(m_ignore_map_edit_events)
3363 MapEditEvent *e = event->clone();
3364 m_unsent_map_edit_queue.push_back(e);
3367 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3369 if(id == "current_player")
3371 assert(c->current_player);
3372 return &(c->current_player->inventory);
3376 std::string id0 = fn.next(":");
3378 if(id0 == "nodemeta")
3381 p.X = stoi(fn.next(","));
3382 p.Y = stoi(fn.next(","));
3383 p.Z = stoi(fn.next(","));
3384 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3386 return meta->getInventory();
3387 infostream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3388 <<"no metadata found"<<std::endl;
3392 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3395 void Server::inventoryModified(InventoryContext *c, std::string id)
3397 if(id == "current_player")
3399 assert(c->current_player);
3401 UpdateCrafting(c->current_player->peer_id);
3402 SendInventory(c->current_player->peer_id);
3407 std::string id0 = fn.next(":");
3409 if(id0 == "nodemeta")
3412 p.X = stoi(fn.next(","));
3413 p.Y = stoi(fn.next(","));
3414 p.Z = stoi(fn.next(","));
3415 v3s16 blockpos = getNodeBlockPos(p);
3417 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3419 meta->inventoryModified();
3421 for(core::map<u16, RemoteClient*>::Iterator
3422 i = m_clients.getIterator();
3423 i.atEnd()==false; i++)
3425 RemoteClient *client = i.getNode()->getValue();
3426 client->SetBlockNotSent(blockpos);
3432 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3435 core::list<PlayerInfo> Server::getPlayerInfo()
3437 DSTACK(__FUNCTION_NAME);
3438 JMutexAutoLock envlock(m_env_mutex);
3439 JMutexAutoLock conlock(m_con_mutex);
3441 core::list<PlayerInfo> list;
3443 core::list<Player*> players = m_env.getPlayers();
3445 core::list<Player*>::Iterator i;
3446 for(i = players.begin();
3447 i != players.end(); i++)
3451 Player *player = *i;
3454 con::Peer *peer = m_con.GetPeer(player->peer_id);
3455 // Copy info from peer to info struct
3457 info.address = peer->address;
3458 info.avg_rtt = peer->avg_rtt;
3460 catch(con::PeerNotFoundException &e)
3462 // Set dummy peer info
3464 info.address = Address(0,0,0,0,0);
3468 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3469 info.position = player->getPosition();
3471 list.push_back(info);
3478 void Server::peerAdded(con::Peer *peer)
3480 DSTACK(__FUNCTION_NAME);
3481 infostream<<"Server::peerAdded(): peer->id="
3482 <<peer->id<<std::endl;
3485 c.type = PEER_ADDED;
3486 c.peer_id = peer->id;
3488 m_peer_change_queue.push_back(c);
3491 void Server::deletingPeer(con::Peer *peer, bool timeout)
3493 DSTACK(__FUNCTION_NAME);
3494 infostream<<"Server::deletingPeer(): peer->id="
3495 <<peer->id<<", timeout="<<timeout<<std::endl;
3498 c.type = PEER_REMOVED;
3499 c.peer_id = peer->id;
3500 c.timeout = timeout;
3501 m_peer_change_queue.push_back(c);
3508 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3510 DSTACK(__FUNCTION_NAME);
3511 std::ostringstream os(std::ios_base::binary);
3513 writeU16(os, TOCLIENT_HP);
3517 std::string s = os.str();
3518 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3520 con.Send(peer_id, 0, data, true);
3523 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3524 const std::wstring &reason)
3526 DSTACK(__FUNCTION_NAME);
3527 std::ostringstream os(std::ios_base::binary);
3529 writeU16(os, TOCLIENT_ACCESS_DENIED);
3530 os<<serializeWideString(reason);
3533 std::string s = os.str();
3534 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3536 con.Send(peer_id, 0, data, true);
3539 void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
3540 bool set_camera_point_target, v3f camera_point_target)
3542 DSTACK(__FUNCTION_NAME);
3543 std::ostringstream os(std::ios_base::binary);
3545 writeU16(os, TOCLIENT_DEATHSCREEN);
3546 writeU8(os, set_camera_point_target);
3547 writeV3F1000(os, camera_point_target);
3550 std::string s = os.str();
3551 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3553 con.Send(peer_id, 0, data, true);
3557 Non-static send methods
3560 void Server::SendObjectData(float dtime)
3562 DSTACK(__FUNCTION_NAME);
3564 core::map<v3s16, bool> stepped_blocks;
3566 for(core::map<u16, RemoteClient*>::Iterator
3567 i = m_clients.getIterator();
3568 i.atEnd() == false; i++)
3570 u16 peer_id = i.getNode()->getKey();
3571 RemoteClient *client = i.getNode()->getValue();
3572 assert(client->peer_id == peer_id);
3574 if(client->serialization_version == SER_FMT_VER_INVALID)
3577 client->SendObjectData(this, dtime, stepped_blocks);
3581 void Server::SendPlayerInfos()
3583 DSTACK(__FUNCTION_NAME);
3585 //JMutexAutoLock envlock(m_env_mutex);
3587 // Get connected players
3588 core::list<Player*> players = m_env.getPlayers(true);
3590 u32 player_count = players.getSize();
3591 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3593 SharedBuffer<u8> data(datasize);
3594 writeU16(&data[0], TOCLIENT_PLAYERINFO);
3597 core::list<Player*>::Iterator i;
3598 for(i = players.begin();
3599 i != players.end(); i++)
3601 Player *player = *i;
3603 /*infostream<<"Server sending player info for player with "
3604 "peer_id="<<player->peer_id<<std::endl;*/
3606 writeU16(&data[start], player->peer_id);
3607 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3608 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3609 start += 2+PLAYERNAME_SIZE;
3612 //JMutexAutoLock conlock(m_con_mutex);
3615 m_con.SendToAll(0, data, true);
3618 void Server::SendInventory(u16 peer_id)
3620 DSTACK(__FUNCTION_NAME);
3622 Player* player = m_env.getPlayer(peer_id);
3629 std::ostringstream os;
3630 //os.imbue(std::locale("C"));
3632 player->inventory.serialize(os);
3634 std::string s = os.str();
3636 SharedBuffer<u8> data(s.size()+2);
3637 writeU16(&data[0], TOCLIENT_INVENTORY);
3638 memcpy(&data[2], s.c_str(), s.size());
3641 m_con.Send(peer_id, 0, data, true);
3644 std::string getWieldedItemString(const Player *player)
3646 const InventoryItem *item = player->getWieldItem();
3648 return std::string("");
3649 std::ostringstream os(std::ios_base::binary);
3650 item->serialize(os);
3654 void Server::SendWieldedItem(const Player* player)
3656 DSTACK(__FUNCTION_NAME);
3660 std::ostringstream os(std::ios_base::binary);
3662 writeU16(os, TOCLIENT_PLAYERITEM);
3664 writeU16(os, player->peer_id);
3665 os<<serializeString(getWieldedItemString(player));
3668 std::string s = os.str();
3669 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3671 m_con.SendToAll(0, data, true);
3674 void Server::SendPlayerItems()
3676 DSTACK(__FUNCTION_NAME);
3678 std::ostringstream os(std::ios_base::binary);
3679 core::list<Player *> players = m_env.getPlayers(true);
3681 writeU16(os, TOCLIENT_PLAYERITEM);
3682 writeU16(os, players.size());
3683 core::list<Player *>::Iterator i;
3684 for(i = players.begin(); i != players.end(); ++i)
3687 writeU16(os, p->peer_id);
3688 os<<serializeString(getWieldedItemString(p));
3692 std::string s = os.str();
3693 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3695 m_con.SendToAll(0, data, true);
3698 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3700 DSTACK(__FUNCTION_NAME);
3702 std::ostringstream os(std::ios_base::binary);
3706 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3707 os.write((char*)buf, 2);
3710 writeU16(buf, message.size());
3711 os.write((char*)buf, 2);
3714 for(u32 i=0; i<message.size(); i++)
3718 os.write((char*)buf, 2);
3722 std::string s = os.str();
3723 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3725 m_con.Send(peer_id, 0, data, true);
3728 void Server::BroadcastChatMessage(const std::wstring &message)
3730 for(core::map<u16, RemoteClient*>::Iterator
3731 i = m_clients.getIterator();
3732 i.atEnd() == false; i++)
3734 // Get client and check that it is valid
3735 RemoteClient *client = i.getNode()->getValue();
3736 assert(client->peer_id == i.getNode()->getKey());
3737 if(client->serialization_version == SER_FMT_VER_INVALID)
3740 SendChatMessage(client->peer_id, message);
3744 void Server::SendPlayerHP(Player *player)
3746 SendHP(m_con, player->peer_id, player->hp);
3749 void Server::SendMovePlayer(Player *player)
3751 DSTACK(__FUNCTION_NAME);
3752 std::ostringstream os(std::ios_base::binary);
3754 writeU16(os, TOCLIENT_MOVE_PLAYER);
3755 writeV3F1000(os, player->getPosition());
3756 writeF1000(os, player->getPitch());
3757 writeF1000(os, player->getYaw());
3760 v3f pos = player->getPosition();
3761 f32 pitch = player->getPitch();
3762 f32 yaw = player->getYaw();
3763 infostream<<"Server sending TOCLIENT_MOVE_PLAYER"
3764 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3771 std::string s = os.str();
3772 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3774 m_con.Send(player->peer_id, 0, data, true);
3777 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3778 core::list<u16> *far_players, float far_d_nodes)
3780 float maxd = far_d_nodes*BS;
3781 v3f p_f = intToFloat(p, BS);
3785 SharedBuffer<u8> reply(replysize);
3786 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3787 writeS16(&reply[2], p.X);
3788 writeS16(&reply[4], p.Y);
3789 writeS16(&reply[6], p.Z);
3791 for(core::map<u16, RemoteClient*>::Iterator
3792 i = m_clients.getIterator();
3793 i.atEnd() == false; i++)
3795 // Get client and check that it is valid
3796 RemoteClient *client = i.getNode()->getValue();
3797 assert(client->peer_id == i.getNode()->getKey());
3798 if(client->serialization_version == SER_FMT_VER_INVALID)
3801 // Don't send if it's the same one
3802 if(client->peer_id == ignore_id)
3808 Player *player = m_env.getPlayer(client->peer_id);
3811 // If player is far away, only set modified blocks not sent
3812 v3f player_pos = player->getPosition();
3813 if(player_pos.getDistanceFrom(p_f) > maxd)
3815 far_players->push_back(client->peer_id);
3822 m_con.Send(client->peer_id, 0, reply, true);
3826 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3827 core::list<u16> *far_players, float far_d_nodes)
3829 float maxd = far_d_nodes*BS;
3830 v3f p_f = intToFloat(p, BS);
3832 for(core::map<u16, RemoteClient*>::Iterator
3833 i = m_clients.getIterator();
3834 i.atEnd() == false; i++)
3836 // Get client and check that it is valid
3837 RemoteClient *client = i.getNode()->getValue();
3838 assert(client->peer_id == i.getNode()->getKey());
3839 if(client->serialization_version == SER_FMT_VER_INVALID)
3842 // Don't send if it's the same one
3843 if(client->peer_id == ignore_id)
3849 Player *player = m_env.getPlayer(client->peer_id);
3852 // If player is far away, only set modified blocks not sent
3853 v3f player_pos = player->getPosition();
3854 if(player_pos.getDistanceFrom(p_f) > maxd)
3856 far_players->push_back(client->peer_id);
3863 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3864 SharedBuffer<u8> reply(replysize);
3865 writeU16(&reply[0], TOCLIENT_ADDNODE);
3866 writeS16(&reply[2], p.X);
3867 writeS16(&reply[4], p.Y);
3868 writeS16(&reply[6], p.Z);
3869 n.serialize(&reply[8], client->serialization_version);
3872 m_con.Send(client->peer_id, 0, reply, true);
3876 void Server::setBlockNotSent(v3s16 p)
3878 for(core::map<u16, RemoteClient*>::Iterator
3879 i = m_clients.getIterator();
3880 i.atEnd()==false; i++)
3882 RemoteClient *client = i.getNode()->getValue();
3883 client->SetBlockNotSent(p);
3887 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3889 DSTACK(__FUNCTION_NAME);
3891 v3s16 p = block->getPos();
3895 bool completely_air = true;
3896 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3897 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3898 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3900 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
3902 completely_air = false;
3903 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
3908 infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
3910 infostream<<"[completely air] ";
3911 infostream<<std::endl;
3915 Create a packet with the block in the right format
3918 std::ostringstream os(std::ios_base::binary);
3919 block->serialize(os, ver);
3920 std::string s = os.str();
3921 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3923 u32 replysize = 8 + blockdata.getSize();
3924 SharedBuffer<u8> reply(replysize);
3925 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3926 writeS16(&reply[2], p.X);
3927 writeS16(&reply[4], p.Y);
3928 writeS16(&reply[6], p.Z);
3929 memcpy(&reply[8], *blockdata, blockdata.getSize());
3931 /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3932 <<": \tpacket size: "<<replysize<<std::endl;*/
3937 m_con.Send(peer_id, 1, reply, true);
3940 void Server::SendBlocks(float dtime)
3942 DSTACK(__FUNCTION_NAME);
3944 JMutexAutoLock envlock(m_env_mutex);
3945 JMutexAutoLock conlock(m_con_mutex);
3947 //TimeTaker timer("Server::SendBlocks");
3949 core::array<PrioritySortedBlockTransfer> queue;
3951 s32 total_sending = 0;
3954 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
3956 for(core::map<u16, RemoteClient*>::Iterator
3957 i = m_clients.getIterator();
3958 i.atEnd() == false; i++)
3960 RemoteClient *client = i.getNode()->getValue();
3961 assert(client->peer_id == i.getNode()->getKey());
3963 total_sending += client->SendingCount();
3965 if(client->serialization_version == SER_FMT_VER_INVALID)
3968 client->GetNextBlocks(this, dtime, queue);
3973 // Lowest priority number comes first.
3974 // Lowest is most important.
3977 for(u32 i=0; i<queue.size(); i++)
3979 //TODO: Calculate limit dynamically
3980 if(total_sending >= g_settings->getS32
3981 ("max_simultaneous_block_sends_server_total"))
3984 PrioritySortedBlockTransfer q = queue[i];
3986 MapBlock *block = NULL;
3989 block = m_env.getMap().getBlockNoCreate(q.pos);
3991 catch(InvalidPositionException &e)
3996 RemoteClient *client = getClient(q.peer_id);
3998 SendBlockNoLock(q.peer_id, block, client->serialization_version);
4000 client->SentBlock(q.pos);
4010 void Server::HandlePlayerHP(Player *player, s16 damage)
4012 if(player->hp > damage)
4014 player->hp -= damage;
4015 SendPlayerHP(player);
4019 infostream<<"Server::HandlePlayerHP(): Player "
4020 <<player->getName()<<" dies"<<std::endl;
4024 //TODO: Throw items around
4026 // Handle players that are not connected
4027 if(player->peer_id == PEER_ID_INEXISTENT){
4028 RespawnPlayer(player);
4032 SendPlayerHP(player);
4034 RemoteClient *client = getClient(player->peer_id);
4035 if(client->net_proto_version >= 3)
4037 SendDeathscreen(m_con, player->peer_id, false, v3f(0,0,0));
4041 RespawnPlayer(player);
4046 void Server::RespawnPlayer(Player *player)
4048 v3f pos = findSpawnPos(m_env.getServerMap());
4049 player->setPosition(pos);
4051 SendMovePlayer(player);
4052 SendPlayerHP(player);
4055 void Server::UpdateCrafting(u16 peer_id)
4057 DSTACK(__FUNCTION_NAME);
4059 Player* player = m_env.getPlayer(peer_id);
4063 Calculate crafting stuff
4065 if(g_settings->getBool("creative_mode") == false)
4067 InventoryList *clist = player->inventory.getList("craft");
4068 InventoryList *rlist = player->inventory.getList("craftresult");
4070 if(rlist && rlist->getUsedSlots() == 0)
4071 player->craftresult_is_preview = true;
4073 if(rlist && player->craftresult_is_preview)
4075 rlist->clearItems();
4077 if(clist && rlist && player->craftresult_is_preview)
4079 InventoryItem *items[9];
4080 for(u16 i=0; i<9; i++)
4082 items[i] = clist->getItem(i);
4085 // Get result of crafting grid
4086 InventoryItem *result = craft_get_result(items);
4088 rlist->addItem(result);
4091 } // if creative_mode == false
4094 RemoteClient* Server::getClient(u16 peer_id)
4096 DSTACK(__FUNCTION_NAME);
4097 //JMutexAutoLock lock(m_con_mutex);
4098 core::map<u16, RemoteClient*>::Node *n;
4099 n = m_clients.find(peer_id);
4100 // A client should exist for all peers
4102 return n->getValue();
4105 std::wstring Server::getStatusString()
4107 std::wostringstream os(std::ios_base::binary);
4110 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4112 os<<L", uptime="<<m_uptime.get();
4113 // Information about clients
4115 for(core::map<u16, RemoteClient*>::Iterator
4116 i = m_clients.getIterator();
4117 i.atEnd() == false; i++)
4119 // Get client and check that it is valid
4120 RemoteClient *client = i.getNode()->getValue();
4121 assert(client->peer_id == i.getNode()->getKey());
4122 if(client->serialization_version == SER_FMT_VER_INVALID)
4125 Player *player = m_env.getPlayer(client->peer_id);
4126 // Get name of player
4127 std::wstring name = L"unknown";
4129 name = narrow_to_wide(player->getName());
4130 // Add name to information string
4134 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
4135 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4136 if(g_settings->get("motd") != "")
4137 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4141 // Saves g_settings to configpath given at initialization
4142 void Server::saveConfig()
4144 if(m_configpath != "")
4145 g_settings->updateConfigFile(m_configpath.c_str());
4148 v3f findSpawnPos(ServerMap &map)
4150 //return v3f(50,50,50)*BS;
4153 s16 groundheight = 0;
4156 nodepos = v2s16(0,0);
4161 // Try to find a good place a few times
4162 for(s32 i=0; i<1000; i++)
4165 // We're going to try to throw the player to this position
4166 nodepos = v2s16(-range + (myrand()%(range*2)),
4167 -range + (myrand()%(range*2)));
4168 v2s16 sectorpos = getNodeSectorPos(nodepos);
4169 // Get sector (NOTE: Don't get because it's slow)
4170 //m_env.getMap().emergeSector(sectorpos);
4171 // Get ground height at point (fallbacks to heightmap function)
4172 groundheight = map.findGroundLevel(nodepos);
4173 // Don't go underwater
4174 if(groundheight < WATER_LEVEL)
4176 //infostream<<"-> Underwater"<<std::endl;
4179 // Don't go to high places
4180 if(groundheight > WATER_LEVEL + 4)
4182 //infostream<<"-> Underwater"<<std::endl;
4186 // Found a good place
4187 //infostream<<"Searched through "<<i<<" places."<<std::endl;
4192 // If no suitable place was not found, go above water at least.
4193 if(groundheight < WATER_LEVEL)
4194 groundheight = WATER_LEVEL;
4196 return intToFloat(v3s16(
4203 Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id)
4206 Try to get an existing player
4208 Player *player = m_env.getPlayer(name);
4211 // If player is already connected, cancel
4212 if(player->peer_id != 0)
4214 infostream<<"emergePlayer(): Player already connected"<<std::endl;
4219 player->peer_id = peer_id;
4221 // Reset inventory to creative if in creative mode
4222 if(g_settings->getBool("creative_mode"))
4224 // Warning: double code below
4225 // Backup actual inventory
4226 player->inventory_backup = new Inventory();
4227 *(player->inventory_backup) = player->inventory;
4228 // Set creative inventory
4229 craft_set_creative_inventory(player);
4236 If player with the wanted peer_id already exists, cancel.
4238 if(m_env.getPlayer(peer_id) != NULL)
4240 infostream<<"emergePlayer(): Player with wrong name but same"
4241 " peer_id already exists"<<std::endl;
4249 player = new ServerRemotePlayer();
4250 //player->peer_id = c.peer_id;
4251 //player->peer_id = PEER_ID_INEXISTENT;
4252 player->peer_id = peer_id;
4253 player->updateName(name);
4254 m_authmanager.add(name);
4255 m_authmanager.setPassword(name, password);
4256 m_authmanager.setPrivs(name,
4257 stringToPrivs(g_settings->get("default_privs")));
4263 infostream<<"Server: Finding spawn place for player \""
4264 <<player->getName()<<"\""<<std::endl;
4266 v3f pos = findSpawnPos(m_env.getServerMap());
4268 player->setPosition(pos);
4271 Add player to environment
4274 m_env.addPlayer(player);
4277 Add stuff to inventory
4280 if(g_settings->getBool("creative_mode"))
4282 // Warning: double code above
4283 // Backup actual inventory
4284 player->inventory_backup = new Inventory();
4285 *(player->inventory_backup) = player->inventory;
4286 // Set creative inventory
4287 craft_set_creative_inventory(player);
4289 else if(g_settings->getBool("give_initial_stuff"))
4291 craft_give_initial_stuff(player);
4296 } // create new player
4299 void Server::handlePeerChange(PeerChange &c)
4301 JMutexAutoLock envlock(m_env_mutex);
4302 JMutexAutoLock conlock(m_con_mutex);
4304 if(c.type == PEER_ADDED)
4311 core::map<u16, RemoteClient*>::Node *n;
4312 n = m_clients.find(c.peer_id);
4313 // The client shouldn't already exist
4317 RemoteClient *client = new RemoteClient();
4318 client->peer_id = c.peer_id;
4319 m_clients.insert(client->peer_id, client);
4322 else if(c.type == PEER_REMOVED)
4329 core::map<u16, RemoteClient*>::Node *n;
4330 n = m_clients.find(c.peer_id);
4331 // The client should exist
4335 Mark objects to be not known by the client
4337 RemoteClient *client = n->getValue();
4339 for(core::map<u16, bool>::Iterator
4340 i = client->m_known_objects.getIterator();
4341 i.atEnd()==false; i++)
4344 u16 id = i.getNode()->getKey();
4345 ServerActiveObject* obj = m_env.getActiveObject(id);
4347 if(obj && obj->m_known_by_count > 0)
4348 obj->m_known_by_count--;
4351 // Collect information about leaving in chat
4352 std::wstring message;
4354 Player *player = m_env.getPlayer(c.peer_id);
4357 std::wstring name = narrow_to_wide(player->getName());
4360 message += L" left game";
4362 message += L" (timed out)";
4368 m_env.removePlayer(c.peer_id);
4371 // Set player client disconnected
4373 Player *player = m_env.getPlayer(c.peer_id);
4375 player->peer_id = 0;
4382 std::ostringstream os(std::ios_base::binary);
4383 for(core::map<u16, RemoteClient*>::Iterator
4384 i = m_clients.getIterator();
4385 i.atEnd() == false; i++)
4387 RemoteClient *client = i.getNode()->getValue();
4388 assert(client->peer_id == i.getNode()->getKey());
4389 if(client->serialization_version == SER_FMT_VER_INVALID)
4392 Player *player = m_env.getPlayer(client->peer_id);
4395 // Get name of player
4396 os<<player->getName()<<" ";
4399 actionstream<<player->getName()
4400 <<" leaves game. List of players: "
4401 <<os.str()<<std::endl;
4406 delete m_clients[c.peer_id];
4407 m_clients.remove(c.peer_id);
4409 // Send player info to all remaining clients
4412 // Send leave chat message to all remaining clients
4413 BroadcastChatMessage(message);
4422 void Server::handlePeerChanges()
4424 while(m_peer_change_queue.size() > 0)
4426 PeerChange c = m_peer_change_queue.pop_front();
4428 infostream<<"Server: Handling peer change: "
4429 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4432 handlePeerChange(c);
4436 u64 Server::getPlayerPrivs(Player *player)
4440 std::string playername = player->getName();
4441 // Local player gets all privileges regardless of
4442 // what's set on their account.
4443 if(g_settings->get("name") == playername)
4449 return getPlayerAuthPrivs(playername);
4453 void dedicated_server_loop(Server &server, bool &kill)
4455 DSTACK(__FUNCTION_NAME);
4457 infostream<<DTIME<<std::endl;
4458 infostream<<"========================"<<std::endl;
4459 infostream<<"Running dedicated server"<<std::endl;
4460 infostream<<"========================"<<std::endl;
4461 infostream<<std::endl;
4463 IntervalLimiter m_profiler_interval;
4467 // This is kind of a hack but can be done like this
4468 // because server.step() is very light
4470 ScopeProfiler sp(g_profiler, "dedicated server sleep");
4475 if(server.getShutdownRequested() || kill)
4477 infostream<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4484 float profiler_print_interval =
4485 g_settings->getFloat("profiler_print_interval");
4486 if(profiler_print_interval != 0)
4488 if(m_profiler_interval.step(0.030, profiler_print_interval))
4490 infostream<<"Profiler:"<<std::endl;
4491 g_profiler->print(infostream);
4492 g_profiler->clear();
4499 static int counter = 0;
4505 core::list<PlayerInfo> list = server.getPlayerInfo();
4506 core::list<PlayerInfo>::Iterator i;
4507 static u32 sum_old = 0;
4508 u32 sum = PIChecksum(list);
4511 infostream<<DTIME<<"Player info:"<<std::endl;
4512 for(i=list.begin(); i!=list.end(); i++)
4514 i->PrintLine(&infostream);