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(net_proto_version < 2)
1932 SendAccessDenied(m_con, peer_id,
1933 L"Your client is too old. Please upgrade.");
1942 char playername[PLAYERNAME_SIZE];
1943 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
1945 playername[i] = data[3+i];
1947 playername[PLAYERNAME_SIZE-1] = 0;
1949 if(playername[0]=='\0')
1951 derr_server<<DTIME<<"Server: Player has empty name"<<std::endl;
1952 SendAccessDenied(m_con, peer_id,
1957 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
1959 derr_server<<DTIME<<"Server: Player has invalid name"<<std::endl;
1960 SendAccessDenied(m_con, peer_id,
1961 L"Name contains unallowed characters");
1966 char password[PASSWORD_SIZE];
1967 if(datasize < 2+1+PLAYERNAME_SIZE+PASSWORD_SIZE)
1969 // old version - assume blank password
1974 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
1976 password[i] = data[23+i];
1978 password[PASSWORD_SIZE-1] = 0;
1981 std::string checkpwd;
1982 if(m_authmanager.exists(playername))
1984 checkpwd = m_authmanager.getPassword(playername);
1988 checkpwd = g_settings->get("default_password");
1991 /*dstream<<"Server: Client gave password '"<<password
1992 <<"', the correct one is '"<<checkpwd<<"'"<<std::endl;*/
1994 if(password != checkpwd && m_authmanager.exists(playername))
1996 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1997 <<": supplied invalid password for "
1998 <<playername<<std::endl;
1999 SendAccessDenied(m_con, peer_id, L"Invalid password");
2003 // Add player to auth manager
2004 if(m_authmanager.exists(playername) == false)
2006 derr_server<<DTIME<<"Server: adding player "<<playername
2007 <<" to auth manager"<<std::endl;
2008 m_authmanager.add(playername);
2009 m_authmanager.setPassword(playername, checkpwd);
2010 m_authmanager.setPrivs(playername,
2011 stringToPrivs(g_settings->get("default_privs")));
2012 m_authmanager.save();
2015 // Enforce user limit.
2016 // Don't enforce for users that have some admin right
2017 if(m_clients.size() >= g_settings->getU16("max_users") &&
2018 (m_authmanager.getPrivs(playername)
2019 & (PRIV_SERVER|PRIV_BAN|PRIV_PRIVS)) == 0 &&
2020 playername != g_settings->get("name"))
2022 SendAccessDenied(m_con, peer_id, L"Too many users.");
2027 Player *player = emergePlayer(playername, password, peer_id);
2029 // If failed, cancel
2032 derr_server<<DTIME<<"Server: peer_id="<<peer_id
2033 <<": failed to emerge player"<<std::endl;
2038 Answer with a TOCLIENT_INIT
2041 SharedBuffer<u8> reply(2+1+6+8);
2042 writeU16(&reply[0], TOCLIENT_INIT);
2043 writeU8(&reply[2], deployed);
2044 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
2045 writeU64(&reply[2+1+6], m_env.getServerMap().getSeed());
2048 m_con.Send(peer_id, 0, reply, true);
2052 Send complete position information
2054 SendMovePlayer(player);
2059 if(command == TOSERVER_INIT2)
2061 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
2062 <<peer->id<<std::endl;
2065 getClient(peer->id)->serialization_version
2066 = getClient(peer->id)->pending_serialization_version;
2069 Send some initialization data
2072 // Send player info to all players
2075 // Send inventory to player
2076 UpdateCrafting(peer->id);
2077 SendInventory(peer->id);
2079 // Send player items to all players
2084 Player *player = m_env.getPlayer(peer_id);
2085 SendPlayerHP(player);
2090 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2091 m_env.getTimeOfDay());
2092 m_con.Send(peer->id, 0, data, true);
2095 // Send information about server to player in chat
2096 SendChatMessage(peer_id, getStatusString());
2098 // Send information about joining in chat
2100 std::wstring name = L"unknown";
2101 Player *player = m_env.getPlayer(peer_id);
2103 name = narrow_to_wide(player->getName());
2105 std::wstring message;
2108 message += L" joined game";
2109 BroadcastChatMessage(message);
2112 // Warnings about protocol version can be issued here
2113 if(getClient(peer->id)->net_proto_version < 3)
2115 SendChatMessage(peer_id, L"# Server: WARNING: YOUR CLIENT IS OLD AND DOES NOT WORK PROPERLY WITH THIS SERVER");
2119 Check HP, respawn if necessary
2122 Player *player = m_env.getPlayer(peer_id);
2123 HandlePlayerHP(player, 0);
2129 if(peer_ser_ver == SER_FMT_VER_INVALID)
2131 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
2132 " serialization format invalid or not initialized."
2133 " Skipping incoming command="<<command<<std::endl;
2137 Player *player = m_env.getPlayer(peer_id);
2140 derr_server<<"Server::ProcessData(): Cancelling: "
2141 "No player for peer_id="<<peer_id
2145 if(command == TOSERVER_PLAYERPOS)
2147 if(datasize < 2+12+12+4+4)
2151 v3s32 ps = readV3S32(&data[start+2]);
2152 v3s32 ss = readV3S32(&data[start+2+12]);
2153 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2154 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2155 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2156 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2157 pitch = wrapDegrees(pitch);
2158 yaw = wrapDegrees(yaw);
2159 player->setPosition(position);
2160 player->setSpeed(speed);
2161 player->setPitch(pitch);
2162 player->setYaw(yaw);
2164 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2165 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2166 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2168 else if(command == TOSERVER_GOTBLOCKS)
2181 u16 count = data[2];
2182 for(u16 i=0; i<count; i++)
2184 if((s16)datasize < 2+1+(i+1)*6)
2185 throw con::InvalidIncomingDataException
2186 ("GOTBLOCKS length is too short");
2187 v3s16 p = readV3S16(&data[2+1+i*6]);
2188 /*dstream<<"Server: GOTBLOCKS ("
2189 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2190 RemoteClient *client = getClient(peer_id);
2191 client->GotBlock(p);
2194 else if(command == TOSERVER_DELETEDBLOCKS)
2207 u16 count = data[2];
2208 for(u16 i=0; i<count; i++)
2210 if((s16)datasize < 2+1+(i+1)*6)
2211 throw con::InvalidIncomingDataException
2212 ("DELETEDBLOCKS length is too short");
2213 v3s16 p = readV3S16(&data[2+1+i*6]);
2214 /*dstream<<"Server: DELETEDBLOCKS ("
2215 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2216 RemoteClient *client = getClient(peer_id);
2217 client->SetBlockNotSent(p);
2220 else if(command == TOSERVER_CLICK_OBJECT)
2222 derr_server<<"Server: CLICK_OBJECT not supported anymore"<<std::endl;
2225 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2230 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2236 [2] u8 button (0=left, 1=right)
2240 u8 button = readU8(&data[2]);
2241 u16 id = readS16(&data[3]);
2242 u16 item_i = readU16(&data[5]);
2244 ServerActiveObject *obj = m_env.getActiveObject(id);
2248 derr_server<<"Server: CLICK_ACTIVEOBJECT: object not found"
2253 // Skip if object has been removed
2257 //TODO: Check that object is reasonably close
2259 // Left click, pick object up (usually)
2263 Try creating inventory item
2265 InventoryItem *item = obj->createPickedUpItem();
2269 InventoryList *ilist = player->inventory.getList("main");
2272 if(g_settings->getBool("creative_mode") == false)
2274 // Skip if inventory has no free space
2275 if(ilist->roomForItem(item) == false)
2277 dout_server<<"Player inventory has no free space"<<std::endl;
2281 // Add to inventory and send inventory
2282 ilist->addItem(item);
2283 UpdateCrafting(player->peer_id);
2284 SendInventory(player->peer_id);
2287 // Remove object from environment
2288 obj->m_removed = true;
2294 Item cannot be picked up. Punch it instead.
2297 ToolItem *titem = NULL;
2298 std::string toolname = "";
2300 InventoryList *mlist = player->inventory.getList("main");
2303 InventoryItem *item = mlist->getItem(item_i);
2304 if(item && (std::string)item->getName() == "ToolItem")
2306 titem = (ToolItem*)item;
2307 toolname = titem->getToolName();
2311 v3f playerpos = player->getPosition();
2312 v3f objpos = obj->getBasePosition();
2313 v3f dir = (objpos - playerpos).normalize();
2315 u16 wear = obj->punch(toolname, dir, player->getName());
2319 bool weared_out = titem->addWear(wear);
2321 mlist->deleteItem(item_i);
2322 SendInventory(player->peer_id);
2326 // Right click, do something with object
2329 // Track hp changes super-crappily
2330 u16 oldhp = player->hp;
2333 obj->rightClick(player);
2336 if(player->hp != oldhp)
2338 SendPlayerHP(player);
2342 else if(command == TOSERVER_GROUND_ACTION)
2350 [3] v3s16 nodepos_undersurface
2351 [9] v3s16 nodepos_abovesurface
2356 2: stop digging (all parameters ignored)
2357 3: digging completed
2359 u8 action = readU8(&data[2]);
2361 p_under.X = readS16(&data[3]);
2362 p_under.Y = readS16(&data[5]);
2363 p_under.Z = readS16(&data[7]);
2365 p_over.X = readS16(&data[9]);
2366 p_over.Y = readS16(&data[11]);
2367 p_over.Z = readS16(&data[13]);
2368 u16 item_i = readU16(&data[15]);
2370 //TODO: Check that target is reasonably close
2378 NOTE: This can be used in the future to check if
2379 somebody is cheating, by checking the timing.
2386 else if(action == 2)
2389 RemoteClient *client = getClient(peer->id);
2390 JMutexAutoLock digmutex(client->m_dig_mutex);
2391 client->m_dig_tool_item = -1;
2396 3: Digging completed
2398 else if(action == 3)
2400 // Mandatory parameter; actually used for nothing
2401 core::map<v3s16, MapBlock*> modified_blocks;
2403 content_t material = CONTENT_IGNORE;
2404 u8 mineral = MINERAL_NONE;
2406 bool cannot_remove_node = false;
2410 MapNode n = m_env.getMap().getNode(p_under);
2412 mineral = n.getMineral();
2413 // Get material at position
2414 material = n.getContent();
2415 // If not yet cancelled
2416 if(cannot_remove_node == false)
2418 // If it's not diggable, do nothing
2419 if(content_diggable(material) == false)
2421 derr_server<<"Server: Not finishing digging: "
2422 <<"Node not diggable"
2424 cannot_remove_node = true;
2427 // If not yet cancelled
2428 if(cannot_remove_node == false)
2430 // Get node metadata
2431 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p_under);
2432 if(meta && meta->nodeRemovalDisabled() == true)
2434 derr_server<<"Server: Not finishing digging: "
2435 <<"Node metadata disables removal"
2437 cannot_remove_node = true;
2441 catch(InvalidPositionException &e)
2443 derr_server<<"Server: Not finishing digging: Node not found."
2444 <<" Adding block to emerge queue."
2446 m_emerge_queue.addBlock(peer_id,
2447 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2448 cannot_remove_node = true;
2451 // Make sure the player is allowed to do it
2452 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2454 dstream<<"Player "<<player->getName()<<" cannot remove node"
2455 <<" because privileges are "<<getPlayerPrivs(player)
2457 cannot_remove_node = true;
2461 If node can't be removed, set block to be re-sent to
2464 if(cannot_remove_node)
2466 derr_server<<"Server: Not finishing digging."<<std::endl;
2468 // Client probably has wrong data.
2469 // Set block not sent, so that client will get
2471 dstream<<"Client "<<peer_id<<" tried to dig "
2472 <<"node; but node cannot be removed."
2473 <<" setting MapBlock not sent."<<std::endl;
2474 RemoteClient *client = getClient(peer_id);
2475 v3s16 blockpos = getNodeBlockPos(p_under);
2476 client->SetBlockNotSent(blockpos);
2482 Send the removal to all close-by players.
2483 - If other player is close, send REMOVENODE
2484 - Otherwise set blocks not sent
2486 core::list<u16> far_players;
2487 sendRemoveNode(p_under, peer_id, &far_players, 30);
2490 Update and send inventory
2493 if(g_settings->getBool("creative_mode") == false)
2498 InventoryList *mlist = player->inventory.getList("main");
2501 InventoryItem *item = mlist->getItem(item_i);
2502 if(item && (std::string)item->getName() == "ToolItem")
2504 ToolItem *titem = (ToolItem*)item;
2505 std::string toolname = titem->getToolName();
2507 // Get digging properties for material and tool
2508 DiggingProperties prop =
2509 getDiggingProperties(material, toolname);
2511 if(prop.diggable == false)
2513 derr_server<<"Server: WARNING: Player digged"
2514 <<" with impossible material + tool"
2515 <<" combination"<<std::endl;
2518 bool weared_out = titem->addWear(prop.wear);
2522 mlist->deleteItem(item_i);
2528 Add dug item to inventory
2531 InventoryItem *item = NULL;
2533 if(mineral != MINERAL_NONE)
2534 item = getDiggedMineralItem(mineral);
2539 std::string &dug_s = content_features(material).dug_item;
2542 std::istringstream is(dug_s, std::ios::binary);
2543 item = InventoryItem::deSerialize(is);
2549 // Add a item to inventory
2550 player->inventory.addItem("main", item);
2553 UpdateCrafting(player->peer_id);
2554 SendInventory(player->peer_id);
2559 if(mineral != MINERAL_NONE)
2560 item = getDiggedMineralItem(mineral);
2565 std::string &extra_dug_s = content_features(material).extra_dug_item;
2566 s32 extra_rarity = content_features(material).extra_dug_item_rarity;
2567 if(extra_dug_s != "" && extra_rarity != 0
2568 && myrand() % extra_rarity == 0)
2570 std::istringstream is(extra_dug_s, std::ios::binary);
2571 item = InventoryItem::deSerialize(is);
2577 // Add a item to inventory
2578 player->inventory.addItem("main", item);
2581 UpdateCrafting(player->peer_id);
2582 SendInventory(player->peer_id);
2588 (this takes some time so it is done after the quick stuff)
2591 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
2593 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2596 Set blocks not sent to far players
2598 for(core::list<u16>::Iterator
2599 i = far_players.begin();
2600 i != far_players.end(); i++)
2603 RemoteClient *client = getClient(peer_id);
2606 client->SetBlocksNotSent(modified_blocks);
2613 else if(action == 1)
2616 InventoryList *ilist = player->inventory.getList("main");
2621 InventoryItem *item = ilist->getItem(item_i);
2623 // If there is no item, it is not possible to add it anywhere
2628 Handle material items
2630 if(std::string("MaterialItem") == item->getName())
2633 // Don't add a node if this is not a free space
2634 MapNode n2 = m_env.getMap().getNode(p_over);
2635 bool no_enough_privs =
2636 ((getPlayerPrivs(player) & PRIV_BUILD)==0);
2638 dstream<<"Player "<<player->getName()<<" cannot add node"
2639 <<" because privileges are "<<getPlayerPrivs(player)
2642 if(content_features(n2).buildable_to == false
2645 // Client probably has wrong data.
2646 // Set block not sent, so that client will get
2648 dstream<<"Client "<<peer_id<<" tried to place"
2649 <<" node in invalid position; setting"
2650 <<" MapBlock not sent."<<std::endl;
2651 RemoteClient *client = getClient(peer_id);
2652 v3s16 blockpos = getNodeBlockPos(p_over);
2653 client->SetBlockNotSent(blockpos);
2657 catch(InvalidPositionException &e)
2659 derr_server<<"Server: Ignoring ADDNODE: Node not found"
2660 <<" Adding block to emerge queue."
2662 m_emerge_queue.addBlock(peer_id,
2663 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2667 // Reset build time counter
2668 getClient(peer->id)->m_time_from_building = 0.0;
2671 MaterialItem *mitem = (MaterialItem*)item;
2673 n.setContent(mitem->getMaterial());
2675 // Calculate direction for wall mounted stuff
2676 if(content_features(n).wall_mounted)
2677 n.param2 = packDir(p_under - p_over);
2679 // Calculate the direction for furnaces and chests and stuff
2680 if(content_features(n).param_type == CPT_FACEDIR_SIMPLE)
2682 v3f playerpos = player->getPosition();
2683 v3f blockpos = intToFloat(p_over, BS) - playerpos;
2684 blockpos = blockpos.normalize();
2686 if (fabs(blockpos.X) > fabs(blockpos.Z)) {
2700 Send to all close-by players
2702 core::list<u16> far_players;
2703 sendAddNode(p_over, n, 0, &far_players, 30);
2708 InventoryList *ilist = player->inventory.getList("main");
2709 if(g_settings->getBool("creative_mode") == false && ilist)
2711 // Remove from inventory and send inventory
2712 if(mitem->getCount() == 1)
2713 ilist->deleteItem(item_i);
2717 UpdateCrafting(peer_id);
2718 SendInventory(peer_id);
2724 This takes some time so it is done after the quick stuff
2726 core::map<v3s16, MapBlock*> modified_blocks;
2728 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
2730 std::string p_name = std::string(player->getName());
2731 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks, p_name);
2734 Set blocks not sent to far players
2736 for(core::list<u16>::Iterator
2737 i = far_players.begin();
2738 i != far_players.end(); i++)
2741 RemoteClient *client = getClient(peer_id);
2744 client->SetBlocksNotSent(modified_blocks);
2748 Calculate special events
2751 /*if(n.d == CONTENT_MESE)
2754 for(s16 z=-1; z<=1; z++)
2755 for(s16 y=-1; y<=1; y++)
2756 for(s16 x=-1; x<=1; x++)
2763 Place other item (not a block)
2767 v3s16 blockpos = getNodeBlockPos(p_over);
2770 Check that the block is loaded so that the item
2771 can properly be added to the static list too
2773 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2776 derr_server<<"Error while placing object: "
2777 "block not found"<<std::endl;
2782 If in creative mode, item dropping is disabled unless
2783 player has build privileges
2785 if(g_settings->getBool("creative_mode") &&
2786 (getPlayerPrivs(player) & PRIV_BUILD) == 0)
2788 derr_server<<"Not allowing player to drop item: "
2789 "creative mode and no build privs"<<std::endl;
2793 dout_server<<"Placing a miscellaneous item on map"
2796 // Calculate a position for it
2797 v3f pos = intToFloat(p_over, BS);
2799 pos.Y -= BS*0.25; // let it drop a bit
2801 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2802 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2807 ServerActiveObject *obj = item->createSAO(&m_env, 0, pos);
2811 derr_server<<"WARNING: item resulted in NULL object, "
2812 <<"not placing onto map"
2817 // Add the object to the environment
2818 m_env.addActiveObject(obj);
2820 dout_server<<"Placed object"<<std::endl;
2822 if(g_settings->getBool("creative_mode") == false)
2824 // Delete the right amount of items from the slot
2825 u16 dropcount = item->getDropCount();
2827 // Delete item if all gone
2828 if(item->getCount() <= dropcount)
2830 if(item->getCount() < dropcount)
2831 dstream<<"WARNING: Server: dropped more items"
2832 <<" than the slot contains"<<std::endl;
2834 InventoryList *ilist = player->inventory.getList("main");
2836 // Remove from inventory and send inventory
2837 ilist->deleteItem(item_i);
2839 // Else decrement it
2841 item->remove(dropcount);
2844 UpdateCrafting(peer_id);
2845 SendInventory(peer_id);
2853 Catch invalid actions
2857 derr_server<<"WARNING: Server: Invalid action "
2858 <<action<<std::endl;
2862 else if(command == TOSERVER_RELEASE)
2871 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2874 else if(command == TOSERVER_SIGNTEXT)
2876 derr_server<<"Server: TOSERVER_SIGNTEXT not supported anymore"
2880 else if(command == TOSERVER_SIGNNODETEXT)
2882 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2890 std::string datastring((char*)&data[2], datasize-2);
2891 std::istringstream is(datastring, std::ios_base::binary);
2894 is.read((char*)buf, 6);
2895 v3s16 p = readV3S16(buf);
2896 is.read((char*)buf, 2);
2897 u16 textlen = readU16(buf);
2899 for(u16 i=0; i<textlen; i++)
2901 is.read((char*)buf, 1);
2902 text += (char)buf[0];
2905 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2908 if(meta->typeId() != CONTENT_SIGN_WALL)
2910 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
2911 signmeta->setText(text);
2913 v3s16 blockpos = getNodeBlockPos(p);
2914 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2917 block->setChangedFlag();
2920 for(core::map<u16, RemoteClient*>::Iterator
2921 i = m_clients.getIterator();
2922 i.atEnd()==false; i++)
2924 RemoteClient *client = i.getNode()->getValue();
2925 client->SetBlockNotSent(blockpos);
2928 else if(command == TOSERVER_INVENTORY_ACTION)
2930 /*// Ignore inventory changes if in creative mode
2931 if(g_settings->getBool("creative_mode") == true)
2933 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2937 // Strip command and create a stream
2938 std::string datastring((char*)&data[2], datasize-2);
2939 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2940 std::istringstream is(datastring, std::ios_base::binary);
2942 InventoryAction *a = InventoryAction::deSerialize(is);
2947 c.current_player = player;
2950 Handle craftresult specially if not in creative mode
2952 bool disable_action = false;
2953 if(a->getType() == IACTION_MOVE
2954 && g_settings->getBool("creative_mode") == false)
2956 IMoveAction *ma = (IMoveAction*)a;
2957 if(ma->to_inv == "current_player" &&
2958 ma->from_inv == "current_player")
2960 InventoryList *rlist = player->inventory.getList("craftresult");
2962 InventoryList *clist = player->inventory.getList("craft");
2964 InventoryList *mlist = player->inventory.getList("main");
2967 Craftresult is no longer preview if something
2970 if(ma->to_list == "craftresult"
2971 && ma->from_list != "craftresult")
2973 // If it currently is a preview, remove
2975 if(player->craftresult_is_preview)
2977 rlist->deleteItem(0);
2979 player->craftresult_is_preview = false;
2982 Crafting takes place if this condition is true.
2984 if(player->craftresult_is_preview &&
2985 ma->from_list == "craftresult")
2987 player->craftresult_is_preview = false;
2988 clist->decrementMaterials(1);
2991 If the craftresult is placed on itself, move it to
2992 main inventory instead of doing the action
2994 if(ma->to_list == "craftresult"
2995 && ma->from_list == "craftresult")
2997 disable_action = true;
2999 InventoryItem *item1 = rlist->changeItem(0, NULL);
3000 mlist->addItem(item1);
3003 // Disallow moving items if not allowed to build
3004 else if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
3008 // if it's a locking chest, only allow the owner or server admins to move items
3009 else if (ma->from_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
3011 Strfnd fn(ma->from_inv);
3012 std::string id0 = fn.next(":");
3013 if(id0 == "nodemeta")
3016 p.X = stoi(fn.next(","));
3017 p.Y = stoi(fn.next(","));
3018 p.Z = stoi(fn.next(","));
3019 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3020 if(meta && meta->typeId() == CONTENT_LOCKABLE_CHEST) {
3021 LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
3022 if (lcm->getOwner() != player->getName())
3027 else if (ma->to_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
3029 Strfnd fn(ma->to_inv);
3030 std::string id0 = fn.next(":");
3031 if(id0 == "nodemeta")
3034 p.X = stoi(fn.next(","));
3035 p.Y = stoi(fn.next(","));
3036 p.Z = stoi(fn.next(","));
3037 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3038 if(meta && meta->typeId() == CONTENT_LOCKABLE_CHEST) {
3039 LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
3040 if (lcm->getOwner() != player->getName())
3047 if(disable_action == false)
3049 // Feed action to player inventory
3057 UpdateCrafting(player->peer_id);
3058 SendInventory(player->peer_id);
3063 dstream<<"TOSERVER_INVENTORY_ACTION: "
3064 <<"InventoryAction::deSerialize() returned NULL"
3068 else if(command == TOSERVER_CHAT_MESSAGE)
3076 std::string datastring((char*)&data[2], datasize-2);
3077 std::istringstream is(datastring, std::ios_base::binary);
3080 is.read((char*)buf, 2);
3081 u16 len = readU16(buf);
3083 std::wstring message;
3084 for(u16 i=0; i<len; i++)
3086 is.read((char*)buf, 2);
3087 message += (wchar_t)readU16(buf);
3090 // Get player name of this client
3091 std::wstring name = narrow_to_wide(player->getName());
3093 // Line to send to players
3095 // Whether to send to the player that sent the line
3096 bool send_to_sender = false;
3097 // Whether to send to other players
3098 bool send_to_others = false;
3100 // Local player gets all privileges regardless of
3101 // what's set on their account.
3102 u64 privs = getPlayerPrivs(player);
3105 if(message[0] == L'/')
3107 size_t strip_size = 1;
3108 if (message[1] == L'#') // support old-style commans
3110 message = message.substr(strip_size);
3112 WStrfnd f1(message);
3113 f1.next(L" "); // Skip over /#whatever
3114 std::wstring paramstring = f1.next(L"");
3116 ServerCommandContext *ctx = new ServerCommandContext(
3117 str_split(message, L' '),
3124 std::wstring reply(processServerCommand(ctx));
3125 send_to_sender = ctx->flags & SEND_TO_SENDER;
3126 send_to_others = ctx->flags & SEND_TO_OTHERS;
3128 if (ctx->flags & SEND_NO_PREFIX)
3131 line += L"Server: " + reply;
3138 if(privs & PRIV_SHOUT)
3144 send_to_others = true;
3148 line += L"Server: You are not allowed to shout";
3149 send_to_sender = true;
3155 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
3158 Send the message to clients
3160 for(core::map<u16, RemoteClient*>::Iterator
3161 i = m_clients.getIterator();
3162 i.atEnd() == false; i++)
3164 // Get client and check that it is valid
3165 RemoteClient *client = i.getNode()->getValue();
3166 assert(client->peer_id == i.getNode()->getKey());
3167 if(client->serialization_version == SER_FMT_VER_INVALID)
3171 bool sender_selected = (peer_id == client->peer_id);
3172 if(sender_selected == true && send_to_sender == false)
3174 if(sender_selected == false && send_to_others == false)
3177 SendChatMessage(client->peer_id, line);
3181 else if(command == TOSERVER_DAMAGE)
3183 std::string datastring((char*)&data[2], datasize-2);
3184 std::istringstream is(datastring, std::ios_base::binary);
3185 u8 damage = readU8(is);
3187 if(g_settings->getBool("enable_damage"))
3189 HandlePlayerHP(player, damage);
3193 SendPlayerHP(player);
3196 else if(command == TOSERVER_PASSWORD)
3199 [0] u16 TOSERVER_PASSWORD
3200 [2] u8[28] old password
3201 [30] u8[28] new password
3204 if(datasize != 2+PASSWORD_SIZE*2)
3206 /*char password[PASSWORD_SIZE];
3207 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3208 password[i] = data[2+i];
3209 password[PASSWORD_SIZE-1] = 0;*/
3211 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3219 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3221 char c = data[2+PASSWORD_SIZE+i];
3227 dstream<<"Server: Client requests a password change from "
3228 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
3230 std::string playername = player->getName();
3232 if(m_authmanager.exists(playername) == false)
3234 dstream<<"Server: playername not found in authmanager"<<std::endl;
3235 // Wrong old password supplied!!
3236 SendChatMessage(peer_id, L"playername not found in authmanager");
3240 std::string checkpwd = m_authmanager.getPassword(playername);
3242 if(oldpwd != checkpwd)
3244 dstream<<"Server: invalid old password"<<std::endl;
3245 // Wrong old password supplied!!
3246 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
3250 m_authmanager.setPassword(playername, newpwd);
3252 dstream<<"Server: password change successful for "<<playername
3254 SendChatMessage(peer_id, L"Password change successful");
3256 else if(command == TOSERVER_PLAYERITEM)
3261 u16 item = readU16(&data[2]);
3262 player->wieldItem(item);
3263 SendWieldedItem(player);
3265 else if(command == TOSERVER_RESPAWN)
3270 RespawnPlayer(player);
3274 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
3275 "unknown command "<<command<<std::endl;
3279 catch(SendFailedException &e)
3281 derr_server<<"Server::ProcessData(): SendFailedException: "
3287 void Server::onMapEditEvent(MapEditEvent *event)
3289 //dstream<<"Server::onMapEditEvent()"<<std::endl;
3290 if(m_ignore_map_edit_events)
3292 MapEditEvent *e = event->clone();
3293 m_unsent_map_edit_queue.push_back(e);
3296 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3298 if(id == "current_player")
3300 assert(c->current_player);
3301 return &(c->current_player->inventory);
3305 std::string id0 = fn.next(":");
3307 if(id0 == "nodemeta")
3310 p.X = stoi(fn.next(","));
3311 p.Y = stoi(fn.next(","));
3312 p.Z = stoi(fn.next(","));
3313 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3315 return meta->getInventory();
3316 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3317 <<"no metadata found"<<std::endl;
3321 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3324 void Server::inventoryModified(InventoryContext *c, std::string id)
3326 if(id == "current_player")
3328 assert(c->current_player);
3330 UpdateCrafting(c->current_player->peer_id);
3331 SendInventory(c->current_player->peer_id);
3336 std::string id0 = fn.next(":");
3338 if(id0 == "nodemeta")
3341 p.X = stoi(fn.next(","));
3342 p.Y = stoi(fn.next(","));
3343 p.Z = stoi(fn.next(","));
3344 v3s16 blockpos = getNodeBlockPos(p);
3346 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3348 meta->inventoryModified();
3350 for(core::map<u16, RemoteClient*>::Iterator
3351 i = m_clients.getIterator();
3352 i.atEnd()==false; i++)
3354 RemoteClient *client = i.getNode()->getValue();
3355 client->SetBlockNotSent(blockpos);
3361 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3364 core::list<PlayerInfo> Server::getPlayerInfo()
3366 DSTACK(__FUNCTION_NAME);
3367 JMutexAutoLock envlock(m_env_mutex);
3368 JMutexAutoLock conlock(m_con_mutex);
3370 core::list<PlayerInfo> list;
3372 core::list<Player*> players = m_env.getPlayers();
3374 core::list<Player*>::Iterator i;
3375 for(i = players.begin();
3376 i != players.end(); i++)
3380 Player *player = *i;
3383 con::Peer *peer = m_con.GetPeer(player->peer_id);
3384 // Copy info from peer to info struct
3386 info.address = peer->address;
3387 info.avg_rtt = peer->avg_rtt;
3389 catch(con::PeerNotFoundException &e)
3391 // Set dummy peer info
3393 info.address = Address(0,0,0,0,0);
3397 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3398 info.position = player->getPosition();
3400 list.push_back(info);
3407 void Server::peerAdded(con::Peer *peer)
3409 DSTACK(__FUNCTION_NAME);
3410 dout_server<<"Server::peerAdded(): peer->id="
3411 <<peer->id<<std::endl;
3414 c.type = PEER_ADDED;
3415 c.peer_id = peer->id;
3417 m_peer_change_queue.push_back(c);
3420 void Server::deletingPeer(con::Peer *peer, bool timeout)
3422 DSTACK(__FUNCTION_NAME);
3423 dout_server<<"Server::deletingPeer(): peer->id="
3424 <<peer->id<<", timeout="<<timeout<<std::endl;
3427 c.type = PEER_REMOVED;
3428 c.peer_id = peer->id;
3429 c.timeout = timeout;
3430 m_peer_change_queue.push_back(c);
3437 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3439 DSTACK(__FUNCTION_NAME);
3440 std::ostringstream os(std::ios_base::binary);
3442 writeU16(os, TOCLIENT_HP);
3446 std::string s = os.str();
3447 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3449 con.Send(peer_id, 0, data, true);
3452 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3453 const std::wstring &reason)
3455 DSTACK(__FUNCTION_NAME);
3456 std::ostringstream os(std::ios_base::binary);
3458 writeU16(os, TOCLIENT_ACCESS_DENIED);
3459 os<<serializeWideString(reason);
3462 std::string s = os.str();
3463 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3465 con.Send(peer_id, 0, data, true);
3468 void Server::SendDeathscreen(con::Connection &con, u16 peer_id,
3469 bool set_camera_point_target, v3f camera_point_target)
3471 DSTACK(__FUNCTION_NAME);
3472 std::ostringstream os(std::ios_base::binary);
3474 writeU16(os, TOCLIENT_DEATHSCREEN);
3475 writeU8(os, set_camera_point_target);
3476 writeV3F1000(os, camera_point_target);
3479 std::string s = os.str();
3480 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3482 con.Send(peer_id, 0, data, true);
3486 Non-static send methods
3489 void Server::SendObjectData(float dtime)
3491 DSTACK(__FUNCTION_NAME);
3493 core::map<v3s16, bool> stepped_blocks;
3495 for(core::map<u16, RemoteClient*>::Iterator
3496 i = m_clients.getIterator();
3497 i.atEnd() == false; i++)
3499 u16 peer_id = i.getNode()->getKey();
3500 RemoteClient *client = i.getNode()->getValue();
3501 assert(client->peer_id == peer_id);
3503 if(client->serialization_version == SER_FMT_VER_INVALID)
3506 client->SendObjectData(this, dtime, stepped_blocks);
3510 void Server::SendPlayerInfos()
3512 DSTACK(__FUNCTION_NAME);
3514 //JMutexAutoLock envlock(m_env_mutex);
3516 // Get connected players
3517 core::list<Player*> players = m_env.getPlayers(true);
3519 u32 player_count = players.getSize();
3520 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3522 SharedBuffer<u8> data(datasize);
3523 writeU16(&data[0], TOCLIENT_PLAYERINFO);
3526 core::list<Player*>::Iterator i;
3527 for(i = players.begin();
3528 i != players.end(); i++)
3530 Player *player = *i;
3532 /*dstream<<"Server sending player info for player with "
3533 "peer_id="<<player->peer_id<<std::endl;*/
3535 writeU16(&data[start], player->peer_id);
3536 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3537 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3538 start += 2+PLAYERNAME_SIZE;
3541 //JMutexAutoLock conlock(m_con_mutex);
3544 m_con.SendToAll(0, data, true);
3547 void Server::SendInventory(u16 peer_id)
3549 DSTACK(__FUNCTION_NAME);
3551 Player* player = m_env.getPlayer(peer_id);
3558 std::ostringstream os;
3559 //os.imbue(std::locale("C"));
3561 player->inventory.serialize(os);
3563 std::string s = os.str();
3565 SharedBuffer<u8> data(s.size()+2);
3566 writeU16(&data[0], TOCLIENT_INVENTORY);
3567 memcpy(&data[2], s.c_str(), s.size());
3570 m_con.Send(peer_id, 0, data, true);
3573 std::string getWieldedItemString(const Player *player)
3575 const InventoryItem *item = player->getWieldItem();
3577 return std::string("");
3578 std::ostringstream os(std::ios_base::binary);
3579 item->serialize(os);
3583 void Server::SendWieldedItem(const Player* player)
3585 DSTACK(__FUNCTION_NAME);
3589 std::ostringstream os(std::ios_base::binary);
3591 writeU16(os, TOCLIENT_PLAYERITEM);
3593 writeU16(os, player->peer_id);
3594 os<<serializeString(getWieldedItemString(player));
3597 std::string s = os.str();
3598 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3600 m_con.SendToAll(0, data, true);
3603 void Server::SendPlayerItems()
3605 DSTACK(__FUNCTION_NAME);
3607 std::ostringstream os(std::ios_base::binary);
3608 core::list<Player *> players = m_env.getPlayers(true);
3610 writeU16(os, TOCLIENT_PLAYERITEM);
3611 writeU16(os, players.size());
3612 core::list<Player *>::Iterator i;
3613 for(i = players.begin(); i != players.end(); ++i)
3616 writeU16(os, p->peer_id);
3617 os<<serializeString(getWieldedItemString(p));
3621 std::string s = os.str();
3622 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3624 m_con.SendToAll(0, data, true);
3627 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3629 DSTACK(__FUNCTION_NAME);
3631 std::ostringstream os(std::ios_base::binary);
3635 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3636 os.write((char*)buf, 2);
3639 writeU16(buf, message.size());
3640 os.write((char*)buf, 2);
3643 for(u32 i=0; i<message.size(); i++)
3647 os.write((char*)buf, 2);
3651 std::string s = os.str();
3652 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3654 m_con.Send(peer_id, 0, data, true);
3657 void Server::BroadcastChatMessage(const std::wstring &message)
3659 for(core::map<u16, RemoteClient*>::Iterator
3660 i = m_clients.getIterator();
3661 i.atEnd() == false; i++)
3663 // Get client and check that it is valid
3664 RemoteClient *client = i.getNode()->getValue();
3665 assert(client->peer_id == i.getNode()->getKey());
3666 if(client->serialization_version == SER_FMT_VER_INVALID)
3669 SendChatMessage(client->peer_id, message);
3673 void Server::SendPlayerHP(Player *player)
3675 SendHP(m_con, player->peer_id, player->hp);
3678 void Server::SendMovePlayer(Player *player)
3680 DSTACK(__FUNCTION_NAME);
3681 std::ostringstream os(std::ios_base::binary);
3683 writeU16(os, TOCLIENT_MOVE_PLAYER);
3684 writeV3F1000(os, player->getPosition());
3685 writeF1000(os, player->getPitch());
3686 writeF1000(os, player->getYaw());
3689 v3f pos = player->getPosition();
3690 f32 pitch = player->getPitch();
3691 f32 yaw = player->getYaw();
3692 dstream<<"Server sending TOCLIENT_MOVE_PLAYER"
3693 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3700 std::string s = os.str();
3701 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3703 m_con.Send(player->peer_id, 0, data, true);
3706 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3707 core::list<u16> *far_players, float far_d_nodes)
3709 float maxd = far_d_nodes*BS;
3710 v3f p_f = intToFloat(p, BS);
3714 SharedBuffer<u8> reply(replysize);
3715 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3716 writeS16(&reply[2], p.X);
3717 writeS16(&reply[4], p.Y);
3718 writeS16(&reply[6], p.Z);
3720 for(core::map<u16, RemoteClient*>::Iterator
3721 i = m_clients.getIterator();
3722 i.atEnd() == false; i++)
3724 // Get client and check that it is valid
3725 RemoteClient *client = i.getNode()->getValue();
3726 assert(client->peer_id == i.getNode()->getKey());
3727 if(client->serialization_version == SER_FMT_VER_INVALID)
3730 // Don't send if it's the same one
3731 if(client->peer_id == ignore_id)
3737 Player *player = m_env.getPlayer(client->peer_id);
3740 // If player is far away, only set modified blocks not sent
3741 v3f player_pos = player->getPosition();
3742 if(player_pos.getDistanceFrom(p_f) > maxd)
3744 far_players->push_back(client->peer_id);
3751 m_con.Send(client->peer_id, 0, reply, true);
3755 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3756 core::list<u16> *far_players, float far_d_nodes)
3758 float maxd = far_d_nodes*BS;
3759 v3f p_f = intToFloat(p, BS);
3761 for(core::map<u16, RemoteClient*>::Iterator
3762 i = m_clients.getIterator();
3763 i.atEnd() == false; i++)
3765 // Get client and check that it is valid
3766 RemoteClient *client = i.getNode()->getValue();
3767 assert(client->peer_id == i.getNode()->getKey());
3768 if(client->serialization_version == SER_FMT_VER_INVALID)
3771 // Don't send if it's the same one
3772 if(client->peer_id == ignore_id)
3778 Player *player = m_env.getPlayer(client->peer_id);
3781 // If player is far away, only set modified blocks not sent
3782 v3f player_pos = player->getPosition();
3783 if(player_pos.getDistanceFrom(p_f) > maxd)
3785 far_players->push_back(client->peer_id);
3792 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3793 SharedBuffer<u8> reply(replysize);
3794 writeU16(&reply[0], TOCLIENT_ADDNODE);
3795 writeS16(&reply[2], p.X);
3796 writeS16(&reply[4], p.Y);
3797 writeS16(&reply[6], p.Z);
3798 n.serialize(&reply[8], client->serialization_version);
3801 m_con.Send(client->peer_id, 0, reply, true);
3805 void Server::setBlockNotSent(v3s16 p)
3807 for(core::map<u16, RemoteClient*>::Iterator
3808 i = m_clients.getIterator();
3809 i.atEnd()==false; i++)
3811 RemoteClient *client = i.getNode()->getValue();
3812 client->SetBlockNotSent(p);
3816 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3818 DSTACK(__FUNCTION_NAME);
3820 v3s16 p = block->getPos();
3824 bool completely_air = true;
3825 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3826 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3827 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3829 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
3831 completely_air = false;
3832 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
3837 dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
3839 dstream<<"[completely air] ";
3844 Create a packet with the block in the right format
3847 std::ostringstream os(std::ios_base::binary);
3848 block->serialize(os, ver);
3849 std::string s = os.str();
3850 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3852 u32 replysize = 8 + blockdata.getSize();
3853 SharedBuffer<u8> reply(replysize);
3854 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3855 writeS16(&reply[2], p.X);
3856 writeS16(&reply[4], p.Y);
3857 writeS16(&reply[6], p.Z);
3858 memcpy(&reply[8], *blockdata, blockdata.getSize());
3860 /*dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3861 <<": \tpacket size: "<<replysize<<std::endl;*/
3866 m_con.Send(peer_id, 1, reply, true);
3869 void Server::SendBlocks(float dtime)
3871 DSTACK(__FUNCTION_NAME);
3873 JMutexAutoLock envlock(m_env_mutex);
3874 JMutexAutoLock conlock(m_con_mutex);
3876 //TimeTaker timer("Server::SendBlocks");
3878 core::array<PrioritySortedBlockTransfer> queue;
3880 s32 total_sending = 0;
3883 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
3885 for(core::map<u16, RemoteClient*>::Iterator
3886 i = m_clients.getIterator();
3887 i.atEnd() == false; i++)
3889 RemoteClient *client = i.getNode()->getValue();
3890 assert(client->peer_id == i.getNode()->getKey());
3892 total_sending += client->SendingCount();
3894 if(client->serialization_version == SER_FMT_VER_INVALID)
3897 client->GetNextBlocks(this, dtime, queue);
3902 // Lowest priority number comes first.
3903 // Lowest is most important.
3906 for(u32 i=0; i<queue.size(); i++)
3908 //TODO: Calculate limit dynamically
3909 if(total_sending >= g_settings->getS32
3910 ("max_simultaneous_block_sends_server_total"))
3913 PrioritySortedBlockTransfer q = queue[i];
3915 MapBlock *block = NULL;
3918 block = m_env.getMap().getBlockNoCreate(q.pos);
3920 catch(InvalidPositionException &e)
3925 RemoteClient *client = getClient(q.peer_id);
3927 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3929 client->SentBlock(q.pos);
3939 void Server::HandlePlayerHP(Player *player, s16 damage)
3941 if(player->hp > damage)
3943 player->hp -= damage;
3944 SendPlayerHP(player);
3948 dstream<<"Server::HandlePlayerHP(): Player "
3949 <<player->getName()<<" dies"<<std::endl;
3953 //TODO: Throw items around
3955 // Handle players that are not connected
3956 if(player->peer_id == PEER_ID_INEXISTENT){
3957 RespawnPlayer(player);
3961 SendPlayerHP(player);
3963 RemoteClient *client = getClient(player->peer_id);
3964 if(client->net_proto_version >= 3)
3966 SendDeathscreen(m_con, player->peer_id, false, v3f(0,0,0));
3970 RespawnPlayer(player);
3975 void Server::RespawnPlayer(Player *player)
3977 v3f pos = findSpawnPos(m_env.getServerMap());
3978 player->setPosition(pos);
3980 SendMovePlayer(player);
3981 SendPlayerHP(player);
3984 void Server::UpdateCrafting(u16 peer_id)
3986 DSTACK(__FUNCTION_NAME);
3988 Player* player = m_env.getPlayer(peer_id);
3992 Calculate crafting stuff
3994 if(g_settings->getBool("creative_mode") == false)
3996 InventoryList *clist = player->inventory.getList("craft");
3997 InventoryList *rlist = player->inventory.getList("craftresult");
3999 if(rlist && rlist->getUsedSlots() == 0)
4000 player->craftresult_is_preview = true;
4002 if(rlist && player->craftresult_is_preview)
4004 rlist->clearItems();
4006 if(clist && rlist && player->craftresult_is_preview)
4008 InventoryItem *items[9];
4009 for(u16 i=0; i<9; i++)
4011 items[i] = clist->getItem(i);
4014 // Get result of crafting grid
4015 InventoryItem *result = craft_get_result(items);
4017 rlist->addItem(result);
4020 } // if creative_mode == false
4023 RemoteClient* Server::getClient(u16 peer_id)
4025 DSTACK(__FUNCTION_NAME);
4026 //JMutexAutoLock lock(m_con_mutex);
4027 core::map<u16, RemoteClient*>::Node *n;
4028 n = m_clients.find(peer_id);
4029 // A client should exist for all peers
4031 return n->getValue();
4034 std::wstring Server::getStatusString()
4036 std::wostringstream os(std::ios_base::binary);
4039 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4041 os<<L", uptime="<<m_uptime.get();
4042 // Information about clients
4044 for(core::map<u16, RemoteClient*>::Iterator
4045 i = m_clients.getIterator();
4046 i.atEnd() == false; i++)
4048 // Get client and check that it is valid
4049 RemoteClient *client = i.getNode()->getValue();
4050 assert(client->peer_id == i.getNode()->getKey());
4051 if(client->serialization_version == SER_FMT_VER_INVALID)
4054 Player *player = m_env.getPlayer(client->peer_id);
4055 // Get name of player
4056 std::wstring name = L"unknown";
4058 name = narrow_to_wide(player->getName());
4059 // Add name to information string
4063 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
4064 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4065 if(g_settings->get("motd") != "")
4066 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4070 // Saves g_settings to configpath given at initialization
4071 void Server::saveConfig()
4073 if(m_configpath != "")
4074 g_settings->updateConfigFile(m_configpath.c_str());
4077 v3f findSpawnPos(ServerMap &map)
4079 //return v3f(50,50,50)*BS;
4082 s16 groundheight = 0;
4085 nodepos = v2s16(0,0);
4090 // Try to find a good place a few times
4091 for(s32 i=0; i<1000; i++)
4094 // We're going to try to throw the player to this position
4095 nodepos = v2s16(-range + (myrand()%(range*2)),
4096 -range + (myrand()%(range*2)));
4097 v2s16 sectorpos = getNodeSectorPos(nodepos);
4098 // Get sector (NOTE: Don't get because it's slow)
4099 //m_env.getMap().emergeSector(sectorpos);
4100 // Get ground height at point (fallbacks to heightmap function)
4101 groundheight = map.findGroundLevel(nodepos);
4102 // Don't go underwater
4103 if(groundheight < WATER_LEVEL)
4105 //dstream<<"-> Underwater"<<std::endl;
4108 // Don't go to high places
4109 if(groundheight > WATER_LEVEL + 4)
4111 //dstream<<"-> Underwater"<<std::endl;
4115 // Found a good place
4116 //dstream<<"Searched through "<<i<<" places."<<std::endl;
4121 // If no suitable place was not found, go above water at least.
4122 if(groundheight < WATER_LEVEL)
4123 groundheight = WATER_LEVEL;
4125 return intToFloat(v3s16(
4132 Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id)
4135 Try to get an existing player
4137 Player *player = m_env.getPlayer(name);
4140 // If player is already connected, cancel
4141 if(player->peer_id != 0)
4143 dstream<<"emergePlayer(): Player already connected"<<std::endl;
4148 player->peer_id = peer_id;
4150 // Reset inventory to creative if in creative mode
4151 if(g_settings->getBool("creative_mode"))
4153 // Warning: double code below
4154 // Backup actual inventory
4155 player->inventory_backup = new Inventory();
4156 *(player->inventory_backup) = player->inventory;
4157 // Set creative inventory
4158 craft_set_creative_inventory(player);
4165 If player with the wanted peer_id already exists, cancel.
4167 if(m_env.getPlayer(peer_id) != NULL)
4169 dstream<<"emergePlayer(): Player with wrong name but same"
4170 " peer_id already exists"<<std::endl;
4178 player = new ServerRemotePlayer();
4179 //player->peer_id = c.peer_id;
4180 //player->peer_id = PEER_ID_INEXISTENT;
4181 player->peer_id = peer_id;
4182 player->updateName(name);
4183 m_authmanager.add(name);
4184 m_authmanager.setPassword(name, password);
4185 m_authmanager.setPrivs(name,
4186 stringToPrivs(g_settings->get("default_privs")));
4192 dstream<<"Server: Finding spawn place for player \""
4193 <<player->getName()<<"\""<<std::endl;
4195 v3f pos = findSpawnPos(m_env.getServerMap());
4197 player->setPosition(pos);
4200 Add player to environment
4203 m_env.addPlayer(player);
4206 Add stuff to inventory
4209 if(g_settings->getBool("creative_mode"))
4211 // Warning: double code above
4212 // Backup actual inventory
4213 player->inventory_backup = new Inventory();
4214 *(player->inventory_backup) = player->inventory;
4215 // Set creative inventory
4216 craft_set_creative_inventory(player);
4218 else if(g_settings->getBool("give_initial_stuff"))
4220 craft_give_initial_stuff(player);
4225 } // create new player
4228 void Server::handlePeerChange(PeerChange &c)
4230 JMutexAutoLock envlock(m_env_mutex);
4231 JMutexAutoLock conlock(m_con_mutex);
4233 if(c.type == PEER_ADDED)
4240 core::map<u16, RemoteClient*>::Node *n;
4241 n = m_clients.find(c.peer_id);
4242 // The client shouldn't already exist
4246 RemoteClient *client = new RemoteClient();
4247 client->peer_id = c.peer_id;
4248 m_clients.insert(client->peer_id, client);
4251 else if(c.type == PEER_REMOVED)
4258 core::map<u16, RemoteClient*>::Node *n;
4259 n = m_clients.find(c.peer_id);
4260 // The client should exist
4264 Mark objects to be not known by the client
4266 RemoteClient *client = n->getValue();
4268 for(core::map<u16, bool>::Iterator
4269 i = client->m_known_objects.getIterator();
4270 i.atEnd()==false; i++)
4273 u16 id = i.getNode()->getKey();
4274 ServerActiveObject* obj = m_env.getActiveObject(id);
4276 if(obj && obj->m_known_by_count > 0)
4277 obj->m_known_by_count--;
4280 // Collect information about leaving in chat
4281 std::wstring message;
4283 Player *player = m_env.getPlayer(c.peer_id);
4286 std::wstring name = narrow_to_wide(player->getName());
4289 message += L" left game";
4291 message += L" (timed out)";
4297 m_env.removePlayer(c.peer_id);
4300 // Set player client disconnected
4302 Player *player = m_env.getPlayer(c.peer_id);
4304 player->peer_id = 0;
4308 delete m_clients[c.peer_id];
4309 m_clients.remove(c.peer_id);
4311 // Send player info to all remaining clients
4314 // Send leave chat message to all remaining clients
4315 BroadcastChatMessage(message);
4324 void Server::handlePeerChanges()
4326 while(m_peer_change_queue.size() > 0)
4328 PeerChange c = m_peer_change_queue.pop_front();
4330 dout_server<<"Server: Handling peer change: "
4331 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4334 handlePeerChange(c);
4338 u64 Server::getPlayerPrivs(Player *player)
4342 std::string playername = player->getName();
4343 // Local player gets all privileges regardless of
4344 // what's set on their account.
4345 if(g_settings->get("name") == playername)
4351 return getPlayerAuthPrivs(playername);
4355 void dedicated_server_loop(Server &server, bool &kill)
4357 DSTACK(__FUNCTION_NAME);
4359 dstream<<DTIME<<std::endl;
4360 dstream<<"========================"<<std::endl;
4361 dstream<<"Running dedicated server"<<std::endl;
4362 dstream<<"========================"<<std::endl;
4365 IntervalLimiter m_profiler_interval;
4369 // This is kind of a hack but can be done like this
4370 // because server.step() is very light
4372 ScopeProfiler sp(g_profiler, "dedicated server sleep");
4377 if(server.getShutdownRequested() || kill)
4379 dstream<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4386 float profiler_print_interval =
4387 g_settings->getFloat("profiler_print_interval");
4388 if(profiler_print_interval != 0)
4390 if(m_profiler_interval.step(0.030, profiler_print_interval))
4392 dstream<<"Profiler:"<<std::endl;
4393 g_profiler->print(dstream);
4394 g_profiler->clear();
4401 static int counter = 0;
4407 core::list<PlayerInfo> list = server.getPlayerInfo();
4408 core::list<PlayerInfo>::Iterator i;
4409 static u32 sum_old = 0;
4410 u32 sum = PIChecksum(list);
4413 dstream<<DTIME<<"Player info:"<<std::endl;
4414 for(i=list.begin(); i!=list.end(); i++)
4416 i->PrintLine(&dstream);