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 std::string itemname = "NULL";
3050 itemname = item->getName();
3051 actionstream<<player->getName()<<" crafts "
3052 <<itemname<<std::endl;
3055 If the craftresult is placed on itself, move it to
3056 main inventory instead of doing the action
3058 if(ma->to_list == "craftresult"
3059 && ma->from_list == "craftresult")
3061 disable_action = true;
3063 InventoryItem *item1 = rlist->changeItem(0, NULL);
3064 mlist->addItem(item1);
3067 // Disallow moving items if not allowed to build
3068 else if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
3072 // if it's a locking chest, only allow the owner or server admins to move items
3073 else if (ma->from_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
3075 Strfnd fn(ma->from_inv);
3076 std::string id0 = fn.next(":");
3077 if(id0 == "nodemeta")
3080 p.X = stoi(fn.next(","));
3081 p.Y = stoi(fn.next(","));
3082 p.Z = stoi(fn.next(","));
3083 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3084 if(meta && meta->typeId() == CONTENT_LOCKABLE_CHEST) {
3085 LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
3086 if (lcm->getOwner() != player->getName())
3091 else if (ma->to_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
3093 Strfnd fn(ma->to_inv);
3094 std::string id0 = fn.next(":");
3095 if(id0 == "nodemeta")
3098 p.X = stoi(fn.next(","));
3099 p.Y = stoi(fn.next(","));
3100 p.Z = stoi(fn.next(","));
3101 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3102 if(meta && meta->typeId() == CONTENT_LOCKABLE_CHEST) {
3103 LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
3104 if (lcm->getOwner() != player->getName())
3111 if(disable_action == false)
3113 // Feed action to player inventory
3121 UpdateCrafting(player->peer_id);
3122 SendInventory(player->peer_id);
3127 infostream<<"TOSERVER_INVENTORY_ACTION: "
3128 <<"InventoryAction::deSerialize() returned NULL"
3132 else if(command == TOSERVER_CHAT_MESSAGE)
3140 std::string datastring((char*)&data[2], datasize-2);
3141 std::istringstream is(datastring, std::ios_base::binary);
3144 is.read((char*)buf, 2);
3145 u16 len = readU16(buf);
3147 std::wstring message;
3148 for(u16 i=0; i<len; i++)
3150 is.read((char*)buf, 2);
3151 message += (wchar_t)readU16(buf);
3154 // Get player name of this client
3155 std::wstring name = narrow_to_wide(player->getName());
3157 // Line to send to players
3159 // Whether to send to the player that sent the line
3160 bool send_to_sender = false;
3161 // Whether to send to other players
3162 bool send_to_others = false;
3164 // Local player gets all privileges regardless of
3165 // what's set on their account.
3166 u64 privs = getPlayerPrivs(player);
3169 if(message[0] == L'/')
3171 size_t strip_size = 1;
3172 if (message[1] == L'#') // support old-style commans
3174 message = message.substr(strip_size);
3176 WStrfnd f1(message);
3177 f1.next(L" "); // Skip over /#whatever
3178 std::wstring paramstring = f1.next(L"");
3180 ServerCommandContext *ctx = new ServerCommandContext(
3181 str_split(message, L' '),
3188 std::wstring reply(processServerCommand(ctx));
3189 send_to_sender = ctx->flags & SEND_TO_SENDER;
3190 send_to_others = ctx->flags & SEND_TO_OTHERS;
3192 if (ctx->flags & SEND_NO_PREFIX)
3195 line += L"Server: " + reply;
3202 if(privs & PRIV_SHOUT)
3208 send_to_others = true;
3212 line += L"Server: You are not allowed to shout";
3213 send_to_sender = true;
3220 actionstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
3223 Send the message to clients
3225 for(core::map<u16, RemoteClient*>::Iterator
3226 i = m_clients.getIterator();
3227 i.atEnd() == false; i++)
3229 // Get client and check that it is valid
3230 RemoteClient *client = i.getNode()->getValue();
3231 assert(client->peer_id == i.getNode()->getKey());
3232 if(client->serialization_version == SER_FMT_VER_INVALID)
3236 bool sender_selected = (peer_id == client->peer_id);
3237 if(sender_selected == true && send_to_sender == false)
3239 if(sender_selected == false && send_to_others == false)
3242 SendChatMessage(client->peer_id, line);
3246 else if(command == TOSERVER_DAMAGE)
3248 std::string datastring((char*)&data[2], datasize-2);
3249 std::istringstream is(datastring, std::ios_base::binary);
3250 u8 damage = readU8(is);
3252 if(g_settings->getBool("enable_damage"))
3254 actionstream<<player->getName()<<" damaged by "
3255 <<(int)damage<<" hp at "<<PP(player->getPosition()/BS)
3258 HandlePlayerHP(player, damage);
3262 SendPlayerHP(player);
3265 else if(command == TOSERVER_PASSWORD)
3268 [0] u16 TOSERVER_PASSWORD
3269 [2] u8[28] old password
3270 [30] u8[28] new password
3273 if(datasize != 2+PASSWORD_SIZE*2)
3275 /*char password[PASSWORD_SIZE];
3276 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3277 password[i] = data[2+i];
3278 password[PASSWORD_SIZE-1] = 0;*/
3280 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3288 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3290 char c = data[2+PASSWORD_SIZE+i];
3296 infostream<<"Server: Client requests a password change from "
3297 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
3299 std::string playername = player->getName();
3301 if(m_authmanager.exists(playername) == false)
3303 infostream<<"Server: playername not found in authmanager"<<std::endl;
3304 // Wrong old password supplied!!
3305 SendChatMessage(peer_id, L"playername not found in authmanager");
3309 std::string checkpwd = m_authmanager.getPassword(playername);
3311 if(oldpwd != checkpwd)
3313 infostream<<"Server: invalid old password"<<std::endl;
3314 // Wrong old password supplied!!
3315 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
3319 actionstream<<player->getName()<<" changes password"<<std::endl;
3321 m_authmanager.setPassword(playername, newpwd);
3323 infostream<<"Server: password change successful for "<<playername
3325 SendChatMessage(peer_id, L"Password change successful");
3327 else if(command == TOSERVER_PLAYERITEM)
3332 u16 item = readU16(&data[2]);
3333 player->wieldItem(item);
3334 SendWieldedItem(player);
3336 else if(command == TOSERVER_RESPAWN)
3341 RespawnPlayer(player);
3343 actionstream<<player->getName()<<" respawns at "
3344 <<PP(player->getPosition()/BS)<<std::endl;
3348 infostream<<"Server::ProcessData(): Ignoring "
3349 "unknown command "<<command<<std::endl;
3353 catch(SendFailedException &e)
3355 errorstream<<"Server::ProcessData(): SendFailedException: "
3361 void Server::onMapEditEvent(MapEditEvent *event)
3363 //infostream<<"Server::onMapEditEvent()"<<std::endl;
3364 if(m_ignore_map_edit_events)
3366 MapEditEvent *e = event->clone();
3367 m_unsent_map_edit_queue.push_back(e);
3370 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3372 if(id == "current_player")
3374 assert(c->current_player);
3375 return &(c->current_player->inventory);
3379 std::string id0 = fn.next(":");
3381 if(id0 == "nodemeta")
3384 p.X = stoi(fn.next(","));
3385 p.Y = stoi(fn.next(","));
3386 p.Z = stoi(fn.next(","));
3387 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3389 return meta->getInventory();
3390 infostream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3391 <<"no metadata found"<<std::endl;
3395 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3398 void Server::inventoryModified(InventoryContext *c, std::string id)
3400 if(id == "current_player")
3402 assert(c->current_player);
3404 UpdateCrafting(c->current_player->peer_id);
3405 SendInventory(c->current_player->peer_id);
3410 std::string id0 = fn.next(":");
3412 if(id0 == "nodemeta")
3415 p.X = stoi(fn.next(","));
3416 p.Y = stoi(fn.next(","));
3417 p.Z = stoi(fn.next(","));
3418 v3s16 blockpos = getNodeBlockPos(p);
3420 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3422 meta->inventoryModified();
3424 for(core::map<u16, RemoteClient*>::Iterator
3425 i = m_clients.getIterator();
3426 i.atEnd()==false; i++)
3428 RemoteClient *client = i.getNode()->getValue();
3429 client->SetBlockNotSent(blockpos);
3435 infostream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3438 core::list<PlayerInfo> Server::getPlayerInfo()
3440 DSTACK(__FUNCTION_NAME);
3441 JMutexAutoLock envlock(m_env_mutex);
3442 JMutexAutoLock conlock(m_con_mutex);
3444 core::list<PlayerInfo> list;
3446 core::list<Player*> players = m_env.getPlayers();
3448 core::list<Player*>::Iterator i;
3449 for(i = players.begin();
3450 i != players.end(); i++)
3454 Player *player = *i;
3457 con::Peer *peer = m_con.GetPeer(player->peer_id);
3458 // Copy info from peer to info struct
3460 info.address = peer->address;
3461 info.avg_rtt = peer->avg_rtt;
3463 catch(con::PeerNotFoundException &e)
3465 // Set dummy peer info
3467 info.address = Address(0,0,0,0,0);
3471 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3472 info.position = player->getPosition();
3474 list.push_back(info);
3481 void Server::peerAdded(con::Peer *peer)
3483 DSTACK(__FUNCTION_NAME);
3484 infostream<<"Server::peerAdded(): peer->id="
3485 <<peer->id<<std::endl;
3488 c.type = PEER_ADDED;
3489 c.peer_id = peer->id;
3491 m_peer_change_queue.push_back(c);
3494 void Server::deletingPeer(con::Peer *peer, bool timeout)
3496 DSTACK(__FUNCTION_NAME);
3497 infostream<<"Server::deletingPeer(): peer->id="
3498 <<peer->id<<", timeout="<<timeout<<std::endl;
3501 c.type = PEER_REMOVED;
3502 c.peer_id = peer->id;
3503 c.timeout = timeout;
3504 m_peer_change_queue.push_back(c);
3511 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3513 DSTACK(__FUNCTION_NAME);
3514 std::ostringstream os(std::ios_base::binary);
3516 writeU16(os, TOCLIENT_HP);
3520 std::string s = os.str();
3521 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3523 con.Send(peer_id, 0, data, true);
3526 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3527 const std::wstring &reason)
3529 DSTACK(__FUNCTION_NAME);
3530 std::ostringstream os(std::ios_base::binary);
3532 writeU16(os, TOCLIENT_ACCESS_DENIED);
3533 os<<serializeWideString(reason);
3536 std::string s = os.str();
3537 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3539 con.Send(peer_id, 0, data, true);
3542 void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
3543 bool set_camera_point_target, v3f camera_point_target)
3545 DSTACK(__FUNCTION_NAME);
3546 std::ostringstream os(std::ios_base::binary);
3548 writeU16(os, TOCLIENT_DEATHSCREEN);
3549 writeU8(os, set_camera_point_target);
3550 writeV3F1000(os, camera_point_target);
3553 std::string s = os.str();
3554 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3556 con.Send(peer_id, 0, data, true);
3560 Non-static send methods
3563 void Server::SendObjectData(float dtime)
3565 DSTACK(__FUNCTION_NAME);
3567 core::map<v3s16, bool> stepped_blocks;
3569 for(core::map<u16, RemoteClient*>::Iterator
3570 i = m_clients.getIterator();
3571 i.atEnd() == false; i++)
3573 u16 peer_id = i.getNode()->getKey();
3574 RemoteClient *client = i.getNode()->getValue();
3575 assert(client->peer_id == peer_id);
3577 if(client->serialization_version == SER_FMT_VER_INVALID)
3580 client->SendObjectData(this, dtime, stepped_blocks);
3584 void Server::SendPlayerInfos()
3586 DSTACK(__FUNCTION_NAME);
3588 //JMutexAutoLock envlock(m_env_mutex);
3590 // Get connected players
3591 core::list<Player*> players = m_env.getPlayers(true);
3593 u32 player_count = players.getSize();
3594 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3596 SharedBuffer<u8> data(datasize);
3597 writeU16(&data[0], TOCLIENT_PLAYERINFO);
3600 core::list<Player*>::Iterator i;
3601 for(i = players.begin();
3602 i != players.end(); i++)
3604 Player *player = *i;
3606 /*infostream<<"Server sending player info for player with "
3607 "peer_id="<<player->peer_id<<std::endl;*/
3609 writeU16(&data[start], player->peer_id);
3610 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3611 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3612 start += 2+PLAYERNAME_SIZE;
3615 //JMutexAutoLock conlock(m_con_mutex);
3618 m_con.SendToAll(0, data, true);
3621 void Server::SendInventory(u16 peer_id)
3623 DSTACK(__FUNCTION_NAME);
3625 Player* player = m_env.getPlayer(peer_id);
3632 std::ostringstream os;
3633 //os.imbue(std::locale("C"));
3635 player->inventory.serialize(os);
3637 std::string s = os.str();
3639 SharedBuffer<u8> data(s.size()+2);
3640 writeU16(&data[0], TOCLIENT_INVENTORY);
3641 memcpy(&data[2], s.c_str(), s.size());
3644 m_con.Send(peer_id, 0, data, true);
3647 std::string getWieldedItemString(const Player *player)
3649 const InventoryItem *item = player->getWieldItem();
3651 return std::string("");
3652 std::ostringstream os(std::ios_base::binary);
3653 item->serialize(os);
3657 void Server::SendWieldedItem(const Player* player)
3659 DSTACK(__FUNCTION_NAME);
3663 std::ostringstream os(std::ios_base::binary);
3665 writeU16(os, TOCLIENT_PLAYERITEM);
3667 writeU16(os, player->peer_id);
3668 os<<serializeString(getWieldedItemString(player));
3671 std::string s = os.str();
3672 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3674 m_con.SendToAll(0, data, true);
3677 void Server::SendPlayerItems()
3679 DSTACK(__FUNCTION_NAME);
3681 std::ostringstream os(std::ios_base::binary);
3682 core::list<Player *> players = m_env.getPlayers(true);
3684 writeU16(os, TOCLIENT_PLAYERITEM);
3685 writeU16(os, players.size());
3686 core::list<Player *>::Iterator i;
3687 for(i = players.begin(); i != players.end(); ++i)
3690 writeU16(os, p->peer_id);
3691 os<<serializeString(getWieldedItemString(p));
3695 std::string s = os.str();
3696 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3698 m_con.SendToAll(0, data, true);
3701 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3703 DSTACK(__FUNCTION_NAME);
3705 std::ostringstream os(std::ios_base::binary);
3709 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3710 os.write((char*)buf, 2);
3713 writeU16(buf, message.size());
3714 os.write((char*)buf, 2);
3717 for(u32 i=0; i<message.size(); i++)
3721 os.write((char*)buf, 2);
3725 std::string s = os.str();
3726 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3728 m_con.Send(peer_id, 0, data, true);
3731 void Server::BroadcastChatMessage(const std::wstring &message)
3733 for(core::map<u16, RemoteClient*>::Iterator
3734 i = m_clients.getIterator();
3735 i.atEnd() == false; i++)
3737 // Get client and check that it is valid
3738 RemoteClient *client = i.getNode()->getValue();
3739 assert(client->peer_id == i.getNode()->getKey());
3740 if(client->serialization_version == SER_FMT_VER_INVALID)
3743 SendChatMessage(client->peer_id, message);
3747 void Server::SendPlayerHP(Player *player)
3749 SendHP(m_con, player->peer_id, player->hp);
3752 void Server::SendMovePlayer(Player *player)
3754 DSTACK(__FUNCTION_NAME);
3755 std::ostringstream os(std::ios_base::binary);
3757 writeU16(os, TOCLIENT_MOVE_PLAYER);
3758 writeV3F1000(os, player->getPosition());
3759 writeF1000(os, player->getPitch());
3760 writeF1000(os, player->getYaw());
3763 v3f pos = player->getPosition();
3764 f32 pitch = player->getPitch();
3765 f32 yaw = player->getYaw();
3766 infostream<<"Server sending TOCLIENT_MOVE_PLAYER"
3767 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3774 std::string s = os.str();
3775 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3777 m_con.Send(player->peer_id, 0, data, true);
3780 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3781 core::list<u16> *far_players, float far_d_nodes)
3783 float maxd = far_d_nodes*BS;
3784 v3f p_f = intToFloat(p, BS);
3788 SharedBuffer<u8> reply(replysize);
3789 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3790 writeS16(&reply[2], p.X);
3791 writeS16(&reply[4], p.Y);
3792 writeS16(&reply[6], p.Z);
3794 for(core::map<u16, RemoteClient*>::Iterator
3795 i = m_clients.getIterator();
3796 i.atEnd() == false; i++)
3798 // Get client and check that it is valid
3799 RemoteClient *client = i.getNode()->getValue();
3800 assert(client->peer_id == i.getNode()->getKey());
3801 if(client->serialization_version == SER_FMT_VER_INVALID)
3804 // Don't send if it's the same one
3805 if(client->peer_id == ignore_id)
3811 Player *player = m_env.getPlayer(client->peer_id);
3814 // If player is far away, only set modified blocks not sent
3815 v3f player_pos = player->getPosition();
3816 if(player_pos.getDistanceFrom(p_f) > maxd)
3818 far_players->push_back(client->peer_id);
3825 m_con.Send(client->peer_id, 0, reply, true);
3829 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3830 core::list<u16> *far_players, float far_d_nodes)
3832 float maxd = far_d_nodes*BS;
3833 v3f p_f = intToFloat(p, BS);
3835 for(core::map<u16, RemoteClient*>::Iterator
3836 i = m_clients.getIterator();
3837 i.atEnd() == false; i++)
3839 // Get client and check that it is valid
3840 RemoteClient *client = i.getNode()->getValue();
3841 assert(client->peer_id == i.getNode()->getKey());
3842 if(client->serialization_version == SER_FMT_VER_INVALID)
3845 // Don't send if it's the same one
3846 if(client->peer_id == ignore_id)
3852 Player *player = m_env.getPlayer(client->peer_id);
3855 // If player is far away, only set modified blocks not sent
3856 v3f player_pos = player->getPosition();
3857 if(player_pos.getDistanceFrom(p_f) > maxd)
3859 far_players->push_back(client->peer_id);
3866 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3867 SharedBuffer<u8> reply(replysize);
3868 writeU16(&reply[0], TOCLIENT_ADDNODE);
3869 writeS16(&reply[2], p.X);
3870 writeS16(&reply[4], p.Y);
3871 writeS16(&reply[6], p.Z);
3872 n.serialize(&reply[8], client->serialization_version);
3875 m_con.Send(client->peer_id, 0, reply, true);
3879 void Server::setBlockNotSent(v3s16 p)
3881 for(core::map<u16, RemoteClient*>::Iterator
3882 i = m_clients.getIterator();
3883 i.atEnd()==false; i++)
3885 RemoteClient *client = i.getNode()->getValue();
3886 client->SetBlockNotSent(p);
3890 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3892 DSTACK(__FUNCTION_NAME);
3894 v3s16 p = block->getPos();
3898 bool completely_air = true;
3899 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3900 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3901 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3903 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
3905 completely_air = false;
3906 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
3911 infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
3913 infostream<<"[completely air] ";
3914 infostream<<std::endl;
3918 Create a packet with the block in the right format
3921 std::ostringstream os(std::ios_base::binary);
3922 block->serialize(os, ver);
3923 std::string s = os.str();
3924 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3926 u32 replysize = 8 + blockdata.getSize();
3927 SharedBuffer<u8> reply(replysize);
3928 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3929 writeS16(&reply[2], p.X);
3930 writeS16(&reply[4], p.Y);
3931 writeS16(&reply[6], p.Z);
3932 memcpy(&reply[8], *blockdata, blockdata.getSize());
3934 /*infostream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3935 <<": \tpacket size: "<<replysize<<std::endl;*/
3940 m_con.Send(peer_id, 1, reply, true);
3943 void Server::SendBlocks(float dtime)
3945 DSTACK(__FUNCTION_NAME);
3947 JMutexAutoLock envlock(m_env_mutex);
3948 JMutexAutoLock conlock(m_con_mutex);
3950 //TimeTaker timer("Server::SendBlocks");
3952 core::array<PrioritySortedBlockTransfer> queue;
3954 s32 total_sending = 0;
3957 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
3959 for(core::map<u16, RemoteClient*>::Iterator
3960 i = m_clients.getIterator();
3961 i.atEnd() == false; i++)
3963 RemoteClient *client = i.getNode()->getValue();
3964 assert(client->peer_id == i.getNode()->getKey());
3966 total_sending += client->SendingCount();
3968 if(client->serialization_version == SER_FMT_VER_INVALID)
3971 client->GetNextBlocks(this, dtime, queue);
3976 // Lowest priority number comes first.
3977 // Lowest is most important.
3980 for(u32 i=0; i<queue.size(); i++)
3982 //TODO: Calculate limit dynamically
3983 if(total_sending >= g_settings->getS32
3984 ("max_simultaneous_block_sends_server_total"))
3987 PrioritySortedBlockTransfer q = queue[i];
3989 MapBlock *block = NULL;
3992 block = m_env.getMap().getBlockNoCreate(q.pos);
3994 catch(InvalidPositionException &e)
3999 RemoteClient *client = getClient(q.peer_id);
4001 SendBlockNoLock(q.peer_id, block, client->serialization_version);
4003 client->SentBlock(q.pos);
4013 void Server::HandlePlayerHP(Player *player, s16 damage)
4015 if(player->hp > damage)
4017 player->hp -= damage;
4018 SendPlayerHP(player);
4022 infostream<<"Server::HandlePlayerHP(): Player "
4023 <<player->getName()<<" dies"<<std::endl;
4027 //TODO: Throw items around
4029 // Handle players that are not connected
4030 if(player->peer_id == PEER_ID_INEXISTENT){
4031 RespawnPlayer(player);
4035 SendPlayerHP(player);
4037 RemoteClient *client = getClient(player->peer_id);
4038 if(client->net_proto_version >= 3)
4040 SendDeathscreen(m_con, player->peer_id, false, v3f(0,0,0));
4044 RespawnPlayer(player);
4049 void Server::RespawnPlayer(Player *player)
4051 v3f pos = findSpawnPos(m_env.getServerMap());
4052 player->setPosition(pos);
4054 SendMovePlayer(player);
4055 SendPlayerHP(player);
4058 void Server::UpdateCrafting(u16 peer_id)
4060 DSTACK(__FUNCTION_NAME);
4062 Player* player = m_env.getPlayer(peer_id);
4066 Calculate crafting stuff
4068 if(g_settings->getBool("creative_mode") == false)
4070 InventoryList *clist = player->inventory.getList("craft");
4071 InventoryList *rlist = player->inventory.getList("craftresult");
4073 if(rlist && rlist->getUsedSlots() == 0)
4074 player->craftresult_is_preview = true;
4076 if(rlist && player->craftresult_is_preview)
4078 rlist->clearItems();
4080 if(clist && rlist && player->craftresult_is_preview)
4082 InventoryItem *items[9];
4083 for(u16 i=0; i<9; i++)
4085 items[i] = clist->getItem(i);
4088 // Get result of crafting grid
4089 InventoryItem *result = craft_get_result(items);
4091 rlist->addItem(result);
4094 } // if creative_mode == false
4097 RemoteClient* Server::getClient(u16 peer_id)
4099 DSTACK(__FUNCTION_NAME);
4100 //JMutexAutoLock lock(m_con_mutex);
4101 core::map<u16, RemoteClient*>::Node *n;
4102 n = m_clients.find(peer_id);
4103 // A client should exist for all peers
4105 return n->getValue();
4108 std::wstring Server::getStatusString()
4110 std::wostringstream os(std::ios_base::binary);
4113 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4115 os<<L", uptime="<<m_uptime.get();
4116 // Information about clients
4118 for(core::map<u16, RemoteClient*>::Iterator
4119 i = m_clients.getIterator();
4120 i.atEnd() == false; i++)
4122 // Get client and check that it is valid
4123 RemoteClient *client = i.getNode()->getValue();
4124 assert(client->peer_id == i.getNode()->getKey());
4125 if(client->serialization_version == SER_FMT_VER_INVALID)
4128 Player *player = m_env.getPlayer(client->peer_id);
4129 // Get name of player
4130 std::wstring name = L"unknown";
4132 name = narrow_to_wide(player->getName());
4133 // Add name to information string
4137 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
4138 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4139 if(g_settings->get("motd") != "")
4140 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4144 // Saves g_settings to configpath given at initialization
4145 void Server::saveConfig()
4147 if(m_configpath != "")
4148 g_settings->updateConfigFile(m_configpath.c_str());
4151 v3f findSpawnPos(ServerMap &map)
4153 //return v3f(50,50,50)*BS;
4156 s16 groundheight = 0;
4159 nodepos = v2s16(0,0);
4164 // Try to find a good place a few times
4165 for(s32 i=0; i<1000; i++)
4168 // We're going to try to throw the player to this position
4169 nodepos = v2s16(-range + (myrand()%(range*2)),
4170 -range + (myrand()%(range*2)));
4171 v2s16 sectorpos = getNodeSectorPos(nodepos);
4172 // Get sector (NOTE: Don't get because it's slow)
4173 //m_env.getMap().emergeSector(sectorpos);
4174 // Get ground height at point (fallbacks to heightmap function)
4175 groundheight = map.findGroundLevel(nodepos);
4176 // Don't go underwater
4177 if(groundheight < WATER_LEVEL)
4179 //infostream<<"-> Underwater"<<std::endl;
4182 // Don't go to high places
4183 if(groundheight > WATER_LEVEL + 4)
4185 //infostream<<"-> Underwater"<<std::endl;
4189 // Found a good place
4190 //infostream<<"Searched through "<<i<<" places."<<std::endl;
4195 // If no suitable place was not found, go above water at least.
4196 if(groundheight < WATER_LEVEL)
4197 groundheight = WATER_LEVEL;
4199 return intToFloat(v3s16(
4206 Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id)
4209 Try to get an existing player
4211 Player *player = m_env.getPlayer(name);
4214 // If player is already connected, cancel
4215 if(player->peer_id != 0)
4217 infostream<<"emergePlayer(): Player already connected"<<std::endl;
4222 player->peer_id = peer_id;
4224 // Reset inventory to creative if in creative mode
4225 if(g_settings->getBool("creative_mode"))
4227 // Warning: double code below
4228 // Backup actual inventory
4229 player->inventory_backup = new Inventory();
4230 *(player->inventory_backup) = player->inventory;
4231 // Set creative inventory
4232 craft_set_creative_inventory(player);
4239 If player with the wanted peer_id already exists, cancel.
4241 if(m_env.getPlayer(peer_id) != NULL)
4243 infostream<<"emergePlayer(): Player with wrong name but same"
4244 " peer_id already exists"<<std::endl;
4252 player = new ServerRemotePlayer();
4253 //player->peer_id = c.peer_id;
4254 //player->peer_id = PEER_ID_INEXISTENT;
4255 player->peer_id = peer_id;
4256 player->updateName(name);
4257 m_authmanager.add(name);
4258 m_authmanager.setPassword(name, password);
4259 m_authmanager.setPrivs(name,
4260 stringToPrivs(g_settings->get("default_privs")));
4266 infostream<<"Server: Finding spawn place for player \""
4267 <<player->getName()<<"\""<<std::endl;
4269 v3f pos = findSpawnPos(m_env.getServerMap());
4271 player->setPosition(pos);
4274 Add player to environment
4277 m_env.addPlayer(player);
4280 Add stuff to inventory
4283 if(g_settings->getBool("creative_mode"))
4285 // Warning: double code above
4286 // Backup actual inventory
4287 player->inventory_backup = new Inventory();
4288 *(player->inventory_backup) = player->inventory;
4289 // Set creative inventory
4290 craft_set_creative_inventory(player);
4292 else if(g_settings->getBool("give_initial_stuff"))
4294 craft_give_initial_stuff(player);
4299 } // create new player
4302 void Server::handlePeerChange(PeerChange &c)
4304 JMutexAutoLock envlock(m_env_mutex);
4305 JMutexAutoLock conlock(m_con_mutex);
4307 if(c.type == PEER_ADDED)
4314 core::map<u16, RemoteClient*>::Node *n;
4315 n = m_clients.find(c.peer_id);
4316 // The client shouldn't already exist
4320 RemoteClient *client = new RemoteClient();
4321 client->peer_id = c.peer_id;
4322 m_clients.insert(client->peer_id, client);
4325 else if(c.type == PEER_REMOVED)
4332 core::map<u16, RemoteClient*>::Node *n;
4333 n = m_clients.find(c.peer_id);
4334 // The client should exist
4338 Mark objects to be not known by the client
4340 RemoteClient *client = n->getValue();
4342 for(core::map<u16, bool>::Iterator
4343 i = client->m_known_objects.getIterator();
4344 i.atEnd()==false; i++)
4347 u16 id = i.getNode()->getKey();
4348 ServerActiveObject* obj = m_env.getActiveObject(id);
4350 if(obj && obj->m_known_by_count > 0)
4351 obj->m_known_by_count--;
4354 // Collect information about leaving in chat
4355 std::wstring message;
4357 Player *player = m_env.getPlayer(c.peer_id);
4360 std::wstring name = narrow_to_wide(player->getName());
4363 message += L" left game";
4365 message += L" (timed out)";
4371 m_env.removePlayer(c.peer_id);
4374 // Set player client disconnected
4376 Player *player = m_env.getPlayer(c.peer_id);
4378 player->peer_id = 0;
4385 std::ostringstream os(std::ios_base::binary);
4386 for(core::map<u16, RemoteClient*>::Iterator
4387 i = m_clients.getIterator();
4388 i.atEnd() == false; i++)
4390 RemoteClient *client = i.getNode()->getValue();
4391 assert(client->peer_id == i.getNode()->getKey());
4392 if(client->serialization_version == SER_FMT_VER_INVALID)
4395 Player *player = m_env.getPlayer(client->peer_id);
4398 // Get name of player
4399 os<<player->getName()<<" ";
4402 actionstream<<player->getName()
4403 <<" leaves game. List of players: "
4404 <<os.str()<<std::endl;
4409 delete m_clients[c.peer_id];
4410 m_clients.remove(c.peer_id);
4412 // Send player info to all remaining clients
4415 // Send leave chat message to all remaining clients
4416 BroadcastChatMessage(message);
4425 void Server::handlePeerChanges()
4427 while(m_peer_change_queue.size() > 0)
4429 PeerChange c = m_peer_change_queue.pop_front();
4431 infostream<<"Server: Handling peer change: "
4432 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4435 handlePeerChange(c);
4439 u64 Server::getPlayerPrivs(Player *player)
4443 std::string playername = player->getName();
4444 // Local player gets all privileges regardless of
4445 // what's set on their account.
4446 if(g_settings->get("name") == playername)
4452 return getPlayerAuthPrivs(playername);
4456 void dedicated_server_loop(Server &server, bool &kill)
4458 DSTACK(__FUNCTION_NAME);
4460 infostream<<DTIME<<std::endl;
4461 infostream<<"========================"<<std::endl;
4462 infostream<<"Running dedicated server"<<std::endl;
4463 infostream<<"========================"<<std::endl;
4464 infostream<<std::endl;
4466 IntervalLimiter m_profiler_interval;
4470 // This is kind of a hack but can be done like this
4471 // because server.step() is very light
4473 ScopeProfiler sp(g_profiler, "dedicated server sleep");
4478 if(server.getShutdownRequested() || kill)
4480 infostream<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4487 float profiler_print_interval =
4488 g_settings->getFloat("profiler_print_interval");
4489 if(profiler_print_interval != 0)
4491 if(m_profiler_interval.step(0.030, profiler_print_interval))
4493 infostream<<"Profiler:"<<std::endl;
4494 g_profiler->print(infostream);
4495 g_profiler->clear();
4502 static int counter = 0;
4508 core::list<PlayerInfo> list = server.getPlayerInfo();
4509 core::list<PlayerInfo>::Iterator i;
4510 static u32 sum_old = 0;
4511 u32 sum = PIChecksum(list);
4514 infostream<<DTIME<<"Player info:"<<std::endl;
4515 for(i=list.begin(); i!=list.end(); i++)
4517 i->PrintLine(&infostream);