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"
39 #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
41 class MapEditEventIgnorer
44 MapEditEventIgnorer(bool *flag):
53 ~MapEditEventIgnorer()
66 void * ServerThread::Thread()
70 DSTACK(__FUNCTION_NAME);
72 BEGIN_DEBUG_EXCEPTION_HANDLER
77 //TimeTaker timer("AsyncRunStep() + Receive()");
80 //TimeTaker timer("AsyncRunStep()");
81 m_server->AsyncRunStep();
84 //dout_server<<"Running m_server->Receive()"<<std::endl;
87 catch(con::NoIncomingDataException &e)
90 catch(con::PeerNotFoundException &e)
92 dout_server<<"Server: PeerNotFoundException"<<std::endl;
96 END_DEBUG_EXCEPTION_HANDLER
101 void * EmergeThread::Thread()
105 DSTACK(__FUNCTION_NAME);
109 BEGIN_DEBUG_EXCEPTION_HANDLER
112 Get block info from queue, emerge them and send them
115 After queue is empty, exit.
119 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
123 SharedPtr<QueuedBlockEmerge> q(qptr);
129 Do not generate over-limit
131 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
132 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
133 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
134 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
135 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
136 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
139 //derr_server<<"EmergeThread::Thread(): running"<<std::endl;
141 //TimeTaker timer("block emerge");
144 Try to emerge it from somewhere.
146 If it is only wanted as optional, only loading from disk
151 Check if any peer wants it as non-optional. In that case it
154 Also decrement the emerge queue count in clients.
157 bool optional = true;
160 core::map<u16, u8>::Iterator i;
161 for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
163 //u16 peer_id = i.getNode()->getKey();
166 u8 flags = i.getNode()->getValue();
167 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
173 /*dstream<<"EmergeThread: p="
174 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
175 <<"optional="<<optional<<std::endl;*/
177 ServerMap &map = ((ServerMap&)m_server->m_env.getMap());
179 //core::map<v3s16, MapBlock*> changed_blocks;
180 //core::map<v3s16, MapBlock*> lighting_invalidated_blocks;
182 MapBlock *block = NULL;
183 bool got_block = true;
184 core::map<v3s16, MapBlock*> modified_blocks;
186 bool only_from_disk = false;
189 only_from_disk = true;
192 Fetch block from map or generate a single block
195 JMutexAutoLock envlock(m_server->m_env_mutex);
197 // Load sector if it isn't loaded
198 if(map.getSectorNoGenerateNoEx(p2d) == NULL)
199 //map.loadSectorFull(p2d);
200 map.loadSectorMeta(p2d);
202 block = map.getBlockNoCreateNoEx(p);
203 if(!block || block->isDummy() || !block->isGenerated())
205 // Get, load or create sector
206 /*ServerMapSector *sector =
207 (ServerMapSector*)map.createSector(p2d);*/
209 // Load/generate block
211 /*block = map.emergeBlock(p, sector, changed_blocks,
212 lighting_invalidated_blocks);*/
214 block = map.loadBlock(p);
216 if(block == NULL && only_from_disk == false)
217 block = map.generateBlock(p, modified_blocks);
218 //block = map.generateBlock(p, changed_blocks);
219 /*block = map.generateBlock(p, block, sector, changed_blocks,
220 lighting_invalidated_blocks);*/
229 Ignore map edit events, they will not need to be
230 sent to anybody because the block hasn't been sent
233 MapEditEventIgnorer ign(&m_server->m_ignore_map_edit_events);
235 // Activate objects and stuff
236 m_server->m_env.activateBlock(block, 3600);
241 /*if(block->getLightingExpired()){
242 lighting_invalidated_blocks[block->getPos()] = block;
246 // TODO: Some additional checking and lighting updating,
251 JMutexAutoLock envlock(m_server->m_env_mutex);
256 Collect a list of blocks that have been modified in
257 addition to the fetched one.
261 if(lighting_invalidated_blocks.size() > 0)
263 /*dstream<<"lighting "<<lighting_invalidated_blocks.size()
264 <<" blocks"<<std::endl;*/
266 // 50-100ms for single block generation
267 //TimeTaker timer("** EmergeThread updateLighting");
269 // Update lighting without locking the environment mutex,
270 // add modified blocks to changed blocks
271 map.updateLighting(lighting_invalidated_blocks, modified_blocks);
274 // Add all from changed_blocks to modified_blocks
275 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
276 i.atEnd() == false; i++)
278 MapBlock *block = i.getNode()->getValue();
279 modified_blocks.insert(block->getPos(), block);
283 // If we got no block, there should be no invalidated blocks
286 //assert(lighting_invalidated_blocks.size() == 0);
292 Set sent status of modified blocks on clients
295 // NOTE: Server's clients are also behind the connection mutex
296 JMutexAutoLock lock(m_server->m_con_mutex);
299 Add the originally fetched block to the modified list
303 modified_blocks.insert(p, block);
307 Set the modified blocks unsent for all the clients
310 for(core::map<u16, RemoteClient*>::Iterator
311 i = m_server->m_clients.getIterator();
312 i.atEnd() == false; i++)
314 RemoteClient *client = i.getNode()->getValue();
316 if(modified_blocks.size() > 0)
318 // Remove block from sent history
319 client->SetBlocksNotSent(modified_blocks);
325 END_DEBUG_EXCEPTION_HANDLER
330 void RemoteClient::GetNextBlocks(Server *server, float dtime,
331 core::array<PrioritySortedBlockTransfer> &dest)
333 DSTACK(__FUNCTION_NAME);
336 TimeTaker timer("RemoteClient::GetNextBlocks", &timer_result);*/
339 m_nothing_to_send_pause_timer -= dtime;
341 if(m_nothing_to_send_pause_timer >= 0)
344 m_nearest_unsent_reset_timer = 0;
348 // Won't send anything if already sending
349 if(m_blocks_sending.size() >= g_settings.getU16
350 ("max_simultaneous_block_sends_per_client"))
352 //dstream<<"Not sending any blocks, Queue full."<<std::endl;
356 //TimeTaker timer("RemoteClient::GetNextBlocks");
358 Player *player = server->m_env.getPlayer(peer_id);
360 assert(player != NULL);
362 v3f playerpos = player->getPosition();
363 v3f playerspeed = player->getSpeed();
364 v3f playerspeeddir(0,0,0);
365 if(playerspeed.getLength() > 1.0*BS)
366 playerspeeddir = playerspeed / playerspeed.getLength();
367 // Predict to next block
368 v3f playerpos_predicted = playerpos + playerspeeddir*MAP_BLOCKSIZE*BS;
370 v3s16 center_nodepos = floatToInt(playerpos_predicted, BS);
372 v3s16 center = getNodeBlockPos(center_nodepos);
374 // Camera position and direction
376 playerpos + v3f(0, BS+BS/2, 0);
377 v3f camera_dir = v3f(0,0,1);
378 camera_dir.rotateYZBy(player->getPitch());
379 camera_dir.rotateXZBy(player->getYaw());
381 /*dstream<<"camera_dir=("<<camera_dir.X<<","<<camera_dir.Y<<","
382 <<camera_dir.Z<<")"<<std::endl;*/
385 Get the starting value of the block finder radius.
388 if(m_last_center != center)
390 m_nearest_unsent_d = 0;
391 m_last_center = center;
394 /*dstream<<"m_nearest_unsent_reset_timer="
395 <<m_nearest_unsent_reset_timer<<std::endl;*/
397 // This has to be incremented only when the nothing to send pause
399 m_nearest_unsent_reset_timer += dtime;
401 // Reset periodically to avoid possible bugs or other mishaps
402 if(m_nearest_unsent_reset_timer > 10.0)
404 m_nearest_unsent_reset_timer = 0;
405 m_nearest_unsent_d = 0;
406 /*dstream<<"Resetting m_nearest_unsent_d for "
407 <<server->getPlayerName(peer_id)<<std::endl;*/
410 //s16 last_nearest_unsent_d = m_nearest_unsent_d;
411 s16 d_start = m_nearest_unsent_d;
413 //dstream<<"d_start="<<d_start<<std::endl;
415 u16 max_simul_sends_setting = g_settings.getU16
416 ("max_simultaneous_block_sends_per_client");
417 u16 max_simul_sends_usually = max_simul_sends_setting;
420 Check the time from last addNode/removeNode.
422 Decrease send rate if player is building stuff.
424 m_time_from_building += dtime;
425 if(m_time_from_building < g_settings.getFloat(
426 "full_block_send_enable_min_time_from_building"))
428 max_simul_sends_usually
429 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
433 Number of blocks sending + number of blocks selected for sending
435 u32 num_blocks_selected = m_blocks_sending.size();
438 next time d will be continued from the d from which the nearest
439 unsent block was found this time.
441 This is because not necessarily any of the blocks found this
442 time are actually sent.
444 s32 new_nearest_unsent_d = -1;
446 s16 d_max = g_settings.getS16("max_block_send_distance");
447 s16 d_max_gen = g_settings.getS16("max_block_generate_distance");
449 // Don't loop very much at a time
450 if(d_max > d_start+1)
452 /*if(d_max_gen > d_start+2)
453 d_max_gen = d_start+2;*/
455 //dstream<<"Starting from "<<d_start<<std::endl;
457 bool sending_something = false;
459 bool no_blocks_found_for_sending = true;
461 bool queue_is_full = false;
464 for(d = d_start; d <= d_max; d++)
466 //dstream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
469 If m_nearest_unsent_d was changed by the EmergeThread
470 (it can change it to 0 through SetBlockNotSent),
472 Else update m_nearest_unsent_d
474 /*if(m_nearest_unsent_d != last_nearest_unsent_d)
476 d = m_nearest_unsent_d;
477 last_nearest_unsent_d = m_nearest_unsent_d;
481 Get the border/face dot coordinates of a "d-radiused"
484 core::list<v3s16> list;
485 getFacePositions(list, d);
487 core::list<v3s16>::Iterator li;
488 for(li=list.begin(); li!=list.end(); li++)
490 v3s16 p = *li + center;
494 - Don't allow too many simultaneous transfers
495 - EXCEPT when the blocks are very close
497 Also, don't send blocks that are already flying.
500 // Start with the usual maximum
501 u16 max_simul_dynamic = max_simul_sends_usually;
503 // If block is very close, allow full maximum
504 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
505 max_simul_dynamic = max_simul_sends_setting;
507 // Don't select too many blocks for sending
508 if(num_blocks_selected >= max_simul_dynamic)
510 queue_is_full = true;
511 goto queue_full_break;
514 // Don't send blocks that are currently being transferred
515 if(m_blocks_sending.find(p) != NULL)
521 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
522 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
523 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
524 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
525 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
526 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
529 // If this is true, inexistent block will be made from scratch
530 bool generate = d <= d_max_gen;
533 /*// Limit the generating area vertically to 2/3
534 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
537 // Limit the send area vertically to 2/3
538 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
544 If block is far away, don't generate it unless it is
550 // Block center y in nodes
551 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
552 // Don't generate if it's very high or very low
553 if(y < -64 || y > 64)
557 v2s16 p2d_nodes_center(
561 // Get ground height in nodes
562 s16 gh = server->m_env.getServerMap().findGroundLevel(
565 // If differs a lot, don't generate
566 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
568 // Actually, don't even send it
574 //dstream<<"d="<<d<<std::endl;
577 Don't generate or send if not in sight
580 if(isBlockInSight(p, camera_pos, camera_dir, 10000*BS) == false)
586 Don't send already sent blocks
589 if(m_blocks_sent.find(p) != NULL)
596 Check if map has this block
598 MapBlock *block = server->m_env.getMap().getBlockNoCreateNoEx(p);
600 bool surely_not_found_on_disk = false;
601 bool block_is_invalid = false;
604 // Block is dummy if data doesn't exist.
605 // It means it has been not found from disk and not generated
608 surely_not_found_on_disk = true;
611 // Block is valid if lighting is up-to-date and data exists
612 if(block->isValid() == false)
614 block_is_invalid = true;
617 /*if(block->isFullyGenerated() == false)
619 block_is_invalid = true;
624 ServerMap *map = (ServerMap*)(&server->m_env.getMap());
625 v2s16 chunkpos = map->sector_to_chunk(p2d);
626 if(map->chunkNonVolatile(chunkpos) == false)
627 block_is_invalid = true;
629 if(block->isGenerated() == false)
630 block_is_invalid = true;
633 If block is not close, don't send it unless it is near
636 Block is near ground level if night-time mesh
637 differs from day-time mesh.
641 if(block->dayNightDiffed() == false)
648 If block has been marked to not exist on disk (dummy)
649 and generating new ones is not wanted, skip block.
651 if(generate == false && surely_not_found_on_disk == true)
658 Record the lowest d from which a block has been
659 found being not sent and possibly to exist
661 if(no_blocks_found_for_sending)
664 new_nearest_unsent_d = d;
667 no_blocks_found_for_sending = false;
670 Add inexistent block to emerge queue.
672 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
674 //TODO: Get value from somewhere
675 // Allow only one block in emerge queue
676 //if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
677 // Allow two blocks in queue per client
678 if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
680 //dstream<<"Adding block to emerge queue"<<std::endl;
682 // Add it to the emerge queue and trigger the thread
685 if(generate == false)
686 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
688 server->m_emerge_queue.addBlock(peer_id, p, flags);
689 server->m_emergethread.trigger();
697 Add block to send queue
700 PrioritySortedBlockTransfer q((float)d, p, peer_id);
704 num_blocks_selected += 1;
705 sending_something = true;
710 //dstream<<"Stopped at "<<d<<std::endl;
712 if(no_blocks_found_for_sending)
714 if(queue_is_full == false)
715 new_nearest_unsent_d = d;
718 if(new_nearest_unsent_d != -1)
719 m_nearest_unsent_d = new_nearest_unsent_d;
721 if(sending_something == false)
723 m_nothing_to_send_counter++;
724 if((s16)m_nothing_to_send_counter >=
725 g_settings.getS16("max_block_send_distance"))
727 // Pause time in seconds
728 m_nothing_to_send_pause_timer = 1.0;
729 /*dstream<<"nothing to send to "
730 <<server->getPlayerName(peer_id)
731 <<" (d="<<d<<")"<<std::endl;*/
736 m_nothing_to_send_counter = 0;
739 /*timer_result = timer.stop(true);
740 if(timer_result != 0)
741 dstream<<"GetNextBlocks duration: "<<timer_result<<" (!=0)"<<std::endl;*/
744 void RemoteClient::SendObjectData(
747 core::map<v3s16, bool> &stepped_blocks
750 DSTACK(__FUNCTION_NAME);
752 // Can't send anything without knowing version
753 if(serialization_version == SER_FMT_VER_INVALID)
755 dstream<<"RemoteClient::SendObjectData(): Not sending, no version."
761 Send a TOCLIENT_OBJECTDATA packet.
765 u16 number of player positions
776 std::ostringstream os(std::ios_base::binary);
780 writeU16(buf, TOCLIENT_OBJECTDATA);
781 os.write((char*)buf, 2);
784 Get and write player data
787 // Get connected players
788 core::list<Player*> players = server->m_env.getPlayers(true);
790 // Write player count
791 u16 playercount = players.size();
792 writeU16(buf, playercount);
793 os.write((char*)buf, 2);
795 core::list<Player*>::Iterator i;
796 for(i = players.begin();
797 i != players.end(); i++)
801 v3f pf = player->getPosition();
802 v3f sf = player->getSpeed();
804 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
805 v3s32 speed_i (sf.X*100, sf.Y*100, sf.Z*100);
806 s32 pitch_i (player->getPitch() * 100);
807 s32 yaw_i (player->getYaw() * 100);
809 writeU16(buf, player->peer_id);
810 os.write((char*)buf, 2);
811 writeV3S32(buf, position_i);
812 os.write((char*)buf, 12);
813 writeV3S32(buf, speed_i);
814 os.write((char*)buf, 12);
815 writeS32(buf, pitch_i);
816 os.write((char*)buf, 4);
817 writeS32(buf, yaw_i);
818 os.write((char*)buf, 4);
822 Get and write object data
828 For making players to be able to build to their nearby
829 environment (building is not possible on blocks that are not
832 - Add blocks to emerge queue if they are not found
834 SUGGESTION: These could be ignored from the backside of the player
837 Player *player = server->m_env.getPlayer(peer_id);
841 v3f playerpos = player->getPosition();
842 v3f playerspeed = player->getSpeed();
844 v3s16 center_nodepos = floatToInt(playerpos, BS);
845 v3s16 center = getNodeBlockPos(center_nodepos);
847 s16 d_max = g_settings.getS16("active_object_range");
849 // Number of blocks whose objects were written to bos
852 std::ostringstream bos(std::ios_base::binary);
854 for(s16 d = 0; d <= d_max; d++)
856 core::list<v3s16> list;
857 getFacePositions(list, d);
859 core::list<v3s16>::Iterator li;
860 for(li=list.begin(); li!=list.end(); li++)
862 v3s16 p = *li + center;
865 Ignore blocks that haven't been sent to the client
868 if(m_blocks_sent.find(p) == NULL)
872 // Try stepping block and add it to a send queue
877 MapBlock *block = server->m_env.getMap().getBlockNoCreate(p);
880 Step block if not in stepped_blocks and add to stepped_blocks.
882 if(stepped_blocks.find(p) == NULL)
884 block->stepObjects(dtime, true, server->m_env.getDayNightRatio());
885 stepped_blocks.insert(p, true);
886 block->setChangedFlag();
889 // Skip block if there are no objects
890 if(block->getObjectCount() == 0)
899 bos.write((char*)buf, 6);
902 //block->serializeObjects(bos, serialization_version); // DEPRECATED
909 Stop collecting objects if data is already too big
911 // Sum of player and object data sizes
912 s32 sum = (s32)os.tellp() + 2 + (s32)bos.tellp();
913 // break out if data too big
914 if(sum > MAX_OBJECTDATA_SIZE)
916 goto skip_subsequent;
920 catch(InvalidPositionException &e)
923 // Add it to the emerge queue and trigger the thread.
924 // Fetch the block only if it is on disk.
926 // Grab and increment counter
927 /*SharedPtr<JMutexAutoLock> lock
928 (m_num_blocks_in_emerge_queue.getLock());
929 m_num_blocks_in_emerge_queue.m_value++;*/
931 // Add to queue as an anonymous fetch from disk
932 u8 flags = BLOCK_EMERGE_FLAG_FROMDISK;
933 server->m_emerge_queue.addBlock(0, p, flags);
934 server->m_emergethread.trigger();
942 writeU16(buf, blockcount);
943 os.write((char*)buf, 2);
945 // Write block objects
952 //dstream<<"Server: Sending object data to "<<peer_id<<std::endl;
955 std::string s = os.str();
956 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
957 // Send as unreliable
958 server->m_con.Send(peer_id, 0, data, false);
961 void RemoteClient::GotBlock(v3s16 p)
963 if(m_blocks_sending.find(p) != NULL)
964 m_blocks_sending.remove(p);
967 /*dstream<<"RemoteClient::GotBlock(): Didn't find in"
968 " m_blocks_sending"<<std::endl;*/
969 m_excess_gotblocks++;
971 m_blocks_sent.insert(p, true);
974 void RemoteClient::SentBlock(v3s16 p)
976 if(m_blocks_sending.find(p) == NULL)
977 m_blocks_sending.insert(p, 0.0);
979 dstream<<"RemoteClient::SentBlock(): Sent block"
980 " already in m_blocks_sending"<<std::endl;
983 void RemoteClient::SetBlockNotSent(v3s16 p)
985 m_nearest_unsent_d = 0;
987 if(m_blocks_sending.find(p) != NULL)
988 m_blocks_sending.remove(p);
989 if(m_blocks_sent.find(p) != NULL)
990 m_blocks_sent.remove(p);
993 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
995 m_nearest_unsent_d = 0;
997 for(core::map<v3s16, MapBlock*>::Iterator
998 i = blocks.getIterator();
999 i.atEnd()==false; i++)
1001 v3s16 p = i.getNode()->getKey();
1003 if(m_blocks_sending.find(p) != NULL)
1004 m_blocks_sending.remove(p);
1005 if(m_blocks_sent.find(p) != NULL)
1006 m_blocks_sent.remove(p);
1014 PlayerInfo::PlayerInfo()
1020 void PlayerInfo::PrintLine(std::ostream *s)
1023 (*s)<<"\""<<name<<"\" ("
1024 <<(position.X/10)<<","<<(position.Y/10)
1025 <<","<<(position.Z/10)<<") ";
1027 (*s)<<" avg_rtt="<<avg_rtt;
1031 u32 PIChecksum(core::list<PlayerInfo> &l)
1033 core::list<PlayerInfo>::Iterator i;
1036 for(i=l.begin(); i!=l.end(); i++)
1038 checksum += a * (i->id+1);
1039 checksum ^= 0x435aafcd;
1050 std::string mapsavedir
1052 m_env(new ServerMap(mapsavedir), this),
1053 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
1054 m_authmanager(mapsavedir+"/auth.txt"),
1056 m_emergethread(this),
1058 m_time_of_day_send_timer(0),
1060 m_mapsavedir(mapsavedir),
1061 m_shutdown_requested(false),
1062 m_ignore_map_edit_events(false),
1063 m_ignore_map_edit_events_peer_id(0)
1065 m_liquid_transform_timer = 0.0;
1066 m_print_info_timer = 0.0;
1067 m_objectdata_timer = 0.0;
1068 m_emergethread_trigger_timer = 0.0;
1069 m_savemap_timer = 0.0;
1073 m_step_dtime_mutex.Init();
1076 // Register us to receive map edit events
1077 m_env.getMap().addEventReceiver(this);
1079 // If file exists, load environment metadata
1080 if(fs::PathExists(m_mapsavedir+"/env_meta.txt"))
1082 dstream<<"Server: Loading environment metadata"<<std::endl;
1083 m_env.loadMeta(m_mapsavedir);
1087 dstream<<"Server: Loading players"<<std::endl;
1088 m_env.deSerializePlayers(m_mapsavedir);
1093 dstream<<"Server::~Server()"<<std::endl;
1096 Send shutdown message
1099 JMutexAutoLock conlock(m_con_mutex);
1101 std::wstring line = L"*** Server shutting down";
1104 Send the message to clients
1106 for(core::map<u16, RemoteClient*>::Iterator
1107 i = m_clients.getIterator();
1108 i.atEnd() == false; i++)
1110 // Get client and check that it is valid
1111 RemoteClient *client = i.getNode()->getValue();
1112 assert(client->peer_id == i.getNode()->getKey());
1113 if(client->serialization_version == SER_FMT_VER_INVALID)
1117 SendChatMessage(client->peer_id, line);
1119 catch(con::PeerNotFoundException &e)
1127 dstream<<"Server: Saving players"<<std::endl;
1128 m_env.serializePlayers(m_mapsavedir);
1131 Save environment metadata
1133 dstream<<"Server: Saving environment metadata"<<std::endl;
1134 m_env.saveMeta(m_mapsavedir);
1145 JMutexAutoLock clientslock(m_con_mutex);
1147 for(core::map<u16, RemoteClient*>::Iterator
1148 i = m_clients.getIterator();
1149 i.atEnd() == false; i++)
1152 // NOTE: These are removed by env destructor
1154 u16 peer_id = i.getNode()->getKey();
1155 JMutexAutoLock envlock(m_env_mutex);
1156 m_env.removePlayer(peer_id);
1160 delete i.getNode()->getValue();
1165 void Server::start(unsigned short port)
1167 DSTACK(__FUNCTION_NAME);
1168 // Stop thread if already running
1171 // Initialize connection
1172 m_con.setTimeoutMs(30);
1176 m_thread.setRun(true);
1179 dout_server<<"Server: Started on port "<<port<<std::endl;
1184 DSTACK(__FUNCTION_NAME);
1186 // Stop threads (set run=false first so both start stopping)
1187 m_thread.setRun(false);
1188 m_emergethread.setRun(false);
1190 m_emergethread.stop();
1192 dout_server<<"Server: Threads stopped"<<std::endl;
1195 void Server::step(float dtime)
1197 DSTACK(__FUNCTION_NAME);
1202 JMutexAutoLock lock(m_step_dtime_mutex);
1203 m_step_dtime += dtime;
1207 void Server::AsyncRunStep()
1209 DSTACK(__FUNCTION_NAME);
1213 JMutexAutoLock lock1(m_step_dtime_mutex);
1214 dtime = m_step_dtime;
1218 ScopeProfiler sp(&g_profiler, "Server: selecting and sending "
1219 "blocks to clients");
1220 // Send blocks to clients
1227 //dstream<<"Server steps "<<dtime<<std::endl;
1228 //dstream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1231 JMutexAutoLock lock1(m_step_dtime_mutex);
1232 m_step_dtime -= dtime;
1239 m_uptime.set(m_uptime.get() + dtime);
1243 Update m_time_of_day and overall game time
1246 JMutexAutoLock envlock(m_env_mutex);
1248 m_time_counter += dtime;
1249 f32 speed = g_settings.getFloat("time_speed") * 24000./(24.*3600);
1250 u32 units = (u32)(m_time_counter*speed);
1251 m_time_counter -= (f32)units / speed;
1253 m_env.setTimeOfDay((m_env.getTimeOfDay() + units) % 24000);
1255 //dstream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1258 Send to clients at constant intervals
1261 m_time_of_day_send_timer -= dtime;
1262 if(m_time_of_day_send_timer < 0.0)
1264 m_time_of_day_send_timer = g_settings.getFloat("time_send_interval");
1266 //JMutexAutoLock envlock(m_env_mutex);
1267 JMutexAutoLock conlock(m_con_mutex);
1269 for(core::map<u16, RemoteClient*>::Iterator
1270 i = m_clients.getIterator();
1271 i.atEnd() == false; i++)
1273 RemoteClient *client = i.getNode()->getValue();
1274 //Player *player = m_env.getPlayer(client->peer_id);
1276 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1277 m_env.getTimeOfDay());
1279 m_con.Send(client->peer_id, 0, data, true);
1285 // Process connection's timeouts
1286 JMutexAutoLock lock2(m_con_mutex);
1287 ScopeProfiler sp(&g_profiler, "Server: connection timeout processing");
1288 m_con.RunTimeouts(dtime);
1292 // This has to be called so that the client list gets synced
1293 // with the peer list of the connection
1294 ScopeProfiler sp(&g_profiler, "Server: peer change handling");
1295 handlePeerChanges();
1300 // This also runs Map's timers
1301 JMutexAutoLock lock(m_env_mutex);
1302 ScopeProfiler sp(&g_profiler, "Server: environment step");
1313 m_liquid_transform_timer += dtime;
1314 if(m_liquid_transform_timer >= 1.00)
1316 m_liquid_transform_timer -= 1.00;
1318 JMutexAutoLock lock(m_env_mutex);
1320 ScopeProfiler sp(&g_profiler, "Server: liquid transform");
1322 core::map<v3s16, MapBlock*> modified_blocks;
1323 m_env.getMap().transformLiquids(modified_blocks);
1328 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1329 ServerMap &map = ((ServerMap&)m_env.getMap());
1330 map.updateLighting(modified_blocks, lighting_modified_blocks);
1332 // Add blocks modified by lighting to modified_blocks
1333 for(core::map<v3s16, MapBlock*>::Iterator
1334 i = lighting_modified_blocks.getIterator();
1335 i.atEnd() == false; i++)
1337 MapBlock *block = i.getNode()->getValue();
1338 modified_blocks.insert(block->getPos(), block);
1342 Set the modified blocks unsent for all the clients
1345 JMutexAutoLock lock2(m_con_mutex);
1347 for(core::map<u16, RemoteClient*>::Iterator
1348 i = m_clients.getIterator();
1349 i.atEnd() == false; i++)
1351 RemoteClient *client = i.getNode()->getValue();
1353 if(modified_blocks.size() > 0)
1355 // Remove block from sent history
1356 client->SetBlocksNotSent(modified_blocks);
1361 // Periodically print some info
1363 float &counter = m_print_info_timer;
1369 JMutexAutoLock lock2(m_con_mutex);
1371 for(core::map<u16, RemoteClient*>::Iterator
1372 i = m_clients.getIterator();
1373 i.atEnd() == false; i++)
1375 //u16 peer_id = i.getNode()->getKey();
1376 RemoteClient *client = i.getNode()->getValue();
1377 Player *player = m_env.getPlayer(client->peer_id);
1380 std::cout<<player->getName()<<"\t";
1381 client->PrintInfo(std::cout);
1386 //if(g_settings.getBool("enable_experimental"))
1390 Check added and deleted active objects
1393 //dstream<<"Server: Checking added and deleted active objects"<<std::endl;
1394 JMutexAutoLock envlock(m_env_mutex);
1395 JMutexAutoLock conlock(m_con_mutex);
1397 ScopeProfiler sp(&g_profiler, "Server: checking added and deleted objects");
1399 // Radius inside which objects are active
1402 for(core::map<u16, RemoteClient*>::Iterator
1403 i = m_clients.getIterator();
1404 i.atEnd() == false; i++)
1406 RemoteClient *client = i.getNode()->getValue();
1407 Player *player = m_env.getPlayer(client->peer_id);
1410 // This can happen if the client timeouts somehow
1411 /*dstream<<"WARNING: "<<__FUNCTION_NAME<<": Client "
1413 <<" has no associated player"<<std::endl;*/
1416 v3s16 pos = floatToInt(player->getPosition(), BS);
1418 core::map<u16, bool> removed_objects;
1419 core::map<u16, bool> added_objects;
1420 m_env.getRemovedActiveObjects(pos, radius,
1421 client->m_known_objects, removed_objects);
1422 m_env.getAddedActiveObjects(pos, radius,
1423 client->m_known_objects, added_objects);
1425 // Ignore if nothing happened
1426 if(removed_objects.size() == 0 && added_objects.size() == 0)
1428 //dstream<<"INFO: active objects: none changed"<<std::endl;
1432 std::string data_buffer;
1436 // Handle removed objects
1437 writeU16((u8*)buf, removed_objects.size());
1438 data_buffer.append(buf, 2);
1439 for(core::map<u16, bool>::Iterator
1440 i = removed_objects.getIterator();
1441 i.atEnd()==false; i++)
1444 u16 id = i.getNode()->getKey();
1445 ServerActiveObject* obj = m_env.getActiveObject(id);
1447 // Add to data buffer for sending
1448 writeU16((u8*)buf, i.getNode()->getKey());
1449 data_buffer.append(buf, 2);
1451 // Remove from known objects
1452 client->m_known_objects.remove(i.getNode()->getKey());
1454 if(obj && obj->m_known_by_count > 0)
1455 obj->m_known_by_count--;
1458 // Handle added objects
1459 writeU16((u8*)buf, added_objects.size());
1460 data_buffer.append(buf, 2);
1461 for(core::map<u16, bool>::Iterator
1462 i = added_objects.getIterator();
1463 i.atEnd()==false; i++)
1466 u16 id = i.getNode()->getKey();
1467 ServerActiveObject* obj = m_env.getActiveObject(id);
1470 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1472 dstream<<"WARNING: "<<__FUNCTION_NAME
1473 <<": NULL object"<<std::endl;
1475 type = obj->getType();
1477 // Add to data buffer for sending
1478 writeU16((u8*)buf, id);
1479 data_buffer.append(buf, 2);
1480 writeU8((u8*)buf, type);
1481 data_buffer.append(buf, 1);
1484 data_buffer.append(serializeLongString(
1485 obj->getClientInitializationData()));
1487 data_buffer.append(serializeLongString(""));
1489 // Add to known objects
1490 client->m_known_objects.insert(i.getNode()->getKey(), false);
1493 obj->m_known_by_count++;
1497 SharedBuffer<u8> reply(2 + data_buffer.size());
1498 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1499 memcpy((char*)&reply[2], data_buffer.c_str(),
1500 data_buffer.size());
1502 m_con.Send(client->peer_id, 0, reply, true);
1504 dstream<<"INFO: Server: Sent object remove/add: "
1505 <<removed_objects.size()<<" removed, "
1506 <<added_objects.size()<<" added, "
1507 <<"packet size is "<<reply.getSize()<<std::endl;
1512 Collect a list of all the objects known by the clients
1513 and report it back to the environment.
1516 core::map<u16, bool> all_known_objects;
1518 for(core::map<u16, RemoteClient*>::Iterator
1519 i = m_clients.getIterator();
1520 i.atEnd() == false; i++)
1522 RemoteClient *client = i.getNode()->getValue();
1523 // Go through all known objects of client
1524 for(core::map<u16, bool>::Iterator
1525 i = client->m_known_objects.getIterator();
1526 i.atEnd()==false; i++)
1528 u16 id = i.getNode()->getKey();
1529 all_known_objects[id] = true;
1533 m_env.setKnownActiveObjects(whatever);
1539 Send object messages
1542 JMutexAutoLock envlock(m_env_mutex);
1543 JMutexAutoLock conlock(m_con_mutex);
1545 ScopeProfiler sp(&g_profiler, "Server: sending object messages");
1548 // Value = data sent by object
1549 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1551 // Get active object messages from environment
1554 ActiveObjectMessage aom = m_env.getActiveObjectMessage();
1558 core::list<ActiveObjectMessage>* message_list = NULL;
1559 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1560 n = buffered_messages.find(aom.id);
1563 message_list = new core::list<ActiveObjectMessage>;
1564 buffered_messages.insert(aom.id, message_list);
1568 message_list = n->getValue();
1570 message_list->push_back(aom);
1573 // Route data to every client
1574 for(core::map<u16, RemoteClient*>::Iterator
1575 i = m_clients.getIterator();
1576 i.atEnd()==false; i++)
1578 RemoteClient *client = i.getNode()->getValue();
1579 std::string reliable_data;
1580 std::string unreliable_data;
1581 // Go through all objects in message buffer
1582 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1583 j = buffered_messages.getIterator();
1584 j.atEnd()==false; j++)
1586 // If object is not known by client, skip it
1587 u16 id = j.getNode()->getKey();
1588 if(client->m_known_objects.find(id) == NULL)
1590 // Get message list of object
1591 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1592 // Go through every message
1593 for(core::list<ActiveObjectMessage>::Iterator
1594 k = list->begin(); k != list->end(); k++)
1596 // Compose the full new data with header
1597 ActiveObjectMessage aom = *k;
1598 std::string new_data;
1601 writeU16((u8*)&buf[0], aom.id);
1602 new_data.append(buf, 2);
1604 new_data += serializeString(aom.datastring);
1605 // Add data to buffer
1607 reliable_data += new_data;
1609 unreliable_data += new_data;
1613 reliable_data and unreliable_data are now ready.
1616 if(reliable_data.size() > 0)
1618 SharedBuffer<u8> reply(2 + reliable_data.size());
1619 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1620 memcpy((char*)&reply[2], reliable_data.c_str(),
1621 reliable_data.size());
1623 m_con.Send(client->peer_id, 0, reply, true);
1625 if(unreliable_data.size() > 0)
1627 SharedBuffer<u8> reply(2 + unreliable_data.size());
1628 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1629 memcpy((char*)&reply[2], unreliable_data.c_str(),
1630 unreliable_data.size());
1631 // Send as unreliable
1632 m_con.Send(client->peer_id, 0, reply, false);
1635 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1637 dstream<<"INFO: Server: Size of object message data: "
1638 <<"reliable: "<<reliable_data.size()
1639 <<", unreliable: "<<unreliable_data.size()
1644 // Clear buffered_messages
1645 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1646 i = buffered_messages.getIterator();
1647 i.atEnd()==false; i++)
1649 delete i.getNode()->getValue();
1653 } // enable_experimental
1656 Send queued-for-sending map edit events.
1659 // Don't send too many at a time
1661 while(m_unsent_map_edit_queue.size() != 0)
1663 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1665 // Players far away from the change are stored here.
1666 // Instead of sending the changes, MapBlocks are set not sent
1668 core::list<u16> far_players;
1670 if(event->type == MEET_ADDNODE)
1672 dstream<<"Server: MEET_ADDNODE"<<std::endl;
1673 sendAddNode(event->p, event->n, event->already_known_by_peer,
1676 else if(event->type == MEET_REMOVENODE)
1678 dstream<<"Server: MEET_REMOVENODE"<<std::endl;
1679 sendRemoveNode(event->p, event->already_known_by_peer,
1682 else if(event->type == MEET_BLOCK_NODE_METADATA_CHANGED)
1684 dstream<<"Server: MEET_BLOCK_NODE_METADATA_CHANGED"<<std::endl;
1685 setBlockNotSent(event->p);
1687 else if(event->type == MEET_OTHER)
1689 dstream<<"WARNING: Server: MEET_OTHER not implemented"
1694 dstream<<"WARNING: Server: Unknown MapEditEvent "
1695 <<((u32)event->type)<<std::endl;
1699 Set blocks not sent to far players
1701 core::map<v3s16, MapBlock*> modified_blocks2;
1702 for(core::map<v3s16, bool>::Iterator
1703 i = event->modified_blocks.getIterator();
1704 i.atEnd()==false; i++)
1706 v3s16 p = i.getNode()->getKey();
1707 modified_blocks2.insert(p, m_env.getMap().getBlockNoCreateNoEx(p));
1709 for(core::list<u16>::Iterator
1710 i = far_players.begin();
1711 i != far_players.end(); i++)
1714 RemoteClient *client = getClient(peer_id);
1717 client->SetBlocksNotSent(modified_blocks2);
1722 // Don't send too many at a time
1724 if(count >= 1 && m_unsent_map_edit_queue.size() < 100)
1730 Send object positions
1731 TODO: Get rid of MapBlockObjects
1734 float &counter = m_objectdata_timer;
1736 if(counter >= g_settings.getFloat("objectdata_interval"))
1738 JMutexAutoLock lock1(m_env_mutex);
1739 JMutexAutoLock lock2(m_con_mutex);
1741 ScopeProfiler sp(&g_profiler, "Server: sending mbo positions");
1743 SendObjectData(counter);
1751 TODO: Move to ServerEnvironment and utilize active block stuff
1754 //TimeTaker timer("Step node metadata");
1756 JMutexAutoLock envlock(m_env_mutex);
1757 JMutexAutoLock conlock(m_con_mutex);
1759 ScopeProfiler sp(&g_profiler, "Server: stepping node metadata");
1761 core::map<v3s16, MapBlock*> changed_blocks;
1762 m_env.getMap().nodeMetadataStep(dtime, changed_blocks);
1764 // Use setBlockNotSent
1766 for(core::map<v3s16, MapBlock*>::Iterator
1767 i = changed_blocks.getIterator();
1768 i.atEnd() == false; i++)
1770 MapBlock *block = i.getNode()->getValue();
1772 for(core::map<u16, RemoteClient*>::Iterator
1773 i = m_clients.getIterator();
1774 i.atEnd()==false; i++)
1776 RemoteClient *client = i.getNode()->getValue();
1777 client->SetBlockNotSent(block->getPos());
1783 Trigger emergethread (it somehow gets to a non-triggered but
1784 bysy state sometimes)
1787 float &counter = m_emergethread_trigger_timer;
1793 m_emergethread.trigger();
1797 // Save map, players and auth stuff
1799 float &counter = m_savemap_timer;
1801 if(counter >= g_settings.getFloat("server_map_save_interval"))
1805 ScopeProfiler sp(&g_profiler, "Server: saving stuff");
1808 if(m_authmanager.isModified())
1809 m_authmanager.save();
1812 JMutexAutoLock lock(m_env_mutex);
1813 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == true)
1815 // Save only changed parts
1816 m_env.getMap().save(true);
1818 // Delete unused sectors
1819 u32 deleted_count = m_env.getMap().unloadUnusedData(
1820 g_settings.getFloat("server_unload_unused_sectors_timeout"));
1821 if(deleted_count > 0)
1823 dout_server<<"Server: Unloaded "<<deleted_count
1824 <<" sectors from memory"<<std::endl;
1828 m_env.serializePlayers(m_mapsavedir);
1830 // Save environment metadata
1831 m_env.saveMeta(m_mapsavedir);
1837 void Server::Receive()
1839 DSTACK(__FUNCTION_NAME);
1840 u32 data_maxsize = 10000;
1841 Buffer<u8> data(data_maxsize);
1846 JMutexAutoLock conlock(m_con_mutex);
1847 datasize = m_con.Receive(peer_id, *data, data_maxsize);
1850 // This has to be called so that the client list gets synced
1851 // with the peer list of the connection
1852 handlePeerChanges();
1854 ProcessData(*data, datasize, peer_id);
1856 catch(con::InvalidIncomingDataException &e)
1858 derr_server<<"Server::Receive(): "
1859 "InvalidIncomingDataException: what()="
1860 <<e.what()<<std::endl;
1862 catch(con::PeerNotFoundException &e)
1864 //NOTE: This is not needed anymore
1866 // The peer has been disconnected.
1867 // Find the associated player and remove it.
1869 /*JMutexAutoLock envlock(m_env_mutex);
1871 dout_server<<"ServerThread: peer_id="<<peer_id
1872 <<" has apparently closed connection. "
1873 <<"Removing player."<<std::endl;
1875 m_env.removePlayer(peer_id);*/
1879 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1881 DSTACK(__FUNCTION_NAME);
1882 // Environment is locked first.
1883 JMutexAutoLock envlock(m_env_mutex);
1884 JMutexAutoLock conlock(m_con_mutex);
1888 peer = m_con.GetPeer(peer_id);
1890 catch(con::PeerNotFoundException &e)
1892 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1893 <<peer_id<<" not found"<<std::endl;
1897 u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1905 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1907 if(command == TOSERVER_INIT)
1909 // [0] u16 TOSERVER_INIT
1910 // [2] u8 SER_FMT_VER_HIGHEST
1911 // [3] u8[20] player_name
1912 // [23] u8[28] password <--- can be sent without this, from old versions
1914 if(datasize < 2+1+PLAYERNAME_SIZE)
1917 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1918 <<peer->id<<std::endl;
1920 // First byte after command is maximum supported
1921 // serialization version
1922 u8 client_max = data[2];
1923 u8 our_max = SER_FMT_VER_HIGHEST;
1924 // Use the highest version supported by both
1925 u8 deployed = core::min_(client_max, our_max);
1926 // If it's lower than the lowest supported, give up.
1927 if(deployed < SER_FMT_VER_LOWEST)
1928 deployed = SER_FMT_VER_INVALID;
1930 //peer->serialization_version = deployed;
1931 getClient(peer->id)->pending_serialization_version = deployed;
1933 if(deployed == SER_FMT_VER_INVALID)
1935 derr_server<<DTIME<<"Server: Cannot negotiate "
1936 "serialization version with peer "
1937 <<peer_id<<std::endl;
1946 char playername[PLAYERNAME_SIZE];
1947 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
1949 playername[i] = data[3+i];
1951 playername[PLAYERNAME_SIZE-1] = 0;
1953 if(playername[0]=='\0')
1955 derr_server<<DTIME<<"Server: Player has empty name"<<std::endl;
1956 SendAccessDenied(m_con, peer_id,
1961 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
1963 derr_server<<DTIME<<"Server: Player has invalid name"<<std::endl;
1964 SendAccessDenied(m_con, peer_id,
1965 L"Name contains unallowed characters");
1970 char password[PASSWORD_SIZE];
1971 if(datasize == 2+1+PLAYERNAME_SIZE)
1973 // old version - assume blank password
1978 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
1980 password[i] = data[23+i];
1982 password[PASSWORD_SIZE-1] = 0;
1985 std::string checkpwd;
1986 if(m_authmanager.exists(playername))
1988 checkpwd = m_authmanager.getPassword(playername);
1992 checkpwd = g_settings.get("default_password");
1995 if(password != checkpwd && checkpwd != "")
1997 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1998 <<": supplied invalid password for "
1999 <<playername<<std::endl;
2000 SendAccessDenied(m_con, peer_id, L"Invalid password");
2004 // Add player to auth manager
2005 if(m_authmanager.exists(playername) == false)
2007 derr_server<<DTIME<<"Server: adding player "<<playername
2008 <<" to auth manager"<<std::endl;
2009 m_authmanager.add(playername);
2010 m_authmanager.setPassword(playername, checkpwd);
2011 m_authmanager.setPrivs(playername,
2012 stringToPrivs(g_settings.get("default_privs")));
2013 m_authmanager.save();
2017 Player *player = emergePlayer(playername, password, peer_id);
2021 // DEBUG: Test serialization
2022 std::ostringstream test_os;
2023 player->serialize(test_os);
2024 dstream<<"Player serialization test: \""<<test_os.str()
2026 std::istringstream test_is(test_os.str());
2027 player->deSerialize(test_is);
2030 // If failed, cancel
2033 derr_server<<DTIME<<"Server: peer_id="<<peer_id
2034 <<": failed to emerge player"<<std::endl;
2039 // If a client is already connected to the player, cancel
2040 if(player->peer_id != 0)
2042 derr_server<<DTIME<<"Server: peer_id="<<peer_id
2043 <<" tried to connect to "
2044 "an already connected player (peer_id="
2045 <<player->peer_id<<")"<<std::endl;
2048 // Set client of player
2049 player->peer_id = peer_id;
2052 // Check if player doesn't exist
2054 throw con::InvalidIncomingDataException
2055 ("Server::ProcessData(): INIT: Player doesn't exist");
2057 /*// update name if it was supplied
2058 if(datasize >= 20+3)
2061 player->updateName((const char*)&data[3]);
2065 Answer with a TOCLIENT_INIT
2068 SharedBuffer<u8> reply(2+1+6+8);
2069 writeU16(&reply[0], TOCLIENT_INIT);
2070 writeU8(&reply[2], deployed);
2071 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
2072 writeU64(&reply[2+1+6], m_env.getServerMap().getSeed());
2075 m_con.Send(peer_id, 0, reply, true);
2079 Send complete position information
2081 SendMovePlayer(player);
2086 if(command == TOSERVER_INIT2)
2088 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
2089 <<peer->id<<std::endl;
2092 getClient(peer->id)->serialization_version
2093 = getClient(peer->id)->pending_serialization_version;
2096 Send some initialization data
2099 // Send player info to all players
2102 // Send inventory to player
2103 UpdateCrafting(peer->id);
2104 SendInventory(peer->id);
2108 Player *player = m_env.getPlayer(peer_id);
2109 SendPlayerHP(player);
2114 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2115 m_env.getTimeOfDay());
2116 m_con.Send(peer->id, 0, data, true);
2119 // Send information about server to player in chat
2120 SendChatMessage(peer_id, getStatusString());
2122 // Send information about joining in chat
2124 std::wstring name = L"unknown";
2125 Player *player = m_env.getPlayer(peer_id);
2127 name = narrow_to_wide(player->getName());
2129 std::wstring message;
2132 message += L" joined game";
2133 BroadcastChatMessage(message);
2139 if(peer_ser_ver == SER_FMT_VER_INVALID)
2141 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
2142 " serialization format invalid or not initialized."
2143 " Skipping incoming command="<<command<<std::endl;
2147 Player *player = m_env.getPlayer(peer_id);
2150 derr_server<<"Server::ProcessData(): Cancelling: "
2151 "No player for peer_id="<<peer_id
2155 if(command == TOSERVER_PLAYERPOS)
2157 if(datasize < 2+12+12+4+4)
2161 v3s32 ps = readV3S32(&data[start+2]);
2162 v3s32 ss = readV3S32(&data[start+2+12]);
2163 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2164 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2165 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2166 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2167 pitch = wrapDegrees(pitch);
2168 yaw = wrapDegrees(yaw);
2169 player->setPosition(position);
2170 player->setSpeed(speed);
2171 player->setPitch(pitch);
2172 player->setYaw(yaw);
2174 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2175 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2176 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2178 else if(command == TOSERVER_GOTBLOCKS)
2191 u16 count = data[2];
2192 for(u16 i=0; i<count; i++)
2194 if((s16)datasize < 2+1+(i+1)*6)
2195 throw con::InvalidIncomingDataException
2196 ("GOTBLOCKS length is too short");
2197 v3s16 p = readV3S16(&data[2+1+i*6]);
2198 /*dstream<<"Server: GOTBLOCKS ("
2199 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2200 RemoteClient *client = getClient(peer_id);
2201 client->GotBlock(p);
2204 else if(command == TOSERVER_DELETEDBLOCKS)
2217 u16 count = data[2];
2218 for(u16 i=0; i<count; i++)
2220 if((s16)datasize < 2+1+(i+1)*6)
2221 throw con::InvalidIncomingDataException
2222 ("DELETEDBLOCKS length is too short");
2223 v3s16 p = readV3S16(&data[2+1+i*6]);
2224 /*dstream<<"Server: DELETEDBLOCKS ("
2225 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2226 RemoteClient *client = getClient(peer_id);
2227 client->SetBlockNotSent(p);
2230 else if(command == TOSERVER_CLICK_OBJECT)
2235 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2240 [2] u8 button (0=left, 1=right)
2245 u8 button = readU8(&data[2]);
2247 p.X = readS16(&data[3]);
2248 p.Y = readS16(&data[5]);
2249 p.Z = readS16(&data[7]);
2250 s16 id = readS16(&data[9]);
2251 //u16 item_i = readU16(&data[11]);
2253 MapBlock *block = NULL;
2256 block = m_env.getMap().getBlockNoCreate(p);
2258 catch(InvalidPositionException &e)
2260 derr_server<<"CLICK_OBJECT block not found"<<std::endl;
2264 MapBlockObject *obj = block->getObject(id);
2268 derr_server<<"CLICK_OBJECT object not found"<<std::endl;
2272 //TODO: Check that object is reasonably close
2277 InventoryList *ilist = player->inventory.getList("main");
2278 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
2281 // Skip if inventory has no free space
2282 if(ilist->getUsedSlots() == ilist->getSize())
2284 dout_server<<"Player inventory has no free space"<<std::endl;
2289 Create the inventory item
2291 InventoryItem *item = NULL;
2292 // If it is an item-object, take the item from it
2293 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
2295 item = ((ItemObject*)obj)->createInventoryItem();
2297 // Else create an item of the object
2300 item = new MapBlockObjectItem
2301 (obj->getInventoryString());
2304 // Add to inventory and send inventory
2305 ilist->addItem(item);
2306 UpdateCrafting(player->peer_id);
2307 SendInventory(player->peer_id);
2310 // Remove from block
2311 block->removeObject(id);
2314 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2319 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2325 [2] u8 button (0=left, 1=right)
2329 u8 button = readU8(&data[2]);
2330 u16 id = readS16(&data[3]);
2331 u16 item_i = readU16(&data[11]);
2333 ServerActiveObject *obj = m_env.getActiveObject(id);
2337 derr_server<<"Server: CLICK_ACTIVEOBJECT: object not found"
2342 //TODO: Check that object is reasonably close
2344 // Left click, pick object up (usually)
2347 InventoryList *ilist = player->inventory.getList("main");
2348 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
2351 // Skip if inventory has no free space
2352 if(ilist->getUsedSlots() == ilist->getSize())
2354 dout_server<<"Player inventory has no free space"<<std::endl;
2358 // Skip if object has been removed
2363 Create the inventory item
2365 InventoryItem *item = obj->createPickedUpItem();
2369 // Add to inventory and send inventory
2370 ilist->addItem(item);
2371 UpdateCrafting(player->peer_id);
2372 SendInventory(player->peer_id);
2374 // Remove object from environment
2375 obj->m_removed = true;
2380 Item cannot be picked up. Punch it instead.
2383 ToolItem *titem = NULL;
2384 std::string toolname = "";
2386 InventoryList *mlist = player->inventory.getList("main");
2389 InventoryItem *item = mlist->getItem(item_i);
2390 if(item && (std::string)item->getName() == "ToolItem")
2392 titem = (ToolItem*)item;
2393 toolname = titem->getToolName();
2397 u16 wear = obj->punch(toolname);
2401 bool weared_out = titem->addWear(wear);
2403 mlist->deleteItem(item_i);
2404 SendInventory(player->peer_id);
2410 else if(command == TOSERVER_GROUND_ACTION)
2418 [3] v3s16 nodepos_undersurface
2419 [9] v3s16 nodepos_abovesurface
2424 2: stop digging (all parameters ignored)
2425 3: digging completed
2427 u8 action = readU8(&data[2]);
2429 p_under.X = readS16(&data[3]);
2430 p_under.Y = readS16(&data[5]);
2431 p_under.Z = readS16(&data[7]);
2433 p_over.X = readS16(&data[9]);
2434 p_over.Y = readS16(&data[11]);
2435 p_over.Z = readS16(&data[13]);
2436 u16 item_i = readU16(&data[15]);
2438 //TODO: Check that target is reasonably close
2446 NOTE: This can be used in the future to check if
2447 somebody is cheating, by checking the timing.
2454 else if(action == 2)
2457 RemoteClient *client = getClient(peer->id);
2458 JMutexAutoLock digmutex(client->m_dig_mutex);
2459 client->m_dig_tool_item = -1;
2464 3: Digging completed
2466 else if(action == 3)
2468 // Mandatory parameter; actually used for nothing
2469 core::map<v3s16, MapBlock*> modified_blocks;
2471 u8 material = CONTENT_IGNORE;
2472 u8 mineral = MINERAL_NONE;
2474 bool cannot_remove_node = false;
2478 MapNode n = m_env.getMap().getNode(p_under);
2480 mineral = n.getMineral();
2481 // Get material at position
2483 // If not yet cancelled
2484 if(cannot_remove_node == false)
2486 // If it's not diggable, do nothing
2487 if(content_diggable(material) == false)
2489 derr_server<<"Server: Not finishing digging: "
2490 <<"Node not diggable"
2492 cannot_remove_node = true;
2495 // If not yet cancelled
2496 if(cannot_remove_node == false)
2498 // Get node metadata
2499 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p_under);
2500 if(meta && meta->nodeRemovalDisabled() == true)
2502 derr_server<<"Server: Not finishing digging: "
2503 <<"Node metadata disables removal"
2505 cannot_remove_node = true;
2509 catch(InvalidPositionException &e)
2511 derr_server<<"Server: Not finishing digging: Node not found."
2512 <<" Adding block to emerge queue."
2514 m_emerge_queue.addBlock(peer_id,
2515 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2516 cannot_remove_node = true;
2519 // Make sure the player is allowed to do it
2520 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2522 dstream<<"Player "<<player->getName()<<" cannot remove node"
2523 <<" because privileges are "<<getPlayerPrivs(player)
2525 cannot_remove_node = true;
2529 If node can't be removed, set block to be re-sent to
2532 if(cannot_remove_node)
2534 derr_server<<"Server: Not finishing digging."<<std::endl;
2536 // Client probably has wrong data.
2537 // Set block not sent, so that client will get
2539 dstream<<"Client "<<peer_id<<" tried to dig "
2540 <<"node; but node cannot be removed."
2541 <<" setting MapBlock not sent."<<std::endl;
2542 RemoteClient *client = getClient(peer_id);
2543 v3s16 blockpos = getNodeBlockPos(p_under);
2544 client->SetBlockNotSent(blockpos);
2550 Send the removal to all close-by players.
2551 - If other player is close, send REMOVENODE
2552 - Otherwise set blocks not sent
2554 core::list<u16> far_players;
2555 sendRemoveNode(p_under, peer_id, &far_players, 30);
2558 Update and send inventory
2561 if(g_settings.getBool("creative_mode") == false)
2566 InventoryList *mlist = player->inventory.getList("main");
2569 InventoryItem *item = mlist->getItem(item_i);
2570 if(item && (std::string)item->getName() == "ToolItem")
2572 ToolItem *titem = (ToolItem*)item;
2573 std::string toolname = titem->getToolName();
2575 // Get digging properties for material and tool
2576 DiggingProperties prop =
2577 getDiggingProperties(material, toolname);
2579 if(prop.diggable == false)
2581 derr_server<<"Server: WARNING: Player digged"
2582 <<" with impossible material + tool"
2583 <<" combination"<<std::endl;
2586 bool weared_out = titem->addWear(prop.wear);
2590 mlist->deleteItem(item_i);
2596 Add dug item to inventory
2599 InventoryItem *item = NULL;
2601 if(mineral != MINERAL_NONE)
2602 item = getDiggedMineralItem(mineral);
2607 std::string &dug_s = content_features(material).dug_item;
2610 std::istringstream is(dug_s, std::ios::binary);
2611 item = InventoryItem::deSerialize(is);
2617 // Add a item to inventory
2618 player->inventory.addItem("main", item);
2621 UpdateCrafting(player->peer_id);
2622 SendInventory(player->peer_id);
2628 (this takes some time so it is done after the quick stuff)
2631 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
2633 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2636 Set blocks not sent to far players
2638 for(core::list<u16>::Iterator
2639 i = far_players.begin();
2640 i != far_players.end(); i++)
2643 RemoteClient *client = getClient(peer_id);
2646 client->SetBlocksNotSent(modified_blocks);
2653 else if(action == 1)
2656 InventoryList *ilist = player->inventory.getList("main");
2661 InventoryItem *item = ilist->getItem(item_i);
2663 // If there is no item, it is not possible to add it anywhere
2668 Handle material items
2670 if(std::string("MaterialItem") == item->getName())
2673 // Don't add a node if this is not a free space
2674 MapNode n2 = m_env.getMap().getNode(p_over);
2675 bool no_enough_privs =
2676 ((getPlayerPrivs(player) & PRIV_BUILD)==0);
2678 dstream<<"Player "<<player->getName()<<" cannot add node"
2679 <<" because privileges are "<<getPlayerPrivs(player)
2682 if(content_buildable_to(n2.d) == false
2685 // Client probably has wrong data.
2686 // Set block not sent, so that client will get
2688 dstream<<"Client "<<peer_id<<" tried to place"
2689 <<" node in invalid position; setting"
2690 <<" MapBlock not sent."<<std::endl;
2691 RemoteClient *client = getClient(peer_id);
2692 v3s16 blockpos = getNodeBlockPos(p_over);
2693 client->SetBlockNotSent(blockpos);
2697 catch(InvalidPositionException &e)
2699 derr_server<<"Server: Ignoring ADDNODE: Node not found"
2700 <<" Adding block to emerge queue."
2702 m_emerge_queue.addBlock(peer_id,
2703 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2707 // Reset build time counter
2708 getClient(peer->id)->m_time_from_building = 0.0;
2711 MaterialItem *mitem = (MaterialItem*)item;
2713 n.d = mitem->getMaterial();
2714 if(content_features(n.d).wall_mounted)
2715 n.dir = packDir(p_under - p_over);
2718 Send to all close-by players
2720 core::list<u16> far_players;
2721 sendAddNode(p_over, n, 0, &far_players, 30);
2726 InventoryList *ilist = player->inventory.getList("main");
2727 if(g_settings.getBool("creative_mode") == false && ilist)
2729 // Remove from inventory and send inventory
2730 if(mitem->getCount() == 1)
2731 ilist->deleteItem(item_i);
2735 UpdateCrafting(peer_id);
2736 SendInventory(peer_id);
2742 This takes some time so it is done after the quick stuff
2744 core::map<v3s16, MapBlock*> modified_blocks;
2746 MapEditEventIgnorer ign(&m_ignore_map_edit_events);
2748 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2751 Set blocks not sent to far players
2753 for(core::list<u16>::Iterator
2754 i = far_players.begin();
2755 i != far_players.end(); i++)
2758 RemoteClient *client = getClient(peer_id);
2761 client->SetBlocksNotSent(modified_blocks);
2765 Calculate special events
2768 /*if(n.d == CONTENT_MESE)
2771 for(s16 z=-1; z<=1; z++)
2772 for(s16 y=-1; y<=1; y++)
2773 for(s16 x=-1; x<=1; x++)
2780 Place other item (not a block)
2784 v3s16 blockpos = getNodeBlockPos(p_over);
2787 Check that the block is loaded so that the item
2788 can properly be added to the static list too
2790 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2793 derr_server<<"Error while placing object: "
2794 "block not found"<<std::endl;
2798 dout_server<<"Placing a miscellaneous item on map"
2801 // Calculate a position for it
2802 v3f pos = intToFloat(p_over, BS);
2804 pos.Y -= BS*0.25; // let it drop a bit
2806 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2807 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2812 ServerActiveObject *obj = item->createSAO(&m_env, 0, pos);
2816 derr_server<<"WARNING: item resulted in NULL object, "
2817 <<"not placing onto map"
2822 // Add the object to the environment
2823 m_env.addActiveObject(obj);
2825 dout_server<<"Placed object"<<std::endl;
2827 if(g_settings.getBool("creative_mode") == false)
2829 // Delete the right amount of items from the slot
2830 u16 dropcount = item->getDropCount();
2832 // Delete item if all gone
2833 if(item->getCount() <= dropcount)
2835 if(item->getCount() < dropcount)
2836 dstream<<"WARNING: Server: dropped more items"
2837 <<" than the slot contains"<<std::endl;
2839 InventoryList *ilist = player->inventory.getList("main");
2841 // Remove from inventory and send inventory
2842 ilist->deleteItem(item_i);
2844 // Else decrement it
2846 item->remove(dropcount);
2849 UpdateCrafting(peer_id);
2850 SendInventory(peer_id);
2858 Catch invalid actions
2862 derr_server<<"WARNING: Server: Invalid action "
2863 <<action<<std::endl;
2867 else if(command == TOSERVER_RELEASE)
2876 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2879 else if(command == TOSERVER_SIGNTEXT)
2881 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 blockpos = readV3S16(buf);
2896 is.read((char*)buf, 2);
2897 s16 id = readS16(buf);
2898 is.read((char*)buf, 2);
2899 u16 textlen = readU16(buf);
2901 for(u16 i=0; i<textlen; i++)
2903 is.read((char*)buf, 1);
2904 text += (char)buf[0];
2907 MapBlock *block = NULL;
2910 block = m_env.getMap().getBlockNoCreate(blockpos);
2912 catch(InvalidPositionException &e)
2914 derr_server<<"Error while setting sign text: "
2915 "block not found"<<std::endl;
2919 MapBlockObject *obj = block->getObject(id);
2922 derr_server<<"Error while setting sign text: "
2923 "object not found"<<std::endl;
2927 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2929 derr_server<<"Error while setting sign text: "
2930 "object is not a sign"<<std::endl;
2934 ((SignObject*)obj)->setText(text);
2936 obj->getBlock()->setChangedFlag();
2938 else if(command == TOSERVER_SIGNNODETEXT)
2940 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2948 std::string datastring((char*)&data[2], datasize-2);
2949 std::istringstream is(datastring, std::ios_base::binary);
2952 is.read((char*)buf, 6);
2953 v3s16 p = readV3S16(buf);
2954 is.read((char*)buf, 2);
2955 u16 textlen = readU16(buf);
2957 for(u16 i=0; i<textlen; i++)
2959 is.read((char*)buf, 1);
2960 text += (char)buf[0];
2963 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2966 if(meta->typeId() != CONTENT_SIGN_WALL)
2968 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
2969 signmeta->setText(text);
2971 v3s16 blockpos = getNodeBlockPos(p);
2972 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2975 block->setChangedFlag();
2978 for(core::map<u16, RemoteClient*>::Iterator
2979 i = m_clients.getIterator();
2980 i.atEnd()==false; i++)
2982 RemoteClient *client = i.getNode()->getValue();
2983 client->SetBlockNotSent(blockpos);
2986 else if(command == TOSERVER_INVENTORY_ACTION)
2988 /*// Ignore inventory changes if in creative mode
2989 if(g_settings.getBool("creative_mode") == true)
2991 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2995 // Strip command and create a stream
2996 std::string datastring((char*)&data[2], datasize-2);
2997 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2998 std::istringstream is(datastring, std::ios_base::binary);
3000 InventoryAction *a = InventoryAction::deSerialize(is);
3005 c.current_player = player;
3008 Handle craftresult specially if not in creative mode
3010 bool disable_action = false;
3011 if(a->getType() == IACTION_MOVE
3012 && g_settings.getBool("creative_mode") == false)
3014 IMoveAction *ma = (IMoveAction*)a;
3015 if(ma->to_inv == "current_player" &&
3016 ma->from_inv == "current_player")
3018 InventoryList *rlist = player->inventory.getList("craftresult");
3020 InventoryList *clist = player->inventory.getList("craft");
3022 InventoryList *mlist = player->inventory.getList("main");
3025 Craftresult is no longer preview if something
3028 if(ma->to_list == "craftresult"
3029 && ma->from_list != "craftresult")
3031 // If it currently is a preview, remove
3033 if(player->craftresult_is_preview)
3035 rlist->deleteItem(0);
3037 player->craftresult_is_preview = false;
3040 Crafting takes place if this condition is true.
3042 if(player->craftresult_is_preview &&
3043 ma->from_list == "craftresult")
3045 player->craftresult_is_preview = false;
3046 clist->decrementMaterials(1);
3049 If the craftresult is placed on itself, move it to
3050 main inventory instead of doing the action
3052 if(ma->to_list == "craftresult"
3053 && ma->from_list == "craftresult")
3055 disable_action = true;
3057 InventoryItem *item1 = rlist->changeItem(0, NULL);
3058 mlist->addItem(item1);
3063 if(disable_action == false)
3065 // Feed action to player inventory
3073 UpdateCrafting(player->peer_id);
3074 SendInventory(player->peer_id);
3079 dstream<<"TOSERVER_INVENTORY_ACTION: "
3080 <<"InventoryAction::deSerialize() returned NULL"
3084 else if(command == TOSERVER_CHAT_MESSAGE)
3092 std::string datastring((char*)&data[2], datasize-2);
3093 std::istringstream is(datastring, std::ios_base::binary);
3096 is.read((char*)buf, 2);
3097 u16 len = readU16(buf);
3099 std::wstring message;
3100 for(u16 i=0; i<len; i++)
3102 is.read((char*)buf, 2);
3103 message += (wchar_t)readU16(buf);
3106 // Get player name of this client
3107 std::wstring name = narrow_to_wide(player->getName());
3109 // Line to send to players
3111 // Whether to send to the player that sent the line
3112 bool send_to_sender = false;
3113 // Whether to send to other players
3114 bool send_to_others = false;
3116 // Local player gets all privileges regardless of
3117 // what's set on their account.
3118 u64 privs = getPlayerPrivs(player);
3121 std::wstring commandprefix = L"/#";
3122 if(message.substr(0, commandprefix.size()) == commandprefix)
3124 line += L"Server: ";
3126 message = message.substr(commandprefix.size());
3128 ServerCommandContext *ctx = new ServerCommandContext(
3129 str_split(message, L' '),
3135 line += processServerCommand(ctx);
3136 send_to_sender = ctx->flags & 1;
3137 send_to_others = ctx->flags & 2;
3143 if(privs & PRIV_SHOUT)
3149 send_to_others = true;
3153 line += L"Server: You are not allowed to shout";
3154 send_to_sender = true;
3160 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
3163 Send the message to clients
3165 for(core::map<u16, RemoteClient*>::Iterator
3166 i = m_clients.getIterator();
3167 i.atEnd() == false; i++)
3169 // Get client and check that it is valid
3170 RemoteClient *client = i.getNode()->getValue();
3171 assert(client->peer_id == i.getNode()->getKey());
3172 if(client->serialization_version == SER_FMT_VER_INVALID)
3176 bool sender_selected = (peer_id == client->peer_id);
3177 if(sender_selected == true && send_to_sender == false)
3179 if(sender_selected == false && send_to_others == false)
3182 SendChatMessage(client->peer_id, line);
3186 else if(command == TOSERVER_DAMAGE)
3188 if(g_settings.getBool("enable_damage"))
3190 std::string datastring((char*)&data[2], datasize-2);
3191 std::istringstream is(datastring, std::ios_base::binary);
3192 u8 damage = readU8(is);
3193 if(player->hp > damage)
3195 player->hp -= damage;
3201 dstream<<"TODO: Server: TOSERVER_HP_DECREMENT: Player dies"
3204 v3f pos = findSpawnPos(m_env.getServerMap());
3205 player->setPosition(pos);
3207 SendMovePlayer(player);
3208 SendPlayerHP(player);
3210 //TODO: Throw items around
3214 SendPlayerHP(player);
3216 else if(command == TOSERVER_PASSWORD)
3219 [0] u16 TOSERVER_PASSWORD
3220 [2] u8[28] old password
3221 [30] u8[28] new password
3224 if(datasize != 2+PASSWORD_SIZE*2)
3226 /*char password[PASSWORD_SIZE];
3227 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3228 password[i] = data[2+i];
3229 password[PASSWORD_SIZE-1] = 0;*/
3231 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3239 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3241 char c = data[2+PASSWORD_SIZE+i];
3247 std::string playername = player->getName();
3249 if(m_authmanager.exists(playername) == false)
3251 dstream<<"Server: playername not found in authmanager"<<std::endl;
3252 // Wrong old password supplied!!
3253 SendChatMessage(peer_id, L"playername not found in authmanager");
3257 std::string checkpwd = m_authmanager.getPassword(playername);
3259 if(oldpwd != checkpwd)
3261 dstream<<"Server: invalid old password"<<std::endl;
3262 // Wrong old password supplied!!
3263 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
3267 m_authmanager.setPassword(playername, newpwd);
3269 dstream<<"Server: password change successful for "<<playername
3271 SendChatMessage(peer_id, L"Password change successful");
3275 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
3276 "unknown command "<<command<<std::endl;
3280 catch(SendFailedException &e)
3282 derr_server<<"Server::ProcessData(): SendFailedException: "
3288 void Server::onMapEditEvent(MapEditEvent *event)
3290 dstream<<"Server::onMapEditEvent()"<<std::endl;
3291 if(m_ignore_map_edit_events)
3293 MapEditEvent *e = event->clone();
3294 m_unsent_map_edit_queue.push_back(e);
3297 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3299 if(id == "current_player")
3301 assert(c->current_player);
3302 return &(c->current_player->inventory);
3306 std::string id0 = fn.next(":");
3308 if(id0 == "nodemeta")
3311 p.X = stoi(fn.next(","));
3312 p.Y = stoi(fn.next(","));
3313 p.Z = stoi(fn.next(","));
3314 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3316 return meta->getInventory();
3317 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3318 <<"no metadata found"<<std::endl;
3322 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3325 void Server::inventoryModified(InventoryContext *c, std::string id)
3327 if(id == "current_player")
3329 assert(c->current_player);
3331 UpdateCrafting(c->current_player->peer_id);
3332 SendInventory(c->current_player->peer_id);
3337 std::string id0 = fn.next(":");
3339 if(id0 == "nodemeta")
3342 p.X = stoi(fn.next(","));
3343 p.Y = stoi(fn.next(","));
3344 p.Z = stoi(fn.next(","));
3345 v3s16 blockpos = getNodeBlockPos(p);
3347 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3349 meta->inventoryModified();
3351 for(core::map<u16, RemoteClient*>::Iterator
3352 i = m_clients.getIterator();
3353 i.atEnd()==false; i++)
3355 RemoteClient *client = i.getNode()->getValue();
3356 client->SetBlockNotSent(blockpos);
3362 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3365 core::list<PlayerInfo> Server::getPlayerInfo()
3367 DSTACK(__FUNCTION_NAME);
3368 JMutexAutoLock envlock(m_env_mutex);
3369 JMutexAutoLock conlock(m_con_mutex);
3371 core::list<PlayerInfo> list;
3373 core::list<Player*> players = m_env.getPlayers();
3375 core::list<Player*>::Iterator i;
3376 for(i = players.begin();
3377 i != players.end(); i++)
3381 Player *player = *i;
3384 con::Peer *peer = m_con.GetPeer(player->peer_id);
3385 // Copy info from peer to info struct
3387 info.address = peer->address;
3388 info.avg_rtt = peer->avg_rtt;
3390 catch(con::PeerNotFoundException &e)
3392 // Set dummy peer info
3394 info.address = Address(0,0,0,0,0);
3398 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3399 info.position = player->getPosition();
3401 list.push_back(info);
3408 void Server::peerAdded(con::Peer *peer)
3410 DSTACK(__FUNCTION_NAME);
3411 dout_server<<"Server::peerAdded(): peer->id="
3412 <<peer->id<<std::endl;
3415 c.type = PEER_ADDED;
3416 c.peer_id = peer->id;
3418 m_peer_change_queue.push_back(c);
3421 void Server::deletingPeer(con::Peer *peer, bool timeout)
3423 DSTACK(__FUNCTION_NAME);
3424 dout_server<<"Server::deletingPeer(): peer->id="
3425 <<peer->id<<", timeout="<<timeout<<std::endl;
3428 c.type = PEER_REMOVED;
3429 c.peer_id = peer->id;
3430 c.timeout = timeout;
3431 m_peer_change_queue.push_back(c);
3438 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3440 DSTACK(__FUNCTION_NAME);
3441 std::ostringstream os(std::ios_base::binary);
3443 writeU16(os, TOCLIENT_HP);
3447 std::string s = os.str();
3448 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3450 con.Send(peer_id, 0, data, true);
3453 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3454 const std::wstring &reason)
3456 DSTACK(__FUNCTION_NAME);
3457 std::ostringstream os(std::ios_base::binary);
3459 writeU16(os, TOCLIENT_ACCESS_DENIED);
3460 os<<serializeWideString(reason);
3463 std::string s = os.str();
3464 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3466 con.Send(peer_id, 0, data, true);
3470 Non-static send methods
3473 void Server::SendObjectData(float dtime)
3475 DSTACK(__FUNCTION_NAME);
3477 core::map<v3s16, bool> stepped_blocks;
3479 for(core::map<u16, RemoteClient*>::Iterator
3480 i = m_clients.getIterator();
3481 i.atEnd() == false; i++)
3483 u16 peer_id = i.getNode()->getKey();
3484 RemoteClient *client = i.getNode()->getValue();
3485 assert(client->peer_id == peer_id);
3487 if(client->serialization_version == SER_FMT_VER_INVALID)
3490 client->SendObjectData(this, dtime, stepped_blocks);
3494 void Server::SendPlayerInfos()
3496 DSTACK(__FUNCTION_NAME);
3498 //JMutexAutoLock envlock(m_env_mutex);
3500 // Get connected players
3501 core::list<Player*> players = m_env.getPlayers(true);
3503 u32 player_count = players.getSize();
3504 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3506 SharedBuffer<u8> data(datasize);
3507 writeU16(&data[0], TOCLIENT_PLAYERINFO);
3510 core::list<Player*>::Iterator i;
3511 for(i = players.begin();
3512 i != players.end(); i++)
3514 Player *player = *i;
3516 /*dstream<<"Server sending player info for player with "
3517 "peer_id="<<player->peer_id<<std::endl;*/
3519 writeU16(&data[start], player->peer_id);
3520 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3521 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3522 start += 2+PLAYERNAME_SIZE;
3525 //JMutexAutoLock conlock(m_con_mutex);
3528 m_con.SendToAll(0, data, true);
3531 void Server::SendInventory(u16 peer_id)
3533 DSTACK(__FUNCTION_NAME);
3535 Player* player = m_env.getPlayer(peer_id);
3542 std::ostringstream os;
3543 //os.imbue(std::locale("C"));
3545 player->inventory.serialize(os);
3547 std::string s = os.str();
3549 SharedBuffer<u8> data(s.size()+2);
3550 writeU16(&data[0], TOCLIENT_INVENTORY);
3551 memcpy(&data[2], s.c_str(), s.size());
3554 m_con.Send(peer_id, 0, data, true);
3557 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3559 DSTACK(__FUNCTION_NAME);
3561 std::ostringstream os(std::ios_base::binary);
3565 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3566 os.write((char*)buf, 2);
3569 writeU16(buf, message.size());
3570 os.write((char*)buf, 2);
3573 for(u32 i=0; i<message.size(); i++)
3577 os.write((char*)buf, 2);
3581 std::string s = os.str();
3582 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3584 m_con.Send(peer_id, 0, data, true);
3587 void Server::BroadcastChatMessage(const std::wstring &message)
3589 for(core::map<u16, RemoteClient*>::Iterator
3590 i = m_clients.getIterator();
3591 i.atEnd() == false; i++)
3593 // Get client and check that it is valid
3594 RemoteClient *client = i.getNode()->getValue();
3595 assert(client->peer_id == i.getNode()->getKey());
3596 if(client->serialization_version == SER_FMT_VER_INVALID)
3599 SendChatMessage(client->peer_id, message);
3603 void Server::SendPlayerHP(Player *player)
3605 SendHP(m_con, player->peer_id, player->hp);
3608 void Server::SendMovePlayer(Player *player)
3610 DSTACK(__FUNCTION_NAME);
3611 std::ostringstream os(std::ios_base::binary);
3613 writeU16(os, TOCLIENT_MOVE_PLAYER);
3614 writeV3F1000(os, player->getPosition());
3615 writeF1000(os, player->getPitch());
3616 writeF1000(os, player->getYaw());
3619 v3f pos = player->getPosition();
3620 f32 pitch = player->getPitch();
3621 f32 yaw = player->getYaw();
3622 dstream<<"Server sending TOCLIENT_MOVE_PLAYER"
3623 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3630 std::string s = os.str();
3631 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3633 m_con.Send(player->peer_id, 0, data, true);
3636 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3637 core::list<u16> *far_players, float far_d_nodes)
3639 float maxd = far_d_nodes*BS;
3640 v3f p_f = intToFloat(p, BS);
3644 SharedBuffer<u8> reply(replysize);
3645 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3646 writeS16(&reply[2], p.X);
3647 writeS16(&reply[4], p.Y);
3648 writeS16(&reply[6], p.Z);
3650 for(core::map<u16, RemoteClient*>::Iterator
3651 i = m_clients.getIterator();
3652 i.atEnd() == false; i++)
3654 // Get client and check that it is valid
3655 RemoteClient *client = i.getNode()->getValue();
3656 assert(client->peer_id == i.getNode()->getKey());
3657 if(client->serialization_version == SER_FMT_VER_INVALID)
3660 // Don't send if it's the same one
3661 if(client->peer_id == ignore_id)
3667 Player *player = m_env.getPlayer(client->peer_id);
3670 // If player is far away, only set modified blocks not sent
3671 v3f player_pos = player->getPosition();
3672 if(player_pos.getDistanceFrom(p_f) > maxd)
3674 far_players->push_back(client->peer_id);
3681 m_con.Send(client->peer_id, 0, reply, true);
3685 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3686 core::list<u16> *far_players, float far_d_nodes)
3688 float maxd = far_d_nodes*BS;
3689 v3f p_f = intToFloat(p, BS);
3691 for(core::map<u16, RemoteClient*>::Iterator
3692 i = m_clients.getIterator();
3693 i.atEnd() == false; i++)
3695 // Get client and check that it is valid
3696 RemoteClient *client = i.getNode()->getValue();
3697 assert(client->peer_id == i.getNode()->getKey());
3698 if(client->serialization_version == SER_FMT_VER_INVALID)
3701 // Don't send if it's the same one
3702 if(client->peer_id == ignore_id)
3708 Player *player = m_env.getPlayer(client->peer_id);
3711 // If player is far away, only set modified blocks not sent
3712 v3f player_pos = player->getPosition();
3713 if(player_pos.getDistanceFrom(p_f) > maxd)
3715 far_players->push_back(client->peer_id);
3722 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3723 SharedBuffer<u8> reply(replysize);
3724 writeU16(&reply[0], TOCLIENT_ADDNODE);
3725 writeS16(&reply[2], p.X);
3726 writeS16(&reply[4], p.Y);
3727 writeS16(&reply[6], p.Z);
3728 n.serialize(&reply[8], client->serialization_version);
3731 m_con.Send(client->peer_id, 0, reply, true);
3735 void Server::setBlockNotSent(v3s16 p)
3737 for(core::map<u16, RemoteClient*>::Iterator
3738 i = m_clients.getIterator();
3739 i.atEnd()==false; i++)
3741 RemoteClient *client = i.getNode()->getValue();
3742 client->SetBlockNotSent(p);
3746 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3748 DSTACK(__FUNCTION_NAME);
3750 v3s16 p = block->getPos();
3754 bool completely_air = true;
3755 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3756 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3757 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3759 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
3761 completely_air = false;
3762 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
3767 dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
3769 dstream<<"[completely air] ";
3774 Create a packet with the block in the right format
3777 std::ostringstream os(std::ios_base::binary);
3778 block->serialize(os, ver);
3779 std::string s = os.str();
3780 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3782 u32 replysize = 8 + blockdata.getSize();
3783 SharedBuffer<u8> reply(replysize);
3784 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3785 writeS16(&reply[2], p.X);
3786 writeS16(&reply[4], p.Y);
3787 writeS16(&reply[6], p.Z);
3788 memcpy(&reply[8], *blockdata, blockdata.getSize());
3790 /*dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3791 <<": \tpacket size: "<<replysize<<std::endl;*/
3796 m_con.Send(peer_id, 1, reply, true);
3799 void Server::SendBlocks(float dtime)
3801 DSTACK(__FUNCTION_NAME);
3803 JMutexAutoLock envlock(m_env_mutex);
3804 JMutexAutoLock conlock(m_con_mutex);
3806 //TimeTaker timer("Server::SendBlocks");
3808 core::array<PrioritySortedBlockTransfer> queue;
3810 s32 total_sending = 0;
3813 ScopeProfiler sp(&g_profiler, "Server: selecting blocks for sending");
3815 for(core::map<u16, RemoteClient*>::Iterator
3816 i = m_clients.getIterator();
3817 i.atEnd() == false; i++)
3819 RemoteClient *client = i.getNode()->getValue();
3820 assert(client->peer_id == i.getNode()->getKey());
3822 total_sending += client->SendingCount();
3824 if(client->serialization_version == SER_FMT_VER_INVALID)
3827 client->GetNextBlocks(this, dtime, queue);
3832 // Lowest priority number comes first.
3833 // Lowest is most important.
3836 for(u32 i=0; i<queue.size(); i++)
3838 //TODO: Calculate limit dynamically
3839 if(total_sending >= g_settings.getS32
3840 ("max_simultaneous_block_sends_server_total"))
3843 PrioritySortedBlockTransfer q = queue[i];
3845 MapBlock *block = NULL;
3848 block = m_env.getMap().getBlockNoCreate(q.pos);
3850 catch(InvalidPositionException &e)
3855 RemoteClient *client = getClient(q.peer_id);
3857 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3859 client->SentBlock(q.pos);
3869 void Server::UpdateCrafting(u16 peer_id)
3871 DSTACK(__FUNCTION_NAME);
3873 Player* player = m_env.getPlayer(peer_id);
3877 Calculate crafting stuff
3879 if(g_settings.getBool("creative_mode") == false)
3881 InventoryList *clist = player->inventory.getList("craft");
3882 InventoryList *rlist = player->inventory.getList("craftresult");
3884 if(rlist->getUsedSlots() == 0)
3885 player->craftresult_is_preview = true;
3887 if(rlist && player->craftresult_is_preview)
3889 rlist->clearItems();
3891 if(clist && rlist && player->craftresult_is_preview)
3893 InventoryItem *items[9];
3894 for(u16 i=0; i<9; i++)
3896 items[i] = clist->getItem(i);
3899 // Get result of crafting grid
3900 InventoryItem *result = craft_get_result(items);
3902 rlist->addItem(result);
3905 } // if creative_mode == false
3908 RemoteClient* Server::getClient(u16 peer_id)
3910 DSTACK(__FUNCTION_NAME);
3911 //JMutexAutoLock lock(m_con_mutex);
3912 core::map<u16, RemoteClient*>::Node *n;
3913 n = m_clients.find(peer_id);
3914 // A client should exist for all peers
3916 return n->getValue();
3919 std::wstring Server::getStatusString()
3921 std::wostringstream os(std::ios_base::binary);
3924 os<<L"version="<<narrow_to_wide(VERSION_STRING);
3926 os<<L", uptime="<<m_uptime.get();
3927 // Information about clients
3929 for(core::map<u16, RemoteClient*>::Iterator
3930 i = m_clients.getIterator();
3931 i.atEnd() == false; i++)
3933 // Get client and check that it is valid
3934 RemoteClient *client = i.getNode()->getValue();
3935 assert(client->peer_id == i.getNode()->getKey());
3936 if(client->serialization_version == SER_FMT_VER_INVALID)
3939 Player *player = m_env.getPlayer(client->peer_id);
3940 // Get name of player
3941 std::wstring name = L"unknown";
3943 name = narrow_to_wide(player->getName());
3944 // Add name to information string
3948 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
3949 os<<" WARNING: Map saving is disabled."<<std::endl;
3953 v3f findSpawnPos(ServerMap &map)
3955 //return v3f(50,50,50)*BS;
3958 s16 groundheight = 0;
3961 nodepos = v2s16(0,0);
3966 // Try to find a good place a few times
3967 for(s32 i=0; i<1000; i++)
3970 // We're going to try to throw the player to this position
3971 nodepos = v2s16(-range + (myrand()%(range*2)),
3972 -range + (myrand()%(range*2)));
3973 v2s16 sectorpos = getNodeSectorPos(nodepos);
3974 // Get sector (NOTE: Don't get because it's slow)
3975 //m_env.getMap().emergeSector(sectorpos);
3976 // Get ground height at point (fallbacks to heightmap function)
3977 groundheight = map.findGroundLevel(nodepos);
3978 // Don't go underwater
3979 if(groundheight < WATER_LEVEL)
3981 //dstream<<"-> Underwater"<<std::endl;
3984 // Don't go to high places
3985 if(groundheight > WATER_LEVEL + 4)
3987 //dstream<<"-> Underwater"<<std::endl;
3991 // Found a good place
3992 //dstream<<"Searched through "<<i<<" places."<<std::endl;
3997 // If no suitable place was not found, go above water at least.
3998 if(groundheight < WATER_LEVEL)
3999 groundheight = WATER_LEVEL;
4001 return intToFloat(v3s16(
4008 Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id)
4011 Try to get an existing player
4013 Player *player = m_env.getPlayer(name);
4016 // If player is already connected, cancel
4017 if(player->peer_id != 0)
4019 dstream<<"emergePlayer(): Player already connected"<<std::endl;
4024 player->peer_id = peer_id;
4026 // Reset inventory to creative if in creative mode
4027 if(g_settings.getBool("creative_mode"))
4029 craft_set_creative_inventory(player);
4036 If player with the wanted peer_id already exists, cancel.
4038 if(m_env.getPlayer(peer_id) != NULL)
4040 dstream<<"emergePlayer(): Player with wrong name but same"
4041 " peer_id already exists"<<std::endl;
4049 player = new ServerRemotePlayer();
4050 //player->peer_id = c.peer_id;
4051 //player->peer_id = PEER_ID_INEXISTENT;
4052 player->peer_id = peer_id;
4053 player->updateName(name);
4054 m_authmanager.add(name);
4055 m_authmanager.setPassword(name, password);
4056 m_authmanager.setPrivs(name,
4057 stringToPrivs(g_settings.get("default_privs")));
4063 dstream<<"Server: Finding spawn place for player \""
4064 <<player->getName()<<"\""<<std::endl;
4066 v3f pos = findSpawnPos(m_env.getServerMap());
4068 player->setPosition(pos);
4071 Add player to environment
4074 m_env.addPlayer(player);
4077 Add stuff to inventory
4080 if(g_settings.getBool("creative_mode"))
4082 craft_set_creative_inventory(player);
4084 else if(g_settings.getBool("give_initial_stuff"))
4086 craft_give_initial_stuff(player);
4091 } // create new player
4094 void Server::handlePeerChange(PeerChange &c)
4096 JMutexAutoLock envlock(m_env_mutex);
4097 JMutexAutoLock conlock(m_con_mutex);
4099 if(c.type == PEER_ADDED)
4106 core::map<u16, RemoteClient*>::Node *n;
4107 n = m_clients.find(c.peer_id);
4108 // The client shouldn't already exist
4112 RemoteClient *client = new RemoteClient();
4113 client->peer_id = c.peer_id;
4114 m_clients.insert(client->peer_id, client);
4117 else if(c.type == PEER_REMOVED)
4124 core::map<u16, RemoteClient*>::Node *n;
4125 n = m_clients.find(c.peer_id);
4126 // The client should exist
4130 Mark objects to be not known by the client
4132 RemoteClient *client = n->getValue();
4134 for(core::map<u16, bool>::Iterator
4135 i = client->m_known_objects.getIterator();
4136 i.atEnd()==false; i++)
4139 u16 id = i.getNode()->getKey();
4140 ServerActiveObject* obj = m_env.getActiveObject(id);
4142 if(obj && obj->m_known_by_count > 0)
4143 obj->m_known_by_count--;
4146 // Collect information about leaving in chat
4147 std::wstring message;
4149 std::wstring name = L"unknown";
4150 Player *player = m_env.getPlayer(c.peer_id);
4152 name = narrow_to_wide(player->getName());
4156 message += L" left game";
4158 message += L" (timed out)";
4163 m_env.removePlayer(c.peer_id);
4166 // Set player client disconnected
4168 Player *player = m_env.getPlayer(c.peer_id);
4170 player->peer_id = 0;
4174 delete m_clients[c.peer_id];
4175 m_clients.remove(c.peer_id);
4177 // Send player info to all remaining clients
4180 // Send leave chat message to all remaining clients
4181 BroadcastChatMessage(message);
4190 void Server::handlePeerChanges()
4192 while(m_peer_change_queue.size() > 0)
4194 PeerChange c = m_peer_change_queue.pop_front();
4196 dout_server<<"Server: Handling peer change: "
4197 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4200 handlePeerChange(c);
4204 u64 Server::getPlayerPrivs(Player *player)
4208 std::string playername = player->getName();
4209 // Local player gets all privileges regardless of
4210 // what's set on their account.
4211 if(g_settings.get("name") == playername)
4217 return getPlayerAuthPrivs(playername);
4221 void dedicated_server_loop(Server &server, bool &kill)
4223 DSTACK(__FUNCTION_NAME);
4225 dstream<<DTIME<<std::endl;
4226 dstream<<"========================"<<std::endl;
4227 dstream<<"Running dedicated server"<<std::endl;
4228 dstream<<"========================"<<std::endl;
4231 IntervalLimiter m_profiler_interval;
4235 // This is kind of a hack but can be done like this
4236 // because server.step() is very light
4238 ScopeProfiler sp(&g_profiler, "dedicated server sleep");
4243 if(server.getShutdownRequested() || kill)
4245 dstream<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4252 float profiler_print_interval =
4253 g_settings.getFloat("profiler_print_interval");
4254 if(profiler_print_interval != 0)
4256 if(m_profiler_interval.step(0.030, profiler_print_interval))
4258 dstream<<"Profiler:"<<std::endl;
4259 g_profiler.print(dstream);
4267 static int counter = 0;
4273 core::list<PlayerInfo> list = server.getPlayerInfo();
4274 core::list<PlayerInfo>::Iterator i;
4275 static u32 sum_old = 0;
4276 u32 sum = PIChecksum(list);
4279 dstream<<DTIME<<"Player info:"<<std::endl;
4280 for(i=list.begin(); i!=list.end(); i++)
4282 i->PrintLine(&dstream);