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"
35 #define BLOCK_EMERGE_FLAG_FROMDISK (1<<0)
37 void * ServerThread::Thread()
41 DSTACK(__FUNCTION_NAME);
43 BEGIN_DEBUG_EXCEPTION_HANDLER
48 //TimeTaker timer("AsyncRunStep() + Receive()");
51 //TimeTaker timer("AsyncRunStep()");
52 m_server->AsyncRunStep();
55 //dout_server<<"Running m_server->Receive()"<<std::endl;
58 catch(con::NoIncomingDataException &e)
61 catch(con::PeerNotFoundException &e)
63 dout_server<<"Server: PeerNotFoundException"<<std::endl;
67 END_DEBUG_EXCEPTION_HANDLER
72 void * EmergeThread::Thread()
76 DSTACK(__FUNCTION_NAME);
80 BEGIN_DEBUG_EXCEPTION_HANDLER
83 Get block info from queue, emerge them and send them
86 After queue is empty, exit.
90 QueuedBlockEmerge *qptr = m_server->m_emerge_queue.pop();
94 SharedPtr<QueuedBlockEmerge> q(qptr);
100 Do not generate over-limit
102 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
103 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
104 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
105 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
106 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
107 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
110 //derr_server<<"EmergeThread::Thread(): running"<<std::endl;
112 //TimeTaker timer("block emerge");
115 Try to emerge it from somewhere.
117 If it is only wanted as optional, only loading from disk
122 Check if any peer wants it as non-optional. In that case it
125 Also decrement the emerge queue count in clients.
128 bool optional = true;
131 core::map<u16, u8>::Iterator i;
132 for(i=q->peer_ids.getIterator(); i.atEnd()==false; i++)
134 //u16 peer_id = i.getNode()->getKey();
137 u8 flags = i.getNode()->getValue();
138 if((flags & BLOCK_EMERGE_FLAG_FROMDISK) == false)
144 /*dstream<<"EmergeThread: p="
145 <<"("<<p.X<<","<<p.Y<<","<<p.Z<<") "
146 <<"optional="<<optional<<std::endl;*/
148 ServerMap &map = ((ServerMap&)m_server->m_env.getMap());
150 core::map<v3s16, MapBlock*> changed_blocks;
151 core::map<v3s16, MapBlock*> lighting_invalidated_blocks;
153 MapBlock *block = NULL;
154 bool got_block = true;
155 core::map<v3s16, MapBlock*> modified_blocks;
157 bool only_from_disk = false;
160 only_from_disk = true;
162 v2s16 chunkpos = map.sector_to_chunk(p2d);
164 bool generate_chunk = false;
165 if(only_from_disk == false)
167 JMutexAutoLock envlock(m_server->m_env_mutex);
168 if(map.chunkNonVolatile(chunkpos) == false)
169 generate_chunk = true;
176 JMutexAutoLock envlock(m_server->m_env_mutex);
177 map.initChunkMake(data, chunkpos);
183 JMutexAutoLock envlock(m_server->m_env_mutex);
184 map.finishChunkMake(data, changed_blocks);
189 Fetch block from map or generate a single block
192 JMutexAutoLock envlock(m_server->m_env_mutex);
194 // Load sector if it isn't loaded
195 if(map.getSectorNoGenerateNoEx(p2d) == NULL)
196 map.loadSectorFull(p2d);
198 block = map.getBlockNoCreateNoEx(p);
199 if(!block || block->isDummy())
207 // Get, load or create sector
208 ServerMapSector *sector =
209 (ServerMapSector*)map.createSector(p2d);
211 block = map.generateBlock(p, block, sector, changed_blocks,
212 lighting_invalidated_blocks);
219 if(block->getLightingExpired()){
220 lighting_invalidated_blocks[block->getPos()] = block;
224 // TODO: Some additional checking and lighting updating,
229 JMutexAutoLock envlock(m_server->m_env_mutex);
234 Collect a list of blocks that have been modified in
235 addition to the fetched one.
238 if(lighting_invalidated_blocks.size() > 0)
240 /*dstream<<"lighting "<<lighting_invalidated_blocks.size()
241 <<" blocks"<<std::endl;*/
243 // 50-100ms for single block generation
244 //TimeTaker timer("** EmergeThread updateLighting");
246 // Update lighting without locking the environment mutex,
247 // add modified blocks to changed blocks
248 map.updateLighting(lighting_invalidated_blocks, modified_blocks);
251 // Add all from changed_blocks to modified_blocks
252 for(core::map<v3s16, MapBlock*>::Iterator i = changed_blocks.getIterator();
253 i.atEnd() == false; i++)
255 MapBlock *block = i.getNode()->getValue();
256 modified_blocks.insert(block->getPos(), block);
259 // If we got no block, there should be no invalidated blocks
262 assert(lighting_invalidated_blocks.size() == 0);
268 Set sent status of modified blocks on clients
271 // NOTE: Server's clients are also behind the connection mutex
272 JMutexAutoLock lock(m_server->m_con_mutex);
275 Add the originally fetched block to the modified list
279 modified_blocks.insert(p, block);
283 Set the modified blocks unsent for all the clients
286 for(core::map<u16, RemoteClient*>::Iterator
287 i = m_server->m_clients.getIterator();
288 i.atEnd() == false; i++)
290 RemoteClient *client = i.getNode()->getValue();
292 if(modified_blocks.size() > 0)
294 // Remove block from sent history
295 client->SetBlocksNotSent(modified_blocks);
301 END_DEBUG_EXCEPTION_HANDLER
306 void RemoteClient::GetNextBlocks(Server *server, float dtime,
307 core::array<PrioritySortedBlockTransfer> &dest)
309 DSTACK(__FUNCTION_NAME);
312 TimeTaker timer("RemoteClient::GetNextBlocks", &timer_result);*/
315 m_nothing_to_send_pause_timer -= dtime;
317 if(m_nothing_to_send_pause_timer >= 0)
320 m_nearest_unsent_reset_timer = 0;
324 // Won't send anything if already sending
325 if(m_blocks_sending.size() >= g_settings.getU16
326 ("max_simultaneous_block_sends_per_client"))
328 //dstream<<"Not sending any blocks, Queue full."<<std::endl;
332 //TimeTaker timer("RemoteClient::GetNextBlocks");
334 Player *player = server->m_env.getPlayer(peer_id);
336 assert(player != NULL);
338 v3f playerpos = player->getPosition();
339 v3f playerspeed = player->getSpeed();
340 v3f playerspeeddir(0,0,0);
341 if(playerspeed.getLength() > 1.0*BS)
342 playerspeeddir = playerspeed / playerspeed.getLength();
343 // Predict to next block
344 v3f playerpos_predicted = playerpos + playerspeeddir*MAP_BLOCKSIZE*BS;
346 v3s16 center_nodepos = floatToInt(playerpos_predicted, BS);
348 v3s16 center = getNodeBlockPos(center_nodepos);
350 // Camera position and direction
352 playerpos + v3f(0, BS+BS/2, 0);
353 v3f camera_dir = v3f(0,0,1);
354 camera_dir.rotateYZBy(player->getPitch());
355 camera_dir.rotateXZBy(player->getYaw());
357 /*dstream<<"camera_dir=("<<camera_dir.X<<","<<camera_dir.Y<<","
358 <<camera_dir.Z<<")"<<std::endl;*/
361 Get the starting value of the block finder radius.
364 if(m_last_center != center)
366 m_nearest_unsent_d = 0;
367 m_last_center = center;
370 /*dstream<<"m_nearest_unsent_reset_timer="
371 <<m_nearest_unsent_reset_timer<<std::endl;*/
373 // This has to be incremented only when the nothing to send pause
375 m_nearest_unsent_reset_timer += dtime;
377 // Reset periodically to avoid possible bugs or other mishaps
378 if(m_nearest_unsent_reset_timer > 10.0)
380 m_nearest_unsent_reset_timer = 0;
381 m_nearest_unsent_d = 0;
382 /*dstream<<"Resetting m_nearest_unsent_d for "
383 <<server->getPlayerName(peer_id)<<std::endl;*/
386 //s16 last_nearest_unsent_d = m_nearest_unsent_d;
387 s16 d_start = m_nearest_unsent_d;
389 //dstream<<"d_start="<<d_start<<std::endl;
391 u16 max_simul_sends_setting = g_settings.getU16
392 ("max_simultaneous_block_sends_per_client");
393 u16 max_simul_sends_usually = max_simul_sends_setting;
396 Check the time from last addNode/removeNode.
398 Decrease send rate if player is building stuff.
400 m_time_from_building += dtime;
401 if(m_time_from_building < g_settings.getFloat(
402 "full_block_send_enable_min_time_from_building"))
404 max_simul_sends_usually
405 = LIMITED_MAX_SIMULTANEOUS_BLOCK_SENDS;
409 Number of blocks sending + number of blocks selected for sending
411 u32 num_blocks_selected = m_blocks_sending.size();
414 next time d will be continued from the d from which the nearest
415 unsent block was found this time.
417 This is because not necessarily any of the blocks found this
418 time are actually sent.
420 s32 new_nearest_unsent_d = -1;
422 s16 d_max = g_settings.getS16("max_block_send_distance");
423 s16 d_max_gen = g_settings.getS16("max_block_generate_distance");
425 // Don't loop very much at a time
426 if(d_max > d_start+1)
428 /*if(d_max_gen > d_start+2)
429 d_max_gen = d_start+2;*/
431 //dstream<<"Starting from "<<d_start<<std::endl;
433 bool sending_something = false;
435 bool no_blocks_found_for_sending = true;
437 bool queue_is_full = false;
440 for(d = d_start; d <= d_max; d++)
442 //dstream<<"RemoteClient::SendBlocks(): d="<<d<<std::endl;
445 If m_nearest_unsent_d was changed by the EmergeThread
446 (it can change it to 0 through SetBlockNotSent),
448 Else update m_nearest_unsent_d
450 /*if(m_nearest_unsent_d != last_nearest_unsent_d)
452 d = m_nearest_unsent_d;
453 last_nearest_unsent_d = m_nearest_unsent_d;
457 Get the border/face dot coordinates of a "d-radiused"
460 core::list<v3s16> list;
461 getFacePositions(list, d);
463 core::list<v3s16>::Iterator li;
464 for(li=list.begin(); li!=list.end(); li++)
466 v3s16 p = *li + center;
470 - Don't allow too many simultaneous transfers
471 - EXCEPT when the blocks are very close
473 Also, don't send blocks that are already flying.
476 // Start with the usual maximum
477 u16 max_simul_dynamic = max_simul_sends_usually;
479 // If block is very close, allow full maximum
480 if(d <= BLOCK_SEND_DISABLE_LIMITS_MAX_D)
481 max_simul_dynamic = max_simul_sends_setting;
483 // Don't select too many blocks for sending
484 if(num_blocks_selected >= max_simul_dynamic)
486 queue_is_full = true;
487 goto queue_full_break;
490 // Don't send blocks that are currently being transferred
491 if(m_blocks_sending.find(p) != NULL)
497 if(p.X < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
498 || p.X > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
499 || p.Y < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
500 || p.Y > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
501 || p.Z < -MAP_GENERATION_LIMIT / MAP_BLOCKSIZE
502 || p.Z > MAP_GENERATION_LIMIT / MAP_BLOCKSIZE)
505 // If this is true, inexistent block will be made from scratch
506 bool generate = d <= d_max_gen;
509 /*// Limit the generating area vertically to 2/3
510 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
513 // Limit the send area vertically to 2/3
514 if(abs(p.Y - center.Y) > d_max_gen - d_max_gen / 3)
520 If block is far away, don't generate it unless it is
526 // Block center y in nodes
527 f32 y = (f32)(p.Y * MAP_BLOCKSIZE + MAP_BLOCKSIZE/2);
528 // Don't generate if it's very high or very low
529 if(y < -64 || y > 64)
533 v2s16 p2d_nodes_center(
537 // Get ground height in nodes
538 s16 gh = server->m_env.getServerMap().findGroundLevel(
541 // If differs a lot, don't generate
542 if(fabs(gh - y) > MAP_BLOCKSIZE*2)
544 // Actually, don't even send it
550 //dstream<<"d="<<d<<std::endl;
553 Don't generate or send if not in sight
556 if(isBlockInSight(p, camera_pos, camera_dir, 10000*BS) == false)
562 Don't send already sent blocks
565 if(m_blocks_sent.find(p) != NULL)
572 Check if map has this block
574 MapBlock *block = server->m_env.getMap().getBlockNoCreateNoEx(p);
576 bool surely_not_found_on_disk = false;
577 bool block_is_invalid = false;
580 // Block is dummy if data doesn't exist.
581 // It means it has been not found from disk and not generated
584 surely_not_found_on_disk = true;
587 // Block is valid if lighting is up-to-date and data exists
588 if(block->isValid() == false)
590 block_is_invalid = true;
593 /*if(block->isFullyGenerated() == false)
595 block_is_invalid = true;
599 ServerMap *map = (ServerMap*)(&server->m_env.getMap());
600 v2s16 chunkpos = map->sector_to_chunk(p2d);
601 if(map->chunkNonVolatile(chunkpos) == false)
602 block_is_invalid = true;
605 If block is not close, don't send it unless it is near
608 Block is near ground level if night-time mesh
609 differs from day-time mesh.
613 if(block->dayNightDiffed() == false)
620 If block has been marked to not exist on disk (dummy)
621 and generating new ones is not wanted, skip block.
623 if(generate == false && surely_not_found_on_disk == true)
630 Record the lowest d from which a block has been
631 found being not sent and possibly to exist
633 if(no_blocks_found_for_sending)
636 new_nearest_unsent_d = d;
639 no_blocks_found_for_sending = false;
642 Add inexistent block to emerge queue.
644 if(block == NULL || surely_not_found_on_disk || block_is_invalid)
646 //TODO: Get value from somewhere
647 // Allow only one block in emerge queue
648 //if(server->m_emerge_queue.peerItemCount(peer_id) < 1)
649 if(server->m_emerge_queue.peerItemCount(peer_id) < 2)
651 //dstream<<"Adding block to emerge queue"<<std::endl;
653 // Add it to the emerge queue and trigger the thread
656 if(generate == false)
657 flags |= BLOCK_EMERGE_FLAG_FROMDISK;
659 server->m_emerge_queue.addBlock(peer_id, p, flags);
660 server->m_emergethread.trigger();
668 Add block to send queue
671 PrioritySortedBlockTransfer q((float)d, p, peer_id);
675 num_blocks_selected += 1;
676 sending_something = true;
681 //dstream<<"Stopped at "<<d<<std::endl;
683 if(no_blocks_found_for_sending)
685 if(queue_is_full == false)
686 new_nearest_unsent_d = d;
689 if(new_nearest_unsent_d != -1)
690 m_nearest_unsent_d = new_nearest_unsent_d;
692 if(sending_something == false)
694 m_nothing_to_send_counter++;
695 if((s16)m_nothing_to_send_counter >=
696 g_settings.getS16("max_block_send_distance"))
698 // Pause time in seconds
699 m_nothing_to_send_pause_timer = 1.0;
700 /*dstream<<"nothing to send to "
701 <<server->getPlayerName(peer_id)
702 <<" (d="<<d<<")"<<std::endl;*/
707 m_nothing_to_send_counter = 0;
710 /*timer_result = timer.stop(true);
711 if(timer_result != 0)
712 dstream<<"GetNextBlocks duration: "<<timer_result<<" (!=0)"<<std::endl;*/
715 void RemoteClient::SendObjectData(
718 core::map<v3s16, bool> &stepped_blocks
721 DSTACK(__FUNCTION_NAME);
723 // Can't send anything without knowing version
724 if(serialization_version == SER_FMT_VER_INVALID)
726 dstream<<"RemoteClient::SendObjectData(): Not sending, no version."
732 Send a TOCLIENT_OBJECTDATA packet.
736 u16 number of player positions
747 std::ostringstream os(std::ios_base::binary);
751 writeU16(buf, TOCLIENT_OBJECTDATA);
752 os.write((char*)buf, 2);
755 Get and write player data
758 // Get connected players
759 core::list<Player*> players = server->m_env.getPlayers(true);
761 // Write player count
762 u16 playercount = players.size();
763 writeU16(buf, playercount);
764 os.write((char*)buf, 2);
766 core::list<Player*>::Iterator i;
767 for(i = players.begin();
768 i != players.end(); i++)
772 v3f pf = player->getPosition();
773 v3f sf = player->getSpeed();
775 v3s32 position_i(pf.X*100, pf.Y*100, pf.Z*100);
776 v3s32 speed_i (sf.X*100, sf.Y*100, sf.Z*100);
777 s32 pitch_i (player->getPitch() * 100);
778 s32 yaw_i (player->getYaw() * 100);
780 writeU16(buf, player->peer_id);
781 os.write((char*)buf, 2);
782 writeV3S32(buf, position_i);
783 os.write((char*)buf, 12);
784 writeV3S32(buf, speed_i);
785 os.write((char*)buf, 12);
786 writeS32(buf, pitch_i);
787 os.write((char*)buf, 4);
788 writeS32(buf, yaw_i);
789 os.write((char*)buf, 4);
793 Get and write object data
799 For making players to be able to build to their nearby
800 environment (building is not possible on blocks that are not
803 - Add blocks to emerge queue if they are not found
805 SUGGESTION: These could be ignored from the backside of the player
808 Player *player = server->m_env.getPlayer(peer_id);
812 v3f playerpos = player->getPosition();
813 v3f playerspeed = player->getSpeed();
815 v3s16 center_nodepos = floatToInt(playerpos, BS);
816 v3s16 center = getNodeBlockPos(center_nodepos);
818 s16 d_max = g_settings.getS16("active_object_range");
820 // Number of blocks whose objects were written to bos
823 std::ostringstream bos(std::ios_base::binary);
825 for(s16 d = 0; d <= d_max; d++)
827 core::list<v3s16> list;
828 getFacePositions(list, d);
830 core::list<v3s16>::Iterator li;
831 for(li=list.begin(); li!=list.end(); li++)
833 v3s16 p = *li + center;
836 Ignore blocks that haven't been sent to the client
839 if(m_blocks_sent.find(p) == NULL)
843 // Try stepping block and add it to a send queue
848 MapBlock *block = server->m_env.getMap().getBlockNoCreate(p);
851 Step block if not in stepped_blocks and add to stepped_blocks.
853 if(stepped_blocks.find(p) == NULL)
855 block->stepObjects(dtime, true, server->m_env.getDayNightRatio());
856 stepped_blocks.insert(p, true);
857 block->setChangedFlag();
860 // Skip block if there are no objects
861 if(block->getObjectCount() == 0)
870 bos.write((char*)buf, 6);
873 block->serializeObjects(bos, serialization_version);
878 Stop collecting objects if data is already too big
880 // Sum of player and object data sizes
881 s32 sum = (s32)os.tellp() + 2 + (s32)bos.tellp();
882 // break out if data too big
883 if(sum > MAX_OBJECTDATA_SIZE)
885 goto skip_subsequent;
889 catch(InvalidPositionException &e)
892 // Add it to the emerge queue and trigger the thread.
893 // Fetch the block only if it is on disk.
895 // Grab and increment counter
896 /*SharedPtr<JMutexAutoLock> lock
897 (m_num_blocks_in_emerge_queue.getLock());
898 m_num_blocks_in_emerge_queue.m_value++;*/
900 // Add to queue as an anonymous fetch from disk
901 u8 flags = BLOCK_EMERGE_FLAG_FROMDISK;
902 server->m_emerge_queue.addBlock(0, p, flags);
903 server->m_emergethread.trigger();
911 writeU16(buf, blockcount);
912 os.write((char*)buf, 2);
914 // Write block objects
921 //dstream<<"Server: Sending object data to "<<peer_id<<std::endl;
924 std::string s = os.str();
925 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
926 // Send as unreliable
927 server->m_con.Send(peer_id, 0, data, false);
930 void RemoteClient::GotBlock(v3s16 p)
932 if(m_blocks_sending.find(p) != NULL)
933 m_blocks_sending.remove(p);
936 /*dstream<<"RemoteClient::GotBlock(): Didn't find in"
937 " m_blocks_sending"<<std::endl;*/
938 m_excess_gotblocks++;
940 m_blocks_sent.insert(p, true);
943 void RemoteClient::SentBlock(v3s16 p)
945 if(m_blocks_sending.find(p) == NULL)
946 m_blocks_sending.insert(p, 0.0);
948 dstream<<"RemoteClient::SentBlock(): Sent block"
949 " already in m_blocks_sending"<<std::endl;
952 void RemoteClient::SetBlockNotSent(v3s16 p)
954 m_nearest_unsent_d = 0;
956 if(m_blocks_sending.find(p) != NULL)
957 m_blocks_sending.remove(p);
958 if(m_blocks_sent.find(p) != NULL)
959 m_blocks_sent.remove(p);
962 void RemoteClient::SetBlocksNotSent(core::map<v3s16, MapBlock*> &blocks)
964 m_nearest_unsent_d = 0;
966 for(core::map<v3s16, MapBlock*>::Iterator
967 i = blocks.getIterator();
968 i.atEnd()==false; i++)
970 v3s16 p = i.getNode()->getKey();
972 if(m_blocks_sending.find(p) != NULL)
973 m_blocks_sending.remove(p);
974 if(m_blocks_sent.find(p) != NULL)
975 m_blocks_sent.remove(p);
983 PlayerInfo::PlayerInfo()
989 void PlayerInfo::PrintLine(std::ostream *s)
992 (*s)<<"\""<<name<<"\" ("
993 <<(position.X/10)<<","<<(position.Y/10)
994 <<","<<(position.Z/10)<<") ";
996 (*s)<<" avg_rtt="<<avg_rtt;
1000 u32 PIChecksum(core::list<PlayerInfo> &l)
1002 core::list<PlayerInfo>::Iterator i;
1005 for(i=l.begin(); i!=l.end(); i++)
1007 checksum += a * (i->id+1);
1008 checksum ^= 0x435aafcd;
1019 std::string mapsavedir
1021 m_env(new ServerMap(mapsavedir), this),
1022 m_con(PROTOCOL_ID, 512, CONNECTION_TIMEOUT, this),
1023 m_authmanager(mapsavedir+"/auth.txt"),
1025 m_emergethread(this),
1027 m_time_of_day_send_timer(0),
1029 m_mapsavedir(mapsavedir),
1030 m_shutdown_requested(false),
1031 m_ignore_map_edit_events(false),
1032 m_ignore_map_edit_events_peer_id(0)
1034 m_liquid_transform_timer = 0.0;
1035 m_print_info_timer = 0.0;
1036 m_objectdata_timer = 0.0;
1037 m_emergethread_trigger_timer = 0.0;
1038 m_savemap_timer = 0.0;
1042 m_step_dtime_mutex.Init();
1045 // Register us to receive map edit events
1046 m_env.getMap().addEventReceiver(this);
1048 // If file exists, load environment metadata
1049 if(fs::PathExists(m_mapsavedir+"/env_meta.txt"))
1051 dstream<<"Server: Loading environment metadata"<<std::endl;
1052 m_env.loadMeta(m_mapsavedir);
1056 dstream<<"Server: Loading players"<<std::endl;
1057 m_env.deSerializePlayers(m_mapsavedir);
1062 dstream<<"Server::~Server()"<<std::endl;
1065 Send shutdown message
1068 JMutexAutoLock conlock(m_con_mutex);
1070 std::wstring line = L"*** Server shutting down";
1073 Send the message to clients
1075 for(core::map<u16, RemoteClient*>::Iterator
1076 i = m_clients.getIterator();
1077 i.atEnd() == false; i++)
1079 // Get client and check that it is valid
1080 RemoteClient *client = i.getNode()->getValue();
1081 assert(client->peer_id == i.getNode()->getKey());
1082 if(client->serialization_version == SER_FMT_VER_INVALID)
1086 SendChatMessage(client->peer_id, line);
1088 catch(con::PeerNotFoundException &e)
1096 dstream<<"Server: Saving players"<<std::endl;
1097 m_env.serializePlayers(m_mapsavedir);
1100 Save environment metadata
1102 dstream<<"Server: Saving environment metadata"<<std::endl;
1103 m_env.saveMeta(m_mapsavedir);
1114 JMutexAutoLock clientslock(m_con_mutex);
1116 for(core::map<u16, RemoteClient*>::Iterator
1117 i = m_clients.getIterator();
1118 i.atEnd() == false; i++)
1121 // NOTE: These are removed by env destructor
1123 u16 peer_id = i.getNode()->getKey();
1124 JMutexAutoLock envlock(m_env_mutex);
1125 m_env.removePlayer(peer_id);
1129 delete i.getNode()->getValue();
1134 void Server::start(unsigned short port)
1136 DSTACK(__FUNCTION_NAME);
1137 // Stop thread if already running
1140 // Initialize connection
1141 m_con.setTimeoutMs(30);
1145 m_thread.setRun(true);
1148 dout_server<<"Server: Started on port "<<port<<std::endl;
1153 DSTACK(__FUNCTION_NAME);
1155 // Stop threads (set run=false first so both start stopping)
1156 m_thread.setRun(false);
1157 m_emergethread.setRun(false);
1159 m_emergethread.stop();
1161 dout_server<<"Server: Threads stopped"<<std::endl;
1164 void Server::step(float dtime)
1166 DSTACK(__FUNCTION_NAME);
1171 JMutexAutoLock lock(m_step_dtime_mutex);
1172 m_step_dtime += dtime;
1176 void Server::AsyncRunStep()
1178 DSTACK(__FUNCTION_NAME);
1182 JMutexAutoLock lock1(m_step_dtime_mutex);
1183 dtime = m_step_dtime;
1187 ScopeProfiler sp(&g_profiler, "Server: selecting and sending "
1188 "blocks to clients");
1189 // Send blocks to clients
1196 //dstream<<"Server steps "<<dtime<<std::endl;
1197 //dstream<<"Server::AsyncRunStep(): dtime="<<dtime<<std::endl;
1200 JMutexAutoLock lock1(m_step_dtime_mutex);
1201 m_step_dtime -= dtime;
1208 m_uptime.set(m_uptime.get() + dtime);
1212 Update m_time_of_day and overall game time
1215 JMutexAutoLock envlock(m_env_mutex);
1217 m_time_counter += dtime;
1218 f32 speed = g_settings.getFloat("time_speed") * 24000./(24.*3600);
1219 u32 units = (u32)(m_time_counter*speed);
1220 m_time_counter -= (f32)units / speed;
1222 m_env.setTimeOfDay((m_env.getTimeOfDay() + units) % 24000);
1224 //dstream<<"Server: m_time_of_day = "<<m_time_of_day.get()<<std::endl;
1227 Send to clients at constant intervals
1230 m_time_of_day_send_timer -= dtime;
1231 if(m_time_of_day_send_timer < 0.0)
1233 m_time_of_day_send_timer = g_settings.getFloat("time_send_interval");
1235 //JMutexAutoLock envlock(m_env_mutex);
1236 JMutexAutoLock conlock(m_con_mutex);
1238 for(core::map<u16, RemoteClient*>::Iterator
1239 i = m_clients.getIterator();
1240 i.atEnd() == false; i++)
1242 RemoteClient *client = i.getNode()->getValue();
1243 //Player *player = m_env.getPlayer(client->peer_id);
1245 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
1246 m_env.getTimeOfDay());
1248 m_con.Send(client->peer_id, 0, data, true);
1254 // Process connection's timeouts
1255 JMutexAutoLock lock2(m_con_mutex);
1256 ScopeProfiler sp(&g_profiler, "Server: connection timeout processing");
1257 m_con.RunTimeouts(dtime);
1261 // This has to be called so that the client list gets synced
1262 // with the peer list of the connection
1263 ScopeProfiler sp(&g_profiler, "Server: peer change handling");
1264 handlePeerChanges();
1269 // This also runs Map's timers
1270 JMutexAutoLock lock(m_env_mutex);
1271 ScopeProfiler sp(&g_profiler, "Server: environment step");
1282 m_liquid_transform_timer += dtime;
1283 if(m_liquid_transform_timer >= 1.00)
1285 m_liquid_transform_timer -= 1.00;
1287 JMutexAutoLock lock(m_env_mutex);
1289 ScopeProfiler sp(&g_profiler, "Server: liquid transform");
1291 core::map<v3s16, MapBlock*> modified_blocks;
1292 m_env.getMap().transformLiquids(modified_blocks);
1297 core::map<v3s16, MapBlock*> lighting_modified_blocks;
1298 ServerMap &map = ((ServerMap&)m_env.getMap());
1299 map.updateLighting(modified_blocks, lighting_modified_blocks);
1301 // Add blocks modified by lighting to modified_blocks
1302 for(core::map<v3s16, MapBlock*>::Iterator
1303 i = lighting_modified_blocks.getIterator();
1304 i.atEnd() == false; i++)
1306 MapBlock *block = i.getNode()->getValue();
1307 modified_blocks.insert(block->getPos(), block);
1311 Set the modified blocks unsent for all the clients
1314 JMutexAutoLock lock2(m_con_mutex);
1316 for(core::map<u16, RemoteClient*>::Iterator
1317 i = m_clients.getIterator();
1318 i.atEnd() == false; i++)
1320 RemoteClient *client = i.getNode()->getValue();
1322 if(modified_blocks.size() > 0)
1324 // Remove block from sent history
1325 client->SetBlocksNotSent(modified_blocks);
1330 // Periodically print some info
1332 float &counter = m_print_info_timer;
1338 JMutexAutoLock lock2(m_con_mutex);
1340 for(core::map<u16, RemoteClient*>::Iterator
1341 i = m_clients.getIterator();
1342 i.atEnd() == false; i++)
1344 //u16 peer_id = i.getNode()->getKey();
1345 RemoteClient *client = i.getNode()->getValue();
1346 Player *player = m_env.getPlayer(client->peer_id);
1349 std::cout<<player->getName()<<"\t";
1350 client->PrintInfo(std::cout);
1355 //if(g_settings.getBool("enable_experimental"))
1359 Check added and deleted active objects
1362 //dstream<<"Server: Checking added and deleted active objects"<<std::endl;
1363 JMutexAutoLock envlock(m_env_mutex);
1364 JMutexAutoLock conlock(m_con_mutex);
1366 ScopeProfiler sp(&g_profiler, "Server: checking added and deleted objects");
1368 // Radius inside which objects are active
1371 for(core::map<u16, RemoteClient*>::Iterator
1372 i = m_clients.getIterator();
1373 i.atEnd() == false; i++)
1375 RemoteClient *client = i.getNode()->getValue();
1376 Player *player = m_env.getPlayer(client->peer_id);
1379 dstream<<"WARNING: "<<__FUNCTION_NAME<<": Client "<<client->peer_id
1380 <<" has no associated player"<<std::endl;
1383 v3s16 pos = floatToInt(player->getPosition(), BS);
1385 core::map<u16, bool> removed_objects;
1386 core::map<u16, bool> added_objects;
1387 m_env.getRemovedActiveObjects(pos, radius,
1388 client->m_known_objects, removed_objects);
1389 m_env.getAddedActiveObjects(pos, radius,
1390 client->m_known_objects, added_objects);
1392 // Ignore if nothing happened
1393 if(removed_objects.size() == 0 && added_objects.size() == 0)
1395 //dstream<<"INFO: active objects: none changed"<<std::endl;
1399 std::string data_buffer;
1403 // Handle removed objects
1404 writeU16((u8*)buf, removed_objects.size());
1405 data_buffer.append(buf, 2);
1406 for(core::map<u16, bool>::Iterator
1407 i = removed_objects.getIterator();
1408 i.atEnd()==false; i++)
1411 u16 id = i.getNode()->getKey();
1412 ServerActiveObject* obj = m_env.getActiveObject(id);
1414 // Add to data buffer for sending
1415 writeU16((u8*)buf, i.getNode()->getKey());
1416 data_buffer.append(buf, 2);
1418 // Remove from known objects
1419 client->m_known_objects.remove(i.getNode()->getKey());
1421 if(obj && obj->m_known_by_count > 0)
1422 obj->m_known_by_count--;
1425 // Handle added objects
1426 writeU16((u8*)buf, added_objects.size());
1427 data_buffer.append(buf, 2);
1428 for(core::map<u16, bool>::Iterator
1429 i = added_objects.getIterator();
1430 i.atEnd()==false; i++)
1433 u16 id = i.getNode()->getKey();
1434 ServerActiveObject* obj = m_env.getActiveObject(id);
1437 u8 type = ACTIVEOBJECT_TYPE_INVALID;
1439 dstream<<"WARNING: "<<__FUNCTION_NAME
1440 <<": NULL object"<<std::endl;
1442 type = obj->getType();
1444 // Add to data buffer for sending
1445 writeU16((u8*)buf, id);
1446 data_buffer.append(buf, 2);
1447 writeU8((u8*)buf, type);
1448 data_buffer.append(buf, 1);
1451 data_buffer.append(serializeLongString(
1452 obj->getClientInitializationData()));
1454 data_buffer.append(serializeLongString(""));
1456 // Add to known objects
1457 client->m_known_objects.insert(i.getNode()->getKey(), false);
1460 obj->m_known_by_count++;
1464 SharedBuffer<u8> reply(2 + data_buffer.size());
1465 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_REMOVE_ADD);
1466 memcpy((char*)&reply[2], data_buffer.c_str(),
1467 data_buffer.size());
1469 m_con.Send(client->peer_id, 0, reply, true);
1471 dstream<<"INFO: Server: Sent object remove/add: "
1472 <<removed_objects.size()<<" removed, "
1473 <<added_objects.size()<<" added, "
1474 <<"packet size is "<<reply.getSize()<<std::endl;
1479 Collect a list of all the objects known by the clients
1480 and report it back to the environment.
1483 core::map<u16, bool> all_known_objects;
1485 for(core::map<u16, RemoteClient*>::Iterator
1486 i = m_clients.getIterator();
1487 i.atEnd() == false; i++)
1489 RemoteClient *client = i.getNode()->getValue();
1490 // Go through all known objects of client
1491 for(core::map<u16, bool>::Iterator
1492 i = client->m_known_objects.getIterator();
1493 i.atEnd()==false; i++)
1495 u16 id = i.getNode()->getKey();
1496 all_known_objects[id] = true;
1500 m_env.setKnownActiveObjects(whatever);
1506 Send object messages
1509 JMutexAutoLock envlock(m_env_mutex);
1510 JMutexAutoLock conlock(m_con_mutex);
1512 ScopeProfiler sp(&g_profiler, "Server: sending object messages");
1515 // Value = data sent by object
1516 core::map<u16, core::list<ActiveObjectMessage>* > buffered_messages;
1518 // Get active object messages from environment
1521 ActiveObjectMessage aom = m_env.getActiveObjectMessage();
1525 core::list<ActiveObjectMessage>* message_list = NULL;
1526 core::map<u16, core::list<ActiveObjectMessage>* >::Node *n;
1527 n = buffered_messages.find(aom.id);
1530 message_list = new core::list<ActiveObjectMessage>;
1531 buffered_messages.insert(aom.id, message_list);
1535 message_list = n->getValue();
1537 message_list->push_back(aom);
1540 // Route data to every client
1541 for(core::map<u16, RemoteClient*>::Iterator
1542 i = m_clients.getIterator();
1543 i.atEnd()==false; i++)
1545 RemoteClient *client = i.getNode()->getValue();
1546 std::string reliable_data;
1547 std::string unreliable_data;
1548 // Go through all objects in message buffer
1549 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1550 j = buffered_messages.getIterator();
1551 j.atEnd()==false; j++)
1553 // If object is not known by client, skip it
1554 u16 id = j.getNode()->getKey();
1555 if(client->m_known_objects.find(id) == NULL)
1557 // Get message list of object
1558 core::list<ActiveObjectMessage>* list = j.getNode()->getValue();
1559 // Go through every message
1560 for(core::list<ActiveObjectMessage>::Iterator
1561 k = list->begin(); k != list->end(); k++)
1563 // Compose the full new data with header
1564 ActiveObjectMessage aom = *k;
1565 std::string new_data;
1568 writeU16((u8*)&buf[0], aom.id);
1569 new_data.append(buf, 2);
1571 new_data += serializeString(aom.datastring);
1572 // Add data to buffer
1574 reliable_data += new_data;
1576 unreliable_data += new_data;
1580 reliable_data and unreliable_data are now ready.
1583 if(reliable_data.size() > 0)
1585 SharedBuffer<u8> reply(2 + reliable_data.size());
1586 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1587 memcpy((char*)&reply[2], reliable_data.c_str(),
1588 reliable_data.size());
1590 m_con.Send(client->peer_id, 0, reply, true);
1592 if(unreliable_data.size() > 0)
1594 SharedBuffer<u8> reply(2 + unreliable_data.size());
1595 writeU16(&reply[0], TOCLIENT_ACTIVE_OBJECT_MESSAGES);
1596 memcpy((char*)&reply[2], unreliable_data.c_str(),
1597 unreliable_data.size());
1598 // Send as unreliable
1599 m_con.Send(client->peer_id, 0, reply, false);
1602 /*if(reliable_data.size() > 0 || unreliable_data.size() > 0)
1604 dstream<<"INFO: Server: Size of object message data: "
1605 <<"reliable: "<<reliable_data.size()
1606 <<", unreliable: "<<unreliable_data.size()
1611 // Clear buffered_messages
1612 for(core::map<u16, core::list<ActiveObjectMessage>* >::Iterator
1613 i = buffered_messages.getIterator();
1614 i.atEnd()==false; i++)
1616 delete i.getNode()->getValue();
1620 } // enable_experimental
1623 Send queued-for-sending map edit events.
1626 while(m_unsent_map_edit_queue.size() != 0)
1628 MapEditEvent* event = m_unsent_map_edit_queue.pop_front();
1630 if(event->type == MEET_ADDNODE)
1632 dstream<<"Server: MEET_ADDNODE"<<std::endl;
1633 sendAddNode(event->p, event->n, event->already_known_by_peer);
1635 else if(event->type == MEET_REMOVENODE)
1637 dstream<<"Server: MEET_REMOVENODE"<<std::endl;
1638 sendRemoveNode(event->p, event->already_known_by_peer);
1640 else if(event->type == MEET_OTHER)
1642 dstream<<"WARNING: Server: MEET_OTHER not implemented"
1647 dstream<<"WARNING: Server: Unknown MapEditEvent "
1648 <<((u32)event->type)<<std::endl;
1656 Send object positions
1657 TODO: Get rid of MapBlockObjects
1660 float &counter = m_objectdata_timer;
1662 if(counter >= g_settings.getFloat("objectdata_interval"))
1664 JMutexAutoLock lock1(m_env_mutex);
1665 JMutexAutoLock lock2(m_con_mutex);
1667 ScopeProfiler sp(&g_profiler, "Server: sending mbo positions");
1669 SendObjectData(counter);
1679 //TimeTaker timer("Step node metadata");
1681 JMutexAutoLock envlock(m_env_mutex);
1682 JMutexAutoLock conlock(m_con_mutex);
1684 ScopeProfiler sp(&g_profiler, "Server: stepping node metadata");
1686 core::map<v3s16, MapBlock*> changed_blocks;
1687 m_env.getMap().nodeMetadataStep(dtime, changed_blocks);
1689 for(core::map<v3s16, MapBlock*>::Iterator
1690 i = changed_blocks.getIterator();
1691 i.atEnd() == false; i++)
1693 MapBlock *block = i.getNode()->getValue();
1695 for(core::map<u16, RemoteClient*>::Iterator
1696 i = m_clients.getIterator();
1697 i.atEnd()==false; i++)
1699 RemoteClient *client = i.getNode()->getValue();
1700 client->SetBlockNotSent(block->getPos());
1706 Trigger emergethread (it somehow gets to a non-triggered but
1707 bysy state sometimes)
1710 float &counter = m_emergethread_trigger_timer;
1716 m_emergethread.trigger();
1720 // Save map, players and auth stuff
1722 float &counter = m_savemap_timer;
1724 if(counter >= g_settings.getFloat("server_map_save_interval"))
1728 ScopeProfiler sp(&g_profiler, "Server: saving stuff");
1731 if(m_authmanager.isModified())
1732 m_authmanager.save();
1735 JMutexAutoLock lock(m_env_mutex);
1736 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == true)
1738 // Save only changed parts
1739 m_env.getMap().save(true);
1741 // Delete unused sectors
1742 u32 deleted_count = m_env.getMap().deleteUnusedSectors(
1743 g_settings.getFloat("server_unload_unused_sectors_timeout"));
1744 if(deleted_count > 0)
1746 dout_server<<"Server: Unloaded "<<deleted_count
1747 <<" sectors from memory"<<std::endl;
1751 m_env.serializePlayers(m_mapsavedir);
1753 // Save environment metadata
1754 m_env.saveMeta(m_mapsavedir);
1760 void Server::Receive()
1762 DSTACK(__FUNCTION_NAME);
1763 u32 data_maxsize = 10000;
1764 Buffer<u8> data(data_maxsize);
1769 JMutexAutoLock conlock(m_con_mutex);
1770 datasize = m_con.Receive(peer_id, *data, data_maxsize);
1773 // This has to be called so that the client list gets synced
1774 // with the peer list of the connection
1775 handlePeerChanges();
1777 ProcessData(*data, datasize, peer_id);
1779 catch(con::InvalidIncomingDataException &e)
1781 derr_server<<"Server::Receive(): "
1782 "InvalidIncomingDataException: what()="
1783 <<e.what()<<std::endl;
1785 catch(con::PeerNotFoundException &e)
1787 //NOTE: This is not needed anymore
1789 // The peer has been disconnected.
1790 // Find the associated player and remove it.
1792 /*JMutexAutoLock envlock(m_env_mutex);
1794 dout_server<<"ServerThread: peer_id="<<peer_id
1795 <<" has apparently closed connection. "
1796 <<"Removing player."<<std::endl;
1798 m_env.removePlayer(peer_id);*/
1802 void Server::ProcessData(u8 *data, u32 datasize, u16 peer_id)
1804 DSTACK(__FUNCTION_NAME);
1805 // Environment is locked first.
1806 JMutexAutoLock envlock(m_env_mutex);
1807 JMutexAutoLock conlock(m_con_mutex);
1811 peer = m_con.GetPeer(peer_id);
1813 catch(con::PeerNotFoundException &e)
1815 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: peer "
1816 <<peer_id<<" not found"<<std::endl;
1820 u8 peer_ser_ver = getClient(peer->id)->serialization_version;
1828 ToServerCommand command = (ToServerCommand)readU16(&data[0]);
1830 if(command == TOSERVER_INIT)
1832 // [0] u16 TOSERVER_INIT
1833 // [2] u8 SER_FMT_VER_HIGHEST
1834 // [3] u8[20] player_name
1835 // [23] u8[28] password <--- can be sent without this, from old versions
1837 if(datasize < 2+1+PLAYERNAME_SIZE)
1840 derr_server<<DTIME<<"Server: Got TOSERVER_INIT from "
1841 <<peer->id<<std::endl;
1843 // First byte after command is maximum supported
1844 // serialization version
1845 u8 client_max = data[2];
1846 u8 our_max = SER_FMT_VER_HIGHEST;
1847 // Use the highest version supported by both
1848 u8 deployed = core::min_(client_max, our_max);
1849 // If it's lower than the lowest supported, give up.
1850 if(deployed < SER_FMT_VER_LOWEST)
1851 deployed = SER_FMT_VER_INVALID;
1853 //peer->serialization_version = deployed;
1854 getClient(peer->id)->pending_serialization_version = deployed;
1856 if(deployed == SER_FMT_VER_INVALID)
1858 derr_server<<DTIME<<"Server: Cannot negotiate "
1859 "serialization version with peer "
1860 <<peer_id<<std::endl;
1869 char playername[PLAYERNAME_SIZE];
1870 for(u32 i=0; i<PLAYERNAME_SIZE-1; i++)
1872 playername[i] = data[3+i];
1874 playername[PLAYERNAME_SIZE-1] = 0;
1876 if(playername[0]=='\0')
1878 derr_server<<DTIME<<"Server: Player has empty name"<<std::endl;
1879 SendAccessDenied(m_con, peer_id,
1884 if(string_allowed(playername, PLAYERNAME_ALLOWED_CHARS)==false)
1886 derr_server<<DTIME<<"Server: Player has invalid name"<<std::endl;
1887 SendAccessDenied(m_con, peer_id,
1888 L"Name contains unallowed characters");
1893 char password[PASSWORD_SIZE];
1894 if(datasize == 2+1+PLAYERNAME_SIZE)
1896 // old version - assume blank password
1901 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
1903 password[i] = data[23+i];
1905 password[PASSWORD_SIZE-1] = 0;
1908 std::string checkpwd;
1909 if(m_authmanager.exists(playername))
1911 checkpwd = m_authmanager.getPassword(playername);
1915 checkpwd = g_settings.get("default_password");
1918 if(password != checkpwd)
1920 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1921 <<": supplied invalid password for "
1922 <<playername<<std::endl;
1923 SendAccessDenied(m_con, peer_id, L"Invalid password");
1927 // Add player to auth manager
1928 if(m_authmanager.exists(playername) == false)
1930 derr_server<<DTIME<<"Server: adding player "<<playername
1931 <<" to auth manager"<<std::endl;
1932 m_authmanager.add(playername);
1933 m_authmanager.setPassword(playername, checkpwd);
1934 m_authmanager.setPrivs(playername,
1935 stringToPrivs(g_settings.get("default_privs")));
1936 m_authmanager.save();
1940 Player *player = emergePlayer(playername, password, peer_id);
1944 // DEBUG: Test serialization
1945 std::ostringstream test_os;
1946 player->serialize(test_os);
1947 dstream<<"Player serialization test: \""<<test_os.str()
1949 std::istringstream test_is(test_os.str());
1950 player->deSerialize(test_is);
1953 // If failed, cancel
1956 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1957 <<": failed to emerge player"<<std::endl;
1962 // If a client is already connected to the player, cancel
1963 if(player->peer_id != 0)
1965 derr_server<<DTIME<<"Server: peer_id="<<peer_id
1966 <<" tried to connect to "
1967 "an already connected player (peer_id="
1968 <<player->peer_id<<")"<<std::endl;
1971 // Set client of player
1972 player->peer_id = peer_id;
1975 // Check if player doesn't exist
1977 throw con::InvalidIncomingDataException
1978 ("Server::ProcessData(): INIT: Player doesn't exist");
1980 /*// update name if it was supplied
1981 if(datasize >= 20+3)
1984 player->updateName((const char*)&data[3]);
1988 Answer with a TOCLIENT_INIT
1991 SharedBuffer<u8> reply(2+1+6+8);
1992 writeU16(&reply[0], TOCLIENT_INIT);
1993 writeU8(&reply[2], deployed);
1994 writeV3S16(&reply[2+1], floatToInt(player->getPosition()+v3f(0,BS/2,0), BS));
1995 //writeU64(&reply[2+1+6], m_env.getServerMap().getSeed());
1996 writeU64(&reply[2+1+6], 0); // no seed
1999 m_con.Send(peer_id, 0, reply, true);
2003 Send complete position information
2005 SendMovePlayer(player);
2010 if(command == TOSERVER_INIT2)
2012 derr_server<<DTIME<<"Server: Got TOSERVER_INIT2 from "
2013 <<peer->id<<std::endl;
2016 getClient(peer->id)->serialization_version
2017 = getClient(peer->id)->pending_serialization_version;
2020 Send some initialization data
2023 // Send player info to all players
2026 // Send inventory to player
2027 UpdateCrafting(peer->id);
2028 SendInventory(peer->id);
2032 Player *player = m_env.getPlayer(peer_id);
2033 SendPlayerHP(player);
2038 SharedBuffer<u8> data = makePacket_TOCLIENT_TIME_OF_DAY(
2039 m_env.getTimeOfDay());
2040 m_con.Send(peer->id, 0, data, true);
2043 // Send information about server to player in chat
2044 SendChatMessage(peer_id, getStatusString());
2046 // Send information about joining in chat
2048 std::wstring name = L"unknown";
2049 Player *player = m_env.getPlayer(peer_id);
2051 name = narrow_to_wide(player->getName());
2053 std::wstring message;
2056 message += L" joined game";
2057 BroadcastChatMessage(message);
2063 if(peer_ser_ver == SER_FMT_VER_INVALID)
2065 derr_server<<DTIME<<"Server::ProcessData(): Cancelling: Peer"
2066 " serialization format invalid or not initialized."
2067 " Skipping incoming command="<<command<<std::endl;
2071 Player *player = m_env.getPlayer(peer_id);
2074 derr_server<<"Server::ProcessData(): Cancelling: "
2075 "No player for peer_id="<<peer_id
2079 if(command == TOSERVER_PLAYERPOS)
2081 if(datasize < 2+12+12+4+4)
2085 v3s32 ps = readV3S32(&data[start+2]);
2086 v3s32 ss = readV3S32(&data[start+2+12]);
2087 f32 pitch = (f32)readS32(&data[2+12+12]) / 100.0;
2088 f32 yaw = (f32)readS32(&data[2+12+12+4]) / 100.0;
2089 v3f position((f32)ps.X/100., (f32)ps.Y/100., (f32)ps.Z/100.);
2090 v3f speed((f32)ss.X/100., (f32)ss.Y/100., (f32)ss.Z/100.);
2091 pitch = wrapDegrees(pitch);
2092 yaw = wrapDegrees(yaw);
2093 player->setPosition(position);
2094 player->setSpeed(speed);
2095 player->setPitch(pitch);
2096 player->setYaw(yaw);
2098 /*dout_server<<"Server::ProcessData(): Moved player "<<peer_id<<" to "
2099 <<"("<<position.X<<","<<position.Y<<","<<position.Z<<")"
2100 <<" pitch="<<pitch<<" yaw="<<yaw<<std::endl;*/
2102 else if(command == TOSERVER_GOTBLOCKS)
2115 u16 count = data[2];
2116 for(u16 i=0; i<count; i++)
2118 if((s16)datasize < 2+1+(i+1)*6)
2119 throw con::InvalidIncomingDataException
2120 ("GOTBLOCKS length is too short");
2121 v3s16 p = readV3S16(&data[2+1+i*6]);
2122 /*dstream<<"Server: GOTBLOCKS ("
2123 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2124 RemoteClient *client = getClient(peer_id);
2125 client->GotBlock(p);
2128 else if(command == TOSERVER_DELETEDBLOCKS)
2141 u16 count = data[2];
2142 for(u16 i=0; i<count; i++)
2144 if((s16)datasize < 2+1+(i+1)*6)
2145 throw con::InvalidIncomingDataException
2146 ("DELETEDBLOCKS length is too short");
2147 v3s16 p = readV3S16(&data[2+1+i*6]);
2148 /*dstream<<"Server: DELETEDBLOCKS ("
2149 <<p.X<<","<<p.Y<<","<<p.Z<<")"<<std::endl;*/
2150 RemoteClient *client = getClient(peer_id);
2151 client->SetBlockNotSent(p);
2154 else if(command == TOSERVER_CLICK_OBJECT)
2159 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2164 [2] u8 button (0=left, 1=right)
2169 u8 button = readU8(&data[2]);
2171 p.X = readS16(&data[3]);
2172 p.Y = readS16(&data[5]);
2173 p.Z = readS16(&data[7]);
2174 s16 id = readS16(&data[9]);
2175 //u16 item_i = readU16(&data[11]);
2177 MapBlock *block = NULL;
2180 block = m_env.getMap().getBlockNoCreate(p);
2182 catch(InvalidPositionException &e)
2184 derr_server<<"CLICK_OBJECT block not found"<<std::endl;
2188 MapBlockObject *obj = block->getObject(id);
2192 derr_server<<"CLICK_OBJECT object not found"<<std::endl;
2196 //TODO: Check that object is reasonably close
2201 InventoryList *ilist = player->inventory.getList("main");
2202 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
2205 // Skip if inventory has no free space
2206 if(ilist->getUsedSlots() == ilist->getSize())
2208 dout_server<<"Player inventory has no free space"<<std::endl;
2213 Create the inventory item
2215 InventoryItem *item = NULL;
2216 // If it is an item-object, take the item from it
2217 if(obj->getTypeId() == MAPBLOCKOBJECT_TYPE_ITEM)
2219 item = ((ItemObject*)obj)->createInventoryItem();
2221 // Else create an item of the object
2224 item = new MapBlockObjectItem
2225 (obj->getInventoryString());
2228 // Add to inventory and send inventory
2229 ilist->addItem(item);
2230 UpdateCrafting(player->peer_id);
2231 SendInventory(player->peer_id);
2234 // Remove from block
2235 block->removeObject(id);
2238 else if(command == TOSERVER_CLICK_ACTIVEOBJECT)
2243 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2249 [2] u8 button (0=left, 1=right)
2253 u8 button = readU8(&data[2]);
2254 u16 id = readS16(&data[3]);
2255 u16 item_i = readU16(&data[11]);
2257 ServerActiveObject *obj = m_env.getActiveObject(id);
2261 derr_server<<"Server: CLICK_ACTIVEOBJECT: object not found"
2266 //TODO: Check that object is reasonably close
2268 // Left click, pick object up (usually)
2271 InventoryList *ilist = player->inventory.getList("main");
2272 if(g_settings.getBool("creative_mode") == false && ilist != NULL)
2275 // Skip if inventory has no free space
2276 if(ilist->getUsedSlots() == ilist->getSize())
2278 dout_server<<"Player inventory has no free space"<<std::endl;
2282 // Skip if object has been removed
2287 Create the inventory item
2289 InventoryItem *item = obj->createPickedUpItem();
2293 // Add to inventory and send inventory
2294 ilist->addItem(item);
2295 UpdateCrafting(player->peer_id);
2296 SendInventory(player->peer_id);
2298 // Remove object from environment
2299 obj->m_removed = true;
2304 Item cannot be picked up. Punch it instead.
2307 ToolItem *titem = NULL;
2308 std::string toolname = "";
2310 InventoryList *mlist = player->inventory.getList("main");
2313 InventoryItem *item = mlist->getItem(item_i);
2314 if(item && (std::string)item->getName() == "ToolItem")
2316 titem = (ToolItem*)item;
2317 toolname = titem->getToolName();
2321 u16 wear = obj->punch(toolname);
2325 bool weared_out = titem->addWear(wear);
2327 mlist->deleteItem(item_i);
2328 SendInventory(player->peer_id);
2334 else if(command == TOSERVER_GROUND_ACTION)
2342 [3] v3s16 nodepos_undersurface
2343 [9] v3s16 nodepos_abovesurface
2348 2: stop digging (all parameters ignored)
2349 3: digging completed
2351 u8 action = readU8(&data[2]);
2353 p_under.X = readS16(&data[3]);
2354 p_under.Y = readS16(&data[5]);
2355 p_under.Z = readS16(&data[7]);
2357 p_over.X = readS16(&data[9]);
2358 p_over.Y = readS16(&data[11]);
2359 p_over.Z = readS16(&data[13]);
2360 u16 item_i = readU16(&data[15]);
2362 //TODO: Check that target is reasonably close
2370 NOTE: This can be used in the future to check if
2371 somebody is cheating, by checking the timing.
2378 else if(action == 2)
2381 RemoteClient *client = getClient(peer->id);
2382 JMutexAutoLock digmutex(client->m_dig_mutex);
2383 client->m_dig_tool_item = -1;
2388 3: Digging completed
2390 else if(action == 3)
2392 // Mandatory parameter; actually used for nothing
2393 core::map<v3s16, MapBlock*> modified_blocks;
2395 u8 material = CONTENT_IGNORE;
2396 u8 mineral = MINERAL_NONE;
2398 bool cannot_remove_node = false;
2402 MapNode n = m_env.getMap().getNode(p_under);
2404 mineral = n.getMineral();
2405 // Get material at position
2407 // If not yet cancelled
2408 if(cannot_remove_node == false)
2410 // If it's not diggable, do nothing
2411 if(content_diggable(material) == false)
2413 derr_server<<"Server: Not finishing digging: "
2414 <<"Node not diggable"
2416 cannot_remove_node = true;
2419 // If not yet cancelled
2420 if(cannot_remove_node == false)
2422 // Get node metadata
2423 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p_under);
2424 if(meta && meta->nodeRemovalDisabled() == true)
2426 derr_server<<"Server: Not finishing digging: "
2427 <<"Node metadata disables removal"
2429 cannot_remove_node = true;
2433 catch(InvalidPositionException &e)
2435 derr_server<<"Server: Not finishing digging: Node not found."
2436 <<" Adding block to emerge queue."
2438 m_emerge_queue.addBlock(peer_id,
2439 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2440 cannot_remove_node = true;
2443 // Make sure the player is allowed to do it
2444 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2446 dstream<<"Player "<<player->getName()<<" cannot remove node"
2447 <<" because privileges are "<<getPlayerPrivs(player)
2449 cannot_remove_node = true;
2453 If node can't be removed, set block to be re-sent to
2456 if(cannot_remove_node)
2458 derr_server<<"Server: Not finishing digging."<<std::endl;
2460 // Client probably has wrong data.
2461 // Set block not sent, so that client will get
2463 dstream<<"Client "<<peer_id<<" tried to dig "
2464 <<"node; but node cannot be removed."
2465 <<" setting MapBlock not sent."<<std::endl;
2466 RemoteClient *client = getClient(peer_id);
2467 v3s16 blockpos = getNodeBlockPos(p_under);
2468 client->SetBlockNotSent(blockpos);
2474 Send the removal to all other clients.
2475 - If other player is close, send REMOVENODE
2476 - Otherwise set blocks not sent
2478 core::list<u16> far_players;
2479 sendRemoveNode(p_under, peer_id, &far_players, 100);
2482 Update and send inventory
2485 if(g_settings.getBool("creative_mode") == false)
2490 InventoryList *mlist = player->inventory.getList("main");
2493 InventoryItem *item = mlist->getItem(item_i);
2494 if(item && (std::string)item->getName() == "ToolItem")
2496 ToolItem *titem = (ToolItem*)item;
2497 std::string toolname = titem->getToolName();
2499 // Get digging properties for material and tool
2500 DiggingProperties prop =
2501 getDiggingProperties(material, toolname);
2503 if(prop.diggable == false)
2505 derr_server<<"Server: WARNING: Player digged"
2506 <<" with impossible material + tool"
2507 <<" combination"<<std::endl;
2510 bool weared_out = titem->addWear(prop.wear);
2514 mlist->deleteItem(item_i);
2520 Add dug item to inventory
2523 InventoryItem *item = NULL;
2525 if(mineral != MINERAL_NONE)
2526 item = getDiggedMineralItem(mineral);
2531 std::string &dug_s = content_features(material).dug_item;
2534 std::istringstream is(dug_s, std::ios::binary);
2535 item = InventoryItem::deSerialize(is);
2541 // Add a item to inventory
2542 player->inventory.addItem("main", item);
2545 UpdateCrafting(player->peer_id);
2546 SendInventory(player->peer_id);
2552 (this takes some time so it is done after the quick stuff)
2554 m_ignore_map_edit_events = true;
2555 m_env.getMap().removeNodeAndUpdate(p_under, modified_blocks);
2556 m_ignore_map_edit_events = false;
2559 Set blocks not sent to far players
2561 for(core::list<u16>::Iterator
2562 i = far_players.begin();
2563 i != far_players.end(); i++)
2566 RemoteClient *client = getClient(peer_id);
2569 client->SetBlocksNotSent(modified_blocks);
2576 else if(action == 1)
2579 InventoryList *ilist = player->inventory.getList("main");
2584 InventoryItem *item = ilist->getItem(item_i);
2586 // If there is no item, it is not possible to add it anywhere
2591 Handle material items
2593 if(std::string("MaterialItem") == item->getName())
2596 // Don't add a node if this is not a free space
2597 MapNode n2 = m_env.getMap().getNode(p_over);
2598 bool no_enough_privs =
2599 ((getPlayerPrivs(player) & PRIV_BUILD)==0);
2601 dstream<<"Player "<<player->getName()<<" cannot add node"
2602 <<" because privileges are "<<getPlayerPrivs(player)
2605 if(content_buildable_to(n2.d) == false
2608 // Client probably has wrong data.
2609 // Set block not sent, so that client will get
2611 dstream<<"Client "<<peer_id<<" tried to place"
2612 <<" node in invalid position; setting"
2613 <<" MapBlock not sent."<<std::endl;
2614 RemoteClient *client = getClient(peer_id);
2615 v3s16 blockpos = getNodeBlockPos(p_over);
2616 client->SetBlockNotSent(blockpos);
2620 catch(InvalidPositionException &e)
2622 derr_server<<"Server: Ignoring ADDNODE: Node not found"
2623 <<" Adding block to emerge queue."
2625 m_emerge_queue.addBlock(peer_id,
2626 getNodeBlockPos(p_over), BLOCK_EMERGE_FLAG_FROMDISK);
2630 // Reset build time counter
2631 getClient(peer->id)->m_time_from_building = 0.0;
2634 MaterialItem *mitem = (MaterialItem*)item;
2636 n.d = mitem->getMaterial();
2637 if(content_features(n.d).wall_mounted)
2638 n.dir = packDir(p_under - p_over);
2643 core::list<u16> far_players;
2644 sendAddNode(p_over, n, 0, &far_players, 100);
2649 InventoryList *ilist = player->inventory.getList("main");
2650 if(g_settings.getBool("creative_mode") == false && ilist)
2652 // Remove from inventory and send inventory
2653 if(mitem->getCount() == 1)
2654 ilist->deleteItem(item_i);
2658 UpdateCrafting(peer_id);
2659 SendInventory(peer_id);
2665 This takes some time so it is done after the quick stuff
2667 core::map<v3s16, MapBlock*> modified_blocks;
2668 m_ignore_map_edit_events = true;
2669 m_env.getMap().addNodeAndUpdate(p_over, n, modified_blocks);
2670 m_ignore_map_edit_events = false;
2673 Set blocks not sent to far players
2675 for(core::list<u16>::Iterator
2676 i = far_players.begin();
2677 i != far_players.end(); i++)
2680 RemoteClient *client = getClient(peer_id);
2683 client->SetBlocksNotSent(modified_blocks);
2687 Calculate special events
2690 /*if(n.d == CONTENT_MESE)
2693 for(s16 z=-1; z<=1; z++)
2694 for(s16 y=-1; y<=1; y++)
2695 for(s16 x=-1; x<=1; x++)
2702 Place other item (not a block)
2706 v3s16 blockpos = getNodeBlockPos(p_over);
2709 Check that the block is loaded so that the item
2710 can properly be added to the static list too
2712 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2715 derr_server<<"Error while placing object: "
2716 "block not found"<<std::endl;
2720 dout_server<<"Placing a miscellaneous item on map"
2723 // Calculate a position for it
2724 v3f pos = intToFloat(p_over, BS);
2726 pos.Y -= BS*0.25; // let it drop a bit
2728 pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2729 pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
2734 ServerActiveObject *obj = item->createSAO(&m_env, 0, pos);
2738 derr_server<<"WARNING: item resulted in NULL object, "
2739 <<"not placing onto map"
2744 // Add the object to the environment
2745 m_env.addActiveObject(obj);
2747 dout_server<<"Placed object"<<std::endl;
2749 if(g_settings.getBool("creative_mode") == false)
2751 // Delete the right amount of items from the slot
2752 u16 dropcount = item->getDropCount();
2754 // Delete item if all gone
2755 if(item->getCount() <= dropcount)
2757 if(item->getCount() < dropcount)
2758 dstream<<"WARNING: Server: dropped more items"
2759 <<" than the slot contains"<<std::endl;
2761 InventoryList *ilist = player->inventory.getList("main");
2763 // Remove from inventory and send inventory
2764 ilist->deleteItem(item_i);
2766 // Else decrement it
2768 item->remove(dropcount);
2771 UpdateCrafting(peer_id);
2772 SendInventory(peer_id);
2780 Catch invalid actions
2784 derr_server<<"WARNING: Server: Invalid action "
2785 <<action<<std::endl;
2789 else if(command == TOSERVER_RELEASE)
2798 dstream<<"TOSERVER_RELEASE ignored"<<std::endl;
2801 else if(command == TOSERVER_SIGNTEXT)
2803 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2812 std::string datastring((char*)&data[2], datasize-2);
2813 std::istringstream is(datastring, std::ios_base::binary);
2816 is.read((char*)buf, 6);
2817 v3s16 blockpos = readV3S16(buf);
2818 is.read((char*)buf, 2);
2819 s16 id = readS16(buf);
2820 is.read((char*)buf, 2);
2821 u16 textlen = readU16(buf);
2823 for(u16 i=0; i<textlen; i++)
2825 is.read((char*)buf, 1);
2826 text += (char)buf[0];
2829 MapBlock *block = NULL;
2832 block = m_env.getMap().getBlockNoCreate(blockpos);
2834 catch(InvalidPositionException &e)
2836 derr_server<<"Error while setting sign text: "
2837 "block not found"<<std::endl;
2841 MapBlockObject *obj = block->getObject(id);
2844 derr_server<<"Error while setting sign text: "
2845 "object not found"<<std::endl;
2849 if(obj->getTypeId() != MAPBLOCKOBJECT_TYPE_SIGN)
2851 derr_server<<"Error while setting sign text: "
2852 "object is not a sign"<<std::endl;
2856 ((SignObject*)obj)->setText(text);
2858 obj->getBlock()->setChangedFlag();
2860 else if(command == TOSERVER_SIGNNODETEXT)
2862 if((getPlayerPrivs(player) & PRIV_BUILD) == 0)
2870 std::string datastring((char*)&data[2], datasize-2);
2871 std::istringstream is(datastring, std::ios_base::binary);
2874 is.read((char*)buf, 6);
2875 v3s16 p = readV3S16(buf);
2876 is.read((char*)buf, 2);
2877 u16 textlen = readU16(buf);
2879 for(u16 i=0; i<textlen; i++)
2881 is.read((char*)buf, 1);
2882 text += (char)buf[0];
2885 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
2888 if(meta->typeId() != CONTENT_SIGN_WALL)
2890 SignNodeMetadata *signmeta = (SignNodeMetadata*)meta;
2891 signmeta->setText(text);
2893 v3s16 blockpos = getNodeBlockPos(p);
2894 MapBlock *block = m_env.getMap().getBlockNoCreateNoEx(blockpos);
2897 block->setChangedFlag();
2900 for(core::map<u16, RemoteClient*>::Iterator
2901 i = m_clients.getIterator();
2902 i.atEnd()==false; i++)
2904 RemoteClient *client = i.getNode()->getValue();
2905 client->SetBlockNotSent(blockpos);
2908 else if(command == TOSERVER_INVENTORY_ACTION)
2910 /*// Ignore inventory changes if in creative mode
2911 if(g_settings.getBool("creative_mode") == true)
2913 dstream<<"TOSERVER_INVENTORY_ACTION: ignoring in creative mode"
2917 // Strip command and create a stream
2918 std::string datastring((char*)&data[2], datasize-2);
2919 dstream<<"TOSERVER_INVENTORY_ACTION: data="<<datastring<<std::endl;
2920 std::istringstream is(datastring, std::ios_base::binary);
2922 InventoryAction *a = InventoryAction::deSerialize(is);
2927 c.current_player = player;
2930 Handle craftresult specially if not in creative mode
2932 bool disable_action = false;
2933 if(a->getType() == IACTION_MOVE
2934 && g_settings.getBool("creative_mode") == false)
2936 IMoveAction *ma = (IMoveAction*)a;
2937 if(ma->to_inv == "current_player" &&
2938 ma->from_inv == "current_player")
2940 InventoryList *rlist = player->inventory.getList("craftresult");
2942 InventoryList *clist = player->inventory.getList("craft");
2944 InventoryList *mlist = player->inventory.getList("main");
2947 Craftresult is no longer preview if something
2950 if(ma->to_list == "craftresult"
2951 && ma->from_list != "craftresult")
2953 // If it currently is a preview, remove
2955 if(player->craftresult_is_preview)
2957 rlist->deleteItem(0);
2959 player->craftresult_is_preview = false;
2962 Crafting takes place if this condition is true.
2964 if(player->craftresult_is_preview &&
2965 ma->from_list == "craftresult")
2967 player->craftresult_is_preview = false;
2968 clist->decrementMaterials(1);
2971 If the craftresult is placed on itself, move it to
2972 main inventory instead of doing the action
2974 if(ma->to_list == "craftresult"
2975 && ma->from_list == "craftresult")
2977 disable_action = true;
2979 InventoryItem *item1 = rlist->changeItem(0, NULL);
2980 mlist->addItem(item1);
2985 if(disable_action == false)
2987 // Feed action to player inventory
2995 UpdateCrafting(player->peer_id);
2996 SendInventory(player->peer_id);
3001 dstream<<"TOSERVER_INVENTORY_ACTION: "
3002 <<"InventoryAction::deSerialize() returned NULL"
3006 else if(command == TOSERVER_CHAT_MESSAGE)
3014 std::string datastring((char*)&data[2], datasize-2);
3015 std::istringstream is(datastring, std::ios_base::binary);
3018 is.read((char*)buf, 2);
3019 u16 len = readU16(buf);
3021 std::wstring message;
3022 for(u16 i=0; i<len; i++)
3024 is.read((char*)buf, 2);
3025 message += (wchar_t)readU16(buf);
3028 // Get player name of this client
3029 std::wstring name = narrow_to_wide(player->getName());
3031 // Line to send to players
3033 // Whether to send to the player that sent the line
3034 bool send_to_sender = false;
3035 // Whether to send to other players
3036 bool send_to_others = false;
3038 // Local player gets all privileges regardless of
3039 // what's set on their account.
3040 u64 privs = getPlayerPrivs(player);
3043 std::wstring commandprefix = L"/#";
3044 if(message.substr(0, commandprefix.size()) == commandprefix)
3046 line += L"Server: ";
3048 message = message.substr(commandprefix.size());
3050 ServerCommandContext *ctx = new ServerCommandContext(
3051 str_split(message, L' '),
3057 line += processServerCommand(ctx);
3058 send_to_sender = ctx->flags & 1;
3059 send_to_others = ctx->flags & 2;
3065 if(privs & PRIV_SHOUT)
3071 send_to_others = true;
3075 line += L"Server: You are not allowed to shout";
3076 send_to_sender = true;
3082 dstream<<"CHAT: "<<wide_to_narrow(line)<<std::endl;
3085 Send the message to clients
3087 for(core::map<u16, RemoteClient*>::Iterator
3088 i = m_clients.getIterator();
3089 i.atEnd() == false; i++)
3091 // Get client and check that it is valid
3092 RemoteClient *client = i.getNode()->getValue();
3093 assert(client->peer_id == i.getNode()->getKey());
3094 if(client->serialization_version == SER_FMT_VER_INVALID)
3098 bool sender_selected = (peer_id == client->peer_id);
3099 if(sender_selected == true && send_to_sender == false)
3101 if(sender_selected == false && send_to_others == false)
3104 SendChatMessage(client->peer_id, line);
3108 else if(command == TOSERVER_DAMAGE)
3110 if(g_settings.getBool("enable_damage"))
3112 std::string datastring((char*)&data[2], datasize-2);
3113 std::istringstream is(datastring, std::ios_base::binary);
3114 u8 damage = readU8(is);
3115 if(player->hp > damage)
3117 player->hp -= damage;
3123 dstream<<"TODO: Server: TOSERVER_HP_DECREMENT: Player dies"
3126 v3f pos = findSpawnPos(m_env.getServerMap());
3127 player->setPosition(pos);
3129 SendMovePlayer(player);
3130 SendPlayerHP(player);
3132 //TODO: Throw items around
3136 SendPlayerHP(player);
3138 else if(command == TOSERVER_PASSWORD)
3141 [0] u16 TOSERVER_PASSWORD
3142 [2] u8[28] old password
3143 [30] u8[28] new password
3146 if(datasize != 2+PASSWORD_SIZE*2)
3148 /*char password[PASSWORD_SIZE];
3149 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3150 password[i] = data[2+i];
3151 password[PASSWORD_SIZE-1] = 0;*/
3153 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3161 for(u32 i=0; i<PASSWORD_SIZE-1; i++)
3163 char c = data[2+PASSWORD_SIZE+i];
3169 std::string playername = player->getName();
3171 if(m_authmanager.exists(playername) == false)
3173 dstream<<"Server: playername not found in authmanager"<<std::endl;
3174 // Wrong old password supplied!!
3175 SendChatMessage(peer_id, L"playername not found in authmanager");
3179 std::string checkpwd = m_authmanager.getPassword(playername);
3181 if(oldpwd != checkpwd)
3183 dstream<<"Server: invalid old password"<<std::endl;
3184 // Wrong old password supplied!!
3185 SendChatMessage(peer_id, L"Invalid old password supplied. Password NOT changed.");
3189 m_authmanager.setPassword(playername, newpwd);
3191 dstream<<"Server: password change successful for "<<playername
3193 SendChatMessage(peer_id, L"Password change successful");
3197 derr_server<<"WARNING: Server::ProcessData(): Ignoring "
3198 "unknown command "<<command<<std::endl;
3202 catch(SendFailedException &e)
3204 derr_server<<"Server::ProcessData(): SendFailedException: "
3210 void Server::onMapEditEvent(MapEditEvent *event)
3212 dstream<<"Server::onMapEditEvent()"<<std::endl;
3213 if(m_ignore_map_edit_events)
3215 MapEditEvent *e = event->clone();
3216 m_unsent_map_edit_queue.push_back(e);
3219 Inventory* Server::getInventory(InventoryContext *c, std::string id)
3221 if(id == "current_player")
3223 assert(c->current_player);
3224 return &(c->current_player->inventory);
3228 std::string id0 = fn.next(":");
3230 if(id0 == "nodemeta")
3233 p.X = stoi(fn.next(","));
3234 p.Y = stoi(fn.next(","));
3235 p.Z = stoi(fn.next(","));
3236 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3238 return meta->getInventory();
3239 dstream<<"nodemeta at ("<<p.X<<","<<p.Y<<","<<p.Z<<"): "
3240 <<"no metadata found"<<std::endl;
3244 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3247 void Server::inventoryModified(InventoryContext *c, std::string id)
3249 if(id == "current_player")
3251 assert(c->current_player);
3253 UpdateCrafting(c->current_player->peer_id);
3254 SendInventory(c->current_player->peer_id);
3259 std::string id0 = fn.next(":");
3261 if(id0 == "nodemeta")
3264 p.X = stoi(fn.next(","));
3265 p.Y = stoi(fn.next(","));
3266 p.Z = stoi(fn.next(","));
3267 v3s16 blockpos = getNodeBlockPos(p);
3269 NodeMetadata *meta = m_env.getMap().getNodeMetadata(p);
3271 meta->inventoryModified();
3273 for(core::map<u16, RemoteClient*>::Iterator
3274 i = m_clients.getIterator();
3275 i.atEnd()==false; i++)
3277 RemoteClient *client = i.getNode()->getValue();
3278 client->SetBlockNotSent(blockpos);
3284 dstream<<__FUNCTION_NAME<<": unknown id "<<id<<std::endl;
3287 core::list<PlayerInfo> Server::getPlayerInfo()
3289 DSTACK(__FUNCTION_NAME);
3290 JMutexAutoLock envlock(m_env_mutex);
3291 JMutexAutoLock conlock(m_con_mutex);
3293 core::list<PlayerInfo> list;
3295 core::list<Player*> players = m_env.getPlayers();
3297 core::list<Player*>::Iterator i;
3298 for(i = players.begin();
3299 i != players.end(); i++)
3303 Player *player = *i;
3306 con::Peer *peer = m_con.GetPeer(player->peer_id);
3307 // Copy info from peer to info struct
3309 info.address = peer->address;
3310 info.avg_rtt = peer->avg_rtt;
3312 catch(con::PeerNotFoundException &e)
3314 // Set dummy peer info
3316 info.address = Address(0,0,0,0,0);
3320 snprintf(info.name, PLAYERNAME_SIZE, "%s", player->getName());
3321 info.position = player->getPosition();
3323 list.push_back(info);
3330 void Server::peerAdded(con::Peer *peer)
3332 DSTACK(__FUNCTION_NAME);
3333 dout_server<<"Server::peerAdded(): peer->id="
3334 <<peer->id<<std::endl;
3337 c.type = PEER_ADDED;
3338 c.peer_id = peer->id;
3340 m_peer_change_queue.push_back(c);
3343 void Server::deletingPeer(con::Peer *peer, bool timeout)
3345 DSTACK(__FUNCTION_NAME);
3346 dout_server<<"Server::deletingPeer(): peer->id="
3347 <<peer->id<<", timeout="<<timeout<<std::endl;
3350 c.type = PEER_REMOVED;
3351 c.peer_id = peer->id;
3352 c.timeout = timeout;
3353 m_peer_change_queue.push_back(c);
3360 void Server::SendHP(con::Connection &con, u16 peer_id, u8 hp)
3362 DSTACK(__FUNCTION_NAME);
3363 std::ostringstream os(std::ios_base::binary);
3365 writeU16(os, TOCLIENT_HP);
3369 std::string s = os.str();
3370 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3372 con.Send(peer_id, 0, data, true);
3375 void Server::SendAccessDenied(con::Connection &con, u16 peer_id,
3376 const std::wstring &reason)
3378 DSTACK(__FUNCTION_NAME);
3379 std::ostringstream os(std::ios_base::binary);
3381 writeU16(os, TOCLIENT_ACCESS_DENIED);
3382 os<<serializeWideString(reason);
3385 std::string s = os.str();
3386 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3388 con.Send(peer_id, 0, data, true);
3392 Non-static send methods
3395 void Server::SendObjectData(float dtime)
3397 DSTACK(__FUNCTION_NAME);
3399 core::map<v3s16, bool> stepped_blocks;
3401 for(core::map<u16, RemoteClient*>::Iterator
3402 i = m_clients.getIterator();
3403 i.atEnd() == false; i++)
3405 u16 peer_id = i.getNode()->getKey();
3406 RemoteClient *client = i.getNode()->getValue();
3407 assert(client->peer_id == peer_id);
3409 if(client->serialization_version == SER_FMT_VER_INVALID)
3412 client->SendObjectData(this, dtime, stepped_blocks);
3416 void Server::SendPlayerInfos()
3418 DSTACK(__FUNCTION_NAME);
3420 //JMutexAutoLock envlock(m_env_mutex);
3422 // Get connected players
3423 core::list<Player*> players = m_env.getPlayers(true);
3425 u32 player_count = players.getSize();
3426 u32 datasize = 2+(2+PLAYERNAME_SIZE)*player_count;
3428 SharedBuffer<u8> data(datasize);
3429 writeU16(&data[0], TOCLIENT_PLAYERINFO);
3432 core::list<Player*>::Iterator i;
3433 for(i = players.begin();
3434 i != players.end(); i++)
3436 Player *player = *i;
3438 /*dstream<<"Server sending player info for player with "
3439 "peer_id="<<player->peer_id<<std::endl;*/
3441 writeU16(&data[start], player->peer_id);
3442 memset((char*)&data[start+2], 0, PLAYERNAME_SIZE);
3443 snprintf((char*)&data[start+2], PLAYERNAME_SIZE, "%s", player->getName());
3444 start += 2+PLAYERNAME_SIZE;
3447 //JMutexAutoLock conlock(m_con_mutex);
3450 m_con.SendToAll(0, data, true);
3453 void Server::SendInventory(u16 peer_id)
3455 DSTACK(__FUNCTION_NAME);
3457 Player* player = m_env.getPlayer(peer_id);
3464 std::ostringstream os;
3465 //os.imbue(std::locale("C"));
3467 player->inventory.serialize(os);
3469 std::string s = os.str();
3471 SharedBuffer<u8> data(s.size()+2);
3472 writeU16(&data[0], TOCLIENT_INVENTORY);
3473 memcpy(&data[2], s.c_str(), s.size());
3476 m_con.Send(peer_id, 0, data, true);
3479 void Server::SendChatMessage(u16 peer_id, const std::wstring &message)
3481 DSTACK(__FUNCTION_NAME);
3483 std::ostringstream os(std::ios_base::binary);
3487 writeU16(buf, TOCLIENT_CHAT_MESSAGE);
3488 os.write((char*)buf, 2);
3491 writeU16(buf, message.size());
3492 os.write((char*)buf, 2);
3495 for(u32 i=0; i<message.size(); i++)
3499 os.write((char*)buf, 2);
3503 std::string s = os.str();
3504 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3506 m_con.Send(peer_id, 0, data, true);
3509 void Server::BroadcastChatMessage(const std::wstring &message)
3511 for(core::map<u16, RemoteClient*>::Iterator
3512 i = m_clients.getIterator();
3513 i.atEnd() == false; i++)
3515 // Get client and check that it is valid
3516 RemoteClient *client = i.getNode()->getValue();
3517 assert(client->peer_id == i.getNode()->getKey());
3518 if(client->serialization_version == SER_FMT_VER_INVALID)
3521 SendChatMessage(client->peer_id, message);
3525 void Server::SendPlayerHP(Player *player)
3527 SendHP(m_con, player->peer_id, player->hp);
3530 void Server::SendMovePlayer(Player *player)
3532 DSTACK(__FUNCTION_NAME);
3533 std::ostringstream os(std::ios_base::binary);
3535 writeU16(os, TOCLIENT_MOVE_PLAYER);
3536 writeV3F1000(os, player->getPosition());
3537 writeF1000(os, player->getPitch());
3538 writeF1000(os, player->getYaw());
3541 v3f pos = player->getPosition();
3542 f32 pitch = player->getPitch();
3543 f32 yaw = player->getYaw();
3544 dstream<<"Server sending TOCLIENT_MOVE_PLAYER"
3545 <<" pos=("<<pos.X<<","<<pos.Y<<","<<pos.Z<<")"
3552 std::string s = os.str();
3553 SharedBuffer<u8> data((u8*)s.c_str(), s.size());
3555 m_con.Send(player->peer_id, 0, data, true);
3558 void Server::sendRemoveNode(v3s16 p, u16 ignore_id,
3559 core::list<u16> *far_players, float far_d_nodes)
3561 float maxd = far_d_nodes*BS;
3562 v3f p_f = intToFloat(p, BS);
3566 SharedBuffer<u8> reply(replysize);
3567 writeU16(&reply[0], TOCLIENT_REMOVENODE);
3568 writeS16(&reply[2], p.X);
3569 writeS16(&reply[4], p.Y);
3570 writeS16(&reply[6], p.Z);
3572 for(core::map<u16, RemoteClient*>::Iterator
3573 i = m_clients.getIterator();
3574 i.atEnd() == false; i++)
3576 // Get client and check that it is valid
3577 RemoteClient *client = i.getNode()->getValue();
3578 assert(client->peer_id == i.getNode()->getKey());
3579 if(client->serialization_version == SER_FMT_VER_INVALID)
3582 // Don't send if it's the same one
3583 if(client->peer_id == ignore_id)
3589 Player *player = m_env.getPlayer(client->peer_id);
3592 // If player is far away, only set modified blocks not sent
3593 v3f player_pos = player->getPosition();
3594 if(player_pos.getDistanceFrom(p_f) > maxd)
3596 far_players->push_back(client->peer_id);
3603 m_con.Send(client->peer_id, 0, reply, true);
3607 void Server::sendAddNode(v3s16 p, MapNode n, u16 ignore_id,
3608 core::list<u16> *far_players, float far_d_nodes)
3610 float maxd = far_d_nodes*BS;
3611 v3f p_f = intToFloat(p, BS);
3613 for(core::map<u16, RemoteClient*>::Iterator
3614 i = m_clients.getIterator();
3615 i.atEnd() == false; i++)
3617 // Get client and check that it is valid
3618 RemoteClient *client = i.getNode()->getValue();
3619 assert(client->peer_id == i.getNode()->getKey());
3620 if(client->serialization_version == SER_FMT_VER_INVALID)
3623 // Don't send if it's the same one
3624 if(client->peer_id == ignore_id)
3630 Player *player = m_env.getPlayer(client->peer_id);
3633 // If player is far away, only set modified blocks not sent
3634 v3f player_pos = player->getPosition();
3635 if(player_pos.getDistanceFrom(p_f) > maxd)
3637 far_players->push_back(client->peer_id);
3644 u32 replysize = 8 + MapNode::serializedLength(client->serialization_version);
3645 SharedBuffer<u8> reply(replysize);
3646 writeU16(&reply[0], TOCLIENT_ADDNODE);
3647 writeS16(&reply[2], p.X);
3648 writeS16(&reply[4], p.Y);
3649 writeS16(&reply[6], p.Z);
3650 n.serialize(&reply[8], client->serialization_version);
3653 m_con.Send(client->peer_id, 0, reply, true);
3657 void Server::SendBlockNoLock(u16 peer_id, MapBlock *block, u8 ver)
3659 DSTACK(__FUNCTION_NAME);
3661 v3s16 p = block->getPos();
3665 bool completely_air = true;
3666 for(s16 z0=0; z0<MAP_BLOCKSIZE; z0++)
3667 for(s16 x0=0; x0<MAP_BLOCKSIZE; x0++)
3668 for(s16 y0=0; y0<MAP_BLOCKSIZE; y0++)
3670 if(block->getNodeNoEx(v3s16(x0,y0,z0)).d != CONTENT_AIR)
3672 completely_air = false;
3673 x0 = y0 = z0 = MAP_BLOCKSIZE; // Break out
3678 dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<"): ";
3680 dstream<<"[completely air] ";
3685 Create a packet with the block in the right format
3688 std::ostringstream os(std::ios_base::binary);
3689 block->serialize(os, ver);
3690 std::string s = os.str();
3691 SharedBuffer<u8> blockdata((u8*)s.c_str(), s.size());
3693 u32 replysize = 8 + blockdata.getSize();
3694 SharedBuffer<u8> reply(replysize);
3695 writeU16(&reply[0], TOCLIENT_BLOCKDATA);
3696 writeS16(&reply[2], p.X);
3697 writeS16(&reply[4], p.Y);
3698 writeS16(&reply[6], p.Z);
3699 memcpy(&reply[8], *blockdata, blockdata.getSize());
3701 /*dstream<<"Server: Sending block ("<<p.X<<","<<p.Y<<","<<p.Z<<")"
3702 <<": \tpacket size: "<<replysize<<std::endl;*/
3707 m_con.Send(peer_id, 1, reply, true);
3710 void Server::SendBlocks(float dtime)
3712 DSTACK(__FUNCTION_NAME);
3714 JMutexAutoLock envlock(m_env_mutex);
3715 JMutexAutoLock conlock(m_con_mutex);
3717 //TimeTaker timer("Server::SendBlocks");
3719 core::array<PrioritySortedBlockTransfer> queue;
3721 s32 total_sending = 0;
3724 ScopeProfiler sp(&g_profiler, "Server: selecting blocks for sending");
3726 for(core::map<u16, RemoteClient*>::Iterator
3727 i = m_clients.getIterator();
3728 i.atEnd() == false; i++)
3730 RemoteClient *client = i.getNode()->getValue();
3731 assert(client->peer_id == i.getNode()->getKey());
3733 total_sending += client->SendingCount();
3735 if(client->serialization_version == SER_FMT_VER_INVALID)
3738 client->GetNextBlocks(this, dtime, queue);
3743 // Lowest priority number comes first.
3744 // Lowest is most important.
3747 for(u32 i=0; i<queue.size(); i++)
3749 //TODO: Calculate limit dynamically
3750 if(total_sending >= g_settings.getS32
3751 ("max_simultaneous_block_sends_server_total"))
3754 PrioritySortedBlockTransfer q = queue[i];
3756 MapBlock *block = NULL;
3759 block = m_env.getMap().getBlockNoCreate(q.pos);
3761 catch(InvalidPositionException &e)
3766 RemoteClient *client = getClient(q.peer_id);
3768 SendBlockNoLock(q.peer_id, block, client->serialization_version);
3770 client->SentBlock(q.pos);
3780 void Server::UpdateCrafting(u16 peer_id)
3782 DSTACK(__FUNCTION_NAME);
3784 Player* player = m_env.getPlayer(peer_id);
3788 Calculate crafting stuff
3790 if(g_settings.getBool("creative_mode") == false)
3792 InventoryList *clist = player->inventory.getList("craft");
3793 InventoryList *rlist = player->inventory.getList("craftresult");
3795 if(rlist->getUsedSlots() == 0)
3796 player->craftresult_is_preview = true;
3798 if(rlist && player->craftresult_is_preview)
3800 rlist->clearItems();
3802 if(clist && rlist && player->craftresult_is_preview)
3804 InventoryItem *items[9];
3805 for(u16 i=0; i<9; i++)
3807 items[i] = clist->getItem(i);
3816 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_TREE);
3817 if(checkItemCombination(items, specs))
3819 rlist->addItem(new MaterialItem(CONTENT_WOOD, 4));
3828 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3829 if(checkItemCombination(items, specs))
3831 rlist->addItem(new CraftItem("Stick", 4));
3840 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
3841 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3842 specs[5] = ItemSpec(ITEM_CRAFT, "Stick");
3843 specs[6] = ItemSpec(ITEM_CRAFT, "Stick");
3844 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3845 specs[8] = ItemSpec(ITEM_CRAFT, "Stick");
3846 if(checkItemCombination(items, specs))
3848 rlist->addItem(new MaterialItem(CONTENT_FENCE, 2));
3857 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3858 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3859 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3860 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3861 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3862 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3863 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3864 if(checkItemCombination(items, specs))
3866 //rlist->addItem(new MapBlockObjectItem("Sign"));
3867 rlist->addItem(new MaterialItem(CONTENT_SIGN_WALL, 1));
3876 specs[0] = ItemSpec(ITEM_CRAFT, "lump_of_coal");
3877 specs[3] = ItemSpec(ITEM_CRAFT, "Stick");
3878 if(checkItemCombination(items, specs))
3880 rlist->addItem(new MaterialItem(CONTENT_TORCH, 4));
3889 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3890 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3891 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3892 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3893 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3894 if(checkItemCombination(items, specs))
3896 rlist->addItem(new ToolItem("WPick", 0));
3905 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3906 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3907 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3908 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3909 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3910 if(checkItemCombination(items, specs))
3912 rlist->addItem(new ToolItem("STPick", 0));
3921 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3922 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3923 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3924 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3925 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3926 if(checkItemCombination(items, specs))
3928 rlist->addItem(new ToolItem("SteelPick", 0));
3937 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3938 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3939 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_MESE);
3940 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3941 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3942 if(checkItemCombination(items, specs))
3944 rlist->addItem(new ToolItem("MesePick", 0));
3953 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3954 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3955 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3956 if(checkItemCombination(items, specs))
3958 rlist->addItem(new ToolItem("WShovel", 0));
3967 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
3968 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3969 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3970 if(checkItemCombination(items, specs))
3972 rlist->addItem(new ToolItem("STShovel", 0));
3981 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
3982 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3983 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
3984 if(checkItemCombination(items, specs))
3986 rlist->addItem(new ToolItem("SteelShovel", 0));
3995 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3996 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3997 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
3998 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
3999 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
4000 if(checkItemCombination(items, specs))
4002 rlist->addItem(new ToolItem("WAxe", 0));
4011 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4012 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4013 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4014 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
4015 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
4016 if(checkItemCombination(items, specs))
4018 rlist->addItem(new ToolItem("STAxe", 0));
4027 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4028 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4029 specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4030 specs[4] = ItemSpec(ITEM_CRAFT, "Stick");
4031 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
4032 if(checkItemCombination(items, specs))
4034 rlist->addItem(new ToolItem("SteelAxe", 0));
4043 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4044 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4045 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
4046 if(checkItemCombination(items, specs))
4048 rlist->addItem(new ToolItem("WSword", 0));
4057 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4058 specs[4] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4059 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
4060 if(checkItemCombination(items, specs))
4062 rlist->addItem(new ToolItem("STSword", 0));
4071 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4072 specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4073 specs[7] = ItemSpec(ITEM_CRAFT, "Stick");
4074 if(checkItemCombination(items, specs))
4076 rlist->addItem(new ToolItem("SteelSword", 0));
4085 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4086 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4087 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4088 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4089 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4090 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4091 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4092 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_WOOD);
4093 if(checkItemCombination(items, specs))
4095 rlist->addItem(new MaterialItem(CONTENT_CHEST, 1));
4104 specs[0] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4105 specs[1] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4106 specs[2] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4107 specs[3] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4108 specs[5] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4109 specs[6] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4110 specs[7] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4111 specs[8] = ItemSpec(ITEM_MATERIAL, CONTENT_COBBLE);
4112 if(checkItemCombination(items, specs))
4114 rlist->addItem(new MaterialItem(CONTENT_FURNACE, 1));
4123 specs[0] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4124 specs[1] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4125 specs[2] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4126 specs[3] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4127 specs[4] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4128 specs[5] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4129 specs[6] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4130 specs[7] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4131 specs[8] = ItemSpec(ITEM_CRAFT, "steel_ingot");
4132 if(checkItemCombination(items, specs))
4134 rlist->addItem(new MaterialItem(CONTENT_STEEL, 1));
4140 } // if creative_mode == false
4143 RemoteClient* Server::getClient(u16 peer_id)
4145 DSTACK(__FUNCTION_NAME);
4146 //JMutexAutoLock lock(m_con_mutex);
4147 core::map<u16, RemoteClient*>::Node *n;
4148 n = m_clients.find(peer_id);
4149 // A client should exist for all peers
4151 return n->getValue();
4154 std::wstring Server::getStatusString()
4156 std::wostringstream os(std::ios_base::binary);
4159 os<<L"version="<<narrow_to_wide(VERSION_STRING);
4161 os<<L", uptime="<<m_uptime.get();
4162 // Information about clients
4164 for(core::map<u16, RemoteClient*>::Iterator
4165 i = m_clients.getIterator();
4166 i.atEnd() == false; i++)
4168 // Get client and check that it is valid
4169 RemoteClient *client = i.getNode()->getValue();
4170 assert(client->peer_id == i.getNode()->getKey());
4171 if(client->serialization_version == SER_FMT_VER_INVALID)
4174 Player *player = m_env.getPlayer(client->peer_id);
4175 // Get name of player
4176 std::wstring name = L"unknown";
4178 name = narrow_to_wide(player->getName());
4179 // Add name to information string
4183 if(((ServerMap*)(&m_env.getMap()))->isSavingEnabled() == false)
4184 os<<" WARNING: Map saving is disabled."<<std::endl;
4189 void setCreativeInventory(Player *player)
4191 player->resetInventory();
4193 // Give some good tools
4195 InventoryItem *item = new ToolItem("MesePick", 0);
4196 void* r = player->inventory.addItem("main", item);
4200 InventoryItem *item = new ToolItem("SteelPick", 0);
4201 void* r = player->inventory.addItem("main", item);
4205 InventoryItem *item = new ToolItem("SteelAxe", 0);
4206 void* r = player->inventory.addItem("main", item);
4210 InventoryItem *item = new ToolItem("SteelShovel", 0);
4211 void* r = player->inventory.addItem("main", item);
4219 // CONTENT_IGNORE-terminated list
4220 u8 material_items[] = {
4231 CONTENT_WATERSOURCE,
4239 u8 *mip = material_items;
4240 for(u16 i=0; i<PLAYER_INVENTORY_SIZE; i++)
4242 if(*mip == CONTENT_IGNORE)
4245 InventoryItem *item = new MaterialItem(*mip, 1);
4246 player->inventory.addItem("main", item);
4252 assert(USEFUL_CONTENT_COUNT <= PLAYER_INVENTORY_SIZE);
4255 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 1);
4256 player->inventory.addItem("main", item);
4259 for(u16 i=0; i<USEFUL_CONTENT_COUNT; i++)
4261 // Skip some materials
4262 if(i == CONTENT_WATER || i == CONTENT_TORCH
4263 || i == CONTENT_COALSTONE)
4266 InventoryItem *item = new MaterialItem(i, 1);
4267 player->inventory.addItem("main", item);
4273 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
4274 void* r = player->inventory.addItem("main", item);
4279 v3f findSpawnPos(ServerMap &map)
4281 //return v3f(50,50,50)*BS;
4284 s16 groundheight = 0;
4286 // Try to find a good place a few times
4287 for(s32 i=0; i<1000; i++)
4290 // We're going to try to throw the player to this position
4291 nodepos = v2s16(-range + (myrand()%(range*2)),
4292 -range + (myrand()%(range*2)));
4293 v2s16 sectorpos = getNodeSectorPos(nodepos);
4294 // Get sector (NOTE: Don't get because it's slow)
4295 //m_env.getMap().emergeSector(sectorpos);
4296 // Get ground height at point (fallbacks to heightmap function)
4297 groundheight = map.findGroundLevel(nodepos);
4298 // Don't go underwater
4299 if(groundheight < WATER_LEVEL)
4301 //dstream<<"-> Underwater"<<std::endl;
4304 // Don't go to high places
4305 if(groundheight > WATER_LEVEL + 4)
4307 //dstream<<"-> Underwater"<<std::endl;
4311 // Found a good place
4312 //dstream<<"Searched through "<<i<<" places."<<std::endl;
4316 // If no suitable place was not found, go above water at least.
4317 if(groundheight < WATER_LEVEL)
4318 groundheight = WATER_LEVEL;
4320 return intToFloat(v3s16(
4327 Player *Server::emergePlayer(const char *name, const char *password, u16 peer_id)
4330 Try to get an existing player
4332 Player *player = m_env.getPlayer(name);
4335 // If player is already connected, cancel
4336 if(player->peer_id != 0)
4338 dstream<<"emergePlayer(): Player already connected"<<std::endl;
4343 player->peer_id = peer_id;
4345 // Reset inventory to creative if in creative mode
4346 if(g_settings.getBool("creative_mode"))
4348 setCreativeInventory(player);
4355 If player with the wanted peer_id already exists, cancel.
4357 if(m_env.getPlayer(peer_id) != NULL)
4359 dstream<<"emergePlayer(): Player with wrong name but same"
4360 " peer_id already exists"<<std::endl;
4368 player = new ServerRemotePlayer();
4369 //player->peer_id = c.peer_id;
4370 //player->peer_id = PEER_ID_INEXISTENT;
4371 player->peer_id = peer_id;
4372 player->updateName(name);
4373 m_authmanager.add(name);
4374 m_authmanager.setPassword(name, password);
4375 m_authmanager.setPrivs(name,
4376 stringToPrivs(g_settings.get("default_privs")));
4382 dstream<<"Server: Finding spawn place for player \""
4383 <<player->getName()<<"\""<<std::endl;
4385 v3f pos = findSpawnPos(m_env.getServerMap());
4387 player->setPosition(pos);
4390 Add player to environment
4393 m_env.addPlayer(player);
4396 Add stuff to inventory
4399 if(g_settings.getBool("creative_mode"))
4401 setCreativeInventory(player);
4403 else if(g_settings.getBool("give_initial_stuff"))
4406 InventoryItem *item = new ToolItem("SteelPick", 0);
4407 void* r = player->inventory.addItem("main", item);
4411 InventoryItem *item = new MaterialItem(CONTENT_TORCH, 99);
4412 void* r = player->inventory.addItem("main", item);
4416 InventoryItem *item = new ToolItem("SteelAxe", 0);
4417 void* r = player->inventory.addItem("main", item);
4421 InventoryItem *item = new ToolItem("SteelShovel", 0);
4422 void* r = player->inventory.addItem("main", item);
4426 InventoryItem *item = new MaterialItem(CONTENT_COBBLE, 99);
4427 void* r = player->inventory.addItem("main", item);
4431 InventoryItem *item = new MaterialItem(CONTENT_MESE, 6);
4432 void* r = player->inventory.addItem("main", item);
4436 InventoryItem *item = new MaterialItem(CONTENT_COALSTONE, 6);
4437 void* r = player->inventory.addItem("main", item);
4441 InventoryItem *item = new MaterialItem(CONTENT_WOOD, 6);
4442 void* r = player->inventory.addItem("main", item);
4446 InventoryItem *item = new CraftItem("Stick", 4);
4447 void* r = player->inventory.addItem("main", item);
4451 InventoryItem *item = new ToolItem("WPick", 32000);
4452 void* r = player->inventory.addItem("main", item);
4456 InventoryItem *item = new ToolItem("STPick", 32000);
4457 void* r = player->inventory.addItem("main", item);
4461 for(u16 i=0; i<4; i++)
4463 InventoryItem *item = new MapBlockObjectItem("Sign Example text");
4464 bool r = player->inventory.addItem("main", item);
4467 /*// Give some other stuff
4469 InventoryItem *item = new MaterialItem(CONTENT_TREE, 999);
4470 bool r = player->inventory.addItem("main", item);
4477 } // create new player
4480 void Server::handlePeerChange(PeerChange &c)
4482 JMutexAutoLock envlock(m_env_mutex);
4483 JMutexAutoLock conlock(m_con_mutex);
4485 if(c.type == PEER_ADDED)
4492 core::map<u16, RemoteClient*>::Node *n;
4493 n = m_clients.find(c.peer_id);
4494 // The client shouldn't already exist
4498 RemoteClient *client = new RemoteClient();
4499 client->peer_id = c.peer_id;
4500 m_clients.insert(client->peer_id, client);
4503 else if(c.type == PEER_REMOVED)
4510 core::map<u16, RemoteClient*>::Node *n;
4511 n = m_clients.find(c.peer_id);
4512 // The client should exist
4516 Mark objects to be not known by the client
4518 RemoteClient *client = n->getValue();
4520 for(core::map<u16, bool>::Iterator
4521 i = client->m_known_objects.getIterator();
4522 i.atEnd()==false; i++)
4525 u16 id = i.getNode()->getKey();
4526 ServerActiveObject* obj = m_env.getActiveObject(id);
4528 if(obj && obj->m_known_by_count > 0)
4529 obj->m_known_by_count--;
4532 // Collect information about leaving in chat
4533 std::wstring message;
4535 std::wstring name = L"unknown";
4536 Player *player = m_env.getPlayer(c.peer_id);
4538 name = narrow_to_wide(player->getName());
4542 message += L" left game";
4544 message += L" (timed out)";
4549 m_env.removePlayer(c.peer_id);
4552 // Set player client disconnected
4554 Player *player = m_env.getPlayer(c.peer_id);
4556 player->peer_id = 0;
4560 delete m_clients[c.peer_id];
4561 m_clients.remove(c.peer_id);
4563 // Send player info to all remaining clients
4566 // Send leave chat message to all remaining clients
4567 BroadcastChatMessage(message);
4576 void Server::handlePeerChanges()
4578 while(m_peer_change_queue.size() > 0)
4580 PeerChange c = m_peer_change_queue.pop_front();
4582 dout_server<<"Server: Handling peer change: "
4583 <<"id="<<c.peer_id<<", timeout="<<c.timeout
4586 handlePeerChange(c);
4590 u64 Server::getPlayerPrivs(Player *player)
4594 std::string playername = player->getName();
4595 // Local player gets all privileges regardless of
4596 // what's set on their account.
4597 if(g_settings.get("name") == playername)
4603 return getPlayerAuthPrivs(playername);
4607 void dedicated_server_loop(Server &server, bool &kill)
4609 DSTACK(__FUNCTION_NAME);
4611 dstream<<DTIME<<std::endl;
4612 dstream<<"========================"<<std::endl;
4613 dstream<<"Running dedicated server"<<std::endl;
4614 dstream<<"========================"<<std::endl;
4617 IntervalLimiter m_profiler_interval;
4621 // This is kind of a hack but can be done like this
4622 // because server.step() is very light
4624 ScopeProfiler sp(&g_profiler, "dedicated server sleep");
4629 if(server.getShutdownRequested() || kill)
4631 dstream<<DTIME<<" dedicated_server_loop(): Quitting."<<std::endl;
4638 float profiler_print_interval =
4639 g_settings.getFloat("profiler_print_interval");
4640 if(profiler_print_interval != 0)
4642 if(m_profiler_interval.step(0.030, profiler_print_interval))
4644 dstream<<"Profiler:"<<std::endl;
4645 g_profiler.print(dstream);
4653 static int counter = 0;
4659 core::list<PlayerInfo> list = server.getPlayerInfo();
4660 core::list<PlayerInfo>::Iterator i;
4661 static u32 sum_old = 0;
4662 u32 sum = PIChecksum(list);
4665 dstream<<DTIME<<"Player info:"<<std::endl;
4666 for(i=list.begin(); i!=list.end(); i++)
4668 i->PrintLine(&dstream);