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);
2030 // DEBUG: Test serialization
2031 std::ostringstream test_os;
2032 player->serialize(test_os);
2033 dstream<<"Player serialization test: \""<<test_os.str()
2035 std::istringstream test_is(test_os.str());
2036 player->deSerialize(test_is);
2039 // If failed, cancel
2042 derr_server<<DTIME<<"Server: peer_id="<<peer_id
2043 <<": failed to emerge player"<<std::endl;
2048 // If a client is already connected to the player, cancel
2049 if(player->peer_id != 0)
2051 derr_server<<DTIME<<"Server: peer_id="<<peer_id
2052 <<" tried to connect to "
2053 "an already connected player (peer_id="
2054 <<player->peer_id<<")"<<std::endl;
2057 // Set client of player
2058 player->peer_id = peer_id;
2061 // Check if player doesn't exist
2063 throw con::InvalidIncomingDataException
2064 ("Server::ProcessData(): INIT: Player doesn't exist");
2066 /*// update name if it was supplied
2067 if(datasize >= 20+3)
2070 player->updateName((const char*)&data[3]);
2074 Answer with a TOCLIENT_INIT
2077 SharedBuffer<u8> reply(2+1+6+8);
2078 writeU16(&reply[0], TOCLIENT_INIT);
2079 writeU8(&reply[2], deployed);
2080 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
2081 writeU64(&reply[2+1+6], m_env.getServerMap().getSeed());
2084 m_con.Send(peer_id, 0, reply, true);
2088 Send complete position information
2090 SendMovePlayer(player);
2095 if(command == TOSERVER_INIT2)
2097 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
2098 <<peer->id<<std::endl;
2101 getClient(peer->id)->serialization_version
2102 = getClient(peer->id)->pending_serialization_version;
2105 Send some initialization data
2108 // Send player info to all players
2111 // Send inventory to player
2112 UpdateCrafting(peer->id);
2113 SendInventory(peer->id);
2115 // Send player items to all players
2120 Player *player = m_env.getPlayer(peer_id);
2121 SendPlayerHP(player);
2126 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2127 m_env.getTimeOfDay());
2128 m_con.Send(peer->id, 0, data, true);
2131 // Send information about server to player in chat
2132 SendChatMessage(peer_id, getStatusString());
2134 // Send information about joining in chat
2136 std::wstring name = L"unknown";
2137 Player *player = m_env.getPlayer(peer_id);
2139 name = narrow_to_wide(player->getName());
2141 std::wstring message;
2144 message += L" joined game";
2145 BroadcastChatMessage(message);
2148 // Warnings about protocol version can be issued here
2149 /*if(getClient(peer->id)->net_proto_version == 0)
2151 SendChatMessage(peer_id, L"# Server: NOTE: YOUR CLIENT IS OLD AND DOES NOT WORK PROPERLY WITH THIS SERVER");
2157 if(peer_ser_ver == SER_FMT_VER_INVALID)
2159 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
2160 " serialization format invalid or not initialized."
2161 " Skipping incoming command="<<command<<std::endl;
2165 Player *player = m_env.getPlayer(peer_id);
2168 derr_server<<"Server::ProcessData(): Cancelling: "
2169 "No player for peer_id="<<peer_id
2173 if(command == TOSERVER_PLAYERPOS)
2175 if(datasize < 2+12+12+4+4)
2179 v3s32 ps = readV3S32(&data[start+2]);
2180 v3s32 ss = readV3S32(&data[start+2+12]);
2181 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2182 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2183 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2184 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2185 pitch = wrapDegrees(pitch);
2186 yaw = wrapDegrees(yaw);
2187 player->setPosition(position);
2188 player->setSpeed(speed);
2189 player->setPitch(pitch);
2190 player->setYaw(yaw);
2192 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2193 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2194 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2196 else if(command == TOSERVER_GOTBLOCKS)
2209 u16 count = data[2];
2210 for(u16 i=0; i<count; i++)
2212 if((s16)datasize < 2+1+(i+1)*6)
2213 throw con::InvalidIncomingDataException
2214 ("GOTBLOCKS length is too short");
2215 v3s16 p = readV3S16(&data[2+1+i*6]);
2216 /*dstream<<"Server: GOTBLOCKS ("
2217 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2218 RemoteClient *client = getClient(peer_id);
2219 client->GotBlock(p);
2222 else if(command == TOSERVER_DELETEDBLOCKS)
2235 u16 count = data[2];
2236 for(u16 i=0; i<count; i++)
2238 if((s16)datasize < 2+1+(i+1)*6)
2239 throw con::InvalidIncomingDataException
2240 ("DELETEDBLOCKS length is too short");
2241 v3s16 p = readV3S16(&data[2+1+i*6]);
2242 /*dstream<<"Server: DELETEDBLOCKS ("
2243 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2244 RemoteClient *client = getClient(peer_id);
2245 client->SetBlockNotSent(p);
2248 else if(command == TOSERVER_CLICK_OBJECT)
2250 derr_server<<"Server: CLICK_OBJECT not supported anymore"<<std::endl;
2253 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2258 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2264 [2] u8 button (0=left, 1=right)
2268 u8 button = readU8(&data[2]);
2269 u16 id = readS16(&data[3]);
2270 u16 item_i = readU16(&data[5]);
2272 ServerActiveObject *obj = m_env.getActiveObject(id);
2276 derr_server<<"Server: CLICK_ACTIVEOBJECT: object not found"
2281 // Skip if object has been removed
2285 //TODO: Check that object is reasonably close
2287 // Left click, pick object up (usually)
2291 Try creating inventory item
2293 InventoryItem *item = obj->createPickedUpItem();
2297 InventoryList *ilist = player->inventory.getList("main");
2300 if(g_settings->getBool("creative_mode") == false)
2302 // Skip if inventory has no free space
2303 if(ilist->roomForItem(item) == false)
2305 dout_server<<"Player inventory has no free space"<<std::endl;
2309 // Add to inventory and send inventory
2310 ilist->addItem(item);
2311 UpdateCrafting(player->peer_id);
2312 SendInventory(player->peer_id);
2315 // Remove object from environment
2316 obj->m_removed = true;
2322 Item cannot be picked up. Punch it instead.
2325 ToolItem *titem = NULL;
2326 std::string toolname = "";
2328 InventoryList *mlist = player->inventory.getList("main");
2331 InventoryItem *item = mlist->getItem(item_i);
2332 if(item && (std::string)item->getName() == "ToolItem")
2334 titem = (ToolItem*)item;
2335 toolname = titem->getToolName();
2339 v3f playerpos = player->getPosition();
2340 v3f objpos = obj->getBasePosition();
2341 v3f dir = (objpos - playerpos).normalize();
2343 u16 wear = obj->punch(toolname, dir);
2347 bool weared_out = titem->addWear(wear);
2349 mlist->deleteItem(item_i);
2350 SendInventory(player->peer_id);
2354 // Right click, do something with object
2357 // Track hp changes super-crappily
2358 u16 oldhp = player->hp;
2361 obj->rightClick(player);
2364 if(player->hp != oldhp)
2366 SendPlayerHP(player);
2370 else if(command == TOSERVER_GROUND_ACTION)
2378 [3] v3s16 nodepos_undersurface
2379 [9] v3s16 nodepos_abovesurface
2384 2: stop digging (all parameters ignored)
2385 3: digging completed
2387 u8 action = readU8(&data[2]);
2389 p_under.X = readS16(&data[3]);
2390 p_under.Y = readS16(&data[5]);
2391 p_under.Z = readS16(&data[7]);
2393 p_over.X = readS16(&data[9]);
2394 p_over.Y = readS16(&data[11]);
2395 p_over.Z = readS16(&data[13]);
2396 u16 item_i = readU16(&data[15]);
2398 //TODO: Check that target is reasonably close
2406 NOTE: This can be used in the future to check if
2407 somebody is cheating, by checking the timing.
2414 else if(action == 2)
2417 RemoteClient *client = getClient(peer->id);
2418 JMutexAutoLock digmutex(client->m_dig_mutex);
2419 client->m_dig_tool_item = -1;
2424 3: Digging completed
2426 else if(action == 3)
2428 // Mandatory parameter; actually used for nothing
2429 core::map<v3s16, MapBlock*> modified_blocks;
2431 content_t material = CONTENT_IGNORE;
2432 u8 mineral = MINERAL_NONE;
2434 bool cannot_remove_node = false;
2438 MapNode n = m_env.getMap().getNode(p_under);
2440 mineral = n.getMineral();
2441 // Get material at position
2442 material = n.getContent();
2443 // If not yet cancelled
2444 if(cannot_remove_node == false)
2446 // If it's not diggable, do nothing
2447 if(content_diggable(material) == false)
2449 derr_server<<"Server: Not finishing digging: "
2450 <<"Node not diggable"
2452 cannot_remove_node = true;
2455 // If not yet cancelled
2456 if(cannot_remove_node == false)
2458 // Get node metadata
2459 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p_under);
2460 if(meta && meta->nodeRemovalDisabled() == true)
2462 derr_server<<"Server: Not finishing digging: "
2463 <<"Node metadata disables removal"
2465 cannot_remove_node = true;
2469 catch(InvalidPositionException &e)
2471 derr_server<<"Server: Not finishing digging: Node not found."
2472 <<" Adding block to emerge queue."
2474 m_emerge_queue.addBlock(peer_id,
2475 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2476 cannot_remove_node = true;
2479 // Make sure the player is allowed to do it
2480 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2482 dstream<<"Player "<<player->getName()<<" cannot remove node"
2483 <<" because privileges are "<<getPlayerPrivs(player)
2485 cannot_remove_node = true;
2489 If node can't be removed, set block to be re-sent to
2492 if(cannot_remove_node)
2494 derr_server<<"Server: Not finishing digging."<<std::endl;
2496 // Client probably has wrong data.
2497 // Set block not sent, so that client will get
2499 dstream<<"Client "<<peer_id<<" tried to dig "
2500 <<"node; but node cannot be removed."
2501 <<" setting MapBlock not sent."<<std::endl;
2502 RemoteClient *client = getClient(peer_id);
2503 v3s16 blockpos = getNodeBlockPos(p_under);
2504 client->SetBlockNotSent(blockpos);
2510 Send the removal to all close-by players.
2511 - If other player is close, send REMOVENODE
2512 - Otherwise set blocks not sent
2514 core::list<u16> far_players;
2515 sendRemoveNode(p_under, peer_id, &far_players, 30);
2518 Update and send inventory
2521 if(g_settings->getBool("creative_mode") == false)
2526 InventoryList *mlist = player->inventory.getList("main");
2529 InventoryItem *item = mlist->getItem(item_i);
2530 if(item && (std::string)item->getName() == "ToolItem")
2532 ToolItem *titem = (ToolItem*)item;
2533 std::string toolname = titem->getToolName();
2535 // Get digging properties for material and tool
2536 DiggingProperties prop =
2537 getDiggingProperties(material, toolname);
2539 if(prop.diggable == false)
2541 derr_server<<"Server: WARNING: Player digged"
2542 <<" with impossible material + tool"
2543 <<" combination"<<std::endl;
2546 bool weared_out = titem->addWear(prop.wear);
2550 mlist->deleteItem(item_i);
2556 Add dug item to inventory
2559 InventoryItem *item = NULL;
2561 if(mineral != MINERAL_NONE)
2562 item = getDiggedMineralItem(mineral);
2567 std::string &dug_s = content_features(material).dug_item;
2570 std::istringstream is(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);
2587 if(mineral != MINERAL_NONE)
2588 item = getDiggedMineralItem(mineral);
2593 std::string &extra_dug_s = content_features(material).extra_dug_item;
2594 s32 extra_rarity = content_features(material).extra_dug_item_rarity;
2595 if(extra_dug_s != "" && extra_rarity != 0
2596 && myrand() % extra_rarity == 0)
2598 std::istringstream is(extra_dug_s, std::ios::binary);
2599 item = InventoryItem::deSerialize(is);
2605 // Add a item to inventory
2606 player->inventory.addItem("main", item);
2609 UpdateCrafting(player->peer_id);
2610 SendInventory(player->peer_id);
2616 (this takes some time so it is done after the quick stuff)
2619 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
2621 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2624 Set blocks not sent to far players
2626 for(core::list<u16>::Iterator
2627 i = far_players.begin();
2628 i != far_players.end(); i++)
2631 RemoteClient *client = getClient(peer_id);
2634 client->SetBlocksNotSent(modified_blocks);
2641 else if(action == 1)
2644 InventoryList *ilist = player->inventory.getList("main");
2649 InventoryItem *item = ilist->getItem(item_i);
2651 // If there is no item, it is not possible to add it anywhere
2656 Handle material items
2658 if(std::string("MaterialItem") == item->getName())
2661 // Don't add a node if this is not a free space
2662 MapNode n2 = m_env.getMap().getNode(p_over);
2663 bool no_enough_privs =
2664 ((getPlayerPrivs(player) & PRIV_BUILD)==0);
2666 dstream<<"Player "<<player->getName()<<" cannot add node"
2667 <<" because privileges are "<<getPlayerPrivs(player)
2670 if(content_features(n2).buildable_to == false
2673 // Client probably has wrong data.
2674 // Set block not sent, so that client will get
2676 dstream<<"Client "<<peer_id<<" tried to place"
2677 <<" node in invalid position; setting"
2678 <<" MapBlock not sent."<<std::endl;
2679 RemoteClient *client = getClient(peer_id);
2680 v3s16 blockpos = getNodeBlockPos(p_over);
2681 client->SetBlockNotSent(blockpos);
2685 catch(InvalidPositionException &e)
2687 derr_server<<"Server: Ignoring ADDNODE: Node not found"
2688 <<" Adding block to emerge queue."
2690 m_emerge_queue.addBlock(peer_id,
2691 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2695 // Reset build time counter
2696 getClient(peer->id)->m_time_from_building = 0.0;
2699 MaterialItem *mitem = (MaterialItem*)item;
2701 n.setContent(mitem->getMaterial());
2703 // Calculate direction for wall mounted stuff
2704 if(content_features(n).wall_mounted)
2705 n.param2 = packDir(p_under - p_over);
2707 // Calculate the direction for furnaces and chests and stuff
2708 if(content_features(n).param_type == CPT_FACEDIR_SIMPLE)
2710 v3f playerpos = player->getPosition();
2711 v3f blockpos = intToFloat(p_over, BS) - playerpos;
2712 blockpos = blockpos.normalize();
2714 if (fabs(blockpos.X) > fabs(blockpos.Z)) {
2728 Send to all close-by players
2730 core::list<u16> far_players;
2731 sendAddNode(p_over, n, 0, &far_players, 30);
2736 InventoryList *ilist = player->inventory.getList("main");
2737 if(g_settings->getBool("creative_mode") == false && ilist)
2739 // Remove from inventory and send inventory
2740 if(mitem->getCount() == 1)
2741 ilist->deleteItem(item_i);
2745 UpdateCrafting(peer_id);
2746 SendInventory(peer_id);
2752 This takes some time so it is done after the quick stuff
2754 core::map<v3s16, MapBlock*> modified_blocks;
2756 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
2758 std::string p_name = std::string(player->getName());
2759 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks, p_name);
2762 Set blocks not sent to far players
2764 for(core::list<u16>::Iterator
2765 i = far_players.begin();
2766 i != far_players.end(); i++)
2769 RemoteClient *client = getClient(peer_id);
2772 client->SetBlocksNotSent(modified_blocks);
2776 Calculate special events
2779 /*if(n.d == CONTENT_MESE)
2782 for(s16 z=-1; z<=1; z++)
2783 for(s16 y=-1; y<=1; y++)
2784 for(s16 x=-1; x<=1; x++)
2791 Place other item (not a block)
2795 v3s16 blockpos = getNodeBlockPos(p_over);
2798 Check that the block is loaded so that the item
2799 can properly be added to the static list too
2801 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2804 derr_server<<"Error while placing object: "
2805 "block not found"<<std::endl;
2810 If in creative mode, item dropping is disabled unless
2811 player has build privileges
2813 if(g_settings->getBool("creative_mode") &&
2814 (getPlayerPrivs(player) & PRIV_BUILD) == 0)
2816 derr_server<<"Not allowing player to drop item: "
2817 "creative mode and no build privs"<<std::endl;
2821 dout_server<<"Placing a miscellaneous item on map"
2824 // Calculate a position for it
2825 v3f pos = intToFloat(p_over, BS);
2827 pos.Y -= BS*0.25; // let it drop a bit
2829 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2830 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2835 ServerActiveObject *obj = item->createSAO(&m_env, 0, pos);
2839 derr_server<<"WARNING: item resulted in NULL object, "
2840 <<"not placing onto map"
2845 // Add the object to the environment
2846 m_env.addActiveObject(obj);
2848 dout_server<<"Placed object"<<std::endl;
2850 if(g_settings->getBool("creative_mode") == false)
2852 // Delete the right amount of items from the slot
2853 u16 dropcount = item->getDropCount();
2855 // Delete item if all gone
2856 if(item->getCount() <= dropcount)
2858 if(item->getCount() < dropcount)
2859 dstream<<"WARNING: Server: dropped more items"
2860 <<" than the slot contains"<<std::endl;
2862 InventoryList *ilist = player->inventory.getList("main");
2864 // Remove from inventory and send inventory
2865 ilist->deleteItem(item_i);
2867 // Else decrement it
2869 item->remove(dropcount);
2872 UpdateCrafting(peer_id);
2873 SendInventory(peer_id);
2881 Catch invalid actions
2885 derr_server<<"WARNING: Server: Invalid action "
2886 <<action<<std::endl;
2890 else if(command == TOSERVER_RELEASE)
2899 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2902 else if(command == TOSERVER_SIGNTEXT)
2904 derr_server<<"Server: TOSERVER_SIGNTEXT not supported anymore"
2908 else if(command == TOSERVER_SIGNNODETEXT)
2910 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2918 std::string datastring((char*)&data[2], datasize-2);
2919 std::istringstream is(datastring, std::ios_base::binary);
2922 is.read((char*)buf, 6);
2923 v3s16 p = readV3S16(buf);
2924 is.read((char*)buf, 2);
2925 u16 textlen = readU16(buf);
2927 for(u16 i=0; i<textlen; i++)
2929 is.read((char*)buf, 1);
2930 text += (char)buf[0];
2933 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2936 if(meta->typeId() != CONTENT_SIGN_WALL)
2938 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
2939 signmeta->setText(text);
2941 v3s16 blockpos = getNodeBlockPos(p);
2942 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2945 block->setChangedFlag();
2948 for(core::map<u16, RemoteClient*>::Iterator
2949 i = m_clients.getIterator();
2950 i.atEnd()==false; i++)
2952 RemoteClient *client = i.getNode()->getValue();
2953 client->SetBlockNotSent(blockpos);
2956 else if(command == TOSERVER_INVENTORY_ACTION)
2958 /*// Ignore inventory changes if in creative mode
2959 if(g_settings->getBool("creative_mode") == true)
2961 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2965 // Strip command and create a stream
2966 std::string datastring((char*)&data[2], datasize-2);
2967 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2968 std::istringstream is(datastring, std::ios_base::binary);
2970 InventoryAction *a = InventoryAction::deSerialize(is);
2975 c.current_player = player;
2978 Handle craftresult specially if not in creative mode
2980 bool disable_action = false;
2981 if(a->getType() == IACTION_MOVE
2982 && g_settings->getBool("creative_mode") == false)
2984 IMoveAction *ma = (IMoveAction*)a;
2985 if(ma->to_inv == "current_player" &&
2986 ma->from_inv == "current_player")
2988 InventoryList *rlist = player->inventory.getList("craftresult");
2990 InventoryList *clist = player->inventory.getList("craft");
2992 InventoryList *mlist = player->inventory.getList("main");
2995 Craftresult is no longer preview if something
2998 if(ma->to_list == "craftresult"
2999 && ma->from_list != "craftresult")
3001 // If it currently is a preview, remove
3003 if(player->craftresult_is_preview)
3005 rlist->deleteItem(0);
3007 player->craftresult_is_preview = false;
3010 Crafting takes place if this condition is true.
3012 if(player->craftresult_is_preview &&
3013 ma->from_list == "craftresult")
3015 player->craftresult_is_preview = false;
3016 clist->decrementMaterials(1);
3019 If the craftresult is placed on itself, move it to
3020 main inventory instead of doing the action
3022 if(ma->to_list == "craftresult"
3023 && ma->from_list == "craftresult")
3025 disable_action = true;
3027 InventoryItem *item1 = rlist->changeItem(0, NULL);
3028 mlist->addItem(item1);
3031 // Disallow moving items if not allowed to build
3032 else if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
3036 // if it's a locking chest, only allow the owner or server admins to move items
3037 else if (ma->from_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
3039 Strfnd fn(ma->from_inv);
3040 std::string id0 = fn.next(":");
3041 if(id0 == "nodemeta")
3044 p.X = stoi(fn.next(","));
3045 p.Y = stoi(fn.next(","));
3046 p.Z = stoi(fn.next(","));
3047 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3048 if(meta && meta->typeId() == CONTENT_LOCKABLE_CHEST) {
3049 LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
3050 if (lcm->getOwner() != player->getName())
3055 else if (ma->to_inv != "current_player" && (getPlayerPrivs(player) & PRIV_SERVER) == 0)
3057 Strfnd fn(ma->to_inv);
3058 std::string id0 = fn.next(":");
3059 if(id0 == "nodemeta")
3062 p.X = stoi(fn.next(","));
3063 p.Y = stoi(fn.next(","));
3064 p.Z = stoi(fn.next(","));
3065 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3066 if(meta && meta->typeId() == CONTENT_LOCKABLE_CHEST) {
3067 LockingChestNodeMetadata *lcm = (LockingChestNodeMetadata*)meta;
3068 if (lcm->getOwner() != player->getName())
3075 if(disable_action == false)
3077 // Feed action to player inventory
3085 UpdateCrafting(player->peer_id);
3086 SendInventory(player->peer_id);
3091 dstream<<"TOSERVER_INVENTORY_ACTION: "
3092 <<"InventoryAction::deSerialize() returned NULL"
3096 else if(command == TOSERVER_CHAT_MESSAGE)
3104 std::string datastring((char*)&data[2], datasize-2);
3105 std::istringstream is(datastring, std::ios_base::binary);
3108 is.read((char*)buf, 2);
3109 u16 len = readU16(buf);
3111 std::wstring message;
3112 for(u16 i=0; i<len; i++)
3114 is.read((char*)buf, 2);
3115 message += (wchar_t)readU16(buf);
3118 // Get player name of this client
3119 std::wstring name = narrow_to_wide(player->getName());
3121 // Line to send to players
3123 // Whether to send to the player that sent the line
3124 bool send_to_sender = false;
3125 // Whether to send to other players
3126 bool send_to_others = false;
3128 // Local player gets all privileges regardless of
3129 // what's set on their account.
3130 u64 privs = getPlayerPrivs(player);
3133 if(message[0] == L'/')
3135 size_t strip_size = 1;
3136 if (message[1] == L'#') // support old-style commans
3138 message = message.substr(strip_size);
3140 WStrfnd f1(message);
3141 f1.next(L" "); // Skip over /#whatever
3142 std::wstring paramstring = f1.next(L"");
3144 ServerCommandContext *ctx = new ServerCommandContext(
3145 str_split(message, L' '),
3152 std::wstring reply(processServerCommand(ctx));
3153 send_to_sender = ctx->flags & SEND_TO_SENDER;
3154 send_to_others = ctx->flags & SEND_TO_OTHERS;
3156 if (ctx->flags & SEND_NO_PREFIX)
3159 line += L"Server: " + reply;
3166 if(privs & PRIV_SHOUT)
3172 send_to_others = true;
3176 line += L"Server: You are not allowed to shout";
3177 send_to_sender = true;
3183 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
3186 Send the message to clients
3188 for(core::map<u16, RemoteClient*>::Iterator
3189 i = m_clients.getIterator();
3190 i.atEnd() == false; i++)
3192 // Get client and check that it is valid
3193 RemoteClient *client = i.getNode()->getValue();
3194 assert(client->peer_id == i.getNode()->getKey());
3195 if(client->serialization_version == SER_FMT_VER_INVALID)
3199 bool sender_selected = (peer_id == client->peer_id);
3200 if(sender_selected == true && send_to_sender == false)
3202 if(sender_selected == false && send_to_others == false)
3205 SendChatMessage(client->peer_id, line);
3209 else if(command == TOSERVER_DAMAGE)
3211 if(g_settings->getBool("enable_damage"))
3213 std::string datastring((char*)&data[2], datasize-2);
3214 std::istringstream is(datastring, std::ios_base::binary);
3215 u8 damage = readU8(is);
3216 if(player->hp > damage)
3218 player->hp -= damage;
3224 dstream<<"TODO: Server: TOSERVER_HP_DECREMENT: Player dies"
3227 v3f pos = findSpawnPos(m_env.getServerMap());
3228 player->setPosition(pos);
3230 SendMovePlayer(player);
3231 SendPlayerHP(player);
3233 //TODO: Throw items around
3237 SendPlayerHP(player);
3239 else if(command == TOSERVER_PASSWORD)
3242 [0] u16 TOSERVER_PASSWORD
3243 [2] u8[28] old password
3244 [30] u8[28] new password
3247 if(datasize != 2+PASSWORD_SIZE*2)
3249 /*char password[PASSWORD_SIZE];
3250 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3251 password[i] = data[2+i];
3252 password[PASSWORD_SIZE-1] = 0;*/
3254 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3262 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3264 char c = data[2+PASSWORD_SIZE+i];
3270 dstream<<"Server: Client requests a password change from "
3271 <<"'"<<oldpwd<<"' to '"<<newpwd<<"'"<<std::endl;
3273 std::string playername = player->getName();
3275 if(m_authmanager.exists(playername) == false)
3277 dstream<<"Server: playername not found in authmanager"<<std::endl;
3278 // Wrong old password supplied!!
3279 SendChatMessage(peer_id, L"playername not found in authmanager");
3283 std::string checkpwd = m_authmanager.getPassword(playername);
3285 if(oldpwd != checkpwd)
3287 dstream<<"Server: invalid old password"<<std::endl;
3288 // Wrong old password supplied!!
3289 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
3293 m_authmanager.setPassword(playername, newpwd);
3295 dstream<<"Server: password change successful for "<<playername
3297 SendChatMessage(peer_id, L"Password change successful");
3299 else if (command == TOSERVER_PLAYERITEM)
3304 u16 item = readU16(&data[2]);
3305 player->wieldItem(item);
3306 SendWieldedItem(player);
3310 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
3311 "unknown command "<<command<<std::endl;
3315 catch(SendFailedException &e)
3317 derr_server<<"Server::ProcessData(): SendFailedException: "
3323 void Server::onMapEditEvent(MapEditEvent *event)
3325 //dstream<<"Server::onMapEditEvent()"<<std::endl;
3326 if(m_ignore_map_edit_events)
3328 MapEditEvent *e = event->clone();
3329 m_unsent_map_edit_queue.push_back(e);
3332 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3334 if(id == "current_player")
3336 assert(c->current_player);
3337 return &(c->current_player->inventory);
3341 std::string id0 = fn.next(":");
3343 if(id0 == "nodemeta")
3346 p.X = stoi(fn.next(","));
3347 p.Y = stoi(fn.next(","));
3348 p.Z = stoi(fn.next(","));
3349 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3351 return meta->getInventory();
3352 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3353 <<"no metadata found"<<std::endl;
3357 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3360 void Server::inventoryModified(InventoryContext *c, std::string id)
3362 if(id == "current_player")
3364 assert(c->current_player);
3366 UpdateCrafting(c->current_player->peer_id);
3367 SendInventory(c->current_player->peer_id);
3372 std::string id0 = fn.next(":");
3374 if(id0 == "nodemeta")
3377 p.X = stoi(fn.next(","));
3378 p.Y = stoi(fn.next(","));
3379 p.Z = stoi(fn.next(","));
3380 v3s16 blockpos = getNodeBlockPos(p);
3382 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3384 meta->inventoryModified();
3386 for(core::map<u16, RemoteClient*>::Iterator
3387 i = m_clients.getIterator();
3388 i.atEnd()==false; i++)
3390 RemoteClient *client = i.getNode()->getValue();
3391 client->SetBlockNotSent(blockpos);
3397 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3400 core::list<PlayerInfo> Server::getPlayerInfo()
3402 DSTACK(__FUNCTION_NAME);
3403 JMutexAutoLock envlock(m_env_mutex);
3404 JMutexAutoLock conlock(m_con_mutex);
3406 core::list<PlayerInfo> list;
3408 core::list<Player*> players = m_env.getPlayers();
3410 core::list<Player*>::Iterator i;
3411 for(i = players.begin();
3412 i != players.end(); i++)
3416 Player *player = *i;
3419 con::Peer *peer = m_con.GetPeer(player->peer_id);
3420 // Copy info from peer to info struct
3422 info.address = peer->address;
3423 info.avg_rtt = peer->avg_rtt;
3425 catch(con::PeerNotFoundException &e)
3427 // Set dummy peer info
3429 info.address = Address(0,0,0,0,0);
3433 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3434 info.position = player->getPosition();
3436 list.push_back(info);
3443 void Server::peerAdded(con::Peer *peer)
3445 DSTACK(__FUNCTION_NAME);
3446 dout_server<<"Server::peerAdded(): peer->id="
3447 <<peer->id<<std::endl;
3450 c.type = PEER_ADDED;
3451 c.peer_id = peer->id;
3453 m_peer_change_queue.push_back(c);
3456 void Server::deletingPeer(con::Peer *peer, bool timeout)
3458 DSTACK(__FUNCTION_NAME);
3459 dout_server<<"Server::deletingPeer(): peer->id="
3460 <<peer->id<<", timeout="<<timeout<<std::endl;
3463 c.type = PEER_REMOVED;
3464 c.peer_id = peer->id;
3465 c.timeout = timeout;
3466 m_peer_change_queue.push_back(c);
3473 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3475 DSTACK(__FUNCTION_NAME);
3476 std::ostringstream os(std::ios_base::binary);
3478 writeU16(os, TOCLIENT_HP);
3482 std::string s = os.str();
3483 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3485 con.Send(peer_id, 0, data, true);
3488 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3489 const std::wstring &reason)
3491 DSTACK(__FUNCTION_NAME);
3492 std::ostringstream os(std::ios_base::binary);
3494 writeU16(os, TOCLIENT_ACCESS_DENIED);
3495 os<<serializeWideString(reason);
3498 std::string s = os.str();
3499 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3501 con.Send(peer_id, 0, data, true);
3505 Non-static send methods
3508 void Server::SendObjectData(float dtime)
3510 DSTACK(__FUNCTION_NAME);
3512 core::map<v3s16, bool> stepped_blocks;
3514 for(core::map<u16, RemoteClient*>::Iterator
3515 i = m_clients.getIterator();
3516 i.atEnd() == false; i++)
3518 u16 peer_id = i.getNode()->getKey();
3519 RemoteClient *client = i.getNode()->getValue();
3520 assert(client->peer_id == peer_id);
3522 if(client->serialization_version == SER_FMT_VER_INVALID)
3525 client->SendObjectData(this, dtime, stepped_blocks);
3529 void Server::SendPlayerInfos()
3531 DSTACK(__FUNCTION_NAME);
3533 //JMutexAutoLock envlock(m_env_mutex);
3535 // Get connected players
3536 core::list<Player*> players = m_env.getPlayers(true);
3538 u32 player_count = players.getSize();
3539 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3541 SharedBuffer<u8> data(datasize);
3542 writeU16(&data[0], TOCLIENT_PLAYERINFO);
3545 core::list<Player*>::Iterator i;
3546 for(i = players.begin();
3547 i != players.end(); i++)
3549 Player *player = *i;
3551 /*dstream<<"Server sending player info for player with "
3552 "peer_id="<<player->peer_id<<std::endl;*/
3554 writeU16(&data[start], player->peer_id);
3555 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3556 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3557 start += 2+PLAYERNAME_SIZE;
3560 //JMutexAutoLock conlock(m_con_mutex);
3563 m_con.SendToAll(0, data, true);
3566 void Server::SendInventory(u16 peer_id)
3568 DSTACK(__FUNCTION_NAME);
3570 Player* player = m_env.getPlayer(peer_id);
3577 std::ostringstream os;
3578 //os.imbue(std::locale("C"));
3580 player->inventory.serialize(os);
3582 std::string s = os.str();
3584 SharedBuffer<u8> data(s.size()+2);
3585 writeU16(&data[0], TOCLIENT_INVENTORY);
3586 memcpy(&data[2], s.c_str(), s.size());
3589 m_con.Send(peer_id, 0, data, true);
3592 std::string getWieldedItemString(const Player *player)
3594 const InventoryItem *item = player->getWieldItem();
3596 return std::string("");
3597 std::ostringstream os(std::ios_base::binary);
3598 item->serialize(os);
3602 void Server::SendWieldedItem(const Player* player)
3604 DSTACK(__FUNCTION_NAME);
3608 std::ostringstream os(std::ios_base::binary);
3610 writeU16(os, TOCLIENT_PLAYERITEM);
3612 writeU16(os, player->peer_id);
3613 os<<serializeString(getWieldedItemString(player));
3616 std::string s = os.str();
3617 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3619 m_con.SendToAll(0, data, true);
3622 void Server::SendPlayerItems()
3624 DSTACK(__FUNCTION_NAME);
3626 std::ostringstream os(std::ios_base::binary);
3627 core::list<Player *> players = m_env.getPlayers(true);
3629 writeU16(os, TOCLIENT_PLAYERITEM);
3630 writeU16(os, players.size());
3631 core::list<Player *>::Iterator i;
3632 for(i = players.begin(); i != players.end(); ++i)
3635 writeU16(os, p->peer_id);
3636 os<<serializeString(getWieldedItemString(p));
3640 std::string s = os.str();
3641 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3643 m_con.SendToAll(0, data, true);
3646 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3648 DSTACK(__FUNCTION_NAME);
3650 std::ostringstream os(std::ios_base::binary);
3654 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3655 os.write((char*)buf, 2);
3658 writeU16(buf, message.size());
3659 os.write((char*)buf, 2);
3662 for(u32 i=0; i<message.size(); i++)
3666 os.write((char*)buf, 2);
3670 std::string s = os.str();
3671 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3673 m_con.Send(peer_id, 0, data, true);
3676 void Server::BroadcastChatMessage(const std::wstring &message)
3678 for(core::map<u16, RemoteClient*>::Iterator
3679 i = m_clients.getIterator();
3680 i.atEnd() == false; i++)
3682 // Get client and check that it is valid
3683 RemoteClient *client = i.getNode()->getValue();
3684 assert(client->peer_id == i.getNode()->getKey());
3685 if(client->serialization_version == SER_FMT_VER_INVALID)
3688 SendChatMessage(client->peer_id, message);
3692 void Server::SendPlayerHP(Player *player)
3694 SendHP(m_con, player->peer_id, player->hp);
3697 void Server::SendMovePlayer(Player *player)
3699 DSTACK(__FUNCTION_NAME);
3700 std::ostringstream os(std::ios_base::binary);
3702 writeU16(os, TOCLIENT_MOVE_PLAYER);
3703 writeV3F1000(os, player->getPosition());
3704 writeF1000(os, player->getPitch());
3705 writeF1000(os, player->getYaw());
3708 v3f pos = player->getPosition();
3709 f32 pitch = player->getPitch();
3710 f32 yaw = player->getYaw();
3711 dstream<<"Server sending TOCLIENT_MOVE_PLAYER"
3712 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3719 std::string s = os.str();
3720 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3722 m_con.Send(player->peer_id, 0, data, true);
3725 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3726 core::list<u16> *far_players, float far_d_nodes)
3728 float maxd = far_d_nodes*BS;
3729 v3f p_f = intToFloat(p, BS);
3733 SharedBuffer<u8> reply(replysize);
3734 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3735 writeS16(&reply[2], p.X);
3736 writeS16(&reply[4], p.Y);
3737 writeS16(&reply[6], p.Z);
3739 for(core::map<u16, RemoteClient*>::Iterator
3740 i = m_clients.getIterator();
3741 i.atEnd() == false; i++)
3743 // Get client and check that it is valid
3744 RemoteClient *client = i.getNode()->getValue();
3745 assert(client->peer_id == i.getNode()->getKey());
3746 if(client->serialization_version == SER_FMT_VER_INVALID)
3749 // Don't send if it's the same one
3750 if(client->peer_id == ignore_id)
3756 Player *player = m_env.getPlayer(client->peer_id);
3759 // If player is far away, only set modified blocks not sent
3760 v3f player_pos = player->getPosition();
3761 if(player_pos.getDistanceFrom(p_f) > maxd)
3763 far_players->push_back(client->peer_id);
3770 m_con.Send(client->peer_id, 0, reply, true);
3774 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3775 core::list<u16> *far_players, float far_d_nodes)
3777 float maxd = far_d_nodes*BS;
3778 v3f p_f = intToFloat(p, BS);
3780 for(core::map<u16, RemoteClient*>::Iterator
3781 i = m_clients.getIterator();
3782 i.atEnd() == false; i++)
3784 // Get client and check that it is valid
3785 RemoteClient *client = i.getNode()->getValue();
3786 assert(client->peer_id == i.getNode()->getKey());
3787 if(client->serialization_version == SER_FMT_VER_INVALID)
3790 // Don't send if it's the same one
3791 if(client->peer_id == ignore_id)
3797 Player *player = m_env.getPlayer(client->peer_id);
3800 // If player is far away, only set modified blocks not sent
3801 v3f player_pos = player->getPosition();
3802 if(player_pos.getDistanceFrom(p_f) > maxd)
3804 far_players->push_back(client->peer_id);
3811 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3812 SharedBuffer<u8> reply(replysize);
3813 writeU16(&reply[0], TOCLIENT_ADDNODE);
3814 writeS16(&reply[2], p.X);
3815 writeS16(&reply[4], p.Y);
3816 writeS16(&reply[6], p.Z);
3817 n.serialize(&reply[8], client->serialization_version);
3820 m_con.Send(client->peer_id, 0, reply, true);
3824 void Server::setBlockNotSent(v3s16 p)
3826 for(core::map<u16, RemoteClient*>::Iterator
3827 i = m_clients.getIterator();
3828 i.atEnd()==false; i++)
3830 RemoteClient *client = i.getNode()->getValue();
3831 client->SetBlockNotSent(p);
3835 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3837 DSTACK(__FUNCTION_NAME);
3839 v3s16 p = block->getPos();
3843 bool completely_air = true;
3844 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3845 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3846 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3848 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
3850 completely_air = false;
3851 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
3856 dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
3858 dstream<<"[completely air] ";
3863 Create a packet with the block in the right format
3866 std::ostringstream os(std::ios_base::binary);
3867 block->serialize(os, ver);
3868 std::string s = os.str();
3869 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3871 u32 replysize = 8 + blockdata.getSize();
3872 SharedBuffer<u8> reply(replysize);
3873 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3874 writeS16(&reply[2], p.X);
3875 writeS16(&reply[4], p.Y);
3876 writeS16(&reply[6], p.Z);
3877 memcpy(&reply[8], *blockdata, blockdata.getSize());
3879 /*dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3880 <<": \tpacket size: "<<replysize<<std::endl;*/
3885 m_con.Send(peer_id, 1, reply, true);
3888 void Server::SendBlocks(float dtime)
3890 DSTACK(__FUNCTION_NAME);
3892 JMutexAutoLock envlock(m_env_mutex);
3893 JMutexAutoLock conlock(m_con_mutex);
3895 //TimeTaker timer("Server::SendBlocks");
3897 core::array<PrioritySortedBlockTransfer> queue;
3899 s32 total_sending = 0;
3902 ScopeProfiler sp(g_profiler, "Server: selecting blocks for sending");
3904 for(core::map<u16, RemoteClient*>::Iterator
3905 i = m_clients.getIterator();
3906 i.atEnd() == false; i++)
3908 RemoteClient *client = i.getNode()->getValue();
3909 assert(client->peer_id == i.getNode()->getKey());
3911 total_sending += client->SendingCount();
3913 if(client->serialization_version == SER_FMT_VER_INVALID)
3916 client->GetNextBlocks(this, dtime, queue);
3921 // Lowest priority number comes first.
3922 // Lowest is most important.
3925 for(u32 i=0; i<queue.size(); i++)
3927 //TODO: Calculate limit dynamically
3928 if(total_sending >= g_settings->getS32
3929 ("max_simultaneous_block_sends_server_total"))
3932 PrioritySortedBlockTransfer q = queue[i];
3934 MapBlock *block = NULL;
3937 block = m_env.getMap().getBlockNoCreate(q.pos);
3939 catch(InvalidPositionException &e)
3944 RemoteClient *client = getClient(q.peer_id);
3946 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3948 client->SentBlock(q.pos);
3958 void Server::UpdateCrafting(u16 peer_id)
3960 DSTACK(__FUNCTION_NAME);
3962 Player* player = m_env.getPlayer(peer_id);
3966 Calculate crafting stuff
3968 if(g_settings->getBool("creative_mode") == false)
3970 InventoryList *clist = player->inventory.getList("craft");
3971 InventoryList *rlist = player->inventory.getList("craftresult");
3973 if(rlist && rlist->getUsedSlots() == 0)
3974 player->craftresult_is_preview = true;
3976 if(rlist && player->craftresult_is_preview)
3978 rlist->clearItems();
3980 if(clist && rlist && player->craftresult_is_preview)
3982 InventoryItem *items[9];
3983 for(u16 i=0; i<9; i++)
3985 items[i] = clist->getItem(i);
3988 // Get result of crafting grid
3989 InventoryItem *result = craft_get_result(items);
3991 rlist->addItem(result);
3994 } // if creative_mode == false
3997 RemoteClient* Server::getClient(u16 peer_id)
3999 DSTACK(__FUNCTION_NAME);
4000 //JMutexAutoLock lock(m_con_mutex);
4001 core::map<u16, RemoteClient*>::Node *n;
4002 n = m_clients.find(peer_id);
4003 // A client should exist for all peers
4005 return n->getValue();
4008 std::wstring Server::getStatusString()
4010 std::wostringstream os(std::ios_base::binary);
4013 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4015 os<<L", uptime="<<m_uptime.get();
4016 // Information about clients
4018 for(core::map<u16, RemoteClient*>::Iterator
4019 i = m_clients.getIterator();
4020 i.atEnd() == false; i++)
4022 // Get client and check that it is valid
4023 RemoteClient *client = i.getNode()->getValue();
4024 assert(client->peer_id == i.getNode()->getKey());
4025 if(client->serialization_version == SER_FMT_VER_INVALID)
4028 Player *player = m_env.getPlayer(client->peer_id);
4029 // Get name of player
4030 std::wstring name = L"unknown";
4032 name = narrow_to_wide(player->getName());
4033 // Add name to information string
4037 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
4038 os<<std::endl<<L"# Server: "<<" WARNING: Map saving is disabled.";
4039 if(g_settings->get("motd") != "")
4040 os<<std::endl<<L"# Server: "<<narrow_to_wide(g_settings->get("motd"));
4044 // Saves g_settings to configpath given at initialization
4045 void Server::saveConfig()
4047 if(m_configpath != "")
4048 g_settings->updateConfigFile(m_configpath.c_str());
4051 v3f findSpawnPos(ServerMap &map)
4053 //return v3f(50,50,50)*BS;
4056 s16 groundheight = 0;
4059 nodepos = v2s16(0,0);
4064 // Try to find a good place a few times
4065 for(s32 i=0; i<1000; i++)
4068 // We're going to try to throw the player to this position
4069 nodepos = v2s16(-range + (myrand()%(range*2)),
4070 -range + (myrand()%(range*2)));
4071 v2s16 sectorpos = getNodeSectorPos(nodepos);
4072 // Get sector (NOTE: Don't get because it's slow)
4073 //m_env.getMap().emergeSector(sectorpos);
4074 // Get ground height at point (fallbacks to heightmap function)
4075 groundheight = map.findGroundLevel(nodepos);
4076 // Don't go underwater
4077 if(groundheight < WATER_LEVEL)
4079 //dstream<<"-> Underwater"<<std::endl;
4082 // Don't go to high places
4083 if(groundheight > WATER_LEVEL + 4)
4085 //dstream<<"-> Underwater"<<std::endl;
4089 // Found a good place
4090 //dstream<<"Searched through "<<i<<" places."<<std::endl;
4095 // If no suitable place was not found, go above water at least.
4096 if(groundheight < WATER_LEVEL)
4097 groundheight = WATER_LEVEL;
4099 return intToFloat(v3s16(
4106 Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id)
4109 Try to get an existing player
4111 Player *player = m_env.getPlayer(name);
4114 // If player is already connected, cancel
4115 if(player->peer_id != 0)
4117 dstream<<"emergePlayer(): Player already connected"<<std::endl;
4122 player->peer_id = peer_id;
4124 // Reset inventory to creative if in creative mode
4125 if(g_settings->getBool("creative_mode"))
4127 // Warning: double code below
4128 // Backup actual inventory
4129 player->inventory_backup = new Inventory();
4130 *(player->inventory_backup) = player->inventory;
4131 // Set creative inventory
4132 craft_set_creative_inventory(player);
4139 If player with the wanted peer_id already exists, cancel.
4141 if(m_env.getPlayer(peer_id) != NULL)
4143 dstream<<"emergePlayer(): Player with wrong name but same"
4144 " peer_id already exists"<<std::endl;
4152 player = new ServerRemotePlayer();
4153 //player->peer_id = c.peer_id;
4154 //player->peer_id = PEER_ID_INEXISTENT;
4155 player->peer_id = peer_id;
4156 player->updateName(name);
4157 m_authmanager.add(name);
4158 m_authmanager.setPassword(name, password);
4159 m_authmanager.setPrivs(name,
4160 stringToPrivs(g_settings->get("default_privs")));
4166 dstream<<"Server: Finding spawn place for player \""
4167 <<player->getName()<<"\""<<std::endl;
4169 v3f pos = findSpawnPos(m_env.getServerMap());
4171 player->setPosition(pos);
4174 Add player to environment
4177 m_env.addPlayer(player);
4180 Add stuff to inventory
4183 if(g_settings->getBool("creative_mode"))
4185 // Warning: double code above
4186 // Backup actual inventory
4187 player->inventory_backup = new Inventory();
4188 *(player->inventory_backup) = player->inventory;
4189 // Set creative inventory
4190 craft_set_creative_inventory(player);
4192 else if(g_settings->getBool("give_initial_stuff"))
4194 craft_give_initial_stuff(player);
4199 } // create new player
4202 void Server::handlePeerChange(PeerChange &c)
4204 JMutexAutoLock envlock(m_env_mutex);
4205 JMutexAutoLock conlock(m_con_mutex);
4207 if(c.type == PEER_ADDED)
4214 core::map<u16, RemoteClient*>::Node *n;
4215 n = m_clients.find(c.peer_id);
4216 // The client shouldn't already exist
4220 RemoteClient *client = new RemoteClient();
4221 client->peer_id = c.peer_id;
4222 m_clients.insert(client->peer_id, client);
4225 else if(c.type == PEER_REMOVED)
4232 core::map<u16, RemoteClient*>::Node *n;
4233 n = m_clients.find(c.peer_id);
4234 // The client should exist
4238 Mark objects to be not known by the client
4240 RemoteClient *client = n->getValue();
4242 for(core::map<u16, bool>::Iterator
4243 i = client->m_known_objects.getIterator();
4244 i.atEnd()==false; i++)
4247 u16 id = i.getNode()->getKey();
4248 ServerActiveObject* obj = m_env.getActiveObject(id);
4250 if(obj && obj->m_known_by_count > 0)
4251 obj->m_known_by_count--;
4254 // Collect information about leaving in chat
4255 std::wstring message;
4257 Player *player = m_env.getPlayer(c.peer_id);
4260 std::wstring name = narrow_to_wide(player->getName());
4263 message += L" left game";
4265 message += L" (timed out)";
4271 m_env.removePlayer(c.peer_id);
4274 // Set player client disconnected
4276 Player *player = m_env.getPlayer(c.peer_id);
4278 player->peer_id = 0;
4282 delete m_clients[c.peer_id];
4283 m_clients.remove(c.peer_id);
4285 // Send player info to all remaining clients
4288 // Send leave chat message to all remaining clients
4289 BroadcastChatMessage(message);
4298 void Server::handlePeerChanges()
4300 while(m_peer_change_queue.size() > 0)
4302 PeerChange c = m_peer_change_queue.pop_front();
4304 dout_server<<"Server: Handling peer change: "
4305 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4308 handlePeerChange(c);
4312 u64 Server::getPlayerPrivs(Player *player)
4316 std::string playername = player->getName();
4317 // Local player gets all privileges regardless of
4318 // what's set on their account.
4319 if(g_settings->get("name") == playername)
4325 return getPlayerAuthPrivs(playername);
4329 void dedicated_server_loop(Server &server, bool &kill)
4331 DSTACK(__FUNCTION_NAME);
4333 dstream<<DTIME<<std::endl;
4334 dstream<<"========================"<<std::endl;
4335 dstream<<"Running dedicated server"<<std::endl;
4336 dstream<<"========================"<<std::endl;
4339 IntervalLimiter m_profiler_interval;
4343 // This is kind of a hack but can be done like this
4344 // because server.step() is very light
4346 ScopeProfiler sp(g_profiler, "dedicated server sleep");
4351 if(server.getShutdownRequested() || kill)
4353 dstream<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4360 float profiler_print_interval =
4361 g_settings->getFloat("profiler_print_interval");
4362 if(profiler_print_interval != 0)
4364 if(m_profiler_interval.step(0.030, profiler_print_interval))
4366 dstream<<"Profiler:"<<std::endl;
4367 g_profiler->print(dstream);
4368 g_profiler->clear();
4375 static int counter = 0;
4381 core::list<PlayerInfo> list = server.getPlayerInfo();
4382 core::list<PlayerInfo>::Iterator i;
4383 static u32 sum_old = 0;
4384 u32 sum = PIChecksum(list);
4387 dstream<<DTIME<<"Player info:"<<std::endl;
4388 for(i=list.begin(); i!=list.end(); i++)
4390 i->PrintLine(&dstream);