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"
42 #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
44 class MapEditEventIgnorer
47 MapEditEventIgnorer(bool *flag):
56 ~MapEditEventIgnorer()
69 void * ServerThread::Thread()
73 DSTACK(__FUNCTION_NAME);
75 BEGIN_DEBUG_EXCEPTION_HANDLER
80 //TimeTaker timer("AsyncRunStep() + Receive()");
83 //TimeTaker timer("AsyncRunStep()");
84 m_server->AsyncRunStep();
87 //dout_server<<"Running m_server->Receive()"<<std::endl;
90 catch(con::NoIncomingDataException &e)
93 catch(con::PeerNotFoundException &e)
95 dout_server<<"Server: PeerNotFoundException"<<std::endl;
99 END_DEBUG_EXCEPTION_HANDLER
104 void * EmergeThread::Thread()
108 DSTACK(__FUNCTION_NAME);
110 BEGIN_DEBUG_EXCEPTION_HANDLER
112 bool enable_mapgen_debug_info = g_settings->getBool("enable_mapgen_debug_info");
115 Get block info from queue, emerge them and send them
118 After queue is empty, exit.
122 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
126 SharedPtr<QueuedBlockEmerge> q(qptr);
132 Do not generate over-limit
134 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
135 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
136 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
137 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
138 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
139 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
142 //derr_server<<"EmergeThread::Thread(): running"<<std::endl;
144 //TimeTaker timer("block emerge");
147 Try to emerge it from somewhere.
149 If it is only wanted as optional, only loading from disk
154 Check if any peer wants it as non-optional. In that case it
157 Also decrement the emerge queue count in clients.
160 bool only_from_disk = true;
163 core::map<u16, u8>::Iterator i;
164 for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
166 //u16 peer_id = i.getNode()->getKey();
169 u8 flags = i.getNode()->getValue();
170 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
171 only_from_disk = false;
176 if(enable_mapgen_debug_info)
177 dstream<<"EmergeThread: p="
178 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
179 <<"only_from_disk="<<only_from_disk<<std::endl;
181 ServerMap &map = ((ServerMap&)m_server->m_env.getMap());
183 //core::map<v3s16, MapBlock*> changed_blocks;
184 //core::map<v3s16, MapBlock*> lighting_invalidated_blocks;
186 MapBlock *block = NULL;
187 bool got_block = true;
188 core::map<v3s16, MapBlock*> modified_blocks;
191 Fetch block from map or generate a single block
194 JMutexAutoLock envlock(m_server->m_env_mutex);
196 // Load sector if it isn't loaded
197 if(map.getSectorNoGenerateNoEx(p2d) == NULL)
198 //map.loadSectorFull(p2d);
199 map.loadSectorMeta(p2d);
201 block = map.getBlockNoCreateNoEx(p);
202 if(!block || block->isDummy() || !block->isGenerated())
204 if(enable_mapgen_debug_info)
205 dstream<<"EmergeThread: not in memory, loading"<<std::endl;
207 // Get, load or create sector
208 /*ServerMapSector *sector =
209 (ServerMapSector*)map.createSector(p2d);*/
211 // Load/generate block
213 /*block = map.emergeBlock(p, sector, changed_blocks,
214 lighting_invalidated_blocks);*/
216 block = map.loadBlock(p);
218 if(only_from_disk == false)
220 if(block == NULL || block->isGenerated() == false)
222 if(enable_mapgen_debug_info)
223 dstream<<"EmergeThread: generating"<<std::endl;
224 block = map.generateBlock(p, modified_blocks);
228 if(enable_mapgen_debug_info)
229 dstream<<"EmergeThread: ended up with: "
230 <<analyze_block(block)<<std::endl;
239 Ignore map edit events, they will not need to be
240 sent to anybody because the block hasn't been sent
243 MapEditEventIgnorer ign(&m_server->m_ignore_map_edit_events);
245 // Activate objects and stuff
246 m_server->m_env.activateBlock(block, 3600);
251 /*if(block->getLightingExpired()){
252 lighting_invalidated_blocks[block->getPos()] = block;
256 // TODO: Some additional checking and lighting updating,
261 JMutexAutoLock envlock(m_server->m_env_mutex);
266 Collect a list of blocks that have been modified in
267 addition to the fetched one.
271 if(lighting_invalidated_blocks.size() > 0)
273 /*dstream<<"lighting "<<lighting_invalidated_blocks.size()
274 <<" blocks"<<std::endl;*/
276 // 50-100ms for single block generation
277 //TimeTaker timer("** EmergeThread updateLighting");
279 // Update lighting without locking the environment mutex,
280 // add modified blocks to changed blocks
281 map.updateLighting(lighting_invalidated_blocks, modified_blocks);
284 // Add all from changed_blocks to modified_blocks
285 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
286 i.atEnd() == false; i++)
288 MapBlock *block = i.getNode()->getValue();
289 modified_blocks.insert(block->getPos(), block);
293 // If we got no block, there should be no invalidated blocks
296 //assert(lighting_invalidated_blocks.size() == 0);
302 Set sent status of modified blocks on clients
305 // NOTE: Server's clients are also behind the connection mutex
306 JMutexAutoLock lock(m_server->m_con_mutex);
309 Add the originally fetched block to the modified list
313 modified_blocks.insert(p, block);
317 Set the modified blocks unsent for all the clients
320 for(core::map<u16, RemoteClient*>::Iterator
321 i = m_server->m_clients.getIterator();
322 i.atEnd() == false; i++)
324 RemoteClient *client = i.getNode()->getValue();
326 if(modified_blocks.size() > 0)
328 // Remove block from sent history
329 client->SetBlocksNotSent(modified_blocks);
335 END_DEBUG_EXCEPTION_HANDLER
340 void RemoteClient::GetNextBlocks(Server *server, float dtime,
341 core::array<PrioritySortedBlockTransfer> &dest)
343 DSTACK(__FUNCTION_NAME);
346 TimeTaker timer("RemoteClient::GetNextBlocks", &timer_result);*/
349 m_nothing_to_send_pause_timer -= dtime;
351 if(m_nothing_to_send_pause_timer >= 0)
354 m_nearest_unsent_reset_timer = 0;
358 // Won't send anything if already sending
359 if(m_blocks_sending.size() >= g_settings->getU16
360 ("max_simultaneous_block_sends_per_client"))
362 //dstream<<"Not sending any blocks, Queue full."<<std::endl;
366 //TimeTaker timer("RemoteClient::GetNextBlocks");
368 Player *player = server->m_env.getPlayer(peer_id);
370 assert(player != NULL);
372 v3f playerpos = player->getPosition();
373 v3f playerspeed = player->getSpeed();
374 v3f playerspeeddir(0,0,0);
375 if(playerspeed.getLength() > 1.0*BS)
376 playerspeeddir = playerspeed / playerspeed.getLength();
377 // Predict to next block
378 v3f playerpos_predicted = playerpos + playerspeeddir*MAP_BLOCKSIZE*BS;
380 v3s16 center_nodepos = floatToInt(playerpos_predicted, BS);
382 v3s16 center = getNodeBlockPos(center_nodepos);
384 // Camera position and direction
385 v3f camera_pos = player->getEyePosition();
386 v3f camera_dir = v3f(0,0,1);
387 camera_dir.rotateYZBy(player->getPitch());
388 camera_dir.rotateXZBy(player->getYaw());
390 /*dstream<<"camera_dir=("<<camera_dir.X<<","<<camera_dir.Y<<","
391 <<camera_dir.Z<<")"<<std::endl;*/
394 Get the starting value of the block finder radius.
397 if(m_last_center != center)
399 m_nearest_unsent_d = 0;
400 m_last_center = center;
403 /*dstream<<"m_nearest_unsent_reset_timer="
404 <<m_nearest_unsent_reset_timer<<std::endl;*/
406 // This has to be incremented only when the nothing to send pause
408 m_nearest_unsent_reset_timer += dtime;
410 // Reset periodically to avoid possible bugs or other mishaps
411 if(m_nearest_unsent_reset_timer > 10.0)
413 m_nearest_unsent_reset_timer = 0;
414 m_nearest_unsent_d = 0;
415 /*dstream<<"Resetting m_nearest_unsent_d for "
416 <<server->getPlayerName(peer_id)<<std::endl;*/
419 //s16 last_nearest_unsent_d = m_nearest_unsent_d;
420 s16 d_start = m_nearest_unsent_d;
422 //dstream<<"d_start="<<d_start<<std::endl;
424 u16 max_simul_sends_setting = g_settings->getU16
425 ("max_simultaneous_block_sends_per_client");
426 u16 max_simul_sends_usually = max_simul_sends_setting;
429 Check the time from last addNode/removeNode.
431 Decrease send rate if player is building stuff.
433 m_time_from_building += dtime;
434 if(m_time_from_building < g_settings->getFloat(
435 "full_block_send_enable_min_time_from_building"))
437 max_simul_sends_usually
438 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
442 Number of blocks sending + number of blocks selected for sending
444 u32 num_blocks_selected = m_blocks_sending.size();
447 next time d will be continued from the d from which the nearest
448 unsent block was found this time.
450 This is because not necessarily any of the blocks found this
451 time are actually sent.
453 s32 new_nearest_unsent_d = -1;
455 s16 d_max = g_settings->getS16("max_block_send_distance");
456 s16 d_max_gen = g_settings->getS16("max_block_generate_distance");
458 // Don't loop very much at a time
459 if(d_max > d_start+1)
461 /*if(d_max_gen > d_start+2)
462 d_max_gen = d_start+2;*/
464 //dstream<<"Starting from "<<d_start<<std::endl;
466 bool sending_something = false;
468 bool no_blocks_found_for_sending = true;
470 bool queue_is_full = false;
473 for(d = d_start; d <= d_max; d++)
475 //dstream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
478 If m_nearest_unsent_d was changed by the EmergeThread
479 (it can change it to 0 through SetBlockNotSent),
481 Else update m_nearest_unsent_d
483 /*if(m_nearest_unsent_d != last_nearest_unsent_d)
485 d = m_nearest_unsent_d;
486 last_nearest_unsent_d = m_nearest_unsent_d;
490 Get the border/face dot coordinates of a "d-radiused"
493 core::list<v3s16> list;
494 getFacePositions(list, d);
496 core::list<v3s16>::Iterator li;
497 for(li=list.begin(); li!=list.end(); li++)
499 v3s16 p = *li + center;
503 - Don't allow too many simultaneous transfers
504 - EXCEPT when the blocks are very close
506 Also, don't send blocks that are already flying.
509 // Start with the usual maximum
510 u16 max_simul_dynamic = max_simul_sends_usually;
512 // If block is very close, allow full maximum
513 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
514 max_simul_dynamic = max_simul_sends_setting;
516 // Don't select too many blocks for sending
517 if(num_blocks_selected >= max_simul_dynamic)
519 queue_is_full = true;
520 goto queue_full_break;
523 // Don't send blocks that are currently being transferred
524 if(m_blocks_sending.find(p) != NULL)
530 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
531 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
532 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
533 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
534 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
535 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
538 // If this is true, inexistent block will be made from scratch
539 bool generate = d <= d_max_gen;
542 /*// Limit the generating area vertically to 2/3
543 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
546 // Limit the send area vertically to 2/3
547 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
553 If block is far away, don't generate it unless it is
559 // Block center y in nodes
560 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
561 // Don't generate if it's very high or very low
562 if(y < -64 || y > 64)
566 v2s16 p2d_nodes_center(
570 // Get ground height in nodes
571 s16 gh = server->m_env.getServerMap().findGroundLevel(
574 // If differs a lot, don't generate
575 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
577 // Actually, don't even send it
583 //dstream<<"d="<<d<<std::endl;
586 Don't generate or send if not in sight
587 FIXME This only works if the client uses a small enough
588 FOV setting. The default of 72 degrees is fine.
591 float camera_fov = (72.0*PI/180) * 4./3.;
592 if(isBlockInSight(p, camera_pos, camera_dir, camera_fov, 10000*BS) == false)
598 Don't send already sent blocks
601 if(m_blocks_sent.find(p) != NULL)
608 Check if map has this block
610 MapBlock *block = server->m_env.getMap().getBlockNoCreateNoEx(p);
612 bool surely_not_found_on_disk = false;
613 bool block_is_invalid = false;
616 // Reset usage timer, this block will be of use in the future.
617 block->resetUsageTimer();
619 // Block is dummy if data doesn't exist.
620 // It means it has been not found from disk and not generated
623 surely_not_found_on_disk = true;
626 // Block is valid if lighting is up-to-date and data exists
627 if(block->isValid() == false)
629 block_is_invalid = true;
632 /*if(block->isFullyGenerated() == false)
634 block_is_invalid = true;
639 ServerMap *map = (ServerMap*)(&server->m_env.getMap());
640 v2s16 chunkpos = map->sector_to_chunk(p2d);
641 if(map->chunkNonVolatile(chunkpos) == false)
642 block_is_invalid = true;
644 if(block->isGenerated() == false)
645 block_is_invalid = true;
648 If block is not close, don't send it unless it is near
651 Block is near ground level if night-time mesh
652 differs from day-time mesh.
656 if(block->dayNightDiffed() == false)
663 If block has been marked to not exist on disk (dummy)
664 and generating new ones is not wanted, skip block.
666 if(generate == false && surely_not_found_on_disk == true)
673 Record the lowest d from which a block has been
674 found being not sent and possibly to exist
676 if(no_blocks_found_for_sending)
679 new_nearest_unsent_d = d;
682 no_blocks_found_for_sending = false;
685 Add inexistent block to emerge queue.
687 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
689 //TODO: Get value from somewhere
690 // Allow only one block in emerge queue
691 //if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
692 // Allow two blocks in queue per client
693 if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
695 //dstream<<"Adding block to emerge queue"<<std::endl;
697 // Add it to the emerge queue and trigger the thread
700 if(generate == false)
701 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
703 server->m_emerge_queue.addBlock(peer_id, p, flags);
704 server->m_emergethread.trigger();
712 Add block to send queue
715 PrioritySortedBlockTransfer q((float)d, p, peer_id);
719 num_blocks_selected += 1;
720 sending_something = true;
725 //dstream<<"Stopped at "<<d<<std::endl;
727 if(no_blocks_found_for_sending)
729 if(queue_is_full == false)
730 new_nearest_unsent_d = d;
733 if(new_nearest_unsent_d != -1)
734 m_nearest_unsent_d = new_nearest_unsent_d;
736 if(sending_something == false)
738 m_nothing_to_send_counter++;
739 if((s16)m_nothing_to_send_counter >=
740 g_settings->getS16("max_block_send_distance"))
742 // Pause time in seconds
743 m_nothing_to_send_pause_timer = 1.0;
744 /*dstream<<"nothing to send to "
745 <<server->getPlayerName(peer_id)
746 <<" (d="<<d<<")"<<std::endl;*/
751 m_nothing_to_send_counter = 0;
754 /*timer_result = timer.stop(true);
755 if(timer_result != 0)
756 dstream<<"GetNextBlocks duration: "<<timer_result<<" (!=0)"<<std::endl;*/
759 void RemoteClient::SendObjectData(
762 core::map<v3s16, bool> &stepped_blocks
765 DSTACK(__FUNCTION_NAME);
767 // Can't send anything without knowing version
768 if(serialization_version == SER_FMT_VER_INVALID)
770 dstream<<"RemoteClient::SendObjectData(): Not sending, no version."
776 Send a TOCLIENT_OBJECTDATA packet.
780 u16 number of player positions
792 std::ostringstream os(std::ios_base::binary);
796 writeU16(buf, TOCLIENT_OBJECTDATA);
797 os.write((char*)buf, 2);
800 Get and write player data
803 // Get connected players
804 core::list<Player*> players = server->m_env.getPlayers(true);
806 // Write player count
807 u16 playercount = players.size();
808 writeU16(buf, playercount);
809 os.write((char*)buf, 2);
811 core::list<Player*>::Iterator i;
812 for(i = players.begin();
813 i != players.end(); i++)
817 v3f pf = player->getPosition();
818 v3f sf = player->getSpeed();
820 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
821 v3s32 speed_i (sf.X*100, sf.Y*100, sf.Z*100);
822 s32 pitch_i (player->getPitch() * 100);
823 s32 yaw_i (player->getYaw() * 100);
825 writeU16(buf, player->peer_id);
826 os.write((char*)buf, 2);
827 writeV3S32(buf, position_i);
828 os.write((char*)buf, 12);
829 writeV3S32(buf, speed_i);
830 os.write((char*)buf, 12);
831 writeS32(buf, pitch_i);
832 os.write((char*)buf, 4);
833 writeS32(buf, yaw_i);
834 os.write((char*)buf, 4);
838 Get and write object data
844 For making players to be able to build to their nearby
845 environment (building is not possible on blocks that are not
848 - Add blocks to emerge queue if they are not found
850 SUGGESTION: These could be ignored from the backside of the player
853 Player *player = server->m_env.getPlayer(peer_id);
857 v3f playerpos = player->getPosition();
858 v3f playerspeed = player->getSpeed();
860 v3s16 center_nodepos = floatToInt(playerpos, BS);
861 v3s16 center = getNodeBlockPos(center_nodepos);
865 os.write((char*)buf, 2);
871 //dstream<<"Server: Sending object data to "<<peer_id<<std::endl;
874 std::string s = os.str();
875 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
876 // Send as unreliable
877 server->m_con.Send(peer_id, 0, data, false);
880 void RemoteClient::GotBlock(v3s16 p)
882 if(m_blocks_sending.find(p) != NULL)
883 m_blocks_sending.remove(p);
886 /*dstream<<"RemoteClient::GotBlock(): Didn't find in"
887 " m_blocks_sending"<<std::endl;*/
888 m_excess_gotblocks++;
890 m_blocks_sent.insert(p, true);
893 void RemoteClient::SentBlock(v3s16 p)
895 if(m_blocks_sending.find(p) == NULL)
896 m_blocks_sending.insert(p, 0.0);
898 dstream<<"RemoteClient::SentBlock(): Sent block"
899 " already in m_blocks_sending"<<std::endl;
902 void RemoteClient::SetBlockNotSent(v3s16 p)
904 m_nearest_unsent_d = 0;
906 if(m_blocks_sending.find(p) != NULL)
907 m_blocks_sending.remove(p);
908 if(m_blocks_sent.find(p) != NULL)
909 m_blocks_sent.remove(p);
912 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
914 m_nearest_unsent_d = 0;
916 for(core::map<v3s16, MapBlock*>::Iterator
917 i = blocks.getIterator();
918 i.atEnd()==false; i++)
920 v3s16 p = i.getNode()->getKey();
922 if(m_blocks_sending.find(p) != NULL)
923 m_blocks_sending.remove(p);
924 if(m_blocks_sent.find(p) != NULL)
925 m_blocks_sent.remove(p);
933 PlayerInfo::PlayerInfo()
939 void PlayerInfo::PrintLine(std::ostream *s)
942 (*s)<<"\""<<name<<"\" ("
943 <<(position.X/10)<<","<<(position.Y/10)
944 <<","<<(position.Z/10)<<") ";
946 (*s)<<" avg_rtt="<<avg_rtt;
950 u32 PIChecksum(core::list<PlayerInfo> &l)
952 core::list<PlayerInfo>::Iterator i;
955 for(i=l.begin(); i!=l.end(); i++)
957 checksum += a * (i->id+1);
958 checksum ^= 0x435aafcd;
969 std::string mapsavedir,
970 std::string configpath
972 m_env(new ServerMap(mapsavedir), this),
973 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
974 m_authmanager(mapsavedir+"/auth.txt"),
975 m_banmanager(mapsavedir+"/ipban.txt"),
977 m_emergethread(this),
979 m_time_of_day_send_timer(0),
981 m_mapsavedir(mapsavedir),
982 m_configpath(configpath),
983 m_shutdown_requested(false),
984 m_ignore_map_edit_events(false),
985 m_ignore_map_edit_events_peer_id(0)
987 m_liquid_transform_timer = 0.0;
988 m_print_info_timer = 0.0;
989 m_objectdata_timer = 0.0;
990 m_emergethread_trigger_timer = 0.0;
991 m_savemap_timer = 0.0;
995 m_step_dtime_mutex.Init();
998 // Register us to receive map edit events
999 m_env.getMap().addEventReceiver(this);
1001 // If file exists, load environment metadata
1002 if(fs::PathExists(m_mapsavedir+"/env_meta.txt"))
1004 dstream<<"Server: Loading environment metadata"<<std::endl;
1005 m_env.loadMeta(m_mapsavedir);
1009 dstream<<"Server: Loading players"<<std::endl;
1010 m_env.deSerializePlayers(m_mapsavedir);
1015 dstream<<"Server::~Server()"<<std::endl;
1018 Send shutdown message
1021 JMutexAutoLock conlock(m_con_mutex);
1023 std::wstring line = L"*** Server shutting down";
1026 Send the message to clients
1028 for(core::map<u16, RemoteClient*>::Iterator
1029 i = m_clients.getIterator();
1030 i.atEnd() == false; i++)
1032 // Get client and check that it is valid
1033 RemoteClient *client = i.getNode()->getValue();
1034 assert(client->peer_id == i.getNode()->getKey());
1035 if(client->serialization_version == SER_FMT_VER_INVALID)
1039 SendChatMessage(client->peer_id, line);
1041 catch(con::PeerNotFoundException &e)
1047 JMutexAutoLock envlock(m_env_mutex);
1052 dstream<<"Server: Saving players"<<std::endl;
1053 m_env.serializePlayers(m_mapsavedir);
1056 Save environment metadata
1058 dstream<<"Server: Saving environment metadata"<<std::endl;
1059 m_env.saveMeta(m_mapsavedir);
1071 JMutexAutoLock clientslock(m_con_mutex);
1073 for(core::map<u16, RemoteClient*>::Iterator
1074 i = m_clients.getIterator();
1075 i.atEnd() == false; i++)
1078 // NOTE: These are removed by env destructor
1080 u16 peer_id = i.getNode()->getKey();
1081 JMutexAutoLock envlock(m_env_mutex);
1082 m_env.removePlayer(peer_id);
1086 delete i.getNode()->getValue();
1091 void Server::start(unsigned short port)
1093 DSTACK(__FUNCTION_NAME);
1094 // Stop thread if already running
1097 // Initialize connection
1098 m_con.setTimeoutMs(30);
1102 m_thread.setRun(true);
1105 dout_server<<"Server: Started on port "<<port<<std::endl;
1110 DSTACK(__FUNCTION_NAME);
1112 dout_server<<"Server: Stopping and waiting threads"<<std::endl;
1114 // Stop threads (set run=false first so both start stopping)
1115 m_thread.setRun(false);
1116 m_emergethread.setRun(false);
1118 m_emergethread.stop();
1120 dout_server<<"Server: Threads stopped"<<std::endl;
1123 void Server::step(float dtime)
1125 DSTACK(__FUNCTION_NAME);
1130 JMutexAutoLock lock(m_step_dtime_mutex);
1131 m_step_dtime += dtime;
1135 void Server::AsyncRunStep()
1137 DSTACK(__FUNCTION_NAME);
1141 JMutexAutoLock lock1(m_step_dtime_mutex);
1142 dtime = m_step_dtime;
1146 ScopeProfiler sp(g_profiler, "Server: selecting and sending "
1147 "blocks to clients");
1148 // Send blocks to clients
1155 //dstream<<"Server steps "<<dtime<<std::endl;
1156 //dstream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1159 JMutexAutoLock lock1(m_step_dtime_mutex);
1160 m_step_dtime -= dtime;
1167 m_uptime.set(m_uptime.get() + dtime);
1171 // Process connection's timeouts
1172 JMutexAutoLock lock2(m_con_mutex);
1173 ScopeProfiler sp(g_profiler, "Server: connection timeout processing");
1174 m_con.RunTimeouts(dtime);
1178 // This has to be called so that the client list gets synced
1179 // with the peer list of the connection
1180 ScopeProfiler sp(g_profiler, "Server: peer change handling");
1181 handlePeerChanges();
1185 Update m_time_of_day and overall game time
1188 JMutexAutoLock envlock(m_env_mutex);
1190 m_time_counter += dtime;
1191 f32 speed = g_settings->getFloat("time_speed") * 24000./(24.*3600);
1192 u32 units = (u32)(m_time_counter*speed);
1193 m_time_counter -= (f32)units / speed;
1195 m_env.setTimeOfDay((m_env.getTimeOfDay() + units) % 24000);
1197 //dstream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1200 Send to clients at constant intervals
1203 m_time_of_day_send_timer -= dtime;
1204 if(m_time_of_day_send_timer < 0.0)
1206 m_time_of_day_send_timer = g_settings->getFloat("time_send_interval");
1208 //JMutexAutoLock envlock(m_env_mutex);
1209 JMutexAutoLock conlock(m_con_mutex);
1211 for(core::map<u16, RemoteClient*>::Iterator
1212 i = m_clients.getIterator();
1213 i.atEnd() == false; i++)
1215 RemoteClient *client = i.getNode()->getValue();
1216 //Player *player = m_env.getPlayer(client->peer_id);
1218 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1219 m_env.getTimeOfDay());
1221 m_con.Send(client->peer_id, 0, data, true);
1227 JMutexAutoLock lock(m_env_mutex);
1229 ScopeProfiler sp(g_profiler, "Server: environment step");
1233 const float map_timer_and_unload_dtime = 5.15;
1234 if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime))
1236 JMutexAutoLock lock(m_env_mutex);
1237 // Run Map's timers and unload unused data
1238 ScopeProfiler sp(g_profiler, "Server: map timer and unload");
1239 m_env.getMap().timerUpdate(map_timer_and_unload_dtime,
1240 g_settings->getFloat("server_unload_unused_data_timeout"));
1250 m_liquid_transform_timer += dtime;
1251 if(m_liquid_transform_timer >= 1.00)
1253 m_liquid_transform_timer -= 1.00;
1255 JMutexAutoLock lock(m_env_mutex);
1257 ScopeProfiler sp(g_profiler, "Server: liquid transform");
1259 core::map<v3s16, MapBlock*> modified_blocks;
1260 m_env.getMap().transformLiquids(modified_blocks);
1265 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1266 ServerMap &map = ((ServerMap&)m_env.getMap());
1267 map.updateLighting(modified_blocks, lighting_modified_blocks);
1269 // Add blocks modified by lighting to modified_blocks
1270 for(core::map<v3s16, MapBlock*>::Iterator
1271 i = lighting_modified_blocks.getIterator();
1272 i.atEnd() == false; i++)
1274 MapBlock *block = i.getNode()->getValue();
1275 modified_blocks.insert(block->getPos(), block);
1279 Set the modified blocks unsent for all the clients
1282 JMutexAutoLock lock2(m_con_mutex);
1284 for(core::map<u16, RemoteClient*>::Iterator
1285 i = m_clients.getIterator();
1286 i.atEnd() == false; i++)
1288 RemoteClient *client = i.getNode()->getValue();
1290 if(modified_blocks.size() > 0)
1292 // Remove block from sent history
1293 client->SetBlocksNotSent(modified_blocks);
1298 // Periodically print some info
1300 float &counter = m_print_info_timer;
1306 JMutexAutoLock lock2(m_con_mutex);
1308 for(core::map<u16, RemoteClient*>::Iterator
1309 i = m_clients.getIterator();
1310 i.atEnd() == false; i++)
1312 //u16 peer_id = i.getNode()->getKey();
1313 RemoteClient *client = i.getNode()->getValue();
1314 Player *player = m_env.getPlayer(client->peer_id);
1317 std::cout<<player->getName()<<"\t";
1318 client->PrintInfo(std::cout);
1323 //if(g_settings->getBool("enable_experimental"))
1327 Check added and deleted active objects
1330 //dstream<<"Server: Checking added and deleted active objects"<<std::endl;
1331 JMutexAutoLock envlock(m_env_mutex);
1332 JMutexAutoLock conlock(m_con_mutex);
1334 ScopeProfiler sp(g_profiler, "Server: checking added and deleted objects");
1336 // Radius inside which objects are active
1337 s16 radius = g_settings->getS16("active_object_send_range_blocks");
1338 radius *= MAP_BLOCKSIZE;
1340 for(core::map<u16, RemoteClient*>::Iterator
1341 i = m_clients.getIterator();
1342 i.atEnd() == false; i++)
1344 RemoteClient *client = i.getNode()->getValue();
1345 Player *player = m_env.getPlayer(client->peer_id);
1348 // This can happen if the client timeouts somehow
1349 /*dstream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
1351 <<" has no associated player"<<std::endl;*/
1354 v3s16 pos = floatToInt(player->getPosition(), BS);
1356 core::map<u16, bool> removed_objects;
1357 core::map<u16, bool> added_objects;
1358 m_env.getRemovedActiveObjects(pos, radius,
1359 client->m_known_objects, removed_objects);
1360 m_env.getAddedActiveObjects(pos, radius,
1361 client->m_known_objects, added_objects);
1363 // Ignore if nothing happened
1364 if(removed_objects.size() == 0 && added_objects.size() == 0)
1366 //dstream<<"INFO: active objects: none changed"<<std::endl;
1370 std::string data_buffer;
1374 // Handle removed objects
1375 writeU16((u8*)buf, removed_objects.size());
1376 data_buffer.append(buf, 2);
1377 for(core::map<u16, bool>::Iterator
1378 i = removed_objects.getIterator();
1379 i.atEnd()==false; i++)
1382 u16 id = i.getNode()->getKey();
1383 ServerActiveObject* obj = m_env.getActiveObject(id);
1385 // Add to data buffer for sending
1386 writeU16((u8*)buf, i.getNode()->getKey());
1387 data_buffer.append(buf, 2);
1389 // Remove from known objects
1390 client->m_known_objects.remove(i.getNode()->getKey());
1392 if(obj && obj->m_known_by_count > 0)
1393 obj->m_known_by_count--;
1396 // Handle added objects
1397 writeU16((u8*)buf, added_objects.size());
1398 data_buffer.append(buf, 2);
1399 for(core::map<u16, bool>::Iterator
1400 i = added_objects.getIterator();
1401 i.atEnd()==false; i++)
1404 u16 id = i.getNode()->getKey();
1405 ServerActiveObject* obj = m_env.getActiveObject(id);
1408 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1410 dstream<<"WARNING: "<<__FUNCTION_NAME
1411 <<": NULL object"<<std::endl;
1413 type = obj->getType();
1415 // Add to data buffer for sending
1416 writeU16((u8*)buf, id);
1417 data_buffer.append(buf, 2);
1418 writeU8((u8*)buf, type);
1419 data_buffer.append(buf, 1);
1422 data_buffer.append(serializeLongString(
1423 obj->getClientInitializationData()));
1425 data_buffer.append(serializeLongString(""));
1427 // Add to known objects
1428 client->m_known_objects.insert(i.getNode()->getKey(), false);
1431 obj->m_known_by_count++;
1435 SharedBuffer<u8> reply(2 + data_buffer.size());
1436 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1437 memcpy((char*)&reply[2], data_buffer.c_str(),
1438 data_buffer.size());
1440 m_con.Send(client->peer_id, 0, reply, true);
1442 dstream<<"INFO: Server: Sent object remove/add: "
1443 <<removed_objects.size()<<" removed, "
1444 <<added_objects.size()<<" added, "
1445 <<"packet size is "<<reply.getSize()<<std::endl;
1450 Collect a list of all the objects known by the clients
1451 and report it back to the environment.
1454 core::map<u16, bool> all_known_objects;
1456 for(core::map<u16, RemoteClient*>::Iterator
1457 i = m_clients.getIterator();
1458 i.atEnd() == false; i++)
1460 RemoteClient *client = i.getNode()->getValue();
1461 // Go through all known objects of client
1462 for(core::map<u16, bool>::Iterator
1463 i = client->m_known_objects.getIterator();
1464 i.atEnd()==false; i++)
1466 u16 id = i.getNode()->getKey();
1467 all_known_objects[id] = true;
1471 m_env.setKnownActiveObjects(whatever);
1477 Send object messages
1480 JMutexAutoLock envlock(m_env_mutex);
1481 JMutexAutoLock conlock(m_con_mutex);
1483 ScopeProfiler sp(g_profiler, "Server: sending object messages");
1486 // Value = data sent by object
1487 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1489 // Get active object messages from environment
1492 ActiveObjectMessage aom = m_env.getActiveObjectMessage();
1496 core::list<ActiveObjectMessage>* message_list = NULL;
1497 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1498 n = buffered_messages.find(aom.id);
1501 message_list = new core::list<ActiveObjectMessage>;
1502 buffered_messages.insert(aom.id, message_list);
1506 message_list = n->getValue();
1508 message_list->push_back(aom);
1511 // Route data to every client
1512 for(core::map<u16, RemoteClient*>::Iterator
1513 i = m_clients.getIterator();
1514 i.atEnd()==false; i++)
1516 RemoteClient *client = i.getNode()->getValue();
1517 std::string reliable_data;
1518 std::string unreliable_data;
1519 // Go through all objects in message buffer
1520 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1521 j = buffered_messages.getIterator();
1522 j.atEnd()==false; j++)
1524 // If object is not known by client, skip it
1525 u16 id = j.getNode()->getKey();
1526 if(client->m_known_objects.find(id) == NULL)
1528 // Get message list of object
1529 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1530 // Go through every message
1531 for(core::list<ActiveObjectMessage>::Iterator
1532 k = list->begin(); k != list->end(); k++)
1534 // Compose the full new data with header
1535 ActiveObjectMessage aom = *k;
1536 std::string new_data;
1539 writeU16((u8*)&buf[0], aom.id);
1540 new_data.append(buf, 2);
1542 new_data += serializeString(aom.datastring);
1543 // Add data to buffer
1545 reliable_data += new_data;
1547 unreliable_data += new_data;
1551 reliable_data and unreliable_data are now ready.
1554 if(reliable_data.size() > 0)
1556 SharedBuffer<u8> reply(2 + reliable_data.size());
1557 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1558 memcpy((char*)&reply[2], reliable_data.c_str(),
1559 reliable_data.size());
1561 m_con.Send(client->peer_id, 0, reply, true);
1563 if(unreliable_data.size() > 0)
1565 SharedBuffer<u8> reply(2 + unreliable_data.size());
1566 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1567 memcpy((char*)&reply[2], unreliable_data.c_str(),
1568 unreliable_data.size());
1569 // Send as unreliable
1570 m_con.Send(client->peer_id, 0, reply, false);
1573 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1575 dstream<<"INFO: Server: Size of object message data: "
1576 <<"reliable: "<<reliable_data.size()
1577 <<", unreliable: "<<unreliable_data.size()
1582 // Clear buffered_messages
1583 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1584 i = buffered_messages.getIterator();
1585 i.atEnd()==false; i++)
1587 delete i.getNode()->getValue();
1591 } // enable_experimental
1594 Send queued-for-sending map edit events.
1597 // Don't send too many at a time
1600 // Single change sending is disabled if queue size is not small
1601 bool disable_single_change_sending = false;
1602 if(m_unsent_map_edit_queue.size() >= 4)
1603 disable_single_change_sending = true;
1605 bool got_any_events = false;
1607 // We'll log the amount of each
1610 while(m_unsent_map_edit_queue.size() != 0)
1612 got_any_events = true;
1614 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1616 // Players far away from the change are stored here.
1617 // Instead of sending the changes, MapBlocks are set not sent
1619 core::list<u16> far_players;
1621 if(event->type == MEET_ADDNODE)
1623 //dstream<<"Server: MEET_ADDNODE"<<std::endl;
1624 prof.add("MEET_ADDNODE", 1);
1625 if(disable_single_change_sending)
1626 sendAddNode(event->p, event->n, event->already_known_by_peer,
1629 sendAddNode(event->p, event->n, event->already_known_by_peer,
1632 else if(event->type == MEET_REMOVENODE)
1634 //dstream<<"Server: MEET_REMOVENODE"<<std::endl;
1635 prof.add("MEET_REMOVENODE", 1);
1636 if(disable_single_change_sending)
1637 sendRemoveNode(event->p, event->already_known_by_peer,
1640 sendRemoveNode(event->p, event->already_known_by_peer,
1643 else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
1645 dstream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
1646 prof.add("MEET_BLOCK_NODE_METADATA_CHANGED", 1);
1647 setBlockNotSent(event->p);
1649 else if(event->type == MEET_OTHER)
1651 dstream<<"Server: MEET_OTHER"<<std::endl;
1652 prof.add("MEET_OTHER", 1);
1653 for(core::map<v3s16, bool>::Iterator
1654 i = event->modified_blocks.getIterator();
1655 i.atEnd()==false; i++)
1657 v3s16 p = i.getNode()->getKey();
1663 prof.add("unknown", 1);
1664 dstream<<"WARNING: Server: Unknown MapEditEvent "
1665 <<((u32)event->type)<<std::endl;
1669 Set blocks not sent to far players
1671 if(far_players.size() > 0)
1673 // Convert list format to that wanted by SetBlocksNotSent
1674 core::map<v3s16, MapBlock*> modified_blocks2;
1675 for(core::map<v3s16, bool>::Iterator
1676 i = event->modified_blocks.getIterator();
1677 i.atEnd()==false; i++)
1679 v3s16 p = i.getNode()->getKey();
1680 modified_blocks2.insert(p,
1681 m_env.getMap().getBlockNoCreateNoEx(p));
1683 // Set blocks not sent
1684 for(core::list<u16>::Iterator
1685 i = far_players.begin();
1686 i != far_players.end(); i++)
1689 RemoteClient *client = getClient(peer_id);
1692 client->SetBlocksNotSent(modified_blocks2);
1698 /*// Don't send too many at a time
1700 if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
1706 dstream<<"Server: MapEditEvents:"<<std::endl;
1707 prof.print(dstream);
1713 Send object positions
1714 TODO: Get rid of MapBlockObjects
1717 float &counter = m_objectdata_timer;
1719 if(counter >= g_settings->getFloat("objectdata_interval"))
1721 JMutexAutoLock lock1(m_env_mutex);
1722 JMutexAutoLock lock2(m_con_mutex);
1724 ScopeProfiler sp(g_profiler, "Server: sending mbo positions");
1726 SendObjectData(counter);
1733 Trigger emergethread (it somehow gets to a non-triggered but
1734 bysy state sometimes)
1737 float &counter = m_emergethread_trigger_timer;
1743 m_emergethread.trigger();
1747 // Save map, players and auth stuff
1749 float &counter = m_savemap_timer;
1751 if(counter >= g_settings->getFloat("server_map_save_interval"))
1755 ScopeProfiler sp(g_profiler, "Server: saving stuff");
1758 if(m_authmanager.isModified())
1759 m_authmanager.save();
1762 if(m_banmanager.isModified())
1763 m_banmanager.save();
1766 JMutexAutoLock lock(m_env_mutex);
1768 /*// Unload unused data (delete from memory)
1769 m_env.getMap().unloadUnusedData(
1770 g_settings->getFloat("server_unload_unused_sectors_timeout"));
1772 /*u32 deleted_count = m_env.getMap().unloadUnusedData(
1773 g_settings->getFloat("server_unload_unused_sectors_timeout"));
1776 // Save only changed parts
1777 m_env.getMap().save(true);
1779 /*if(deleted_count > 0)
1781 dout_server<<"Server: Unloaded "<<deleted_count
1782 <<" blocks from memory"<<std::endl;
1786 m_env.serializePlayers(m_mapsavedir);
1788 // Save environment metadata
1789 m_env.saveMeta(m_mapsavedir);
1794 void Server::Receive()
1796 DSTACK(__FUNCTION_NAME);
1797 u32 data_maxsize = 10000;
1798 Buffer<u8> data(data_maxsize);
1803 JMutexAutoLock conlock(m_con_mutex);
1804 datasize = m_con.Receive(peer_id, *data, data_maxsize);
1807 // This has to be called so that the client list gets synced
1808 // with the peer list of the connection
1809 handlePeerChanges();
1811 ProcessData(*data, datasize, peer_id);
1813 catch(con::InvalidIncomingDataException &e)
1815 derr_server<<"Server::Receive(): "
1816 "InvalidIncomingDataException: what()="
1817 <<e.what()<<std::endl;
1819 catch(con::PeerNotFoundException &e)
1821 //NOTE: This is not needed anymore
1823 // The peer has been disconnected.
1824 // Find the associated player and remove it.
1826 /*JMutexAutoLock envlock(m_env_mutex);
1828 dout_server<<"ServerThread: peer_id="<<peer_id
1829 <<" has apparently closed connection. "
1830 <<"Removing player."<<std::endl;
1832 m_env.removePlayer(peer_id);*/
1836 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1838 DSTACK(__FUNCTION_NAME);
1839 // Environment is locked first.
1840 JMutexAutoLock envlock(m_env_mutex);
1841 JMutexAutoLock conlock(m_con_mutex);
1845 peer = m_con.GetPeer(peer_id);
1847 catch(con::PeerNotFoundException &e)
1849 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1850 <<peer_id<<" not found"<<std::endl;
1854 // drop player if is ip is banned
1855 if(m_banmanager.isIpBanned(peer->address.serializeString())){
1856 SendAccessDenied(m_con, peer_id,
1857 L"Your ip is banned. Banned name was "
1858 +narrow_to_wide(m_banmanager.getBanName(
1859 peer->address.serializeString())));
1860 m_con.deletePeer(peer_id, false);
1864 u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1872 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1874 if(command == TOSERVER_INIT)
1876 // [0] u16 TOSERVER_INIT
1877 // [2] u8 SER_FMT_VER_HIGHEST
1878 // [3] u8[20] player_name
1879 // [23] u8[28] password <--- can be sent without this, from old versions
1881 if(datasize < 2+1+PLAYERNAME_SIZE)
1884 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1885 <<peer->id<<std::endl;
1887 // First byte after command is maximum supported
1888 // serialization version
1889 u8 client_max = data[2];
1890 u8 our_max = SER_FMT_VER_HIGHEST;
1891 // Use the highest version supported by both
1892 u8 deployed = core::min_(client_max, our_max);
1893 // If it's lower than the lowest supported, give up.
1894 if(deployed < SER_FMT_VER_LOWEST)
1895 deployed = SER_FMT_VER_INVALID;
1897 //peer->serialization_version = deployed;
1898 getClient(peer->id)->pending_serialization_version = deployed;
1900 if(deployed == SER_FMT_VER_INVALID)
1902 derr_server<<DTIME<<"Server: Cannot negotiate "
1903 "serialization version with peer "
1904 <<peer_id<<std::endl;
1905 SendAccessDenied(m_con, peer_id,
1906 L"Your client is too old (map format)");
1911 Read and check network protocol version
1914 u16 net_proto_version = 0;
1915 if(datasize >= 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE+2)
1917 net_proto_version = readU16(&data[2+1+PLAYERNAME_SIZE+PASSWORD_SIZE]);
1920 getClient(peer->id)->net_proto_version = net_proto_version;
1922 if(net_proto_version == 0)
1924 SendAccessDenied(m_con, peer_id,
1925 L"Your client is too old. Please upgrade.");
1929 /* Uhh... this should actually be a warning but let's do it like this */
1930 if(g_settings->getBool("strict_protocol_version_checking"))
1932 if(net_proto_version < PROTOCOL_VERSION)
1934 SendAccessDenied(m_con, peer_id,
1935 L"Your client is too old. Please upgrade.");
1945 char playername[PLAYERNAME_SIZE];
1946 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
1948 playername[i] = data[3+i];
1950 playername[PLAYERNAME_SIZE-1] = 0;
1952 if(playername[0]=='\0')
1954 derr_server<<DTIME<<"Server: Player has empty name"<<std::endl;
1955 SendAccessDenied(m_con, peer_id,
1960 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
1962 derr_server<<DTIME<<"Server: Player has invalid name"<<std::endl;
1963 SendAccessDenied(m_con, peer_id,
1964 L"Name contains unallowed characters");
1969 char password[PASSWORD_SIZE];
1970 if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
1972 // old version - assume blank password
1977 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
1979 password[i] = data[23+i];
1981 password[PASSWORD_SIZE-1] = 0;
1984 std::string checkpwd;
1985 if(m_authmanager.exists(playername))
1987 checkpwd = m_authmanager.getPassword(playername);
1991 checkpwd = g_settings->get("default_password");
1994 /*dstream<<"Server: Client gave password '"<<password
1995 <<"', the correct one is '"<<checkpwd<<"'"<<std::endl;*/
1997 if(password != checkpwd && m_authmanager.exists(playername))
1999 derr_server<<DTIME<<"Server: peer_id="<<peer_id
2000 <<": supplied invalid password for "
2001 <<playername<<std::endl;
2002 SendAccessDenied(m_con, peer_id, L"Invalid password");
2006 // Add player to auth manager
2007 if(m_authmanager.exists(playername) == false)
2009 derr_server<<DTIME<<"Server: adding player "<<playername
2010 <<" to auth manager"<<std::endl;
2011 m_authmanager.add(playername);
2012 m_authmanager.setPassword(playername, checkpwd);
2013 m_authmanager.setPrivs(playername,
2014 stringToPrivs(g_settings->get("default_privs")));
2015 m_authmanager.save();
2018 // Enforce user limit.
2019 // Don't enforce for users that have some admin right
2020 if(m_clients.size() >= g_settings->getU16("max_users") &&
2021 (m_authmanager.getPrivs(playername)
2022 & (PRIV_SERVER|PRIV_BAN|PRIV_PRIVS)) == 0 &&
2023 playername != g_settings->get("name"))
2025 SendAccessDenied(m_con, peer_id, L"Too many users.");
2030 Player *player = emergePlayer(playername, password, peer_id);
2032 // If failed, cancel
2035 derr_server<<DTIME<<"Server: peer_id="<<peer_id
2036 <<": failed to emerge player"<<std::endl;
2041 Answer with a TOCLIENT_INIT
2044 SharedBuffer<u8> reply(2+1+6+8);
2045 writeU16(&reply[0], TOCLIENT_INIT);
2046 writeU8(&reply[2], deployed);
2047 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
2048 writeU64(&reply[2+1+6], m_env.getServerMap().getSeed());
2051 m_con.Send(peer_id, 0, reply, true);
2055 Send complete position information
2057 SendMovePlayer(player);
2062 if(command == TOSERVER_INIT2)
2064 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
2065 <<peer->id<<std::endl;
2068 getClient(peer->id)->serialization_version
2069 = getClient(peer->id)->pending_serialization_version;
2072 Send some initialization data
2075 // Send player info to all players
2078 // Send inventory to player
2079 UpdateCrafting(peer->id);
2080 SendInventory(peer->id);
2082 // Send player items to all players
2087 Player *player = m_env.getPlayer(peer_id);
2088 SendPlayerHP(player);
2093 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2094 m_env.getTimeOfDay());
2095 m_con.Send(peer->id, 0, data, true);
2098 // Send information about server to player in chat
2099 SendChatMessage(peer_id, getStatusString());
2101 // Send information about joining in chat
2103 std::wstring name = L"unknown";
2104 Player *player = m_env.getPlayer(peer_id);
2106 name = narrow_to_wide(player->getName());
2108 std::wstring message;
2111 message += L" joined game";
2112 BroadcastChatMessage(message);
2115 // Warnings about protocol version can be issued here
2116 if(getClient(peer->id)->net_proto_version < PROTOCOL_VERSION)
2118 SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT IS OLD AND MAY WORK PROPERLY WITH THIS SERVER");
2122 Check HP, respawn if necessary
2125 Player *player = m_env.getPlayer(peer_id);
2126 HandlePlayerHP(player, 0);
2132 if(peer_ser_ver == SER_FMT_VER_INVALID)
2134 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
2135 " serialization format invalid or not initialized."
2136 " Skipping incoming command="<<command<<std::endl;
2140 Player *player = m_env.getPlayer(peer_id);
2143 derr_server<<"Server::ProcessData(): Cancelling: "
2144 "No player for peer_id="<<peer_id
2148 if(command == TOSERVER_PLAYERPOS)
2150 if(datasize < 2+12+12+4+4)
2154 v3s32 ps = readV3S32(&data[start+2]);
2155 v3s32 ss = readV3S32(&data[start+2+12]);
2156 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2157 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2158 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2159 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2160 pitch = wrapDegrees(pitch);
2161 yaw = wrapDegrees(yaw);
2162 player->setPosition(position);
2163 player->setSpeed(speed);
2164 player->setPitch(pitch);
2165 player->setYaw(yaw);
2167 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2168 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2169 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2171 else if(command == TOSERVER_GOTBLOCKS)
2184 u16 count = data[2];
2185 for(u16 i=0; i<count; i++)
2187 if((s16)datasize < 2+1+(i+1)*6)
2188 throw con::InvalidIncomingDataException
2189 ("GOTBLOCKS length is too short");
2190 v3s16 p = readV3S16(&data[2+1+i*6]);
2191 /*dstream<<"Server: GOTBLOCKS ("
2192 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2193 RemoteClient *client = getClient(peer_id);
2194 client->GotBlock(p);
2197 else if(command == TOSERVER_DELETEDBLOCKS)
2210 u16 count = data[2];
2211 for(u16 i=0; i<count; i++)
2213 if((s16)datasize < 2+1+(i+1)*6)
2214 throw con::InvalidIncomingDataException
2215 ("DELETEDBLOCKS length is too short");
2216 v3s16 p = readV3S16(&data[2+1+i*6]);
2217 /*dstream<<"Server: DELETEDBLOCKS ("
2218 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2219 RemoteClient *client = getClient(peer_id);
2220 client->SetBlockNotSent(p);
2223 else if(command == TOSERVER_CLICK_OBJECT)
2225 derr_server<<"Server: CLICK_OBJECT not supported anymore"<<std::endl;
2228 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2233 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2239 [2] u8 button (0=left, 1=right)
2243 u8 button = readU8(&data[2]);
2244 u16 id = readS16(&data[3]);
2245 u16 item_i = readU16(&data[5]);
2247 ServerActiveObject *obj = m_env.getActiveObject(id);
2251 derr_server<<"Server: CLICK_ACTIVEOBJECT: object not found"
2256 // Skip if object has been removed
2260 //TODO: Check that object is reasonably close
2262 // Left click, pick object up (usually)
2266 Try creating inventory item
2268 InventoryItem *item = obj->createPickedUpItem();
2272 InventoryList *ilist = player->inventory.getList("main");
2275 if(g_settings->getBool("creative_mode") == false)
2277 // Skip if inventory has no free space
2278 if(ilist->roomForItem(item) == false)
2280 dout_server<<"Player inventory has no free space"<<std::endl;
2284 // Add to inventory and send inventory
2285 ilist->addItem(item);
2286 UpdateCrafting(player->peer_id);
2287 SendInventory(player->peer_id);
2290 // Remove object from environment
2291 obj->m_removed = true;
2297 Item cannot be picked up. Punch it instead.
2300 ToolItem *titem = NULL;
2301 std::string toolname = "";
2303 InventoryList *mlist = player->inventory.getList("main");
2306 InventoryItem *item = mlist->getItem(item_i);
2307 if(item && (std::string)item->getName() == "ToolItem")
2309 titem = (ToolItem*)item;
2310 toolname = titem->getToolName();
2314 v3f playerpos = player->getPosition();
2315 v3f objpos = obj->getBasePosition();
2316 v3f dir = (objpos - playerpos).normalize();
2318 u16 wear = obj->punch(toolname, dir, player->getName());
2322 bool weared_out = titem->addWear(wear);
2324 mlist->deleteItem(item_i);
2325 SendInventory(player->peer_id);
2329 // Right click, do something with object
2332 // Track hp changes super-crappily
2333 u16 oldhp = player->hp;
2336 obj->rightClick(player);
2339 if(player->hp != oldhp)
2341 SendPlayerHP(player);
2345 else if(command == TOSERVER_GROUND_ACTION)
2353 [3] v3s16 nodepos_undersurface
2354 [9] v3s16 nodepos_abovesurface
2359 2: stop digging (all parameters ignored)
2360 3: digging completed
2362 u8 action = readU8(&data[2]);
2364 p_under.X = readS16(&data[3]);
2365 p_under.Y = readS16(&data[5]);
2366 p_under.Z = readS16(&data[7]);
2368 p_over.X = readS16(&data[9]);
2369 p_over.Y = readS16(&data[11]);
2370 p_over.Z = readS16(&data[13]);
2371 u16 item_i = readU16(&data[15]);
2373 //TODO: Check that target is reasonably close
2381 NOTE: This can be used in the future to check if
2382 somebody is cheating, by checking the timing.
2389 else if(action == 2)
2392 RemoteClient *client = getClient(peer->id);
2393 JMutexAutoLock digmutex(client->m_dig_mutex);
2394 client->m_dig_tool_item = -1;
2399 3: Digging completed
2401 else if(action == 3)
2403 // Mandatory parameter; actually used for nothing
2404 core::map<v3s16, MapBlock*> modified_blocks;
2406 content_t material = CONTENT_IGNORE;
2407 u8 mineral = MINERAL_NONE;
2409 bool cannot_remove_node = false;
2413 MapNode n = m_env.getMap().getNode(p_under);
2415 mineral = n.getMineral();
2416 // Get material at position
2417 material = n.getContent();
2418 // If not yet cancelled
2419 if(cannot_remove_node == false)
2421 // If it's not diggable, do nothing
2422 if(content_diggable(material) == false)
2424 derr_server<<"Server: Not finishing digging: "
2425 <<"Node not diggable"
2427 cannot_remove_node = true;
2430 // If not yet cancelled
2431 if(cannot_remove_node == false)
2433 // Get node metadata
2434 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p_under);
2435 if(meta && meta->nodeRemovalDisabled() == true)
2437 derr_server<<"Server: Not finishing digging: "
2438 <<"Node metadata disables removal"
2440 cannot_remove_node = true;
2444 catch(InvalidPositionException &e)
2446 derr_server<<"Server: Not finishing digging: Node not found."
2447 <<" Adding block to emerge queue."
2449 m_emerge_queue.addBlock(peer_id,
2450 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2451 cannot_remove_node = true;
2454 // Make sure the player is allowed to do it
2455 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2457 dstream<<"Player "<<player->getName()<<" cannot remove node"
2458 <<" because privileges are "<<getPlayerPrivs(player)
2460 cannot_remove_node = true;
2464 If node can't be removed, set block to be re-sent to
2467 if(cannot_remove_node)
2469 derr_server<<"Server: Not finishing digging."<<std::endl;
2471 // Client probably has wrong data.
2472 // Set block not sent, so that client will get
2474 dstream<<"Client "<<peer_id<<" tried to dig "
2475 <<"node; but node cannot be removed."
2476 <<" setting MapBlock not sent."<<std::endl;
2477 RemoteClient *client = getClient(peer_id);
2478 v3s16 blockpos = getNodeBlockPos(p_under);
2479 client->SetBlockNotSent(blockpos);
2485 Send the removal to all close-by players.
2486 - If other player is close, send REMOVENODE
2487 - Otherwise set blocks not sent
2489 core::list<u16> far_players;
2490 sendRemoveNode(p_under, peer_id, &far_players, 30);
2493 Update and send inventory
2496 if(g_settings->getBool("creative_mode") == false)
2501 InventoryList *mlist = player->inventory.getList("main");
2504 InventoryItem *item = mlist->getItem(item_i);
2505 if(item && (std::string)item->getName() == "ToolItem")
2507 ToolItem *titem = (ToolItem*)item;
2508 std::string toolname = titem->getToolName();
2510 // Get digging properties for material and tool
2511 DiggingProperties prop =
2512 getDiggingProperties(material, toolname);
2514 if(prop.diggable == false)
2516 derr_server<<"Server: WARNING: Player digged"
2517 <<" with impossible material + tool"
2518 <<" combination"<<std::endl;
2521 bool weared_out = titem->addWear(prop.wear);
2525 mlist->deleteItem(item_i);
2531 Add dug item to inventory
2534 InventoryItem *item = NULL;
2536 if(mineral != MINERAL_NONE)
2537 item = getDiggedMineralItem(mineral);
2542 std::string &dug_s = content_features(material).dug_item;
2545 std::istringstream is(dug_s, std::ios::binary);
2546 item = InventoryItem::deSerialize(is);
2552 // Add a item to inventory
2553 player->inventory.addItem("main", item);
2556 UpdateCrafting(player->peer_id);
2557 SendInventory(player->peer_id);
2562 if(mineral != MINERAL_NONE)
2563 item = getDiggedMineralItem(mineral);
2568 std::string &extra_dug_s = content_features(material).extra_dug_item;
2569 s32 extra_rarity = content_features(material).extra_dug_item_rarity;
2570 if(extra_dug_s != "" && extra_rarity != 0
2571 && myrand() % extra_rarity == 0)
2573 std::istringstream is(extra_dug_s, std::ios::binary);
2574 item = InventoryItem::deSerialize(is);
2580 // Add a item to inventory
2581 player->inventory.addItem("main", item);
2584 UpdateCrafting(player->peer_id);
2585 SendInventory(player->peer_id);
2591 (this takes some time so it is done after the quick stuff)
2594 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
2596 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2599 Set blocks not sent to far players
2601 for(core::list<u16>::Iterator
2602 i = far_players.begin();
2603 i != far_players.end(); i++)
2606 RemoteClient *client = getClient(peer_id);
2609 client->SetBlocksNotSent(modified_blocks);
2616 else if(action == 1)
2619 InventoryList *ilist = player->inventory.getList("main");
2624 InventoryItem *item = ilist->getItem(item_i);
2626 // If there is no item, it is not possible to add it anywhere
2631 Handle material items
2633 if(std::string("MaterialItem") == item->getName())
2636 // Don't add a node if this is not a free space
2637 MapNode n2 = m_env.getMap().getNode(p_over);
2638 bool no_enough_privs =
2639 ((getPlayerPrivs(player) & PRIV_BUILD)==0);
2641 dstream<<"Player "<<player->getName()<<" cannot add node"
2642 <<" because privileges are "<<getPlayerPrivs(player)
2645 if(content_features(n2).buildable_to == false
2648 // Client probably has wrong data.
2649 // Set block not sent, so that client will get
2651 dstream<<"Client "<<peer_id<<" tried to place"
2652 <<" node in invalid position; setting"
2653 <<" MapBlock not sent."<<std::endl;
2654 RemoteClient *client = getClient(peer_id);
2655 v3s16 blockpos = getNodeBlockPos(p_over);
2656 client->SetBlockNotSent(blockpos);
2660 catch(InvalidPositionException &e)
2662 derr_server<<"Server: Ignoring ADDNODE: Node not found"
2663 <<" Adding block to emerge queue."
2665 m_emerge_queue.addBlock(peer_id,
2666 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2670 // Reset build time counter
2671 getClient(peer->id)->m_time_from_building = 0.0;
2674 MaterialItem *mitem = (MaterialItem*)item;
2676 n.setContent(mitem->getMaterial());
2678 // Calculate direction for wall mounted stuff
2679 if(content_features(n).wall_mounted)
2680 n.param2 = packDir(p_under - p_over);
2682 // Calculate the direction for furnaces and chests and stuff
2683 if(content_features(n).param_type == CPT_FACEDIR_SIMPLE)
2685 v3f playerpos = player->getPosition();
2686 v3f blockpos = intToFloat(p_over, BS) - playerpos;
2687 blockpos = blockpos.normalize();
2689 if (fabs(blockpos.X) > fabs(blockpos.Z)) {
2703 Send to all close-by players
2705 core::list<u16> far_players;
2706 sendAddNode(p_over, n, 0, &far_players, 30);
2711 InventoryList *ilist = player->inventory.getList("main");
2712 if(g_settings->getBool("creative_mode") == false && ilist)
2714 // Remove from inventory and send inventory
2715 if(mitem->getCount() == 1)
2716 ilist->deleteItem(item_i);
2720 UpdateCrafting(peer_id);
2721 SendInventory(peer_id);
2727 This takes some time so it is done after the quick stuff
2729 core::map<v3s16, MapBlock*> modified_blocks;
2731 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
2733 std::string p_name = std::string(player->getName());
2734 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks, p_name);
2737 Set blocks not sent to far players
2739 for(core::list<u16>::Iterator
2740 i = far_players.begin();
2741 i != far_players.end(); i++)
2744 RemoteClient *client = getClient(peer_id);
2747 client->SetBlocksNotSent(modified_blocks);
2751 Calculate special events
2754 /*if(n.d == CONTENT_MESE)
2757 for(s16 z=-1; z<=1; z++)
2758 for(s16 y=-1; y<=1; y++)
2759 for(s16 x=-1; x<=1; x++)
2766 Place other item (not a block)
2770 v3s16 blockpos = getNodeBlockPos(p_over);
2773 Check that the block is loaded so that the item
2774 can properly be added to the static list too
2776 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2779 derr_server<<"Error while placing object: "
2780 "block not found"<<std::endl;
2785 If in creative mode, item dropping is disabled unless
2786 player has build privileges
2788 if(g_settings->getBool("creative_mode") &&
2789 (getPlayerPrivs(player) & PRIV_BUILD) == 0)
2791 derr_server<<"Not allowing player to drop item: "
2792 "creative mode and no build privs"<<std::endl;
2796 dout_server<<"Placing a miscellaneous item on map"
2799 // Calculate a position for it
2800 v3f pos = intToFloat(p_over, BS);
2802 pos.Y -= BS*0.25; // let it drop a bit
2804 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2805 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2810 ServerActiveObject *obj = item->createSAO(&m_env, 0, pos);
2814 derr_server<<"WARNING: item resulted in NULL object, "
2815 <<"not placing onto map"
2820 // Add the object to the environment
2821 m_env.addActiveObject(obj);
2823 dout_server<<"Placed object"<<std::endl;
2825 if(g_settings->getBool("creative_mode") == false)
2827 // Delete the right amount of items from the slot
2828 u16 dropcount = item->getDropCount();
2830 // Delete item if all gone
2831 if(item->getCount() <= dropcount)
2833 if(item->getCount() < dropcount)
2834 dstream<<"WARNING: Server: dropped more items"
2835 <<" than the slot contains"<<std::endl;
2837 InventoryList *ilist = player->inventory.getList("main");
2839 // Remove from inventory and send inventory
2840 ilist->deleteItem(item_i);
2842 // Else decrement it
2844 item->remove(dropcount);
2847 UpdateCrafting(peer_id);
2848 SendInventory(peer_id);
2856 Catch invalid actions
2860 derr_server<<"WARNING: Server: Invalid action "
2861 <<action<<std::endl;
2865 else if(command == TOSERVER_RELEASE)
2874 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2877 else if(command == TOSERVER_SIGNTEXT)
2879 derr_server<<"Server: TOSERVER_SIGNTEXT not supported anymore"
2883 else if(command == TOSERVER_SIGNNODETEXT)
2885 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2893 std::string datastring((char*)&data[2], datasize-2);
2894 std::istringstream is(datastring, std::ios_base::binary);
2897 is.read((char*)buf, 6);
2898 v3s16 p = readV3S16(buf);
2899 is.read((char*)buf, 2);
2900 u16 textlen = readU16(buf);
2902 for(u16 i=0; i<textlen; i++)
2904 is.read((char*)buf, 1);
2905 text += (char)buf[0];
2908 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2911 if(meta->typeId() != CONTENT_SIGN_WALL)
2913 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
2914 signmeta->setText(text);
2916 v3s16 blockpos = getNodeBlockPos(p);
2917 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2920 block->setChangedFlag();
2923 for(core::map<u16, RemoteClient*>::Iterator
2924 i = m_clients.getIterator();
2925 i.atEnd()==false; i++)
2927 RemoteClient *client = i.getNode()->getValue();
2928 client->SetBlockNotSent(blockpos);
2931 else if(command == TOSERVER_INVENTORY_ACTION)
2933 /*// Ignore inventory changes if in creative mode
2934 if(g_settings->getBool("creative_mode") == true)
2936 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2940 // Strip command and create a stream
2941 std::string datastring((char*)&data[2], datasize-2);
2942 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2943 std::istringstream is(datastring, std::ios_base::binary);
2945 InventoryAction *a = InventoryAction::deSerialize(is);
2950 c.current_player = player;
2953 Handle craftresult specially if not in creative mode
2955 bool disable_action = false;
2956 if(a->getType() == IACTION_MOVE
2957 && g_settings->getBool("creative_mode") == false)
2959 IMoveAction *ma = (IMoveAction*)a;
2960 if(ma->to_inv == "current_player" &&
2961 ma->from_inv == "current_player")
2963 InventoryList *rlist = player->inventory.getList("craftresult");
2965 InventoryList *clist = player->inventory.getList("craft");
2967 InventoryList *mlist = player->inventory.getList("main");
2970 Craftresult is no longer preview if something
2973 if(ma->to_list == "craftresult"
2974 && ma->from_list != "craftresult")
2976 // If it currently is a preview, remove
2978 if(player->craftresult_is_preview)
2980 rlist->deleteItem(0);
2982 player->craftresult_is_preview = false;
2985 Crafting takes place if this condition is true.
2987 if(player->craftresult_is_preview &&
2988 ma->from_list == "craftresult")
2990 player->craftresult_is_preview = false;
2991 clist->decrementMaterials(1);
2994 If the craftresult is placed on itself, move it to
2995 main inventory instead of doing the action
2997 if(ma->to_list == "craftresult"
2998 && ma->from_list == "craftresult")
3000 disable_action = true;
3002 InventoryItem *item1 = rlist->changeItem(0, NULL);
3003 mlist->addItem(item1);
3006 // Disallow moving items if not allowed to build
3007 else if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
3011 // if it's a locking chest, only allow the owner or server admins to move items
3012 else if (ma->from_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
3014 Strfnd fn(ma->from_inv);
3015 std::string id0 = fn.next(":");
3016 if(id0 == "nodemeta")
3019 p.X = stoi(fn.next(","));
3020 p.Y = stoi(fn.next(","));
3021 p.Z = stoi(fn.next(","));
3022 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3023 if(meta && meta->typeId() == CONTENT_LOCKABLE_CHEST) {
3024 LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
3025 if (lcm->getOwner() != player->getName())
3030 else if (ma->to_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
3032 Strfnd fn(ma->to_inv);
3033 std::string id0 = fn.next(":");
3034 if(id0 == "nodemeta")
3037 p.X = stoi(fn.next(","));
3038 p.Y = stoi(fn.next(","));
3039 p.Z = stoi(fn.next(","));
3040 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3041 if(meta && meta->typeId() == CONTENT_LOCKABLE_CHEST) {
3042 LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
3043 if (lcm->getOwner() != player->getName())
3050 if(disable_action == false)
3052 // Feed action to player inventory
3060 UpdateCrafting(player->peer_id);
3061 SendInventory(player->peer_id);
3066 dstream<<"TOSERVER_INVENTORY_ACTION: "
3067 <<"InventoryAction::deSerialize() returned NULL"
3071 else if(command == TOSERVER_CHAT_MESSAGE)
3079 std::string datastring((char*)&data[2], datasize-2);
3080 std::istringstream is(datastring, std::ios_base::binary);
3083 is.read((char*)buf, 2);
3084 u16 len = readU16(buf);
3086 std::wstring message;
3087 for(u16 i=0; i<len; i++)
3089 is.read((char*)buf, 2);
3090 message += (wchar_t)readU16(buf);
3093 // Get player name of this client
3094 std::wstring name = narrow_to_wide(player->getName());
3096 // Line to send to players
3098 // Whether to send to the player that sent the line
3099 bool send_to_sender = false;
3100 // Whether to send to other players
3101 bool send_to_others = false;
3103 // Local player gets all privileges regardless of
3104 // what's set on their account.
3105 u64 privs = getPlayerPrivs(player);
3108 if(message[0] == L'/')
3110 size_t strip_size = 1;
3111 if (message[1] == L'#') // support old-style commans
3113 message = message.substr(strip_size);
3115 WStrfnd f1(message);
3116 f1.next(L" "); // Skip over /#whatever
3117 std::wstring paramstring = f1.next(L"");
3119 ServerCommandContext *ctx = new ServerCommandContext(
3120 str_split(message, L' '),
3127 std::wstring reply(processServerCommand(ctx));
3128 send_to_sender = ctx->flags & SEND_TO_SENDER;
3129 send_to_others = ctx->flags & SEND_TO_OTHERS;
3131 if (ctx->flags & SEND_NO_PREFIX)
3134 line += L"Server: " + reply;
3141 if(privs & PRIV_SHOUT)
3147 send_to_others = true;
3151 line += L"Server: You are not allowed to shout";
3152 send_to_sender = true;
3158 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
3161 Send the message to clients
3163 for(core::map<u16, RemoteClient*>::Iterator
3164 i = m_clients.getIterator();
3165 i.atEnd() == false; i++)
3167 // Get client and check that it is valid
3168 RemoteClient *client = i.getNode()->getValue();
3169 assert(client->peer_id == i.getNode()->getKey());
3170 if(client->serialization_version == SER_FMT_VER_INVALID)
3174 bool sender_selected = (peer_id == client->peer_id);
3175 if(sender_selected == true && send_to_sender == false)
3177 if(sender_selected == false && send_to_others == false)
3180 SendChatMessage(client->peer_id, line);
3184 else if(command == TOSERVER_DAMAGE)
3186 std::string datastring((char*)&data[2], datasize-2);
3187 std::istringstream is(datastring, std::ios_base::binary);
3188 u8 damage = readU8(is);
3190 if(g_settings->getBool("enable_damage"))
3192 HandlePlayerHP(player, damage);
3196 SendPlayerHP(player);
3199 else if(command == TOSERVER_PASSWORD)
3202 [0] u16 TOSERVER_PASSWORD
3203 [2] u8[28] old password
3204 [30] u8[28] new password
3207 if(datasize != 2+PASSWORD_SIZE*2)
3209 /*char password[PASSWORD_SIZE];
3210 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3211 password[i] = data[2+i];
3212 password[PASSWORD_SIZE-1] = 0;*/
3214 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3222 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3224 char c = data[2+PASSWORD_SIZE+i];
3230 dstream<<"Server: Client requests a password change from "
3231 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
3233 std::string playername = player->getName();
3235 if(m_authmanager.exists(playername) == false)
3237 dstream<<"Server: playername not found in authmanager"<<std::endl;
3238 // Wrong old password supplied!!
3239 SendChatMessage(peer_id, L"playername not found in authmanager");
3243 std::string checkpwd = m_authmanager.getPassword(playername);
3245 if(oldpwd != checkpwd)
3247 dstream<<"Server: invalid old password"<<std::endl;
3248 // Wrong old password supplied!!
3249 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
3253 m_authmanager.setPassword(playername, newpwd);
3255 dstream<<"Server: password change successful for "<<playername
3257 SendChatMessage(peer_id, L"Password change successful");
3259 else if(command == TOSERVER_PLAYERITEM)
3264 u16 item = readU16(&data[2]);
3265 player->wieldItem(item);
3266 SendWieldedItem(player);
3268 else if(command == TOSERVER_RESPAWN)
3273 RespawnPlayer(player);
3277 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
3278 "unknown command "<<command<<std::endl;
3282 catch(SendFailedException &e)
3284 derr_server<<"Server::ProcessData(): SendFailedException: "
3290 void Server::onMapEditEvent(MapEditEvent *event)
3292 //dstream<<"Server::onMapEditEvent()"<<std::endl;
3293 if(m_ignore_map_edit_events)
3295 MapEditEvent *e = event->clone();
3296 m_unsent_map_edit_queue.push_back(e);
3299 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3301 if(id == "current_player")
3303 assert(c->current_player);
3304 return &(c->current_player->inventory);
3308 std::string id0 = fn.next(":");
3310 if(id0 == "nodemeta")
3313 p.X = stoi(fn.next(","));
3314 p.Y = stoi(fn.next(","));
3315 p.Z = stoi(fn.next(","));
3316 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3318 return meta->getInventory();
3319 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3320 <<"no metadata found"<<std::endl;
3324 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3327 void Server::inventoryModified(InventoryContext *c, std::string id)
3329 if(id == "current_player")
3331 assert(c->current_player);
3333 UpdateCrafting(c->current_player->peer_id);
3334 SendInventory(c->current_player->peer_id);
3339 std::string id0 = fn.next(":");
3341 if(id0 == "nodemeta")
3344 p.X = stoi(fn.next(","));
3345 p.Y = stoi(fn.next(","));
3346 p.Z = stoi(fn.next(","));
3347 v3s16 blockpos = getNodeBlockPos(p);
3349 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3351 meta->inventoryModified();
3353 for(core::map<u16, RemoteClient*>::Iterator
3354 i = m_clients.getIterator();
3355 i.atEnd()==false; i++)
3357 RemoteClient *client = i.getNode()->getValue();
3358 client->SetBlockNotSent(blockpos);
3364 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3367 core::list<PlayerInfo> Server::getPlayerInfo()
3369 DSTACK(__FUNCTION_NAME);
3370 JMutexAutoLock envlock(m_env_mutex);
3371 JMutexAutoLock conlock(m_con_mutex);
3373 core::list<PlayerInfo> list;
3375 core::list<Player*> players = m_env.getPlayers();
3377 core::list<Player*>::Iterator i;
3378 for(i = players.begin();
3379 i != players.end(); i++)
3383 Player *player = *i;
3386 con::Peer *peer = m_con.GetPeer(player->peer_id);
3387 // Copy info from peer to info struct
3389 info.address = peer->address;
3390 info.avg_rtt = peer->avg_rtt;
3392 catch(con::PeerNotFoundException &e)
3394 // Set dummy peer info
3396 info.address = Address(0,0,0,0,0);
3400 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3401 info.position = player->getPosition();
3403 list.push_back(info);
3410 void Server::peerAdded(con::Peer *peer)
3412 DSTACK(__FUNCTION_NAME);
3413 dout_server<<"Server::peerAdded(): peer->id="
3414 <<peer->id<<std::endl;
3417 c.type = PEER_ADDED;
3418 c.peer_id = peer->id;
3420 m_peer_change_queue.push_back(c);
3423 void Server::deletingPeer(con::Peer *peer, bool timeout)
3425 DSTACK(__FUNCTION_NAME);
3426 dout_server<<"Server::deletingPeer(): peer->id="
3427 <<peer->id<<", timeout="<<timeout<<std::endl;
3430 c.type = PEER_REMOVED;
3431 c.peer_id = peer->id;
3432 c.timeout = timeout;
3433 m_peer_change_queue.push_back(c);
3440 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3442 DSTACK(__FUNCTION_NAME);
3443 std::ostringstream os(std::ios_base::binary);
3445 writeU16(os, TOCLIENT_HP);
3449 std::string s = os.str();
3450 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3452 con.Send(peer_id, 0, data, true);
3455 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3456 const std::wstring &reason)
3458 DSTACK(__FUNCTION_NAME);
3459 std::ostringstream os(std::ios_base::binary);
3461 writeU16(os, TOCLIENT_ACCESS_DENIED);
3462 os<<serializeWideString(reason);
3465 std::string s = os.str();
3466 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3468 con.Send(peer_id, 0, data, true);
3471 void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
3472 bool set_camera_point_target, v3f camera_point_target)
3474 DSTACK(__FUNCTION_NAME);
3475 std::ostringstream os(std::ios_base::binary);
3477 writeU16(os, TOCLIENT_DEATHSCREEN);
3478 writeU8(os, set_camera_point_target);
3479 writeV3F1000(os, camera_point_target);
3482 std::string s = os.str();
3483 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3485 con.Send(peer_id, 0, data, true);
3489 Non-static send methods
3492 void Server::SendObjectData(float dtime)
3494 DSTACK(__FUNCTION_NAME);
3496 core::map<v3s16, bool> stepped_blocks;
3498 for(core::map<u16, RemoteClient*>::Iterator
3499 i = m_clients.getIterator();
3500 i.atEnd() == false; i++)
3502 u16 peer_id = i.getNode()->getKey();
3503 RemoteClient *client = i.getNode()->getValue();
3504 assert(client->peer_id == peer_id);
3506 if(client->serialization_version == SER_FMT_VER_INVALID)
3509 client->SendObjectData(this, dtime, stepped_blocks);
3513 void Server::SendPlayerInfos()
3515 DSTACK(__FUNCTION_NAME);
3517 //JMutexAutoLock envlock(m_env_mutex);
3519 // Get connected players
3520 core::list<Player*> players = m_env.getPlayers(true);
3522 u32 player_count = players.getSize();
3523 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3525 SharedBuffer<u8> data(datasize);
3526 writeU16(&data[0], TOCLIENT_PLAYERINFO);
3529 core::list<Player*>::Iterator i;
3530 for(i = players.begin();
3531 i != players.end(); i++)
3533 Player *player = *i;
3535 /*dstream<<"Server sending player info for player with "
3536 "peer_id="<<player->peer_id<<std::endl;*/
3538 writeU16(&data[start], player->peer_id);
3539 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3540 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3541 start += 2+PLAYERNAME_SIZE;
3544 //JMutexAutoLock conlock(m_con_mutex);
3547 m_con.SendToAll(0, data, true);
3550 void Server::SendInventory(u16 peer_id)
3552 DSTACK(__FUNCTION_NAME);
3554 Player* player = m_env.getPlayer(peer_id);
3561 std::ostringstream os;
3562 //os.imbue(std::locale("C"));
3564 player->inventory.serialize(os);
3566 std::string s = os.str();
3568 SharedBuffer<u8> data(s.size()+2);
3569 writeU16(&data[0], TOCLIENT_INVENTORY);
3570 memcpy(&data[2], s.c_str(), s.size());
3573 m_con.Send(peer_id, 0, data, true);
3576 std::string getWieldedItemString(const Player *player)
3578 const InventoryItem *item = player->getWieldItem();
3580 return std::string("");
3581 std::ostringstream os(std::ios_base::binary);
3582 item->serialize(os);
3586 void Server::SendWieldedItem(const Player* player)
3588 DSTACK(__FUNCTION_NAME);
3592 std::ostringstream os(std::ios_base::binary);
3594 writeU16(os, TOCLIENT_PLAYERITEM);
3596 writeU16(os, player->peer_id);
3597 os<<serializeString(getWieldedItemString(player));
3600 std::string s = os.str();
3601 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3603 m_con.SendToAll(0, data, true);
3606 void Server::SendPlayerItems()
3608 DSTACK(__FUNCTION_NAME);
3610 std::ostringstream os(std::ios_base::binary);
3611 core::list<Player *> players = m_env.getPlayers(true);
3613 writeU16(os, TOCLIENT_PLAYERITEM);
3614 writeU16(os, players.size());
3615 core::list<Player *>::Iterator i;
3616 for(i = players.begin(); i != players.end(); ++i)
3619 writeU16(os, p->peer_id);
3620 os<<serializeString(getWieldedItemString(p));
3624 std::string s = os.str();
3625 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3627 m_con.SendToAll(0, data, true);
3630 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3632 DSTACK(__FUNCTION_NAME);
3634 std::ostringstream os(std::ios_base::binary);
3638 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3639 os.write((char*)buf, 2);
3642 writeU16(buf, message.size());
3643 os.write((char*)buf, 2);
3646 for(u32 i=0; i<message.size(); i++)
3650 os.write((char*)buf, 2);
3654 std::string s = os.str();
3655 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3657 m_con.Send(peer_id, 0, data, true);
3660 void Server::BroadcastChatMessage(const std::wstring &message)
3662 for(core::map<u16, RemoteClient*>::Iterator
3663 i = m_clients.getIterator();
3664 i.atEnd() == false; i++)
3666 // Get client and check that it is valid
3667 RemoteClient *client = i.getNode()->getValue();
3668 assert(client->peer_id == i.getNode()->getKey());
3669 if(client->serialization_version == SER_FMT_VER_INVALID)
3672 SendChatMessage(client->peer_id, message);
3676 void Server::SendPlayerHP(Player *player)
3678 SendHP(m_con, player->peer_id, player->hp);
3681 void Server::SendMovePlayer(Player *player)
3683 DSTACK(__FUNCTION_NAME);
3684 std::ostringstream os(std::ios_base::binary);
3686 writeU16(os, TOCLIENT_MOVE_PLAYER);
3687 writeV3F1000(os, player->getPosition());
3688 writeF1000(os, player->getPitch());
3689 writeF1000(os, player->getYaw());
3692 v3f pos = player->getPosition();
3693 f32 pitch = player->getPitch();
3694 f32 yaw = player->getYaw();
3695 dstream<<"Server sending TOCLIENT_MOVE_PLAYER"
3696 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3703 std::string s = os.str();
3704 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3706 m_con.Send(player->peer_id, 0, data, true);
3709 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3710 core::list<u16> *far_players, float far_d_nodes)
3712 float maxd = far_d_nodes*BS;
3713 v3f p_f = intToFloat(p, BS);
3717 SharedBuffer<u8> reply(replysize);
3718 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3719 writeS16(&reply[2], p.X);
3720 writeS16(&reply[4], p.Y);
3721 writeS16(&reply[6], p.Z);
3723 for(core::map<u16, RemoteClient*>::Iterator
3724 i = m_clients.getIterator();
3725 i.atEnd() == false; i++)
3727 // Get client and check that it is valid
3728 RemoteClient *client = i.getNode()->getValue();
3729 assert(client->peer_id == i.getNode()->getKey());
3730 if(client->serialization_version == SER_FMT_VER_INVALID)
3733 // Don't send if it's the same one
3734 if(client->peer_id == ignore_id)
3740 Player *player = m_env.getPlayer(client->peer_id);
3743 // If player is far away, only set modified blocks not sent
3744 v3f player_pos = player->getPosition();
3745 if(player_pos.getDistanceFrom(p_f) > maxd)
3747 far_players->push_back(client->peer_id);
3754 m_con.Send(client->peer_id, 0, reply, true);
3758 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3759 core::list<u16> *far_players, float far_d_nodes)
3761 float maxd = far_d_nodes*BS;
3762 v3f p_f = intToFloat(p, BS);
3764 for(core::map<u16, RemoteClient*>::Iterator
3765 i = m_clients.getIterator();
3766 i.atEnd() == false; i++)
3768 // Get client and check that it is valid
3769 RemoteClient *client = i.getNode()->getValue();
3770 assert(client->peer_id == i.getNode()->getKey());
3771 if(client->serialization_version == SER_FMT_VER_INVALID)
3774 // Don't send if it's the same one
3775 if(client->peer_id == ignore_id)
3781 Player *player = m_env.getPlayer(client->peer_id);
3784 // If player is far away, only set modified blocks not sent
3785 v3f player_pos = player->getPosition();
3786 if(player_pos.getDistanceFrom(p_f) > maxd)
3788 far_players->push_back(client->peer_id);
3795 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3796 SharedBuffer<u8> reply(replysize);
3797 writeU16(&reply[0], TOCLIENT_ADDNODE);
3798 writeS16(&reply[2], p.X);
3799 writeS16(&reply[4], p.Y);
3800 writeS16(&reply[6], p.Z);
3801 n.serialize(&reply[8], client->serialization_version);
3804 m_con.Send(client->peer_id, 0, reply, true);
3808 void Server::setBlockNotSent(v3s16 p)
3810 for(core::map<u16, RemoteClient*>::Iterator
3811 i = m_clients.getIterator();
3812 i.atEnd()==false; i++)
3814 RemoteClient *client = i.getNode()->getValue();
3815 client->SetBlockNotSent(p);
3819 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3821 DSTACK(__FUNCTION_NAME);
3823 v3s16 p = block->getPos();
3827 bool completely_air = true;
3828 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3829 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3830 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3832 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
3834 completely_air = false;
3835 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
3840 dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
3842 dstream<<"[completely air] ";
3847 Create a packet with the block in the right format
3850 std::ostringstream os(std::ios_base::binary);
3851 block->serialize(os, ver);
3852 std::string s = os.str();
3853 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3855 u32 replysize = 8 + blockdata.getSize();
3856 SharedBuffer<u8> reply(replysize);
3857 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3858 writeS16(&reply[2], p.X);
3859 writeS16(&reply[4], p.Y);
3860 writeS16(&reply[6], p.Z);
3861 memcpy(&reply[8], *blockdata, blockdata.getSize());
3863 /*dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3864 <<": \tpacket size: "<<replysize<<std::endl;*/
3869 m_con.Send(peer_id, 1, reply, true);
3872 void Server::SendBlocks(float dtime)
3874 DSTACK(__FUNCTION_NAME);
3876 JMutexAutoLock envlock(m_env_mutex);
3877 JMutexAutoLock conlock(m_con_mutex);
3879 //TimeTaker timer("Server::SendBlocks");
3881 core::array<PrioritySortedBlockTransfer> queue;
3883 s32 total_sending = 0;
3886 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
3888 for(core::map<u16, RemoteClient*>::Iterator
3889 i = m_clients.getIterator();
3890 i.atEnd() == false; i++)
3892 RemoteClient *client = i.getNode()->getValue();
3893 assert(client->peer_id == i.getNode()->getKey());
3895 total_sending += client->SendingCount();
3897 if(client->serialization_version == SER_FMT_VER_INVALID)
3900 client->GetNextBlocks(this, dtime, queue);
3905 // Lowest priority number comes first.
3906 // Lowest is most important.
3909 for(u32 i=0; i<queue.size(); i++)
3911 //TODO: Calculate limit dynamically
3912 if(total_sending >= g_settings->getS32
3913 ("max_simultaneous_block_sends_server_total"))
3916 PrioritySortedBlockTransfer q = queue[i];
3918 MapBlock *block = NULL;
3921 block = m_env.getMap().getBlockNoCreate(q.pos);
3923 catch(InvalidPositionException &e)
3928 RemoteClient *client = getClient(q.peer_id);
3930 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3932 client->SentBlock(q.pos);
3942 void Server::HandlePlayerHP(Player *player, s16 damage)
3944 if(player->hp > damage)
3946 player->hp -= damage;
3947 SendPlayerHP(player);
3951 dstream<<"Server::HandlePlayerHP(): Player "
3952 <<player->getName()<<" dies"<<std::endl;
3956 //TODO: Throw items around
3958 // Handle players that are not connected
3959 if(player->peer_id == PEER_ID_INEXISTENT){
3960 RespawnPlayer(player);
3964 SendPlayerHP(player);
3966 RemoteClient *client = getClient(player->peer_id);
3967 if(client->net_proto_version >= 3)
3969 SendDeathscreen(m_con, player->peer_id, false, v3f(0,0,0));
3973 RespawnPlayer(player);
3978 void Server::RespawnPlayer(Player *player)
3980 v3f pos = findSpawnPos(m_env.getServerMap());
3981 player->setPosition(pos);
3983 SendMovePlayer(player);
3984 SendPlayerHP(player);
3987 void Server::UpdateCrafting(u16 peer_id)
3989 DSTACK(__FUNCTION_NAME);
3991 Player* player = m_env.getPlayer(peer_id);
3995 Calculate crafting stuff
3997 if(g_settings->getBool("creative_mode") == false)
3999 InventoryList *clist = player->inventory.getList("craft");
4000 InventoryList *rlist = player->inventory.getList("craftresult");
4002 if(rlist && rlist->getUsedSlots() == 0)
4003 player->craftresult_is_preview = true;
4005 if(rlist && player->craftresult_is_preview)
4007 rlist->clearItems();
4009 if(clist && rlist && player->craftresult_is_preview)
4011 InventoryItem *items[9];
4012 for(u16 i=0; i<9; i++)
4014 items[i] = clist->getItem(i);
4017 // Get result of crafting grid
4018 InventoryItem *result = craft_get_result(items);
4020 rlist->addItem(result);
4023 } // if creative_mode == false
4026 RemoteClient* Server::getClient(u16 peer_id)
4028 DSTACK(__FUNCTION_NAME);
4029 //JMutexAutoLock lock(m_con_mutex);
4030 core::map<u16, RemoteClient*>::Node *n;
4031 n = m_clients.find(peer_id);
4032 // A client should exist for all peers
4034 return n->getValue();
4037 std::wstring Server::getStatusString()
4039 std::wostringstream os(std::ios_base::binary);
4042 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4044 os<<L", uptime="<<m_uptime.get();
4045 // Information about clients
4047 for(core::map<u16, RemoteClient*>::Iterator
4048 i = m_clients.getIterator();
4049 i.atEnd() == false; i++)
4051 // Get client and check that it is valid
4052 RemoteClient *client = i.getNode()->getValue();
4053 assert(client->peer_id == i.getNode()->getKey());
4054 if(client->serialization_version == SER_FMT_VER_INVALID)
4057 Player *player = m_env.getPlayer(client->peer_id);
4058 // Get name of player
4059 std::wstring name = L"unknown";
4061 name = narrow_to_wide(player->getName());
4062 // Add name to information string
4066 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
4067 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4068 if(g_settings->get("motd") != "")
4069 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4073 // Saves g_settings to configpath given at initialization
4074 void Server::saveConfig()
4076 if(m_configpath != "")
4077 g_settings->updateConfigFile(m_configpath.c_str());
4080 v3f findSpawnPos(ServerMap &map)
4082 //return v3f(50,50,50)*BS;
4085 s16 groundheight = 0;
4088 nodepos = v2s16(0,0);
4093 // Try to find a good place a few times
4094 for(s32 i=0; i<1000; i++)
4097 // We're going to try to throw the player to this position
4098 nodepos = v2s16(-range + (myrand()%(range*2)),
4099 -range + (myrand()%(range*2)));
4100 v2s16 sectorpos = getNodeSectorPos(nodepos);
4101 // Get sector (NOTE: Don't get because it's slow)
4102 //m_env.getMap().emergeSector(sectorpos);
4103 // Get ground height at point (fallbacks to heightmap function)
4104 groundheight = map.findGroundLevel(nodepos);
4105 // Don't go underwater
4106 if(groundheight < WATER_LEVEL)
4108 //dstream<<"-> Underwater"<<std::endl;
4111 // Don't go to high places
4112 if(groundheight > WATER_LEVEL + 4)
4114 //dstream<<"-> Underwater"<<std::endl;
4118 // Found a good place
4119 //dstream<<"Searched through "<<i<<" places."<<std::endl;
4124 // If no suitable place was not found, go above water at least.
4125 if(groundheight < WATER_LEVEL)
4126 groundheight = WATER_LEVEL;
4128 return intToFloat(v3s16(
4135 Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id)
4138 Try to get an existing player
4140 Player *player = m_env.getPlayer(name);
4143 // If player is already connected, cancel
4144 if(player->peer_id != 0)
4146 dstream<<"emergePlayer(): Player already connected"<<std::endl;
4151 player->peer_id = peer_id;
4153 // Reset inventory to creative if in creative mode
4154 if(g_settings->getBool("creative_mode"))
4156 // Warning: double code below
4157 // Backup actual inventory
4158 player->inventory_backup = new Inventory();
4159 *(player->inventory_backup) = player->inventory;
4160 // Set creative inventory
4161 craft_set_creative_inventory(player);
4168 If player with the wanted peer_id already exists, cancel.
4170 if(m_env.getPlayer(peer_id) != NULL)
4172 dstream<<"emergePlayer(): Player with wrong name but same"
4173 " peer_id already exists"<<std::endl;
4181 player = new ServerRemotePlayer();
4182 //player->peer_id = c.peer_id;
4183 //player->peer_id = PEER_ID_INEXISTENT;
4184 player->peer_id = peer_id;
4185 player->updateName(name);
4186 m_authmanager.add(name);
4187 m_authmanager.setPassword(name, password);
4188 m_authmanager.setPrivs(name,
4189 stringToPrivs(g_settings->get("default_privs")));
4195 dstream<<"Server: Finding spawn place for player \""
4196 <<player->getName()<<"\""<<std::endl;
4198 v3f pos = findSpawnPos(m_env.getServerMap());
4200 player->setPosition(pos);
4203 Add player to environment
4206 m_env.addPlayer(player);
4209 Add stuff to inventory
4212 if(g_settings->getBool("creative_mode"))
4214 // Warning: double code above
4215 // Backup actual inventory
4216 player->inventory_backup = new Inventory();
4217 *(player->inventory_backup) = player->inventory;
4218 // Set creative inventory
4219 craft_set_creative_inventory(player);
4221 else if(g_settings->getBool("give_initial_stuff"))
4223 craft_give_initial_stuff(player);
4228 } // create new player
4231 void Server::handlePeerChange(PeerChange &c)
4233 JMutexAutoLock envlock(m_env_mutex);
4234 JMutexAutoLock conlock(m_con_mutex);
4236 if(c.type == PEER_ADDED)
4243 core::map<u16, RemoteClient*>::Node *n;
4244 n = m_clients.find(c.peer_id);
4245 // The client shouldn't already exist
4249 RemoteClient *client = new RemoteClient();
4250 client->peer_id = c.peer_id;
4251 m_clients.insert(client->peer_id, client);
4254 else if(c.type == PEER_REMOVED)
4261 core::map<u16, RemoteClient*>::Node *n;
4262 n = m_clients.find(c.peer_id);
4263 // The client should exist
4267 Mark objects to be not known by the client
4269 RemoteClient *client = n->getValue();
4271 for(core::map<u16, bool>::Iterator
4272 i = client->m_known_objects.getIterator();
4273 i.atEnd()==false; i++)
4276 u16 id = i.getNode()->getKey();
4277 ServerActiveObject* obj = m_env.getActiveObject(id);
4279 if(obj && obj->m_known_by_count > 0)
4280 obj->m_known_by_count--;
4283 // Collect information about leaving in chat
4284 std::wstring message;
4286 Player *player = m_env.getPlayer(c.peer_id);
4289 std::wstring name = narrow_to_wide(player->getName());
4292 message += L" left game";
4294 message += L" (timed out)";
4300 m_env.removePlayer(c.peer_id);
4303 // Set player client disconnected
4305 Player *player = m_env.getPlayer(c.peer_id);
4307 player->peer_id = 0;
4311 delete m_clients[c.peer_id];
4312 m_clients.remove(c.peer_id);
4314 // Send player info to all remaining clients
4317 // Send leave chat message to all remaining clients
4318 BroadcastChatMessage(message);
4327 void Server::handlePeerChanges()
4329 while(m_peer_change_queue.size() > 0)
4331 PeerChange c = m_peer_change_queue.pop_front();
4333 dout_server<<"Server: Handling peer change: "
4334 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4337 handlePeerChange(c);
4341 u64 Server::getPlayerPrivs(Player *player)
4345 std::string playername = player->getName();
4346 // Local player gets all privileges regardless of
4347 // what's set on their account.
4348 if(g_settings->get("name") == playername)
4354 return getPlayerAuthPrivs(playername);
4358 void dedicated_server_loop(Server &server, bool &kill)
4360 DSTACK(__FUNCTION_NAME);
4362 dstream<<DTIME<<std::endl;
4363 dstream<<"========================"<<std::endl;
4364 dstream<<"Running dedicated server"<<std::endl;
4365 dstream<<"========================"<<std::endl;
4368 IntervalLimiter m_profiler_interval;
4372 // This is kind of a hack but can be done like this
4373 // because server.step() is very light
4375 ScopeProfiler sp(g_profiler, "dedicated server sleep");
4380 if(server.getShutdownRequested() || kill)
4382 dstream<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4389 float profiler_print_interval =
4390 g_settings->getFloat("profiler_print_interval");
4391 if(profiler_print_interval != 0)
4393 if(m_profiler_interval.step(0.030, profiler_print_interval))
4395 dstream<<"Profiler:"<<std::endl;
4396 g_profiler->print(dstream);
4397 g_profiler->clear();
4404 static int counter = 0;
4410 core::list<PlayerInfo> list = server.getPlayerInfo();
4411 core::list<PlayerInfo>::Iterator i;
4412 static u32 sum_old = 0;
4413 u32 sum = PIChecksum(list);
4416 dstream<<DTIME<<"Player info:"<<std::endl;
4417 for(i=list.begin(); i!=list.end(); i++)
4419 i->PrintLine(&dstream);